composer update, composer corelibs update, admin pages update

This commit is contained in:
Clemens Schwaighofer
2023-05-31 16:17:14 +09:00
parent 513b115d57
commit 3d6b461b20
211 changed files with 10013 additions and 1461 deletions

245
composer.lock generated
View File

@@ -225,6 +225,49 @@
],
"time": "2022-02-25T21:32:43+00:00"
},
{
"name": "doctrine/deprecations",
"version": "v1.1.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/deprecations.git",
"reference": "8cffffb2218e01f3b370bf763e00e81697725259"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/deprecations/zipball/8cffffb2218e01f3b370bf763e00e81697725259",
"reference": "8cffffb2218e01f3b370bf763e00e81697725259",
"shasum": ""
},
"require": {
"php": "^7.1|^8.0"
},
"require-dev": {
"doctrine/coding-standard": "^9",
"phpunit/phpunit": "^7.5|^8.5|^9.5",
"psr/log": "^1|^2|^3"
},
"suggest": {
"psr/log": "Allows logging deprecations via PSR-3 logger implementation"
},
"type": "library",
"autoload": {
"psr-4": {
"Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.",
"homepage": "https://www.doctrine-project.org/",
"support": {
"issues": "https://github.com/doctrine/deprecations/issues",
"source": "https://github.com/doctrine/deprecations/tree/v1.1.0"
},
"time": "2023-05-29T18:55:17+00:00"
},
{
"name": "doctrine/instantiator",
"version": "2.0.0",
@@ -446,16 +489,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": {
@@ -491,22 +534,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": {
@@ -547,9 +590,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",
@@ -853,24 +896,27 @@
},
{
"name": "phpdocumentor/type-resolver",
"version": "1.6.2",
"version": "1.7.2",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d"
"reference": "b2fe4d22a5426f38e014855322200b97b5362c0d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/48f445a408c131e38cab1c235aa6d2bb7a0bb20d",
"reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b2fe4d22a5426f38e014855322200b97b5362c0d",
"reference": "b2fe4d22a5426f38e014855322200b97b5362c0d",
"shasum": ""
},
"require": {
"doctrine/deprecations": "^1.0",
"php": "^7.4 || ^8.0",
"phpdocumentor/reflection-common": "^2.0"
"phpdocumentor/reflection-common": "^2.0",
"phpstan/phpdoc-parser": "^1.13"
},
"require-dev": {
"ext-tokenizer": "*",
"phpbench/phpbench": "^1.2",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan": "^1.8",
"phpstan/phpstan-phpunit": "^1.1",
@@ -902,22 +948,68 @@
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
"support": {
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.2"
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.2"
},
"time": "2022-10-14T12:47:21+00:00"
"time": "2023-05-30T18:13:47+00:00"
},
{
"name": "phpstan/phpstan",
"version": "1.10.6",
"name": "phpstan/phpdoc-parser",
"version": "1.21.3",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "50d089a3e0904b0fe7e2cf2d4fd37d427d64235a"
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "b0c366dd2cea79407d635839d25423ba07c55dd6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/50d089a3e0904b0fe7e2cf2d4fd37d427d64235a",
"reference": "50d089a3e0904b0fe7e2cf2d4fd37d427d64235a",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/b0c366dd2cea79407d635839d25423ba07c55dd6",
"reference": "b0c366dd2cea79407d635839d25423ba07c55dd6",
"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",
"phpstan/phpstan-phpunit": "^1.1",
"phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": "^9.5",
"symfony/process": "^5.2"
},
"type": "library",
"autoload": {
"psr-4": {
"PHPStan\\PhpDocParser\\": [
"src/"
]
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.21.3"
},
"time": "2023-05-29T19:31:28+00:00"
},
{
"name": "phpstan/phpstan",
"version": "1.10.15",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "762c4dac4da6f8756eebb80e528c3a47855da9bd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/762c4dac4da6f8756eebb80e528c3a47855da9bd",
"reference": "762c4dac4da6f8756eebb80e528c3a47855da9bd",
"shasum": ""
},
"require": {
@@ -946,8 +1038,11 @@
"static analysis"
],
"support": {
"docs": "https://phpstan.org/user-guide/getting-started",
"forum": "https://github.com/phpstan/phpstan/discussions",
"issues": "https://github.com/phpstan/phpstan/issues",
"source": "https://github.com/phpstan/phpstan/tree/1.10.6"
"security": "https://github.com/phpstan/phpstan/security/policy",
"source": "https://github.com/phpstan/phpstan-src"
},
"funding": [
{
@@ -963,7 +1058,7 @@
"type": "tidelift"
}
],
"time": "2023-03-09T16:55:12+00:00"
"time": "2023-05-09T15:28:01+00:00"
},
{
"name": "phpunit/php-code-coverage",
@@ -1285,16 +1380,16 @@
},
{
"name": "phpunit/phpunit",
"version": "9.6.5",
"version": "9.6.8",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "86e761949019ae83f49240b2f2123fb5ab3b2fc5"
"reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/86e761949019ae83f49240b2f2123fb5ab3b2fc5",
"reference": "86e761949019ae83f49240b2f2123fb5ab3b2fc5",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/17d621b3aff84d0c8b62539e269e87d8d5baa76e",
"reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e",
"shasum": ""
},
"require": {
@@ -1367,7 +1462,8 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.5"
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.8"
},
"funding": [
{
@@ -1383,7 +1479,7 @@
"type": "tidelift"
}
],
"time": "2023-03-09T06:34:10+00:00"
"time": "2023-05-11T05:14:45+00:00"
},
{
"name": "psr/container",
@@ -1854,16 +1950,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": {
@@ -1908,7 +2004,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": [
{
@@ -1916,7 +2012,7 @@
"type": "github"
}
],
"time": "2020-10-26T13:10:38+00:00"
"time": "2023-05-07T05:35:17+00:00"
},
{
"name": "sebastian/environment",
@@ -2520,23 +2616,23 @@
},
{
"name": "symfony/console",
"version": "v6.2.7",
"version": "v6.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "cbad09eb8925b6ad4fb721c7a179344dc4a19d45"
"reference": "8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/cbad09eb8925b6ad4fb721c7a179344dc4a19d45",
"reference": "cbad09eb8925b6ad4fb721c7a179344dc4a19d45",
"url": "https://api.github.com/repos/symfony/console/zipball/8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7",
"reference": "8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7",
"shasum": ""
},
"require": {
"php": ">=8.1",
"symfony/deprecation-contracts": "^2.1|^3",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-mbstring": "~1.0",
"symfony/service-contracts": "^1.1|^2|^3",
"symfony/service-contracts": "^2.5|^3",
"symfony/string": "^5.4|^6.0"
},
"conflict": {
@@ -2558,12 +2654,6 @@
"symfony/process": "^5.4|^6.0",
"symfony/var-dumper": "^5.4|^6.0"
},
"suggest": {
"psr/log": "For using the console logger",
"symfony/event-dispatcher": "",
"symfony/lock": "",
"symfony/process": ""
},
"type": "library",
"autoload": {
"psr-4": {
@@ -2591,12 +2681,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.3.0"
},
"funding": [
{
@@ -2612,20 +2702,20 @@
"type": "tidelift"
}
],
"time": "2023-02-25T17:00:03+00:00"
"time": "2023-05-29T12:49:39+00:00"
},
{
"name": "symfony/deprecation-contracts",
"version": "v3.2.1",
"version": "v3.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e"
"reference": "7c3aff79d10325257a001fcf92d991f24fc967cf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e",
"reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf",
"reference": "7c3aff79d10325257a001fcf92d991f24fc967cf",
"shasum": ""
},
"require": {
@@ -2634,7 +2724,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.3-dev"
"dev-main": "3.4-dev"
},
"thanks": {
"name": "symfony/contracts",
@@ -2663,7 +2753,7 @@
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1"
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0"
},
"funding": [
{
@@ -2679,7 +2769,7 @@
"type": "tidelift"
}
],
"time": "2023-03-01T10:25:55+00:00"
"time": "2023-05-23T14:45:45+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -3096,16 +3186,16 @@
},
{
"name": "symfony/service-contracts",
"version": "v3.2.1",
"version": "v3.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
"reference": "a8c9cedf55f314f3a186041d19537303766df09a"
"reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/a8c9cedf55f314f3a186041d19537303766df09a",
"reference": "a8c9cedf55f314f3a186041d19537303766df09a",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/40da9cc13ec349d9e4966ce18b5fbcd724ab10a4",
"reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4",
"shasum": ""
},
"require": {
@@ -3115,13 +3205,10 @@
"conflict": {
"ext-psr": "<1.1|>=2"
},
"suggest": {
"symfony/service-implementation": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.3-dev"
"dev-main": "3.4-dev"
},
"thanks": {
"name": "symfony/contracts",
@@ -3161,7 +3248,7 @@
"standards"
],
"support": {
"source": "https://github.com/symfony/service-contracts/tree/v3.2.1"
"source": "https://github.com/symfony/service-contracts/tree/v3.3.0"
},
"funding": [
{
@@ -3177,20 +3264,20 @@
"type": "tidelift"
}
],
"time": "2023-03-01T10:32:47+00:00"
"time": "2023-05-23T14:45:45+00:00"
},
{
"name": "symfony/string",
"version": "v6.2.7",
"version": "v6.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "67b8c1eec78296b85dc1c7d9743830160218993d"
"reference": "f2e190ee75ff0f5eced645ec0be5c66fac81f51f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/67b8c1eec78296b85dc1c7d9743830160218993d",
"reference": "67b8c1eec78296b85dc1c7d9743830160218993d",
"url": "https://api.github.com/repos/symfony/string/zipball/f2e190ee75ff0f5eced645ec0be5c66fac81f51f",
"reference": "f2e190ee75ff0f5eced645ec0be5c66fac81f51f",
"shasum": ""
},
"require": {
@@ -3201,13 +3288,13 @@
"symfony/polyfill-mbstring": "~1.0"
},
"conflict": {
"symfony/translation-contracts": "<2.0"
"symfony/translation-contracts": "<2.5"
},
"require-dev": {
"symfony/error-handler": "^5.4|^6.0",
"symfony/http-client": "^5.4|^6.0",
"symfony/intl": "^6.2",
"symfony/translation-contracts": "^2.0|^3.0",
"symfony/translation-contracts": "^2.5|^3.0",
"symfony/var-exporter": "^5.4|^6.0"
},
"type": "library",
@@ -3247,7 +3334,7 @@
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v6.2.7"
"source": "https://github.com/symfony/string/tree/v6.3.0"
},
"funding": [
{
@@ -3263,7 +3350,7 @@
"type": "tidelift"
}
],
"time": "2023-02-24T10:42:00+00:00"
"time": "2023-03-21T21:06:29+00:00"
},
{
"name": "theseer/tokenizer",

View File

@@ -22,8 +22,10 @@ return array(
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
'Phan\\' => array($vendorDir . '/phan/phan/src/Phan'),
'PHPStan\\PhpDocParser\\' => array($vendorDir . '/phpstan/phpdoc-parser/src'),
'Microsoft\\PhpParser\\' => array($vendorDir . '/microsoft/tolerant-php-parser/src'),
'Doctrine\\Instantiator\\' => array($vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator'),
'Doctrine\\Deprecations\\' => array($vendorDir . '/doctrine/deprecations/lib/Doctrine/Deprecations'),
'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'),
'Composer\\XdebugHandler\\' => array($vendorDir . '/composer/xdebug-handler/src'),
'Composer\\Semver\\' => array($vendorDir . '/composer/semver/src'),

View File

@@ -54,6 +54,7 @@ class ComposerStaticInitdd705c6e8ab22e0d642372dec7767718
'Psr\\Container\\' => 14,
'PhpParser\\' => 10,
'Phan\\' => 5,
'PHPStan\\PhpDocParser\\' => 21,
),
'M' =>
array (
@@ -62,6 +63,7 @@ class ComposerStaticInitdd705c6e8ab22e0d642372dec7767718
'D' =>
array (
'Doctrine\\Instantiator\\' => 22,
'Doctrine\\Deprecations\\' => 22,
'DeepCopy\\' => 9,
),
'C' =>
@@ -143,6 +145,10 @@ class ComposerStaticInitdd705c6e8ab22e0d642372dec7767718
array (
0 => __DIR__ . '/..' . '/phan/phan/src/Phan',
),
'PHPStan\\PhpDocParser\\' =>
array (
0 => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src',
),
'Microsoft\\PhpParser\\' =>
array (
0 => __DIR__ . '/..' . '/microsoft/tolerant-php-parser/src',
@@ -151,6 +157,10 @@ class ComposerStaticInitdd705c6e8ab22e0d642372dec7767718
array (
0 => __DIR__ . '/..' . '/doctrine/instantiator/src/Doctrine/Instantiator',
),
'Doctrine\\Deprecations\\' =>
array (
0 => __DIR__ . '/..' . '/doctrine/deprecations/lib/Doctrine/Deprecations',
),
'DeepCopy\\' =>
array (
0 => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy',

View File

@@ -227,6 +227,52 @@
],
"install-path": "./xdebug-handler"
},
{
"name": "doctrine/deprecations",
"version": "v1.1.0",
"version_normalized": "1.1.0.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/deprecations.git",
"reference": "8cffffb2218e01f3b370bf763e00e81697725259"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/deprecations/zipball/8cffffb2218e01f3b370bf763e00e81697725259",
"reference": "8cffffb2218e01f3b370bf763e00e81697725259",
"shasum": ""
},
"require": {
"php": "^7.1|^8.0"
},
"require-dev": {
"doctrine/coding-standard": "^9",
"phpunit/phpunit": "^7.5|^8.5|^9.5",
"psr/log": "^1|^2|^3"
},
"suggest": {
"psr/log": "Allows logging deprecations via PSR-3 logger implementation"
},
"time": "2023-05-29T18:55:17+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.",
"homepage": "https://www.doctrine-project.org/",
"support": {
"issues": "https://github.com/doctrine/deprecations/issues",
"source": "https://github.com/doctrine/deprecations/tree/v1.1.0"
},
"install-path": "../doctrine/deprecations"
},
{
"name": "doctrine/instantiator",
"version": "2.0.0",
@@ -460,17 +506,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": {
@@ -484,7 +530,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": {
@@ -508,23 +554,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": {
@@ -535,7 +581,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"
],
@@ -567,7 +613,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"
},
@@ -888,25 +934,28 @@
},
{
"name": "phpdocumentor/type-resolver",
"version": "1.6.2",
"version_normalized": "1.6.2.0",
"version": "1.7.2",
"version_normalized": "1.7.2.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d"
"reference": "b2fe4d22a5426f38e014855322200b97b5362c0d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/48f445a408c131e38cab1c235aa6d2bb7a0bb20d",
"reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b2fe4d22a5426f38e014855322200b97b5362c0d",
"reference": "b2fe4d22a5426f38e014855322200b97b5362c0d",
"shasum": ""
},
"require": {
"doctrine/deprecations": "^1.0",
"php": "^7.4 || ^8.0",
"phpdocumentor/reflection-common": "^2.0"
"phpdocumentor/reflection-common": "^2.0",
"phpstan/phpdoc-parser": "^1.13"
},
"require-dev": {
"ext-tokenizer": "*",
"phpbench/phpbench": "^1.2",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan": "^1.8",
"phpstan/phpstan-phpunit": "^1.1",
@@ -914,7 +963,7 @@
"rector/rector": "^0.13.9",
"vimeo/psalm": "^4.25"
},
"time": "2022-10-14T12:47:21+00:00",
"time": "2023-05-30T18:13:47+00:00",
"type": "library",
"extra": {
"branch-alias": {
@@ -940,23 +989,72 @@
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
"support": {
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.2"
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.2"
},
"install-path": "../phpdocumentor/type-resolver"
},
{
"name": "phpstan/phpstan",
"version": "1.10.6",
"version_normalized": "1.10.6.0",
"name": "phpstan/phpdoc-parser",
"version": "1.21.3",
"version_normalized": "1.21.3.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "50d089a3e0904b0fe7e2cf2d4fd37d427d64235a"
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "b0c366dd2cea79407d635839d25423ba07c55dd6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/50d089a3e0904b0fe7e2cf2d4fd37d427d64235a",
"reference": "50d089a3e0904b0fe7e2cf2d4fd37d427d64235a",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/b0c366dd2cea79407d635839d25423ba07c55dd6",
"reference": "b0c366dd2cea79407d635839d25423ba07c55dd6",
"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",
"phpstan/phpstan-phpunit": "^1.1",
"phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": "^9.5",
"symfony/process": "^5.2"
},
"time": "2023-05-29T19:31:28+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"PHPStan\\PhpDocParser\\": [
"src/"
]
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.21.3"
},
"install-path": "../phpstan/phpdoc-parser"
},
{
"name": "phpstan/phpstan",
"version": "1.10.15",
"version_normalized": "1.10.15.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "762c4dac4da6f8756eebb80e528c3a47855da9bd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/762c4dac4da6f8756eebb80e528c3a47855da9bd",
"reference": "762c4dac4da6f8756eebb80e528c3a47855da9bd",
"shasum": ""
},
"require": {
@@ -965,7 +1063,7 @@
"conflict": {
"phpstan/phpstan-shim": "*"
},
"time": "2023-03-09T16:55:12+00:00",
"time": "2023-05-09T15:28:01+00:00",
"bin": [
"phpstan",
"phpstan.phar"
@@ -987,8 +1085,11 @@
"static analysis"
],
"support": {
"docs": "https://phpstan.org/user-guide/getting-started",
"forum": "https://github.com/phpstan/phpstan/discussions",
"issues": "https://github.com/phpstan/phpstan/issues",
"source": "https://github.com/phpstan/phpstan/tree/1.10.6"
"security": "https://github.com/phpstan/phpstan/security/policy",
"source": "https://github.com/phpstan/phpstan-src"
},
"funding": [
{
@@ -1341,17 +1442,17 @@
},
{
"name": "phpunit/phpunit",
"version": "9.6.5",
"version_normalized": "9.6.5.0",
"version": "9.6.8",
"version_normalized": "9.6.8.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "86e761949019ae83f49240b2f2123fb5ab3b2fc5"
"reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/86e761949019ae83f49240b2f2123fb5ab3b2fc5",
"reference": "86e761949019ae83f49240b2f2123fb5ab3b2fc5",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/17d621b3aff84d0c8b62539e269e87d8d5baa76e",
"reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e",
"shasum": ""
},
"require": {
@@ -1387,7 +1488,7 @@
"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-03-09T06:34:10+00:00",
"time": "2023-05-11T05:14:45+00:00",
"bin": [
"phpunit"
],
@@ -1426,7 +1527,8 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.5"
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.8"
},
"funding": [
{
@@ -1937,17 +2039,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": {
@@ -1957,7 +2059,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": {
@@ -1994,7 +2096,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": [
{
@@ -2636,24 +2738,24 @@
},
{
"name": "symfony/console",
"version": "v6.2.7",
"version_normalized": "6.2.7.0",
"version": "v6.3.0",
"version_normalized": "6.3.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "cbad09eb8925b6ad4fb721c7a179344dc4a19d45"
"reference": "8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/cbad09eb8925b6ad4fb721c7a179344dc4a19d45",
"reference": "cbad09eb8925b6ad4fb721c7a179344dc4a19d45",
"url": "https://api.github.com/repos/symfony/console/zipball/8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7",
"reference": "8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7",
"shasum": ""
},
"require": {
"php": ">=8.1",
"symfony/deprecation-contracts": "^2.1|^3",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-mbstring": "~1.0",
"symfony/service-contracts": "^1.1|^2|^3",
"symfony/service-contracts": "^2.5|^3",
"symfony/string": "^5.4|^6.0"
},
"conflict": {
@@ -2675,13 +2777,7 @@
"symfony/process": "^5.4|^6.0",
"symfony/var-dumper": "^5.4|^6.0"
},
"suggest": {
"psr/log": "For using the console logger",
"symfony/event-dispatcher": "",
"symfony/lock": "",
"symfony/process": ""
},
"time": "2023-02-25T17:00:03+00:00",
"time": "2023-05-29T12:49:39+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -2710,12 +2806,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.3.0"
},
"funding": [
{
@@ -2735,27 +2831,27 @@
},
{
"name": "symfony/deprecation-contracts",
"version": "v3.2.1",
"version_normalized": "3.2.1.0",
"version": "v3.3.0",
"version_normalized": "3.3.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e"
"reference": "7c3aff79d10325257a001fcf92d991f24fc967cf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e",
"reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf",
"reference": "7c3aff79d10325257a001fcf92d991f24fc967cf",
"shasum": ""
},
"require": {
"php": ">=8.1"
},
"time": "2023-03-01T10:25:55+00:00",
"time": "2023-05-23T14:45:45+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.3-dev"
"dev-main": "3.4-dev"
},
"thanks": {
"name": "symfony/contracts",
@@ -2785,7 +2881,7 @@
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1"
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0"
},
"funding": [
{
@@ -3233,17 +3329,17 @@
},
{
"name": "symfony/service-contracts",
"version": "v3.2.1",
"version_normalized": "3.2.1.0",
"version": "v3.3.0",
"version_normalized": "3.3.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
"reference": "a8c9cedf55f314f3a186041d19537303766df09a"
"reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/a8c9cedf55f314f3a186041d19537303766df09a",
"reference": "a8c9cedf55f314f3a186041d19537303766df09a",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/40da9cc13ec349d9e4966ce18b5fbcd724ab10a4",
"reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4",
"shasum": ""
},
"require": {
@@ -3253,14 +3349,11 @@
"conflict": {
"ext-psr": "<1.1|>=2"
},
"suggest": {
"symfony/service-implementation": ""
},
"time": "2023-03-01T10:32:47+00:00",
"time": "2023-05-23T14:45:45+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.3-dev"
"dev-main": "3.4-dev"
},
"thanks": {
"name": "symfony/contracts",
@@ -3301,7 +3394,7 @@
"standards"
],
"support": {
"source": "https://github.com/symfony/service-contracts/tree/v3.2.1"
"source": "https://github.com/symfony/service-contracts/tree/v3.3.0"
},
"funding": [
{
@@ -3321,17 +3414,17 @@
},
{
"name": "symfony/string",
"version": "v6.2.7",
"version_normalized": "6.2.7.0",
"version": "v6.3.0",
"version_normalized": "6.3.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "67b8c1eec78296b85dc1c7d9743830160218993d"
"reference": "f2e190ee75ff0f5eced645ec0be5c66fac81f51f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/67b8c1eec78296b85dc1c7d9743830160218993d",
"reference": "67b8c1eec78296b85dc1c7d9743830160218993d",
"url": "https://api.github.com/repos/symfony/string/zipball/f2e190ee75ff0f5eced645ec0be5c66fac81f51f",
"reference": "f2e190ee75ff0f5eced645ec0be5c66fac81f51f",
"shasum": ""
},
"require": {
@@ -3342,16 +3435,16 @@
"symfony/polyfill-mbstring": "~1.0"
},
"conflict": {
"symfony/translation-contracts": "<2.0"
"symfony/translation-contracts": "<2.5"
},
"require-dev": {
"symfony/error-handler": "^5.4|^6.0",
"symfony/http-client": "^5.4|^6.0",
"symfony/intl": "^6.2",
"symfony/translation-contracts": "^2.0|^3.0",
"symfony/translation-contracts": "^2.5|^3.0",
"symfony/var-exporter": "^5.4|^6.0"
},
"time": "2023-02-24T10:42:00+00:00",
"time": "2023-03-21T21:06:29+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -3390,7 +3483,7 @@
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v6.2.7"
"source": "https://github.com/symfony/string/tree/v6.3.0"
},
"funding": [
{
@@ -3593,6 +3686,7 @@
"composer/pcre",
"composer/semver",
"composer/xdebug-handler",
"doctrine/deprecations",
"doctrine/instantiator",
"felixfbecker/advanced-json-rpc",
"microsoft/tolerant-php-parser",
@@ -3605,6 +3699,7 @@
"phpdocumentor/reflection-common",
"phpdocumentor/reflection-docblock",
"phpdocumentor/type-resolver",
"phpstan/phpdoc-parser",
"phpstan/phpstan",
"phpunit/php-code-coverage",
"phpunit/php-file-iterator",

View File

@@ -3,7 +3,7 @@
'name' => '__root__',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '1cbe4e5c06aec83ca037690cb9621e39312b9247',
'reference' => '513b115d5729d1001743baf12d8991cc385b20d9',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -13,7 +13,7 @@
'__root__' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '1cbe4e5c06aec83ca037690cb9621e39312b9247',
'reference' => '513b115d5729d1001743baf12d8991cc385b20d9',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -46,6 +46,15 @@
'aliases' => array(),
'dev_requirement' => true,
),
'doctrine/deprecations' => array(
'pretty_version' => 'v1.1.0',
'version' => '1.1.0.0',
'reference' => '8cffffb2218e01f3b370bf763e00e81697725259',
'type' => 'library',
'install_path' => __DIR__ . '/../doctrine/deprecations',
'aliases' => array(),
'dev_requirement' => true,
),
'doctrine/instantiator' => array(
'pretty_version' => '2.0.0',
'version' => '2.0.0.0',
@@ -83,18 +92,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(),
@@ -146,18 +155,27 @@
'dev_requirement' => true,
),
'phpdocumentor/type-resolver' => array(
'pretty_version' => '1.6.2',
'version' => '1.6.2.0',
'reference' => '48f445a408c131e38cab1c235aa6d2bb7a0bb20d',
'pretty_version' => '1.7.2',
'version' => '1.7.2.0',
'reference' => 'b2fe4d22a5426f38e014855322200b97b5362c0d',
'type' => 'library',
'install_path' => __DIR__ . '/../phpdocumentor/type-resolver',
'aliases' => array(),
'dev_requirement' => true,
),
'phpstan/phpdoc-parser' => array(
'pretty_version' => '1.21.3',
'version' => '1.21.3.0',
'reference' => 'b0c366dd2cea79407d635839d25423ba07c55dd6',
'type' => 'library',
'install_path' => __DIR__ . '/../phpstan/phpdoc-parser',
'aliases' => array(),
'dev_requirement' => true,
),
'phpstan/phpstan' => array(
'pretty_version' => '1.10.6',
'version' => '1.10.6.0',
'reference' => '50d089a3e0904b0fe7e2cf2d4fd37d427d64235a',
'pretty_version' => '1.10.15',
'version' => '1.10.15.0',
'reference' => '762c4dac4da6f8756eebb80e528c3a47855da9bd',
'type' => 'library',
'install_path' => __DIR__ . '/../phpstan/phpstan',
'aliases' => array(),
@@ -209,9 +227,9 @@
'dev_requirement' => true,
),
'phpunit/phpunit' => array(
'pretty_version' => '9.6.5',
'version' => '9.6.5.0',
'reference' => '86e761949019ae83f49240b2f2123fb5ab3b2fc5',
'pretty_version' => '9.6.8',
'version' => '9.6.8.0',
'reference' => '17d621b3aff84d0c8b62539e269e87d8d5baa76e',
'type' => 'library',
'install_path' => __DIR__ . '/../phpunit/phpunit',
'aliases' => array(),
@@ -296,9 +314,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(),
@@ -395,18 +413,18 @@
'dev_requirement' => true,
),
'symfony/console' => array(
'pretty_version' => 'v6.2.7',
'version' => '6.2.7.0',
'reference' => 'cbad09eb8925b6ad4fb721c7a179344dc4a19d45',
'pretty_version' => 'v6.3.0',
'version' => '6.3.0.0',
'reference' => '8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/console',
'aliases' => array(),
'dev_requirement' => true,
),
'symfony/deprecation-contracts' => array(
'pretty_version' => 'v3.2.1',
'version' => '3.2.1.0',
'reference' => 'e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e',
'pretty_version' => 'v3.3.0',
'version' => '3.3.0.0',
'reference' => '7c3aff79d10325257a001fcf92d991f24fc967cf',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
'aliases' => array(),
@@ -458,18 +476,18 @@
'dev_requirement' => true,
),
'symfony/service-contracts' => array(
'pretty_version' => 'v3.2.1',
'version' => '3.2.1.0',
'reference' => 'a8c9cedf55f314f3a186041d19537303766df09a',
'pretty_version' => 'v3.3.0',
'version' => '3.3.0.0',
'reference' => '40da9cc13ec349d9e4966ce18b5fbcd724ab10a4',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/service-contracts',
'aliases' => array(),
'dev_requirement' => true,
),
'symfony/string' => array(
'pretty_version' => 'v6.2.7',
'version' => '6.2.7.0',
'reference' => '67b8c1eec78296b85dc1c7d9743830160218993d',
'pretty_version' => 'v6.3.0',
'version' => '6.3.0.0',
'reference' => 'f2e190ee75ff0f5eced645ec0be5c66fac81f51f',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/string',
'aliases' => array(),

19
vendor/doctrine/deprecations/LICENSE vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2020-2021 Doctrine Project
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.

157
vendor/doctrine/deprecations/README.md vendored Normal file
View File

@@ -0,0 +1,157 @@
# Doctrine Deprecations
A small (side-effect free by default) layer on top of
`trigger_error(E_USER_DEPRECATED)` or PSR-3 logging.
- no side-effects by default, making it a perfect fit for libraries that don't know how the error handler works they operate under
- options to avoid having to rely on error handlers global state by using PSR-3 logging
- deduplicate deprecation messages to avoid excessive triggering and reduce overhead
We recommend to collect Deprecations using a PSR logger instead of relying on
the global error handler.
## Usage from consumer perspective:
Enable Doctrine deprecations to be sent to a PSR3 logger:
```php
\Doctrine\Deprecations\Deprecation::enableWithPsrLogger($logger);
```
Enable Doctrine deprecations to be sent as `@trigger_error($message, E_USER_DEPRECATED)`
messages by setting the `DOCTRINE_DEPRECATIONS` environment variable to `trigger`.
Alternatively, call:
```php
\Doctrine\Deprecations\Deprecation::enableWithTriggerError();
```
If you only want to enable deprecation tracking, without logging or calling `trigger_error`
then set the `DOCTRINE_DEPRECATIONS` environment variable to `track`.
Alternatively, call:
```php
\Doctrine\Deprecations\Deprecation::enableTrackingDeprecations();
```
Tracking is enabled with all three modes and provides access to all triggered
deprecations and their individual count:
```php
$deprecations = \Doctrine\Deprecations\Deprecation::getTriggeredDeprecations();
foreach ($deprecations as $identifier => $count) {
echo $identifier . " was triggered " . $count . " times\n";
}
```
### Suppressing Specific Deprecations
Disable triggering about specific deprecations:
```php
\Doctrine\Deprecations\Deprecation::ignoreDeprecations("https://link/to/deprecations-description-identifier");
```
Disable all deprecations from a package
```php
\Doctrine\Deprecations\Deprecation::ignorePackage("doctrine/orm");
```
### Other Operations
When used within PHPUnit or other tools that could collect multiple instances of the same deprecations
the deduplication can be disabled:
```php
\Doctrine\Deprecations\Deprecation::withoutDeduplication();
```
Disable deprecation tracking again:
```php
\Doctrine\Deprecations\Deprecation::disable();
```
## Usage from a library/producer perspective:
When you want to unconditionally trigger a deprecation even when called
from the library itself then the `trigger` method is the way to go:
```php
\Doctrine\Deprecations\Deprecation::trigger(
"doctrine/orm",
"https://link/to/deprecations-description",
"message"
);
```
If variable arguments are provided at the end, they are used with `sprintf` on
the message.
```php
\Doctrine\Deprecations\Deprecation::trigger(
"doctrine/orm",
"https://github.com/doctrine/orm/issue/1234",
"message %s %d",
"foo",
1234
);
```
When you want to trigger a deprecation only when it is called by a function
outside of the current package, but not trigger when the package itself is the cause,
then use:
```php
\Doctrine\Deprecations\Deprecation::triggerIfCalledFromOutside(
"doctrine/orm",
"https://link/to/deprecations-description",
"message"
);
```
Based on the issue link each deprecation message is only triggered once per
request.
A limited stacktrace is included in the deprecation message to find the
offending location.
Note: A producer/library should never call `Deprecation::enableWith` methods
and leave the decision how to handle deprecations to application and
frameworks.
## Usage in PHPUnit tests
There is a `VerifyDeprecations` trait that you can use to make assertions on
the occurrence of deprecations within a test.
```php
use Doctrine\Deprecations\PHPUnit\VerifyDeprecations;
class MyTest extends TestCase
{
use VerifyDeprecations;
public function testSomethingDeprecation()
{
$this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issue/1234');
triggerTheCodeWithDeprecation();
}
public function testSomethingDeprecationFixed()
{
$this->expectNoDeprecationWithIdentifier('https://github.com/doctrine/orm/issue/1234');
triggerTheCodeWithoutDeprecation();
}
}
```
## What is a deprecation identifier?
An identifier for deprecations is just a link to any resource, most often a
Github Issue or Pull Request explaining the deprecation and potentially its
alternative.

View File

@@ -0,0 +1,32 @@
{
"name": "doctrine/deprecations",
"type": "library",
"description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.",
"homepage": "https://www.doctrine-project.org/",
"license": "MIT",
"require": {
"php": "^7.1|^8.0"
},
"require-dev": {
"phpunit/phpunit": "^7.5|^8.5|^9.5",
"psr/log": "^1|^2|^3",
"doctrine/coding-standard": "^9"
},
"suggest": {
"psr/log": "Allows logging deprecations via PSR-3 logger implementation"
},
"autoload": {
"psr-4": {"Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations"}
},
"autoload-dev": {
"psr-4": {
"DeprecationTests\\": "test_fixtures/src",
"Doctrine\\Foo\\": "test_fixtures/vendor/doctrine/foo"
}
},
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
}
}
}

View File

@@ -0,0 +1,294 @@
<?php
declare(strict_types=1);
namespace Doctrine\Deprecations;
use Psr\Log\LoggerInterface;
use function array_key_exists;
use function array_reduce;
use function debug_backtrace;
use function sprintf;
use function strpos;
use function strrpos;
use function substr;
use function trigger_error;
use const DEBUG_BACKTRACE_IGNORE_ARGS;
use const DIRECTORY_SEPARATOR;
use const E_USER_DEPRECATED;
/**
* Manages Deprecation logging in different ways.
*
* By default triggered exceptions are not logged.
*
* To enable different deprecation logging mechanisms you can call the
* following methods:
*
* - Minimal collection of deprecations via getTriggeredDeprecations()
* \Doctrine\Deprecations\Deprecation::enableTrackingDeprecations();
*
* - Uses @trigger_error with E_USER_DEPRECATED
* \Doctrine\Deprecations\Deprecation::enableWithTriggerError();
*
* - Sends deprecation messages via a PSR-3 logger
* \Doctrine\Deprecations\Deprecation::enableWithPsrLogger($logger);
*
* Packages that trigger deprecations should use the `trigger()` or
* `triggerIfCalledFromOutside()` methods.
*/
class Deprecation
{
private const TYPE_NONE = 0;
private const TYPE_TRACK_DEPRECATIONS = 1;
private const TYPE_TRIGGER_ERROR = 2;
private const TYPE_PSR_LOGGER = 4;
/** @var self::TYPE_*|null */
private static $type;
/** @var LoggerInterface|null */
private static $logger;
/** @var array<string,bool> */
private static $ignoredPackages = [];
/** @var array<string,int> */
private static $ignoredLinks = [];
/** @var bool */
private static $deduplication = true;
/**
* Trigger a deprecation for the given package and identfier.
*
* The link should point to a Github issue or Wiki entry detailing the
* deprecation. It is additionally used to de-duplicate the trigger of the
* same deprecation during a request.
*
* @param mixed $args
*/
public static function trigger(string $package, string $link, string $message, ...$args): void
{
$type = self::$type ?? self::getTypeFromEnv();
if ($type === self::TYPE_NONE) {
return;
}
if (array_key_exists($link, self::$ignoredLinks)) {
self::$ignoredLinks[$link]++;
} else {
self::$ignoredLinks[$link] = 1;
}
if (self::$deduplication === true && self::$ignoredLinks[$link] > 1) {
return;
}
if (isset(self::$ignoredPackages[$package])) {
return;
}
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
$message = sprintf($message, ...$args);
self::delegateTriggerToBackend($message, $backtrace, $link, $package);
}
/**
* Trigger a deprecation for the given package and identifier when called from outside.
*
* "Outside" means we assume that $package is currently installed as a
* dependency and the caller is not a file in that package. When $package
* is installed as a root package then deprecations triggered from the
* tests folder are also considered "outside".
*
* This deprecation method assumes that you are using Composer to install
* the dependency and are using the default /vendor/ folder and not a
* Composer plugin to change the install location. The assumption is also
* that $package is the exact composer packge name.
*
* Compared to {@link trigger()} this method causes some overhead when
* deprecation tracking is enabled even during deduplication, because it
* needs to call {@link debug_backtrace()}
*
* @param mixed $args
*/
public static function triggerIfCalledFromOutside(string $package, string $link, string $message, ...$args): void
{
$type = self::$type ?? self::getTypeFromEnv();
if ($type === self::TYPE_NONE) {
return;
}
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
// first check that the caller is not from a tests folder, in which case we always let deprecations pass
if (strpos($backtrace[1]['file'], DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR) === false) {
$path = DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . $package . DIRECTORY_SEPARATOR;
if (strpos($backtrace[0]['file'], $path) === false) {
return;
}
if (strpos($backtrace[1]['file'], $path) !== false) {
return;
}
}
if (array_key_exists($link, self::$ignoredLinks)) {
self::$ignoredLinks[$link]++;
} else {
self::$ignoredLinks[$link] = 1;
}
if (self::$deduplication === true && self::$ignoredLinks[$link] > 1) {
return;
}
if (isset(self::$ignoredPackages[$package])) {
return;
}
$message = sprintf($message, ...$args);
self::delegateTriggerToBackend($message, $backtrace, $link, $package);
}
/**
* @param array<mixed> $backtrace
*/
private static function delegateTriggerToBackend(string $message, array $backtrace, string $link, string $package): void
{
$type = self::$type ?? self::getTypeFromEnv();
if (($type & self::TYPE_PSR_LOGGER) > 0) {
$context = [
'file' => $backtrace[0]['file'],
'line' => $backtrace[0]['line'],
'package' => $package,
'link' => $link,
];
self::$logger->notice($message, $context);
}
if (! (($type & self::TYPE_TRIGGER_ERROR) > 0)) {
return;
}
$message .= sprintf(
' (%s:%d called by %s:%d, %s, package %s)',
self::basename($backtrace[0]['file']),
$backtrace[0]['line'],
self::basename($backtrace[1]['file']),
$backtrace[1]['line'],
$link,
$package
);
@trigger_error($message, E_USER_DEPRECATED);
}
/**
* A non-local-aware version of PHPs basename function.
*/
private static function basename(string $filename): string
{
$pos = strrpos($filename, DIRECTORY_SEPARATOR);
if ($pos === false) {
return $filename;
}
return substr($filename, $pos + 1);
}
public static function enableTrackingDeprecations(): void
{
self::$type |= self::TYPE_TRACK_DEPRECATIONS;
}
public static function enableWithTriggerError(): void
{
self::$type |= self::TYPE_TRIGGER_ERROR;
}
public static function enableWithPsrLogger(LoggerInterface $logger): void
{
self::$type |= self::TYPE_PSR_LOGGER;
self::$logger = $logger;
}
public static function withoutDeduplication(): void
{
self::$deduplication = false;
}
public static function disable(): void
{
self::$type = self::TYPE_NONE;
self::$logger = null;
self::$deduplication = true;
foreach (self::$ignoredLinks as $link => $count) {
self::$ignoredLinks[$link] = 0;
}
}
public static function ignorePackage(string $packageName): void
{
self::$ignoredPackages[$packageName] = true;
}
public static function ignoreDeprecations(string ...$links): void
{
foreach ($links as $link) {
self::$ignoredLinks[$link] = 0;
}
}
public static function getUniqueTriggeredDeprecationsCount(): int
{
return array_reduce(self::$ignoredLinks, static function (int $carry, int $count) {
return $carry + $count;
}, 0);
}
/**
* Returns each triggered deprecation link identifier and the amount of occurrences.
*
* @return array<string,int>
*/
public static function getTriggeredDeprecations(): array
{
return self::$ignoredLinks;
}
/**
* @return self::TYPE_*
*/
private static function getTypeFromEnv(): int
{
switch ($_SERVER['DOCTRINE_DEPRECATIONS'] ?? $_ENV['DOCTRINE_DEPRECATIONS'] ?? null) {
case 'trigger':
self::$type = self::TYPE_TRIGGER_ERROR;
break;
case 'track':
self::$type = self::TYPE_TRACK_DEPRECATIONS;
break;
default:
self::$type = self::TYPE_NONE;
break;
}
return self::$type;
}
}

View File

@@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
namespace Doctrine\Deprecations\PHPUnit;
use Doctrine\Deprecations\Deprecation;
use function sprintf;
trait VerifyDeprecations
{
/** @var array<string,int> */
private $doctrineDeprecationsExpectations = [];
/** @var array<string,int> */
private $doctrineNoDeprecationsExpectations = [];
public function expectDeprecationWithIdentifier(string $identifier): void
{
$this->doctrineDeprecationsExpectations[$identifier] = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0;
}
public function expectNoDeprecationWithIdentifier(string $identifier): void
{
$this->doctrineNoDeprecationsExpectations[$identifier] = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0;
}
/**
* @before
*/
public function enableDeprecationTracking(): void
{
Deprecation::enableTrackingDeprecations();
}
/**
* @after
*/
public function verifyDeprecationsAreTriggered(): void
{
foreach ($this->doctrineDeprecationsExpectations as $identifier => $expectation) {
$actualCount = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0;
$this->assertTrue(
$actualCount > $expectation,
sprintf(
"Expected deprecation with identifier '%s' was not triggered by code executed in test.",
$identifier
)
);
}
foreach ($this->doctrineNoDeprecationsExpectations as $identifier => $expectation) {
$actualCount = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0;
$this->assertTrue(
$actualCount === $expectation,
sprintf(
"Expected deprecation with identifier '%s' was triggered by code executed in test, but expected not to.",
$identifier
)
);
}
}
}

22
vendor/doctrine/deprecations/phpcs.xml vendored Normal file
View File

@@ -0,0 +1,22 @@
<?xml version="1.0"?>
<ruleset>
<arg name="basepath" value="."/>
<arg name="extensions" value="php"/>
<arg name="parallel" value="80"/>
<arg name="cache" value=".phpcs-cache"/>
<arg name="colors"/>
<!-- Ignore warnings, show progress of the run and show sniff names -->
<arg value="nps"/>
<config name="php_version" value="70100"/>
<!-- Directories to be checked -->
<file>lib</file>
<file>tests</file>
<!-- Include full Doctrine Coding Standard -->
<rule ref="Doctrine">
<exclude name="SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingNativeTypeHint" />
</rule>
</ruleset>

View File

@@ -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/

View File

@@ -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
*

View File

@@ -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
);
}
}

View File

@@ -1,65 +0,0 @@
extends: "default"
ignore: |
.build/
.notes/
vendor/
rules:
braces:
max-spaces-inside-empty: 0
max-spaces-inside: 1
min-spaces-inside-empty: 0
min-spaces-inside: 1
brackets:
max-spaces-inside-empty: 0
max-spaces-inside: 0
min-spaces-inside-empty: 0
min-spaces-inside: 0
colons:
max-spaces-after: 1
max-spaces-before: 0
commas:
max-spaces-after: 1
max-spaces-before: 0
min-spaces-after: 1
comments:
ignore-shebangs: true
min-spaces-from-content: 1
require-starting-space: true
comments-indentation: "enable"
document-end:
present: false
document-start:
present: false
indentation:
check-multi-line-strings: false
indent-sequences: true
spaces: 2
empty-lines:
max-end: 0
max-start: 0
max: 1
empty-values:
forbid-in-block-mappings: true
forbid-in-flow-mappings: true
hyphens:
max-spaces-after: 2
key-duplicates: "enable"
key-ordering: "disable"
line-length: "disable"
new-line-at-end-of-file: "enable"
new-lines:
type: "unix"
octal-values:
forbid-implicit-octal: true
quoted-strings:
quote-type: "double"
trailing-spaces: "enable"
truthy:
allowed-values:
- "false"
- "true"
yaml-files:
- "*.yaml"
- "*.yml"

View File

@@ -1,16 +0,0 @@
{
"symbol-whitelist" : [
"null", "true", "false",
"static", "self", "parent",
"array", "string", "int", "float", "bool", "iterable", "callable", "void", "object", "XSLTProcessor",
"T_NAME_QUALIFIED", "T_NAME_FULLY_QUALIFIED"
],
"php-core-extensions" : [
"Core",
"pcre",
"Reflection",
"tokenizer",
"SPL",
"standard"
]
}

View File

@@ -11,7 +11,9 @@
],
"require": {
"php": "^7.4 || ^8.0",
"phpdocumentor/reflection-common": "^2.0"
"phpdocumentor/reflection-common": "^2.0",
"phpstan/phpdoc-parser": "^1.13",
"doctrine/deprecations": "^1.0"
},
"require-dev": {
"ext-tokenizer": "*",
@@ -20,7 +22,8 @@
"phpstan/phpstan-phpunit": "^1.1",
"phpstan/extension-installer": "^1.1",
"vimeo/psalm": "^4.25",
"rector/rector": "^0.13.9"
"rector/rector": "^0.13.9",
"phpbench/phpbench": "^1.2"
},
"autoload": {
"psr-4": {

View File

@@ -1,26 +0,0 @@
<?php
declare(strict_types=1);
use Rector\CodeQuality\Rector\Class_\InlineConstructorDefaultToPropertyRector;
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->paths([
__DIR__ . '/src',
__DIR__ . '/tests/unit'
]);
// register a single rule
$rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
$rectorConfig->rule(Rector\CodeQuality\Rector\Class_\CompleteDynamicPropertiesRector::class);
$rectorConfig->rule(Rector\TypeDeclaration\Rector\Closure\AddClosureReturnTypeRector::class);
$rectorConfig->rule(Rector\PHPUnit\Rector\Class_\AddProphecyTraitRector::class);
$rectorConfig->importNames();
// define sets of rules
$rectorConfig->sets([
LevelSetList::UP_TO_PHP_74
]);
};

View File

@@ -0,0 +1,44 @@
<?php
/*
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*
*/
declare(strict_types=1);
namespace phpDocumentor\Reflection\PseudoTypes;
use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\Array_;
use phpDocumentor\Reflection\Types\ArrayKey;
use phpDocumentor\Reflection\Types\Mixed_;
use function implode;
/** @psalm-immutable */
class ArrayShape implements PseudoType
{
/** @var ArrayShapeItem[] */
private array $items;
public function __construct(ArrayShapeItem ...$items)
{
$this->items = $items;
}
public function underlyingType(): Type
{
return new Array_(new Mixed_(), new ArrayKey());
}
public function __toString(): string
{
return 'array{' . implode(', ', $this->items) . '}';
}
}

View File

@@ -0,0 +1,62 @@
<?php
/*
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*
*/
declare(strict_types=1);
namespace phpDocumentor\Reflection\PseudoTypes;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\Mixed_;
use function sprintf;
final class ArrayShapeItem
{
private ?string $key;
private Type $value;
private bool $optional;
public function __construct(?string $key, ?Type $value, bool $optional)
{
$this->key = $key;
$this->value = $value ?? new Mixed_();
$this->optional = $optional;
}
public function getKey(): ?string
{
return $this->key;
}
public function getValue(): Type
{
return $this->value;
}
public function isOptional(): bool
{
return $this->optional;
}
public function __toString(): string
{
if ($this->key !== null) {
return sprintf(
'%s%s: %s',
$this->key,
$this->optional ? '?' : '',
(string) $this->value
);
}
return (string) $this->value;
}
}

View File

@@ -0,0 +1,53 @@
<?php
/*
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*
*/
declare(strict_types=1);
namespace phpDocumentor\Reflection\PseudoTypes;
use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\Mixed_;
use function sprintf;
/** @psalm-immutable */
final class ConstExpression implements PseudoType
{
private Type $owner;
private string $expression;
public function __construct(Type $owner, string $expression)
{
$this->owner = $owner;
$this->expression = $expression;
}
public function getOwner(): Type
{
return $this->owner;
}
public function getExpression(): string
{
return $this->expression;
}
public function underlyingType(): Type
{
return new Mixed_();
}
public function __toString(): string
{
return sprintf('%s::%s', (string) $this->owner, $this->expression);
}
}

View File

@@ -0,0 +1,44 @@
<?php
/*
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*
*/
declare(strict_types=1);
namespace phpDocumentor\Reflection\PseudoTypes;
use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\Float_;
/** @psalm-immutable */
class FloatValue implements PseudoType
{
private float $value;
public function __construct(float $value)
{
$this->value = $value;
}
public function getValue(): float
{
return $this->value;
}
public function underlyingType(): Type
{
return new Float_();
}
public function __toString(): string
{
return (string) $this->value;
}
}

View File

@@ -0,0 +1,44 @@
<?php
/*
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*
*/
declare(strict_types=1);
namespace phpDocumentor\Reflection\PseudoTypes;
use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\Integer;
/** @psalm-immutable */
final class IntegerValue implements PseudoType
{
private int $value;
public function __construct(int $value)
{
$this->value = $value;
}
public function getValue(): int
{
return $this->value;
}
public function underlyingType(): Type
{
return new Integer();
}
public function __toString(): string
{
return (string) $this->value;
}
}

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\PseudoTypes;
use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\Array_;
use phpDocumentor\Reflection\Types\Integer;
use phpDocumentor\Reflection\Types\Mixed_;
/**
* Value Object representing the type 'non-empty-list'.
*
* @psalm-immutable
*/
final class NonEmptyList extends Array_ implements PseudoType
{
public function underlyingType(): Type
{
return new Array_();
}
public function __construct(?Type $valueType = null)
{
parent::__construct($valueType, new Integer());
}
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString(): string
{
if ($this->valueType instanceof Mixed_) {
return 'non-empty-list';
}
return 'non-empty-list<' . $this->valueType . '>';
}
}

View File

@@ -0,0 +1,46 @@
<?php
/*
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*
*/
declare(strict_types=1);
namespace phpDocumentor\Reflection\PseudoTypes;
use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\String_;
use function sprintf;
/** @psalm-immutable */
class StringValue implements PseudoType
{
private string $value;
public function __construct(string $value)
{
$this->value = $value;
}
public function getValue(): string
{
return $this->value;
}
public function underlyingType(): Type
{
return new String_();
}
public function __toString(): string
{
return sprintf('"%s"', $this->value);
}
}

View File

@@ -13,27 +13,36 @@ declare(strict_types=1);
namespace phpDocumentor\Reflection;
use ArrayIterator;
use Doctrine\Deprecations\Deprecation;
use InvalidArgumentException;
use phpDocumentor\Reflection\PseudoTypes\ArrayShape;
use phpDocumentor\Reflection\PseudoTypes\ArrayShapeItem;
use phpDocumentor\Reflection\PseudoTypes\CallableString;
use phpDocumentor\Reflection\PseudoTypes\ConstExpression;
use phpDocumentor\Reflection\PseudoTypes\False_;
use phpDocumentor\Reflection\PseudoTypes\FloatValue;
use phpDocumentor\Reflection\PseudoTypes\HtmlEscapedString;
use phpDocumentor\Reflection\PseudoTypes\IntegerRange;
use phpDocumentor\Reflection\PseudoTypes\IntegerValue;
use phpDocumentor\Reflection\PseudoTypes\List_;
use phpDocumentor\Reflection\PseudoTypes\LiteralString;
use phpDocumentor\Reflection\PseudoTypes\LowercaseString;
use phpDocumentor\Reflection\PseudoTypes\NegativeInteger;
use phpDocumentor\Reflection\PseudoTypes\NonEmptyList;
use phpDocumentor\Reflection\PseudoTypes\NonEmptyLowercaseString;
use phpDocumentor\Reflection\PseudoTypes\NonEmptyString;
use phpDocumentor\Reflection\PseudoTypes\Numeric_;
use phpDocumentor\Reflection\PseudoTypes\NumericString;
use phpDocumentor\Reflection\PseudoTypes\PositiveInteger;
use phpDocumentor\Reflection\PseudoTypes\StringValue;
use phpDocumentor\Reflection\PseudoTypes\TraitString;
use phpDocumentor\Reflection\PseudoTypes\True_;
use phpDocumentor\Reflection\Types\AggregatedType;
use phpDocumentor\Reflection\Types\Array_;
use phpDocumentor\Reflection\Types\ArrayKey;
use phpDocumentor\Reflection\Types\Boolean;
use phpDocumentor\Reflection\Types\Callable_;
use phpDocumentor\Reflection\Types\CallableParameter;
use phpDocumentor\Reflection\Types\ClassString;
use phpDocumentor\Reflection\Types\Collection;
use phpDocumentor\Reflection\Types\Compound;
@@ -57,46 +66,51 @@ use phpDocumentor\Reflection\Types\Static_;
use phpDocumentor\Reflection\Types\String_;
use phpDocumentor\Reflection\Types\This;
use phpDocumentor\Reflection\Types\Void_;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFloatNode;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode;
use PHPStan\PhpDocParser\Ast\Type\ArrayShapeItemNode;
use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode;
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode;
use PHPStan\PhpDocParser\Ast\Type\CallableTypeParameterNode;
use PHPStan\PhpDocParser\Ast\Type\ConditionalTypeForParameterNode;
use PHPStan\PhpDocParser\Ast\Type\ConditionalTypeNode;
use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode;
use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode;
use PHPStan\PhpDocParser\Ast\Type\OffsetAccessTypeNode;
use PHPStan\PhpDocParser\Ast\Type\ThisTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
use PHPStan\PhpDocParser\Lexer\Lexer;
use PHPStan\PhpDocParser\Parser\ConstExprParser;
use PHPStan\PhpDocParser\Parser\ParserException;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use PHPStan\PhpDocParser\Parser\TypeParser;
use RuntimeException;
use function array_filter;
use function array_key_exists;
use function array_key_last;
use function array_pop;
use function array_values;
use function array_map;
use function array_reverse;
use function class_exists;
use function class_implements;
use function count;
use function current;
use function get_class;
use function in_array;
use function is_numeric;
use function preg_split;
use function sprintf;
use function strpos;
use function strtolower;
use function trim;
use const PREG_SPLIT_DELIM_CAPTURE;
use const PREG_SPLIT_NO_EMPTY;
final class TypeResolver
{
/** @var string Definition of the ARRAY operator for types */
private const OPERATOR_ARRAY = '[]';
/** @var string Definition of the NAMESPACE operator in PHP */
private const OPERATOR_NAMESPACE = '\\';
/** @var int the iterator parser is inside a compound context */
private const PARSER_IN_COMPOUND = 0;
/** @var int the iterator parser is inside a nullable expression context */
private const PARSER_IN_NULLABLE = 1;
/** @var int the iterator parser is inside an array expression context */
private const PARSER_IN_ARRAY_EXPRESSION = 2;
/** @var int the iterator parser is inside a collection expression context */
private const PARSER_IN_COLLECTION_EXPRESSION = 3;
/**
* @var array<string, string> List of recognized keywords and unto which Value Object they map
* @psalm-var array<string, class-string<Type>>
@@ -142,10 +156,15 @@ final class TypeResolver
'iterable' => Iterable_::class,
'never' => Never_::class,
'list' => List_::class,
'non-empty-list' => NonEmptyList::class,
];
/** @psalm-readonly */
private FqsenResolver $fqsenResolver;
/** @psalm-readonly */
private TypeParser $typeParser;
/** @psalm-readonly */
private Lexer $lexer;
/**
* Initializes this TypeResolver with the means to create and resolve Fqsen objects.
@@ -153,6 +172,8 @@ final class TypeResolver
public function __construct(?FqsenResolver $fqsenResolver = null)
{
$this->fqsenResolver = $fqsenResolver ?: new FqsenResolver();
$this->typeParser = new TypeParser(new ConstExprParser());
$this->lexer = new Lexer();
}
/**
@@ -165,9 +186,9 @@ final class TypeResolver
* This method only works as expected if the namespace and aliases are set;
* no dynamic reflection is being performed here.
*
* @uses Context::getNamespace() to determine with what to prefix the type name.
* @uses Context::getNamespaceAliases() to check whether the first part of the relative type name should not be
* replaced with another namespace.
* @uses Context::getNamespace() to determine with what to prefix the type name.
*
* @param string $type The relative or absolute type.
*/
@@ -182,178 +203,219 @@ final class TypeResolver
$context = new Context('');
}
// split the type string into tokens `|`, `?`, `<`, `>`, `,`, `(`, `)`, `[]`, '<', '>' and type names
$tokens = preg_split(
'/(\\||\\?|<|>|&|, ?|\\(|\\)|\\[\\]+)/',
$type,
-1,
PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE
);
$tokens = $this->lexer->tokenize($type);
$tokenIterator = new TokenIterator($tokens);
if ($tokens === false) {
throw new InvalidArgumentException('Unable to split the type string "' . $type . '" into tokens');
}
$ast = $this->parse($tokenIterator);
$type = $this->createType($ast, $context);
/** @var ArrayIterator<int, string|null> $tokenIterator */
$tokenIterator = new ArrayIterator($tokens);
return $this->parseTypes($tokenIterator, $context, self::PARSER_IN_COMPOUND);
return $this->tryParseRemainingCompoundTypes($tokenIterator, $context, $type);
}
/**
* Analyse each tokens and creates types
*
* @param ArrayIterator<int, string|null> $tokens the iterator on tokens
* @param int $parserContext on of self::PARSER_* constants, indicating
* the context where we are in the parsing
*/
private function parseTypes(ArrayIterator $tokens, Context $context, int $parserContext): Type
public function createType(?TypeNode $type, Context $context): Type
{
$types = [];
$token = '';
$compoundToken = '|';
while ($tokens->valid()) {
$token = $tokens->current();
if ($token === null) {
throw new RuntimeException(
'Unexpected nullable character'
);
}
if ($type === null) {
return new Mixed_();
}
if ($token === '|' || $token === '&') {
if (count($types) === 0) {
switch (get_class($type)) {
case ArrayTypeNode::class:
return new Array_(
$this->createType($type->type, $context)
);
case ArrayShapeNode::class:
return new ArrayShape(
...array_map(
fn (ArrayShapeItemNode $item) => new ArrayShapeItem(
(string) $item->keyName,
$this->createType($item->valueType, $context),
$item->optional
),
$type->items
)
);
case CallableTypeNode::class:
return $this->createFromCallable($type, $context);
case ConstTypeNode::class:
return $this->createFromConst($type, $context);
case GenericTypeNode::class:
return $this->createFromGeneric($type, $context);
case IdentifierTypeNode::class:
return $this->resolveSingleType($type->name, $context);
case IntersectionTypeNode::class:
return new Intersection(
array_filter(
array_map(
function (TypeNode $nestedType) use ($context) {
$type = $this->createType($nestedType, $context);
if ($type instanceof AggregatedType) {
return new Expression($type);
}
return $type;
},
$type->types
)
)
);
case NullableTypeNode::class:
$nestedType = $this->createType($type->type, $context);
return new Nullable($nestedType);
case UnionTypeNode::class:
return new Compound(
array_filter(
array_map(
function (TypeNode $nestedType) use ($context) {
$type = $this->createType($nestedType, $context);
if ($type instanceof AggregatedType) {
return new Expression($type);
}
return $type;
},
$type->types
)
)
);
case ThisTypeNode::class:
return new This();
case ConditionalTypeNode::class:
case ConditionalTypeForParameterNode::class:
case OffsetAccessTypeNode::class:
default:
return new Mixed_();
}
}
private function createFromGeneric(GenericTypeNode $type, Context $context): Type
{
switch (strtolower($type->type->name)) {
case 'array':
return $this->createArray($type->genericTypes, $context);
case 'class-string':
$subType = $this->createType($type->genericTypes[0], $context);
if (!$subType instanceof Object_ || $subType->getFqsen() === null) {
throw new RuntimeException(
'A type is missing before a type separator'
$subType . ' is not a class string'
);
}
if (
!in_array($parserContext, [
self::PARSER_IN_COMPOUND,
self::PARSER_IN_ARRAY_EXPRESSION,
self::PARSER_IN_COLLECTION_EXPRESSION,
self::PARSER_IN_NULLABLE,
], true)
) {
return new ClassString(
$subType->getFqsen()
);
case 'interface-string':
$subType = $this->createType($type->genericTypes[0], $context);
if (!$subType instanceof Object_ || $subType->getFqsen() === null) {
throw new RuntimeException(
'Unexpected type separator'
$subType . ' is not a class string'
);
}
$compoundToken = $token;
$tokens->next();
} elseif ($token === '?') {
if (
!in_array($parserContext, [
self::PARSER_IN_COMPOUND,
self::PARSER_IN_ARRAY_EXPRESSION,
self::PARSER_IN_COLLECTION_EXPRESSION,
self::PARSER_IN_NULLABLE,
], true)
) {
throw new RuntimeException(
'Unexpected nullable character'
return new InterfaceString(
$subType->getFqsen()
);
case 'list':
return new List_(
$this->createType($type->genericTypes[0], $context)
);
case 'non-empty-list':
return new NonEmptyList(
$this->createType($type->genericTypes[0], $context)
);
case 'int':
if (isset($type->genericTypes[1]) === false) {
throw new RuntimeException('int<min,max> has not the correct format');
}
return new IntegerRange(
(string) $type->genericTypes[0],
(string) $type->genericTypes[1],
);
case 'iterable':
return new Iterable_(
...array_reverse(
array_map(
fn (TypeNode $genericType) => $this->createType($genericType, $context),
$type->genericTypes
)
)
);
default:
$collectionType = $this->createType($type->type, $context);
if ($collectionType instanceof Object_ === false) {
throw new RuntimeException(sprintf('%s is not a collection', (string) $collectionType));
}
return new Collection(
$collectionType->getFqsen(),
...array_reverse(
array_map(
fn (TypeNode $genericType) => $this->createType($genericType, $context),
$type->genericTypes
)
)
);
}
}
private function createFromCallable(CallableTypeNode $type, Context $context): Callable_
{
return new Callable_(
array_map(
function (CallableTypeParameterNode $param) use ($context) {
return new CallableParameter(
$this->createType($param->type, $context),
$param->parameterName !== '' ? trim($param->parameterName, '$') : null,
$param->isReference,
$param->isVariadic,
$param->isOptional
);
}
},
$type->parameters
),
$this->createType($type->returnType, $context),
);
}
$tokens->next();
$type = $this->parseTypes($tokens, $context, self::PARSER_IN_NULLABLE);
$types[] = new Nullable($type);
} elseif ($token === '(') {
$tokens->next();
$type = $this->parseTypes($tokens, $context, self::PARSER_IN_ARRAY_EXPRESSION);
private function createFromConst(ConstTypeNode $type, Context $context): Type
{
switch (true) {
case $type->constExpr instanceof ConstExprIntegerNode:
return new IntegerValue((int) $type->constExpr->value);
$token = $tokens->current();
if ($token === null) { // Someone did not properly close their array expression ..
break;
}
case $type->constExpr instanceof ConstExprFloatNode:
return new FloatValue((float) $type->constExpr->value);
$tokens->next();
case $type->constExpr instanceof ConstExprStringNode:
return new StringValue($type->constExpr->value);
$resolvedType = new Expression($type);
$types[] = $resolvedType;
} elseif ($parserContext === self::PARSER_IN_ARRAY_EXPRESSION && isset($token[0]) && $token[0] === ')') {
break;
} elseif ($token === '<') {
if (count($types) === 0) {
throw new RuntimeException(
'Unexpected collection operator "<", class name is missing'
);
}
$classType = array_pop($types);
if ($classType !== null) {
if ((string) $classType === 'class-string') {
$types[] = $this->resolveClassString($tokens, $context);
} elseif ((string) $classType === 'int') {
$types[] = $this->resolveIntRange($tokens);
} elseif ((string) $classType === 'interface-string') {
$types[] = $this->resolveInterfaceString($tokens, $context);
} else {
$types[] = $this->resolveCollection($tokens, $classType, $context);
}
}
$tokens->next();
} elseif (
$parserContext === self::PARSER_IN_COLLECTION_EXPRESSION
&& ($token === '>' || trim($token) === ',')
) {
break;
} elseif ($token === self::OPERATOR_ARRAY) {
$last = array_key_last($types);
if ($last === null) {
throw new InvalidArgumentException('Unexpected array operator');
}
$lastItem = $types[$last];
if ($lastItem instanceof Expression) {
$lastItem = $lastItem->getValueType();
}
$types[$last] = new Array_($lastItem);
$tokens->next();
} else {
$types[] = $this->resolveSingleType($token, $context);
$tokens->next();
}
}
if ($token === '|' || $token === '&') {
throw new RuntimeException(
'A type is missing after a type separator'
);
}
if (count($types) === 0) {
if ($parserContext === self::PARSER_IN_NULLABLE) {
throw new RuntimeException(
'A type is missing after a nullable character'
case $type->constExpr instanceof ConstFetchNode:
return new ConstExpression(
$this->resolve($type->constExpr->className, $context),
$type->constExpr->name
);
}
if ($parserContext === self::PARSER_IN_ARRAY_EXPRESSION) {
throw new RuntimeException(
'A type is missing in an array expression'
);
}
if ($parserContext === self::PARSER_IN_COLLECTION_EXPRESSION) {
throw new RuntimeException(
'A type is missing in a collection expression'
);
}
} elseif (count($types) === 1) {
return current($types);
default:
throw new RuntimeException(sprintf('Unsupported constant type %s', get_class($type)));
}
if ($compoundToken === '|') {
return new Compound(array_values($types));
}
return new Intersection(array_values($types));
}
/**
@@ -475,244 +537,87 @@ final class TypeResolver
return new Object_($this->fqsenResolver->resolve($type, $context));
}
/**
* Resolves class string
*
* @param ArrayIterator<int, (string|null)> $tokens
*/
private function resolveClassString(ArrayIterator $tokens, Context $context): Type
/** @param TypeNode[] $typeNodes */
private function createArray(array $typeNodes, Context $context): Array_
{
$tokens->next();
$types = array_reverse(
array_map(
fn (TypeNode $node) => $this->createType($node, $context),
$typeNodes
)
);
$classType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION);
if (!$classType instanceof Object_ || $classType->getFqsen() === null) {
throw new RuntimeException(
$classType . ' is not a class string'
);
if (isset($types[1]) === false) {
return new Array_(...$types);
}
$token = $tokens->current();
if ($token !== '>') {
if (empty($token)) {
throw new RuntimeException(
'class-string: ">" is missing'
);
if ($this->validArrayKeyType($types[1]) || $types[1] instanceof ArrayKey) {
return new Array_(...$types);
}
if ($types[1] instanceof Compound && $types[1]->getIterator()->count() === 2) {
if ($this->validArrayKeyType($types[1]->get(0)) && $this->validArrayKeyType($types[1]->get(1))) {
return new Array_(...$types);
}
throw new RuntimeException(
'Unexpected character "' . $token . '", ">" is missing'
);
}
return new ClassString($classType->getFqsen());
throw new RuntimeException('An array can have only integers or strings as keys');
}
private function validArrayKeyType(?Type $type): bool
{
return $type instanceof String_ || $type instanceof Integer;
}
private function parse(TokenIterator $tokenIterator): TypeNode
{
try {
$ast = $this->typeParser->parse($tokenIterator);
} catch (ParserException $e) {
throw new RuntimeException($e->getMessage(), 0, $e);
}
return $ast;
}
/**
* Resolves integer ranges
* Will try to parse unsupported type notations by phpstan
*
* @param ArrayIterator<int, (string|null)> $tokens
* The phpstan parser doesn't support the illegal nullable combinations like this library does.
* This method will warn the user about those notations but for bc purposes we will still have it here.
*/
private function resolveIntRange(ArrayIterator $tokens): Type
private function tryParseRemainingCompoundTypes(TokenIterator $tokenIterator, Context $context, Type $type): Type
{
$tokens->next();
$token = '';
$minValue = null;
$maxValue = null;
$commaFound = false;
$tokenCounter = 0;
while ($tokens->valid()) {
$tokenCounter++;
$token = $tokens->current();
if ($token === null) {
throw new RuntimeException(
'Unexpected nullable character'
);
}
$token = trim($token);
if ($token === '>') {
break;
}
if ($token === ',') {
$commaFound = true;
}
if ($commaFound === false && $minValue === null) {
if (is_numeric($token) || $token === 'max' || $token === 'min') {
$minValue = $token;
}
}
if ($commaFound === true && $maxValue === null) {
if (is_numeric($token) || $token === 'max' || $token === 'min') {
$maxValue = $token;
}
}
$tokens->next();
}
if ($token !== '>') {
if (empty($token)) {
throw new RuntimeException(
'interface-string: ">" is missing'
);
}
throw new RuntimeException(
'Unexpected character "' . $token . '", ">" is missing'
);
}
if ($minValue === null || $maxValue === null || $tokenCounter > 4) {
throw new RuntimeException(
'int<min,max> has not the correct format'
);
}
return new IntegerRange($minValue, $maxValue);
}
/**
* Resolves class string
*
* @param ArrayIterator<int, (string|null)> $tokens
*/
private function resolveInterfaceString(ArrayIterator $tokens, Context $context): Type
{
$tokens->next();
$classType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION);
if (!$classType instanceof Object_ || $classType->getFqsen() === null) {
throw new RuntimeException(
$classType . ' is not a interface string'
);
}
$token = $tokens->current();
if ($token !== '>') {
if (empty($token)) {
throw new RuntimeException(
'interface-string: ">" is missing'
);
}
throw new RuntimeException(
'Unexpected character "' . $token . '", ">" is missing'
);
}
return new InterfaceString($classType->getFqsen());
}
/**
* Resolves the collection values and keys
*
* @param ArrayIterator<int, (string|null)> $tokens
*
* @return Array_|Iterable_|Collection
*/
private function resolveCollection(ArrayIterator $tokens, Type $classType, Context $context): Type
{
$isArray = ((string) $classType === 'array');
$isIterable = ((string) $classType === 'iterable');
$isList = ((string) $classType === 'list');
// allow only "array", "iterable" or class name before "<"
if (
!$isArray && !$isIterable && !$isList
&& (!$classType instanceof Object_ || $classType->getFqsen() === null)
$tokenIterator->isCurrentTokenType(Lexer::TOKEN_UNION) ||
$tokenIterator->isCurrentTokenType(Lexer::TOKEN_INTERSECTION)
) {
throw new RuntimeException(
$classType . ' is not a collection'
Deprecation::trigger(
'phpdocumentor/type-resolver',
'https://github.com/phpDocumentor/TypeResolver/issues/184',
'Legacy nullable type detected, please update your code as
you are using nullable types in a docblock. support will be removed in v2.0.0',
);
}
$tokens->next();
$valueType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION);
$keyType = null;
$token = $tokens->current();
if ($token !== null && trim($token) === ',' && !$isList) {
// if we have a comma, then we just parsed the key type, not the value type
$keyType = $valueType;
if ($isArray) {
// check the key type for an "array" collection. We allow only
// strings or integers.
if (
!$keyType instanceof ArrayKey &&
!$keyType instanceof String_ &&
!$keyType instanceof Integer &&
!$keyType instanceof Compound
) {
throw new RuntimeException(
'An array can have only integers or strings as keys'
);
}
if ($keyType instanceof Compound) {
foreach ($keyType->getIterator() as $item) {
if (
!$item instanceof ArrayKey &&
!$item instanceof String_ &&
!$item instanceof Integer
) {
throw new RuntimeException(
'An array can have only integers or strings as keys'
);
}
}
}
$continue = true;
while ($continue) {
$continue = false;
while ($tokenIterator->tryConsumeTokenType(Lexer::TOKEN_UNION)) {
$ast = $this->parse($tokenIterator);
$type2 = $this->createType($ast, $context);
$type = new Compound([$type, $type2]);
$continue = true;
}
$tokens->next();
// now let's parse the value type
$valueType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION);
}
$token = $tokens->current();
if ($token !== '>') {
if (empty($token)) {
throw new RuntimeException(
'Collection: ">" is missing'
);
while ($tokenIterator->tryConsumeTokenType(Lexer::TOKEN_INTERSECTION)) {
$ast = $this->typeParser->parse($tokenIterator);
$type2 = $this->createType($ast, $context);
$type = new Intersection([$type, $type2]);
$continue = true;
}
throw new RuntimeException(
'Unexpected character "' . $token . '", ">" is missing'
);
}
if ($isArray) {
return new Array_($valueType, $keyType);
}
if ($isIterable) {
return new Iterable_($valueType, $keyType);
}
if ($isList) {
return new List_($valueType);
}
if ($classType instanceof Object_) {
return $this->makeCollectionFromObject($classType, $valueType, $keyType);
}
throw new RuntimeException('Invalid $classType provided');
}
/**
* @psalm-pure
*/
private function makeCollectionFromObject(Object_ $object, Type $valueType, ?Type $keyType = null): Collection
{
return new Collection($object->getFqsen(), $valueType, $keyType);
return $type;
}
}

View File

@@ -106,7 +106,7 @@ abstract class AggregatedType implements Type, IteratorAggregate
*/
private function add(Type $type): void
{
if ($type instanceof self) {
if ($type instanceof static) {
foreach ($type->getIterator() as $subType) {
$this->add($subType);
}

View File

@@ -0,0 +1,72 @@
<?php
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*/
declare(strict_types=1);
namespace phpDocumentor\Reflection\Types;
use phpDocumentor\Reflection\Type;
/**
* Value Object representing a Callable parameters.
*
* @psalm-immutable
*/
final class CallableParameter
{
private Type $type;
private bool $isReference;
private bool $isVariadic;
private bool $isOptional;
private ?string $name;
public function __construct(
Type $type,
?string $name = null,
bool $isReference = false,
bool $isVariadic = false,
bool $isOptional = false
) {
$this->type = $type;
$this->isReference = $isReference;
$this->isVariadic = $isVariadic;
$this->isOptional = $isOptional;
$this->name = $name;
}
public function getName(): ?string
{
return $this->name;
}
public function getType(): Type
{
return $this->type;
}
public function isReference(): bool
{
return $this->isReference;
}
public function isVariadic(): bool
{
return $this->isVariadic;
}
public function isOptional(): bool
{
return $this->isOptional;
}
}

View File

@@ -22,6 +22,30 @@ use phpDocumentor\Reflection\Type;
*/
final class Callable_ implements Type
{
private ?Type $returnType;
/** @var CallableParameter[] */
private array $parameters;
/**
* @param CallableParameter[] $parameters
*/
public function __construct(array $parameters = [], ?Type $returnType = null)
{
$this->parameters = $parameters;
$this->returnType = $returnType;
}
/** @return CallableParameter[] */
public function getParameters(): array
{
return $this->parameters;
}
public function getReturnType(): ?Type
{
return $this->returnType;
}
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/

21
vendor/phpstan/phpdoc-parser/LICENSE vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016 Ondřej Mirtes
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.

119
vendor/phpstan/phpdoc-parser/README.md vendored Normal file
View File

@@ -0,0 +1,119 @@
<h1 align="center">PHPDoc Parser for PHPStan</h1>
<p align="center">
<a href="https://github.com/phpstan/phpdoc-parser/actions"><img src="https://github.com/phpstan/phpdoc-parser/workflows/Build/badge.svg" alt="Build Status"></a>
<a href="https://packagist.org/packages/phpstan/phpdoc-parser"><img src="https://poser.pugx.org/phpstan/phpdoc-parser/v/stable" alt="Latest Stable Version"></a>
<a href="https://choosealicense.com/licenses/mit/"><img src="https://poser.pugx.org/phpstan/phpstan/license" alt="License"></a>
<a href="https://phpstan.org/"><img src="https://img.shields.io/badge/PHPStan-enabled-brightgreen.svg?style=flat" alt="PHPStan Enabled"></a>
</p>
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.
* [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
<?php
require_once __DIR__ . '/vendor/autoload.php';
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Lexer\Lexer;
use PHPStan\PhpDocParser\Parser\ConstExprParser;
use PHPStan\PhpDocParser\Parser\PhpDocParser;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use PHPStan\PhpDocParser\Parser\TypeParser;
// basic setup
$lexer = new Lexer();
$constExprParser = new ConstExprParser();
$typeParser = new TypeParser($constExprParser);
$phpDocParser = new PhpDocParser($typeParser, $constExprParser);
// parsing and reading a PHPDoc string
$tokens = new TokenIterator($lexer->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
<?php
require_once __DIR__ . '/vendor/autoload.php';
use PHPStan\PhpDocParser\Ast\NodeTraverser;
use PHPStan\PhpDocParser\Ast\NodeVisitor\CloningVisitor;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Lexer\Lexer;
use PHPStan\PhpDocParser\Parser\ConstExprParser;
use PHPStan\PhpDocParser\Parser\PhpDocParser;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use PHPStan\PhpDocParser\Parser\TypeParser;
use PHPStan\PhpDocParser\Printer\Printer;
// basic setup with enabled required lexer attributes
$usedAttributes = ['lines' => 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 */
[$newPhpDocNode] = $cloningTraverser->traverse([$phpDocNode]);
// change something in $newPhpDocNode
$newPhpDocNode->getParamTagValues()[0]->type = new IdentifierTypeNode('Ipsum');
// print changed PHPDoc
$printer = new Printer();
$newPhpDoc = $printer->printFormatPreserving($newPhpDocNode, $phpDocNode, $tokens);
echo $newPhpDoc; // '/** @param Ipsum $a */'
```
## Code of Conduct
This project adheres to a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project and its community, you are expected to uphold this code.
## Building
Initially you need to run `composer install`, or `composer update` in case you aren't working in a folder which was built before.
Afterwards you can either run the whole build including linting and coding standards using
make
or run only tests using
make tests

View File

@@ -0,0 +1,43 @@
{
"name": "phpstan/phpdoc-parser",
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"license": "MIT",
"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",
"phpstan/phpstan-phpunit": "^1.1",
"phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": "^9.5",
"symfony/process": "^5.2"
},
"config": {
"platform": {
"php": "7.4.6"
},
"sort-packages": true,
"allow-plugins": {
"phpstan/extension-installer": true
}
},
"autoload": {
"psr-4": {
"PHPStan\\PhpDocParser\\": [
"src/"
]
}
},
"autoload-dev": {
"psr-4": {
"PHPStan\\PhpDocParser\\": [
"tests/PHPStan"
]
}
},
"minimum-stability": "dev",
"prefer-stable": true
}

View File

@@ -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, array\\<PHPStan\\\\PhpDocParser\\\\Ast\\\\Node\\>\\|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

View File

@@ -0,0 +1,34 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast;
/**
* Inspired by https://github.com/nikic/PHP-Parser/tree/36a6dcd04e7b0285e8f0868f44bd4927802f7df1
*
* Copyright (c) 2011, Nikita Popov
* All rights reserved.
*/
abstract class AbstractNodeVisitor implements NodeVisitor // phpcs:ignore SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming.SuperfluousPrefix
{
public function beforeTraverse(array $nodes): ?array
{
return null;
}
public function enterNode(Node $node)
{
return null;
}
public function leaveNode(Node $node)
{
return null;
}
public function afterTraverse(array $nodes): ?array
{
return null;
}
}

View File

@@ -0,0 +1,16 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast;
final class Attribute
{
public const START_LINE = 'startLine';
public const END_LINE = 'endLine';
public const START_INDEX = 'startIndex';
public const END_INDEX = 'endIndex';
public const ORIGINAL_NODE = 'originalNode';
}

View File

@@ -0,0 +1,36 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\ConstExpr;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function sprintf;
class ConstExprArrayItemNode implements ConstExprNode
{
use NodeAttributes;
/** @var ConstExprNode|null */
public $key;
/** @var ConstExprNode */
public $value;
public function __construct(?ConstExprNode $key, ConstExprNode $value)
{
$this->key = $key;
$this->value = $value;
}
public function __toString(): string
{
if ($this->key !== null) {
return sprintf('%s => %s', $this->key, $this->value);
}
return (string) $this->value;
}
}

View File

@@ -0,0 +1,30 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\ConstExpr;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function implode;
class ConstExprArrayNode implements ConstExprNode
{
use NodeAttributes;
/** @var ConstExprArrayItemNode[] */
public $items;
/**
* @param ConstExprArrayItemNode[] $items
*/
public function __construct(array $items)
{
$this->items = $items;
}
public function __toString(): string
{
return '[' . implode(', ', $this->items) . ']';
}
}

View File

@@ -0,0 +1,17 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\ConstExpr;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
class ConstExprFalseNode implements ConstExprNode
{
use NodeAttributes;
public function __toString(): string
{
return 'false';
}
}

View File

@@ -0,0 +1,26 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\ConstExpr;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
class ConstExprFloatNode implements ConstExprNode
{
use NodeAttributes;
/** @var string */
public $value;
public function __construct(string $value)
{
$this->value = $value;
}
public function __toString(): string
{
return $this->value;
}
}

View File

@@ -0,0 +1,26 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\ConstExpr;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
class ConstExprIntegerNode implements ConstExprNode
{
use NodeAttributes;
/** @var string */
public $value;
public function __construct(string $value)
{
$this->value = $value;
}
public function __toString(): string
{
return $this->value;
}
}

View File

@@ -0,0 +1,10 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\ConstExpr;
use PHPStan\PhpDocParser\Ast\Node;
interface ConstExprNode extends Node
{
}

View File

@@ -0,0 +1,17 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\ConstExpr;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
class ConstExprNullNode implements ConstExprNode
{
use NodeAttributes;
public function __toString(): string
{
return 'null';
}
}

View File

@@ -0,0 +1,26 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\ConstExpr;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
class ConstExprStringNode implements ConstExprNode
{
use NodeAttributes;
/** @var string */
public $value;
public function __construct(string $value)
{
$this->value = $value;
}
public function __toString(): string
{
return $this->value;
}
}

View File

@@ -0,0 +1,17 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\ConstExpr;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
class ConstExprTrueNode implements ConstExprNode
{
use NodeAttributes;
public function __toString(): string
{
return 'true';
}
}

View File

@@ -0,0 +1,35 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\ConstExpr;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
class ConstFetchNode implements ConstExprNode
{
use NodeAttributes;
/** @var string class name for class constants or empty string for non-class constants */
public $className;
/** @var string */
public $name;
public function __construct(string $className, string $name)
{
$this->className = $className;
$this->name = $name;
}
public function __toString(): string
{
if ($this->className === '') {
return $this->name;
}
return "{$this->className}::{$this->name}";
}
}

View File

@@ -0,0 +1,78 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\ConstExpr;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function addcslashes;
use function assert;
use function dechex;
use function ord;
use function preg_replace_callback;
use function sprintf;
use function str_pad;
use function strlen;
use const STR_PAD_LEFT;
class QuoteAwareConstExprStringNode extends ConstExprStringNode implements ConstExprNode
{
public const SINGLE_QUOTED = 1;
public const DOUBLE_QUOTED = 2;
use NodeAttributes;
/** @var self::SINGLE_QUOTED|self::DOUBLE_QUOTED */
public $quoteType;
/**
* @param self::SINGLE_QUOTED|self::DOUBLE_QUOTED $quoteType
*/
public function __construct(string $value, int $quoteType)
{
parent::__construct($value);
$this->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
| (?<![\xC2-\xDF]|[\xE0-\xEF]|[\xE0-\xEF][\x80-\xBF]|[\xF0-\xF4]|[\xF0-\xF4][\x80-\xBF]|[\xF0-\xF4][\x80-\xBF]{2})[\x80-\xBF] # Overlong Sequence
| (?<=[\xE0-\xEF])[\x80-\xBF](?![\x80-\xBF]) # Short 3 byte sequence
| (?<=[\xF0-\xF4])[\x80-\xBF](?![\x80-\xBF]{2}) # Short 4 byte sequence
| (?<=[\xF0-\xF4][\x80-\xBF])[\x80-\xBF](?![\x80-\xBF]) # Short 4 byte sequence (2)
)/x';
return preg_replace_callback($regex, static function ($matches) {
assert(strlen($matches[0]) === 1);
$hex = dechex(ord($matches[0]));
return '\\x' . str_pad($hex, 2, '0', STR_PAD_LEFT);
}, $escaped);
}
}

View File

@@ -0,0 +1,22 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast;
interface Node
{
public function __toString(): string;
/**
* @param mixed $value
*/
public function setAttribute(string $key, $value): void;
public function hasAttribute(string $key): bool;
/**
* @return mixed
*/
public function getAttribute(string $key);
}

View File

@@ -0,0 +1,38 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast;
use function array_key_exists;
trait NodeAttributes
{
/** @var array<string, mixed> */
private $attributes = [];
/**
* @param mixed $value
*/
public function setAttribute(string $key, $value): void
{
$this->attributes[$key] = $value;
}
public function hasAttribute(string $key): bool
{
return array_key_exists($key, $this->attributes);
}
/**
* @return mixed
*/
public function getAttribute(string $key)
{
if ($this->hasAttribute($key)) {
return $this->attributes[$key];
}
return null;
}
}

View File

@@ -0,0 +1,312 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast;
use LogicException;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocChildNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use function array_keys;
use function array_pop;
use function array_splice;
use function count;
use function get_class;
use function get_object_vars;
use function gettype;
use function is_array;
use function sprintf;
/**
* Inspired by https://github.com/nikic/PHP-Parser/tree/36a6dcd04e7b0285e8f0868f44bd4927802f7df1
*
* Copyright (c) 2011, Nikita Popov
* All rights reserved.
*/
final class NodeTraverser
{
/**
* If NodeVisitor::enterNode() returns DONT_TRAVERSE_CHILDREN, child nodes
* of the current node will not be traversed for any visitors.
*
* For subsequent visitors enterNode() will still be called on the current
* node and leaveNode() will also be invoked for the current node.
*/
public const DONT_TRAVERSE_CHILDREN = 1;
/**
* If NodeVisitor::enterNode() or NodeVisitor::leaveNode() returns
* STOP_TRAVERSAL, traversal is aborted.
*
* The afterTraverse() method will still be invoked.
*/
public const STOP_TRAVERSAL = 2;
/**
* If NodeVisitor::leaveNode() returns REMOVE_NODE for a node that occurs
* in an array, it will be removed from the array.
*
* For subsequent visitors leaveNode() will still be invoked for the
* removed node.
*/
public const REMOVE_NODE = 3;
/**
* If NodeVisitor::enterNode() returns DONT_TRAVERSE_CURRENT_AND_CHILDREN, child nodes
* of the current node will not be traversed for any visitors.
*
* For subsequent visitors enterNode() will not be called as well.
* leaveNode() will be invoked for visitors that has enterNode() method invoked.
*/
public const DONT_TRAVERSE_CURRENT_AND_CHILDREN = 4;
/** @var list<NodeVisitor> Visitors */
private $visitors = [];
/** @var bool Whether traversal should be stopped */
private $stopTraversal;
/**
* @param list<NodeVisitor> $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)));
}
}
}

View File

@@ -0,0 +1,87 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast;
/**
* Inspired by https://github.com/nikic/PHP-Parser/tree/36a6dcd04e7b0285e8f0868f44bd4927802f7df1
*
* Copyright (c) 2011, Nikita Popov
* All rights reserved.
*/
interface NodeVisitor
{
/**
* Called once before 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 beforeTraverse(array $nodes): ?array;
/**
* Called when entering a node.
*
* Return value semantics:
* * null
* => $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;
}

View File

@@ -0,0 +1,20 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\NodeVisitor;
use PHPStan\PhpDocParser\Ast\AbstractNodeVisitor;
use PHPStan\PhpDocParser\Ast\Attribute;
use PHPStan\PhpDocParser\Ast\Node;
final class CloningVisitor extends AbstractNodeVisitor
{
public function enterNode(Node $originalNode)
{
$node = clone $originalNode;
$node->setAttribute(Attribute::ORIGINAL_NODE, $originalNode);
return $node;
}
}

View File

@@ -0,0 +1,50 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use function trim;
class AssertTagMethodValueNode implements PhpDocTagValueNode
{
use NodeAttributes;
/** @var TypeNode */
public $type;
/** @var string */
public $parameter;
/** @var string */
public $method;
/** @var bool */
public $isNegated;
/** @var bool */
public $isEquality;
/** @var string (may be empty) */
public $description;
public function __construct(TypeNode $type, string $parameter, string $method, bool $isNegated, string $description, bool $isEquality = false)
{
$this->type = $type;
$this->parameter = $parameter;
$this->method = $method;
$this->isNegated = $isNegated;
$this->isEquality = $isEquality;
$this->description = $description;
}
public function __toString(): string
{
$isNegated = $this->isNegated ? '!' : '';
$isEquality = $this->isEquality ? '=' : '';
return trim("{$isNegated}{$isEquality}{$this->type} {$this->parameter}->{$this->method}() {$this->description}");
}
}

View File

@@ -0,0 +1,50 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use function trim;
class AssertTagPropertyValueNode implements PhpDocTagValueNode
{
use NodeAttributes;
/** @var TypeNode */
public $type;
/** @var string */
public $parameter;
/** @var string */
public $property;
/** @var bool */
public $isNegated;
/** @var bool */
public $isEquality;
/** @var string (may be empty) */
public $description;
public function __construct(TypeNode $type, string $parameter, string $property, bool $isNegated, string $description, bool $isEquality = false)
{
$this->type = $type;
$this->parameter = $parameter;
$this->property = $property;
$this->isNegated = $isNegated;
$this->isEquality = $isEquality;
$this->description = $description;
}
public function __toString(): string
{
$isNegated = $this->isNegated ? '!' : '';
$isEquality = $this->isEquality ? '=' : '';
return trim("{$isNegated}{$isEquality}{$this->type} {$this->parameter}->{$this->property} {$this->description}");
}
}

View File

@@ -0,0 +1,46 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use function trim;
class AssertTagValueNode implements PhpDocTagValueNode
{
use NodeAttributes;
/** @var TypeNode */
public $type;
/** @var string */
public $parameter;
/** @var bool */
public $isNegated;
/** @var bool */
public $isEquality;
/** @var string (may be empty) */
public $description;
public function __construct(TypeNode $type, string $parameter, bool $isNegated, string $description, bool $isEquality = false)
{
$this->type = $type;
$this->parameter = $parameter;
$this->isNegated = $isNegated;
$this->isEquality = $isEquality;
$this->description = $description;
}
public function __toString(): string
{
$isNegated = $this->isNegated ? '!' : '';
$isEquality = $this->isEquality ? '=' : '';
return trim("{$isNegated}{$isEquality}{$this->type} {$this->parameter} {$this->description}");
}
}

View File

@@ -0,0 +1,27 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function trim;
class DeprecatedTagValueNode implements PhpDocTagValueNode
{
use NodeAttributes;
/** @var string (may be empty) */
public $description;
public function __construct(string $description)
{
$this->description = $description;
}
public function __toString(): string
{
return trim($this->description);
}
}

View File

@@ -0,0 +1,32 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
use function trim;
class ExtendsTagValueNode implements PhpDocTagValueNode
{
use NodeAttributes;
/** @var GenericTypeNode */
public $type;
/** @var string (may be empty) */
public $description;
public function __construct(GenericTypeNode $type, string $description)
{
$this->type = $type;
$this->description = $description;
}
public function __toString(): string
{
return trim("{$this->type} {$this->description}");
}
}

View File

@@ -0,0 +1,26 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
class GenericTagValueNode implements PhpDocTagValueNode
{
use NodeAttributes;
/** @var string (may be empty) */
public $value;
public function __construct(string $value)
{
$this->value = $value;
}
public function __toString(): string
{
return $this->value;
}
}

View File

@@ -0,0 +1,32 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
use function trim;
class ImplementsTagValueNode implements PhpDocTagValueNode
{
use NodeAttributes;
/** @var GenericTypeNode */
public $type;
/** @var string (may be empty) */
public $description;
public function __construct(GenericTypeNode $type, string $description)
{
$this->type = $type;
$this->description = $description;
}
public function __toString(): string
{
return trim("{$this->type} {$this->description}");
}
}

View File

@@ -0,0 +1,53 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Parser\ParserException;
use function sprintf;
use function trigger_error;
use const E_USER_WARNING;
/**
* @property ParserException $exception
*/
class InvalidTagValueNode implements PhpDocTagValueNode
{
use NodeAttributes;
/** @var string (may be empty) */
public $value;
/** @var mixed[] */
private $exceptionArgs;
public function __construct(string $value, ParserException $exception)
{
$this->value = $value;
$this->exceptionArgs = [
$exception->getCurrentTokenValue(),
$exception->getCurrentTokenType(),
$exception->getCurrentOffset(),
$exception->getExpectedTokenType(),
$exception->getExpectedTokenValue(),
$exception->getCurrentTokenLine(),
];
}
public function __get(string $name): ?ParserException
{
if ($name !== 'exception') {
trigger_error(sprintf('Undefined property: %s::$%s', self::class, $name), E_USER_WARNING);
return null;
}
return new ParserException(...$this->exceptionArgs);
}
public function __toString(): string
{
return $this->value;
}
}

View File

@@ -0,0 +1,58 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use function count;
use function implode;
class MethodTagValueNode implements PhpDocTagValueNode
{
use NodeAttributes;
/** @var bool */
public $isStatic;
/** @var TypeNode|null */
public $returnType;
/** @var string */
public $methodName;
/** @var TemplateTagValueNode[] */
public $templateTypes;
/** @var MethodTagValueParameterNode[] */
public $parameters;
/** @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;
$this->returnType = $returnType;
$this->methodName = $methodName;
$this->parameters = $parameters;
$this->description = $description;
$this->templateTypes = $templateTypes;
}
public function __toString(): string
{
$static = $this->isStatic ? 'static ' : '';
$returnType = $this->returnType !== null ? "{$this->returnType} " : '';
$parameters = implode(', ', $this->parameters);
$description = $this->description !== '' ? " {$this->description}" : '';
$templateTypes = count($this->templateTypes) > 0 ? '<' . implode(', ', $this->templateTypes) . '>' : '';
return "{$static}{$returnType}{$this->methodName}{$templateTypes}({$parameters}){$description}";
}
}

View File

@@ -0,0 +1,49 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNode;
use PHPStan\PhpDocParser\Ast\Node;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
class MethodTagValueParameterNode implements Node
{
use NodeAttributes;
/** @var TypeNode|null */
public $type;
/** @var bool */
public $isReference;
/** @var bool */
public $isVariadic;
/** @var string */
public $parameterName;
/** @var ConstExprNode|null */
public $defaultValue;
public function __construct(?TypeNode $type, bool $isReference, bool $isVariadic, string $parameterName, ?ConstExprNode $defaultValue)
{
$this->type = $type;
$this->isReference = $isReference;
$this->isVariadic = $isVariadic;
$this->parameterName = $parameterName;
$this->defaultValue = $defaultValue;
}
public function __toString(): string
{
$type = $this->type !== null ? "{$this->type} " : '';
$isReference = $this->isReference ? '&' : '';
$isVariadic = $this->isVariadic ? '...' : '';
$default = $this->defaultValue !== null ? " = {$this->defaultValue}" : '';
return "{$type}{$isReference}{$isVariadic}{$this->parameterName}{$default}";
}
}

View File

@@ -0,0 +1,32 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use function trim;
class MixinTagValueNode implements PhpDocTagValueNode
{
use NodeAttributes;
/** @var TypeNode */
public $type;
/** @var string (may be empty) */
public $description;
public function __construct(TypeNode $type, string $description)
{
$this->type = $type;
$this->description = $description;
}
public function __toString(): string
{
return trim("{$this->type} {$this->description}");
}
}

View File

@@ -0,0 +1,35 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use function trim;
class ParamOutTagValueNode implements PhpDocTagValueNode
{
use NodeAttributes;
/** @var TypeNode */
public $type;
/** @var string */
public $parameterName;
/** @var string (may be empty) */
public $description;
public function __construct(TypeNode $type, string $parameterName, string $description)
{
$this->type = $type;
$this->parameterName = $parameterName;
$this->description = $description;
}
public function __toString(): string
{
return trim("{$this->type} {$this->parameterName} {$this->description}");
}
}

View File

@@ -0,0 +1,46 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use function trim;
class ParamTagValueNode implements PhpDocTagValueNode
{
use NodeAttributes;
/** @var TypeNode */
public $type;
/** @var bool */
public $isReference;
/** @var bool */
public $isVariadic;
/** @var string */
public $parameterName;
/** @var string (may be empty) */
public $description;
public function __construct(TypeNode $type, bool $isVariadic, string $parameterName, string $description, bool $isReference = false)
{
$this->type = $type;
$this->isReference = $isReference;
$this->isVariadic = $isVariadic;
$this->parameterName = $parameterName;
$this->description = $description;
}
public function __toString(): string
{
$reference = $this->isReference ? '&' : '';
$variadic = $this->isVariadic ? '...' : '';
return trim("{$this->type} {$reference}{$variadic}{$this->parameterName} {$this->description}");
}
}

View File

@@ -0,0 +1,10 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\Node;
interface PhpDocChildNode extends Node
{
}

View File

@@ -0,0 +1,371 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\Node;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function array_column;
use function array_filter;
use function array_map;
use function implode;
class PhpDocNode implements Node
{
use NodeAttributes;
/** @var PhpDocChildNode[] */
public $children;
/**
* @param PhpDocChildNode[] $children
*/
public function __construct(array $children)
{
$this->children = $children;
}
/**
* @return PhpDocTagNode[]
*/
public function getTags(): array
{
return array_filter($this->children, static function (PhpDocChildNode $child): bool {
return $child instanceof PhpDocTagNode;
});
}
/**
* @return PhpDocTagNode[]
*/
public function getTagsByName(string $tagName): array
{
return array_filter($this->getTags(), static function (PhpDocTagNode $tag) use ($tagName): bool {
return $tag->name === $tagName;
});
}
/**
* @return VarTagValueNode[]
*/
public function getVarTagValues(string $tagName = '@var'): array
{
return array_filter(
array_column($this->getTagsByName($tagName), 'value'),
static function (PhpDocTagValueNode $value): bool {
return $value instanceof VarTagValueNode;
}
);
}
/**
* @return ParamTagValueNode[]
*/
public function getParamTagValues(string $tagName = '@param'): array
{
return array_filter(
array_column($this->getTagsByName($tagName), 'value'),
static function (PhpDocTagValueNode $value): bool {
return $value instanceof ParamTagValueNode;
}
);
}
/**
* @return TypelessParamTagValueNode[]
*/
public function getTypelessParamTagValues(string $tagName = '@param'): array
{
return array_filter(
array_column($this->getTagsByName($tagName), 'value'),
static function (PhpDocTagValueNode $value): bool {
return $value instanceof TypelessParamTagValueNode;
}
);
}
/**
* @return TemplateTagValueNode[]
*/
public function getTemplateTagValues(string $tagName = '@template'): array
{
return array_filter(
array_column($this->getTagsByName($tagName), 'value'),
static function (PhpDocTagValueNode $value): bool {
return $value instanceof TemplateTagValueNode;
}
);
}
/**
* @return ExtendsTagValueNode[]
*/
public function getExtendsTagValues(string $tagName = '@extends'): array
{
return array_filter(
array_column($this->getTagsByName($tagName), 'value'),
static function (PhpDocTagValueNode $value): bool {
return $value instanceof ExtendsTagValueNode;
}
);
}
/**
* @return ImplementsTagValueNode[]
*/
public function getImplementsTagValues(string $tagName = '@implements'): array
{
return array_filter(
array_column($this->getTagsByName($tagName), 'value'),
static function (PhpDocTagValueNode $value): bool {
return $value instanceof ImplementsTagValueNode;
}
);
}
/**
* @return UsesTagValueNode[]
*/
public function getUsesTagValues(string $tagName = '@use'): array
{
return array_filter(
array_column($this->getTagsByName($tagName), 'value'),
static function (PhpDocTagValueNode $value): bool {
return $value instanceof UsesTagValueNode;
}
);
}
/**
* @return ReturnTagValueNode[]
*/
public function getReturnTagValues(string $tagName = '@return'): array
{
return array_filter(
array_column($this->getTagsByName($tagName), 'value'),
static function (PhpDocTagValueNode $value): bool {
return $value instanceof ReturnTagValueNode;
}
);
}
/**
* @return ThrowsTagValueNode[]
*/
public function getThrowsTagValues(string $tagName = '@throws'): array
{
return array_filter(
array_column($this->getTagsByName($tagName), 'value'),
static function (PhpDocTagValueNode $value): bool {
return $value instanceof ThrowsTagValueNode;
}
);
}
/**
* @return MixinTagValueNode[]
*/
public function getMixinTagValues(string $tagName = '@mixin'): array
{
return array_filter(
array_column($this->getTagsByName($tagName), 'value'),
static function (PhpDocTagValueNode $value): bool {
return $value instanceof MixinTagValueNode;
}
);
}
/**
* @return DeprecatedTagValueNode[]
*/
public function getDeprecatedTagValues(): array
{
return array_filter(
array_column($this->getTagsByName('@deprecated'), 'value'),
static function (PhpDocTagValueNode $value): bool {
return $value instanceof DeprecatedTagValueNode;
}
);
}
/**
* @return PropertyTagValueNode[]
*/
public function getPropertyTagValues(string $tagName = '@property'): array
{
return array_filter(
array_column($this->getTagsByName($tagName), 'value'),
static function (PhpDocTagValueNode $value): bool {
return $value instanceof PropertyTagValueNode;
}
);
}
/**
* @return PropertyTagValueNode[]
*/
public function getPropertyReadTagValues(string $tagName = '@property-read'): array
{
return array_filter(
array_column($this->getTagsByName($tagName), 'value'),
static function (PhpDocTagValueNode $value): bool {
return $value instanceof PropertyTagValueNode;
}
);
}
/**
* @return PropertyTagValueNode[]
*/
public function getPropertyWriteTagValues(string $tagName = '@property-write'): array
{
return array_filter(
array_column($this->getTagsByName($tagName), 'value'),
static function (PhpDocTagValueNode $value): bool {
return $value instanceof PropertyTagValueNode;
}
);
}
/**
* @return MethodTagValueNode[]
*/
public function getMethodTagValues(string $tagName = '@method'): array
{
return array_filter(
array_column($this->getTagsByName($tagName), 'value'),
static function (PhpDocTagValueNode $value): bool {
return $value instanceof MethodTagValueNode;
}
);
}
/**
* @return TypeAliasTagValueNode[]
*/
public function getTypeAliasTagValues(string $tagName = '@phpstan-type'): array
{
return array_filter(
array_column($this->getTagsByName($tagName), 'value'),
static function (PhpDocTagValueNode $value): bool {
return $value instanceof TypeAliasTagValueNode;
}
);
}
/**
* @return TypeAliasImportTagValueNode[]
*/
public function getTypeAliasImportTagValues(string $tagName = '@phpstan-import-type'): array
{
return array_filter(
array_column($this->getTagsByName($tagName), 'value'),
static function (PhpDocTagValueNode $value): bool {
return $value instanceof TypeAliasImportTagValueNode;
}
);
}
/**
* @return AssertTagValueNode[]
*/
public function getAssertTagValues(string $tagName = '@phpstan-assert'): array
{
return array_filter(
array_column($this->getTagsByName($tagName), 'value'),
static function (PhpDocTagValueNode $value): bool {
return $value instanceof AssertTagValueNode;
}
);
}
/**
* @return AssertTagPropertyValueNode[]
*/
public function getAssertPropertyTagValues(string $tagName = '@phpstan-assert'): array
{
return array_filter(
array_column($this->getTagsByName($tagName), 'value'),
static function (PhpDocTagValueNode $value): bool {
return $value instanceof AssertTagPropertyValueNode;
}
);
}
/**
* @return AssertTagMethodValueNode[]
*/
public function getAssertMethodTagValues(string $tagName = '@phpstan-assert'): array
{
return array_filter(
array_column($this->getTagsByName($tagName), 'value'),
static function (PhpDocTagValueNode $value): bool {
return $value instanceof AssertTagMethodValueNode;
}
);
}
/**
* @return SelfOutTagValueNode[]
*/
public function getSelfOutTypeTagValues(string $tagName = '@phpstan-this-out'): array
{
return array_filter(
array_column($this->getTagsByName($tagName), 'value'),
static function (PhpDocTagValueNode $value): bool {
return $value instanceof SelfOutTagValueNode;
}
);
}
/**
* @return ParamOutTagValueNode[]
*/
public function getParamOutTypeTagValues(string $tagName = '@param-out'): array
{
return array_filter(
array_column($this->getTagsByName($tagName), 'value'),
static function (PhpDocTagValueNode $value): bool {
return $value instanceof ParamOutTagValueNode;
}
);
}
public function __toString(): string
{
$children = array_map(
static function (PhpDocChildNode $child): string {
$s = (string) $child;
return $s === '' ? '' : ' ' . $s;
},
$this->children
);
return "/**\n *" . implode("\n *", $children) . "\n */";
}
}

View File

@@ -0,0 +1,31 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function trim;
class PhpDocTagNode implements PhpDocChildNode
{
use NodeAttributes;
/** @var string */
public $name;
/** @var PhpDocTagValueNode */
public $value;
public function __construct(string $name, PhpDocTagValueNode $value)
{
$this->name = $name;
$this->value = $value;
}
public function __toString(): string
{
return trim("{$this->name} {$this->value}");
}
}

View File

@@ -0,0 +1,10 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\Node;
interface PhpDocTagValueNode extends Node
{
}

View File

@@ -0,0 +1,26 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
class PhpDocTextNode implements PhpDocChildNode
{
use NodeAttributes;
/** @var string */
public $text;
public function __construct(string $text)
{
$this->text = $text;
}
public function __toString(): string
{
return $this->text;
}
}

View File

@@ -0,0 +1,36 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use function trim;
class PropertyTagValueNode implements PhpDocTagValueNode
{
use NodeAttributes;
/** @var TypeNode */
public $type;
/** @var string */
public $propertyName;
/** @var string (may be empty) */
public $description;
public function __construct(TypeNode $type, string $propertyName, string $description)
{
$this->type = $type;
$this->propertyName = $propertyName;
$this->description = $description;
}
public function __toString(): string
{
return trim("{$this->type} {$this->propertyName} {$this->description}");
}
}

View File

@@ -0,0 +1,32 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use function trim;
class ReturnTagValueNode implements PhpDocTagValueNode
{
use NodeAttributes;
/** @var TypeNode */
public $type;
/** @var string (may be empty) */
public $description;
public function __construct(TypeNode $type, string $description)
{
$this->type = $type;
$this->description = $description;
}
public function __toString(): string
{
return trim("{$this->type} {$this->description}");
}
}

View File

@@ -0,0 +1,32 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use function trim;
class SelfOutTagValueNode implements PhpDocTagValueNode
{
use NodeAttributes;
/** @var TypeNode */
public $type;
/** @var string (may be empty) */
public $description;
public function __construct(TypeNode $type, string $description)
{
$this->type = $type;
$this->description = $description;
}
public function __toString(): string
{
return trim($this->type . ' ' . $this->description);
}
}

View File

@@ -0,0 +1,42 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use function trim;
class TemplateTagValueNode implements PhpDocTagValueNode
{
use NodeAttributes;
/** @var string */
public $name;
/** @var TypeNode|null */
public $bound;
/** @var TypeNode|null */
public $default;
/** @var string (may be empty) */
public $description;
public function __construct(string $name, ?TypeNode $bound, string $description, ?TypeNode $default = null)
{
$this->name = $name;
$this->bound = $bound;
$this->default = $default;
$this->description = $description;
}
public function __toString(): string
{
$bound = $this->bound !== null ? " of {$this->bound}" : '';
$default = $this->default !== null ? " = {$this->default}" : '';
return trim("{$this->name}{$bound}{$default} {$this->description}");
}
}

View File

@@ -0,0 +1,32 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use function trim;
class ThrowsTagValueNode implements PhpDocTagValueNode
{
use NodeAttributes;
/** @var TypeNode */
public $type;
/** @var string (may be empty) */
public $description;
public function __construct(TypeNode $type, string $description)
{
$this->type = $type;
$this->description = $description;
}
public function __toString(): string
{
return trim("{$this->type} {$this->description}");
}
}

View File

@@ -0,0 +1,38 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use function trim;
class TypeAliasImportTagValueNode implements PhpDocTagValueNode
{
use NodeAttributes;
/** @var string */
public $importedAlias;
/** @var IdentifierTypeNode */
public $importedFrom;
/** @var string|null */
public $importedAs;
public function __construct(string $importedAlias, IdentifierTypeNode $importedFrom, ?string $importedAs)
{
$this->importedAlias = $importedAlias;
$this->importedFrom = $importedFrom;
$this->importedAs = $importedAs;
}
public function __toString(): string
{
return trim(
"{$this->importedAlias} from {$this->importedFrom}"
. ($this->importedAs !== null ? " as {$this->importedAs}" : '')
);
}
}

View File

@@ -0,0 +1,32 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use function trim;
class TypeAliasTagValueNode implements PhpDocTagValueNode
{
use NodeAttributes;
/** @var string */
public $alias;
/** @var TypeNode */
public $type;
public function __construct(string $alias, TypeNode $type)
{
$this->alias = $alias;
$this->type = $type;
}
public function __toString(): string
{
return trim("{$this->alias} {$this->type}");
}
}

View File

@@ -0,0 +1,41 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function trim;
class TypelessParamTagValueNode implements PhpDocTagValueNode
{
use NodeAttributes;
/** @var bool */
public $isReference;
/** @var bool */
public $isVariadic;
/** @var string */
public $parameterName;
/** @var string (may be empty) */
public $description;
public function __construct(bool $isVariadic, string $parameterName, string $description, bool $isReference = false)
{
$this->isReference = $isReference;
$this->isVariadic = $isVariadic;
$this->parameterName = $parameterName;
$this->description = $description;
}
public function __toString(): string
{
$reference = $this->isReference ? '&' : '';
$variadic = $this->isVariadic ? '...' : '';
return trim("{$reference}{$variadic}{$this->parameterName} {$this->description}");
}
}

View File

@@ -0,0 +1,32 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
use function trim;
class UsesTagValueNode implements PhpDocTagValueNode
{
use NodeAttributes;
/** @var GenericTypeNode */
public $type;
/** @var string (may be empty) */
public $description;
public function __construct(GenericTypeNode $type, string $description)
{
$this->type = $type;
$this->description = $description;
}
public function __toString(): string
{
return trim("{$this->type} {$this->description}");
}
}

View File

@@ -0,0 +1,36 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use function trim;
class VarTagValueNode implements PhpDocTagValueNode
{
use NodeAttributes;
/** @var TypeNode */
public $type;
/** @var string (may be empty) */
public $variableName;
/** @var string (may be empty) */
public $description;
public function __construct(TypeNode $type, string $variableName, string $description)
{
$this->type = $type;
$this->variableName = $variableName;
$this->description = $description;
}
public function __toString(): string
{
return trim("$this->type " . trim("{$this->variableName} {$this->description}"));
}
}

View File

@@ -0,0 +1,49 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\Type;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function sprintf;
class ArrayShapeItemNode implements TypeNode
{
use NodeAttributes;
/** @var ConstExprIntegerNode|ConstExprStringNode|IdentifierTypeNode|null */
public $keyName;
/** @var bool */
public $optional;
/** @var TypeNode */
public $valueType;
/**
* @param ConstExprIntegerNode|ConstExprStringNode|IdentifierTypeNode|null $keyName
*/
public function __construct($keyName, bool $optional, TypeNode $valueType)
{
$this->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;
}
}

View File

@@ -0,0 +1,48 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\Type;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function implode;
class ArrayShapeNode implements TypeNode
{
public const KIND_ARRAY = 'array';
public const KIND_LIST = 'list';
use NodeAttributes;
/** @var ArrayShapeItemNode[] */
public $items;
/** @var bool */
public $sealed;
/** @var self::KIND_* */
public $kind;
/**
* @param ArrayShapeItemNode[] $items
* @param self::KIND_* $kind
*/
public function __construct(array $items, bool $sealed = true, string $kind = self::KIND_ARRAY)
{
$this->items = $items;
$this->sealed = $sealed;
$this->kind = $kind;
}
public function __toString(): string
{
$items = $this->items;
if (! $this->sealed) {
$items[] = '...';
}
return $this->kind . '{' . implode(', ', $items) . '}';
}
}

View File

@@ -0,0 +1,34 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\Type;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
class ArrayTypeNode implements TypeNode
{
use NodeAttributes;
/** @var TypeNode */
public $type;
public function __construct(TypeNode $type)
{
$this->type = $type;
}
public function __toString(): string
{
if (
$this->type instanceof CallableTypeNode
|| $this->type instanceof ConstTypeNode
|| $this->type instanceof NullableTypeNode
) {
return '(' . $this->type . ')[]';
}
return $this->type . '[]';
}
}

View File

@@ -0,0 +1,43 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\Type;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function implode;
class CallableTypeNode implements TypeNode
{
use NodeAttributes;
/** @var IdentifierTypeNode */
public $identifier;
/** @var CallableTypeParameterNode[] */
public $parameters;
/** @var TypeNode */
public $returnType;
/**
* @param CallableTypeParameterNode[] $parameters
*/
public function __construct(IdentifierTypeNode $identifier, array $parameters, TypeNode $returnType)
{
$this->identifier = $identifier;
$this->parameters = $parameters;
$this->returnType = $returnType;
}
public function __toString(): string
{
$returnType = $this->returnType;
if ($returnType instanceof self) {
$returnType = "({$returnType})";
}
$parameters = implode(', ', $this->parameters);
return "{$this->identifier}({$parameters}): {$returnType}";
}
}

View File

@@ -0,0 +1,48 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\Type;
use PHPStan\PhpDocParser\Ast\Node;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function trim;
class CallableTypeParameterNode implements Node
{
use NodeAttributes;
/** @var TypeNode */
public $type;
/** @var bool */
public $isReference;
/** @var bool */
public $isVariadic;
/** @var string (may be empty) */
public $parameterName;
/** @var bool */
public $isOptional;
public function __construct(TypeNode $type, bool $isReference, bool $isVariadic, string $parameterName, bool $isOptional)
{
$this->type = $type;
$this->isReference = $isReference;
$this->isVariadic = $isVariadic;
$this->parameterName = $parameterName;
$this->isOptional = $isOptional;
}
public function __toString(): string
{
$type = "{$this->type} ";
$isReference = $this->isReference ? '&' : '';
$isVariadic = $this->isVariadic ? '...' : '';
$isOptional = $this->isOptional ? '=' : '';
return trim("{$type}{$isReference}{$isVariadic}{$this->parameterName}") . $isOptional;
}
}

View File

@@ -0,0 +1,49 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\Type;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function sprintf;
class ConditionalTypeForParameterNode implements TypeNode
{
use NodeAttributes;
/** @var string */
public $parameterName;
/** @var TypeNode */
public $targetType;
/** @var TypeNode */
public $if;
/** @var TypeNode */
public $else;
/** @var bool */
public $negated;
public function __construct(string $parameterName, TypeNode $targetType, TypeNode $if, TypeNode $else, bool $negated)
{
$this->parameterName = $parameterName;
$this->targetType = $targetType;
$this->if = $if;
$this->else = $else;
$this->negated = $negated;
}
public function __toString(): string
{
return sprintf(
'(%s %s %s ? %s : %s)',
$this->parameterName,
$this->negated ? 'is not' : 'is',
$this->targetType,
$this->if,
$this->else
);
}
}

View File

@@ -0,0 +1,49 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\Type;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function sprintf;
class ConditionalTypeNode implements TypeNode
{
use NodeAttributes;
/** @var TypeNode */
public $subjectType;
/** @var TypeNode */
public $targetType;
/** @var TypeNode */
public $if;
/** @var TypeNode */
public $else;
/** @var bool */
public $negated;
public function __construct(TypeNode $subjectType, TypeNode $targetType, TypeNode $if, TypeNode $else, bool $negated)
{
$this->subjectType = $subjectType;
$this->targetType = $targetType;
$this->if = $if;
$this->else = $else;
$this->negated = $negated;
}
public function __toString(): string
{
return sprintf(
'(%s %s %s ? %s : %s)',
$this->subjectType,
$this->negated ? 'is not' : 'is',
$this->targetType,
$this->if,
$this->else
);
}
}

View File

@@ -0,0 +1,26 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\Type;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNode;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
class ConstTypeNode implements TypeNode
{
use NodeAttributes;
/** @var ConstExprNode */
public $constExpr;
public function __construct(ConstExprNode $constExpr)
{
$this->constExpr = $constExpr;
}
public function __toString(): string
{
return $this->constExpr->__toString();
}
}

View File

@@ -0,0 +1,58 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\Type;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function implode;
use function sprintf;
class GenericTypeNode implements TypeNode
{
public const VARIANCE_INVARIANT = 'invariant';
public const VARIANCE_COVARIANT = 'covariant';
public const VARIANCE_CONTRAVARIANT = 'contravariant';
public const VARIANCE_BIVARIANT = 'bivariant';
use NodeAttributes;
/** @var IdentifierTypeNode */
public $type;
/** @var TypeNode[] */
public $genericTypes;
/** @var (self::VARIANCE_*)[] */
public $variances;
/**
* @param TypeNode[] $genericTypes
* @param (self::VARIANCE_*)[] $variances
*/
public function __construct(IdentifierTypeNode $type, array $genericTypes, array $variances = [])
{
$this->type = $type;
$this->genericTypes = $genericTypes;
$this->variances = $variances;
}
public function __toString(): string
{
$genericTypes = [];
foreach ($this->genericTypes as $index => $type) {
$variance = $this->variances[$index] ?? self::VARIANCE_INVARIANT;
if ($variance === self::VARIANCE_INVARIANT) {
$genericTypes[] = (string) $type;
} elseif ($variance === self::VARIANCE_BIVARIANT) {
$genericTypes[] = '*';
} else {
$genericTypes[] = sprintf('%s %s', $variance, $type);
}
}
return $this->type . '<' . implode(', ', $genericTypes) . '>';
}
}

View File

@@ -0,0 +1,26 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\Type;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
class IdentifierTypeNode implements TypeNode
{
use NodeAttributes;
/** @var string */
public $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function __toString(): string
{
return $this->name;
}
}

View File

@@ -0,0 +1,37 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\Type;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function array_map;
use function implode;
class IntersectionTypeNode implements TypeNode
{
use NodeAttributes;
/** @var TypeNode[] */
public $types;
/**
* @param TypeNode[] $types
*/
public function __construct(array $types)
{
$this->types = $types;
}
public function __toString(): string
{
return '(' . implode(' & ', array_map(static function (TypeNode $type): string {
if ($type instanceof NullableTypeNode) {
return '(' . $type . ')';
}
return (string) $type;
}, $this->types)) . ')';
}
}

View File

@@ -0,0 +1,38 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\Type;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Parser\ParserException;
class InvalidTypeNode implements TypeNode
{
use NodeAttributes;
/** @var mixed[] */
private $exceptionArgs;
public function __construct(ParserException $exception)
{
$this->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*';
}
}

View File

@@ -0,0 +1,26 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\Type;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
class NullableTypeNode implements TypeNode
{
use NodeAttributes;
/** @var TypeNode */
public $type;
public function __construct(TypeNode $type)
{
$this->type = $type;
}
public function __toString(): string
{
return '?' . $this->type;
}
}

View File

@@ -0,0 +1,48 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\Type;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function sprintf;
class ObjectShapeItemNode implements TypeNode
{
use NodeAttributes;
/** @var ConstExprStringNode|IdentifierTypeNode */
public $keyName;
/** @var bool */
public $optional;
/** @var TypeNode */
public $valueType;
/**
* @param ConstExprStringNode|IdentifierTypeNode $keyName
*/
public function __construct($keyName, bool $optional, TypeNode $valueType)
{
$this->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;
}
}

View File

@@ -0,0 +1,31 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\Type;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function implode;
class ObjectShapeNode implements TypeNode
{
use NodeAttributes;
/** @var ObjectShapeItemNode[] */
public $items;
/**
* @param ObjectShapeItemNode[] $items
*/
public function __construct(array $items)
{
$this->items = $items;
}
public function __toString(): string
{
$items = $this->items;
return 'object{' . implode(', ', $items) . '}';
}
}

View File

@@ -0,0 +1,37 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\Type;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
class OffsetAccessTypeNode implements TypeNode
{
use NodeAttributes;
/** @var TypeNode */
public $type;
/** @var TypeNode */
public $offset;
public function __construct(TypeNode $type, TypeNode $offset)
{
$this->type = $type;
$this->offset = $offset;
}
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 . ']';
}
}

View File

@@ -0,0 +1,17 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\Type;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
class ThisTypeNode implements TypeNode
{
use NodeAttributes;
public function __toString(): string
{
return '$this';
}
}

View File

@@ -0,0 +1,10 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\Type;
use PHPStan\PhpDocParser\Ast\Node;
interface TypeNode extends Node
{
}

View File

@@ -0,0 +1,37 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\Type;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function array_map;
use function implode;
class UnionTypeNode implements TypeNode
{
use NodeAttributes;
/** @var TypeNode[] */
public $types;
/**
* @param TypeNode[] $types
*/
public function __construct(array $types)
{
$this->types = $types;
}
public function __toString(): string
{
return '(' . implode(' | ', array_map(static function (TypeNode $type): string {
if ($type instanceof NullableTypeNode) {
return '(' . $type . ')';
}
return (string) $type;
}, $this->types)) . ')';
}
}

View File

@@ -0,0 +1,181 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Lexer;
use function implode;
use function preg_match_all;
use const PREG_SET_ORDER;
/**
* Implementation based on Nette Tokenizer (New BSD License; https://github.com/nette/tokenizer)
*/
class Lexer
{
public const TOKEN_REFERENCE = 0;
public const TOKEN_UNION = 1;
public const TOKEN_INTERSECTION = 2;
public const TOKEN_NULLABLE = 3;
public const TOKEN_OPEN_PARENTHESES = 4;
public const TOKEN_CLOSE_PARENTHESES = 5;
public const TOKEN_OPEN_ANGLE_BRACKET = 6;
public const TOKEN_CLOSE_ANGLE_BRACKET = 7;
public const TOKEN_OPEN_SQUARE_BRACKET = 8;
public const TOKEN_CLOSE_SQUARE_BRACKET = 9;
public const TOKEN_COMMA = 10;
public const TOKEN_VARIADIC = 11;
public const TOKEN_DOUBLE_COLON = 12;
public const TOKEN_DOUBLE_ARROW = 13;
public const TOKEN_EQUAL = 14;
public const TOKEN_OPEN_PHPDOC = 15;
public const TOKEN_CLOSE_PHPDOC = 16;
public const TOKEN_PHPDOC_TAG = 17;
public const TOKEN_FLOAT = 18;
public const TOKEN_INTEGER = 19;
public const TOKEN_SINGLE_QUOTED_STRING = 20;
public const TOKEN_DOUBLE_QUOTED_STRING = 21;
public const TOKEN_IDENTIFIER = 22;
public const TOKEN_THIS_VARIABLE = 23;
public const TOKEN_VARIABLE = 24;
public const TOKEN_HORIZONTAL_WS = 25;
public const TOKEN_PHPDOC_EOL = 26;
public const TOKEN_OTHER = 27;
public const TOKEN_END = 28;
public const TOKEN_COLON = 29;
public const TOKEN_WILDCARD = 30;
public const TOKEN_OPEN_CURLY_BRACKET = 31;
public const TOKEN_CLOSE_CURLY_BRACKET = 32;
public const TOKEN_NEGATED = 33;
public const TOKEN_ARROW = 34;
public const TOKEN_LABELS = [
self::TOKEN_REFERENCE => '\'&\'',
self::TOKEN_UNION => '\'|\'',
self::TOKEN_INTERSECTION => '\'&\'',
self::TOKEN_NULLABLE => '\'?\'',
self::TOKEN_NEGATED => '\'!\'',
self::TOKEN_OPEN_PARENTHESES => '\'(\'',
self::TOKEN_CLOSE_PARENTHESES => '\')\'',
self::TOKEN_OPEN_ANGLE_BRACKET => '\'<\'',
self::TOKEN_CLOSE_ANGLE_BRACKET => '\'>\'',
self::TOKEN_OPEN_SQUARE_BRACKET => '\'[\'',
self::TOKEN_CLOSE_SQUARE_BRACKET => '\']\'',
self::TOKEN_OPEN_CURLY_BRACKET => '\'{\'',
self::TOKEN_CLOSE_CURLY_BRACKET => '\'}\'',
self::TOKEN_COMMA => '\',\'',
self::TOKEN_COLON => '\':\'',
self::TOKEN_VARIADIC => '\'...\'',
self::TOKEN_DOUBLE_COLON => '\'::\'',
self::TOKEN_DOUBLE_ARROW => '\'=>\'',
self::TOKEN_ARROW => '\'->\'',
self::TOKEN_EQUAL => '\'=\'',
self::TOKEN_OPEN_PHPDOC => '\'/**\'',
self::TOKEN_CLOSE_PHPDOC => '\'*/\'',
self::TOKEN_PHPDOC_TAG => 'TOKEN_PHPDOC_TAG',
self::TOKEN_PHPDOC_EOL => 'TOKEN_PHPDOC_EOL',
self::TOKEN_FLOAT => 'TOKEN_FLOAT',
self::TOKEN_INTEGER => 'TOKEN_INTEGER',
self::TOKEN_SINGLE_QUOTED_STRING => 'TOKEN_SINGLE_QUOTED_STRING',
self::TOKEN_DOUBLE_QUOTED_STRING => 'TOKEN_DOUBLE_QUOTED_STRING',
self::TOKEN_IDENTIFIER => 'type',
self::TOKEN_THIS_VARIABLE => '\'$this\'',
self::TOKEN_VARIABLE => 'variable',
self::TOKEN_HORIZONTAL_WS => 'TOKEN_HORIZONTAL_WS',
self::TOKEN_OTHER => 'TOKEN_OTHER',
self::TOKEN_END => 'TOKEN_END',
self::TOKEN_WILDCARD => '*',
];
public const VALUE_OFFSET = 0;
public const TYPE_OFFSET = 1;
public const LINE_OFFSET = 2;
/** @var string|null */
private $regexp;
/**
* @return list<array{string, int, int}>
*/
public function tokenize(string $s): array
{
if ($this->regexp === null) {
$this->regexp = $this->generateRegexp();
}
preg_match_all($this->regexp, $s, $matches, PREG_SET_ORDER);
$tokens = [];
$line = 1;
foreach ($matches as $match) {
$type = (int) $match['MARK'];
$tokens[] = [$match[0], $type, $line];
if ($type !== self::TOKEN_PHPDOC_EOL) {
continue;
}
$line++;
}
$tokens[] = ['', self::TOKEN_END, $line];
return $tokens;
}
private function generateRegexp(): string
{
$patterns = [
self::TOKEN_HORIZONTAL_WS => '[\\x09\\x20]++',
self::TOKEN_IDENTIFIER => '(?:[\\\\]?+[a-z_\\x80-\\xFF][0-9a-z_\\x80-\\xFF-]*+)++',
self::TOKEN_THIS_VARIABLE => '\\$this(?![0-9a-z_\\x80-\\xFF])',
self::TOKEN_VARIABLE => '\\$[a-z_\\x80-\\xFF][0-9a-z_\\x80-\\xFF]*+',
// '&' followed by TOKEN_VARIADIC, TOKEN_VARIABLE, TOKEN_EQUAL, TOKEN_EQUAL or TOKEN_CLOSE_PARENTHESES
self::TOKEN_REFERENCE => '&(?=\\s*+(?:[.,=)]|(?:\\$(?!this(?![0-9a-z_\\x80-\\xFF])))))',
self::TOKEN_UNION => '\\|',
self::TOKEN_INTERSECTION => '&',
self::TOKEN_NULLABLE => '\\?',
self::TOKEN_NEGATED => '!',
self::TOKEN_OPEN_PARENTHESES => '\\(',
self::TOKEN_CLOSE_PARENTHESES => '\\)',
self::TOKEN_OPEN_ANGLE_BRACKET => '<',
self::TOKEN_CLOSE_ANGLE_BRACKET => '>',
self::TOKEN_OPEN_SQUARE_BRACKET => '\\[',
self::TOKEN_CLOSE_SQUARE_BRACKET => '\\]',
self::TOKEN_OPEN_CURLY_BRACKET => '\\{',
self::TOKEN_CLOSE_CURLY_BRACKET => '\\}',
self::TOKEN_COMMA => ',',
self::TOKEN_VARIADIC => '\\.\\.\\.',
self::TOKEN_DOUBLE_COLON => '::',
self::TOKEN_DOUBLE_ARROW => '=>',
self::TOKEN_ARROW => '->',
self::TOKEN_EQUAL => '=',
self::TOKEN_COLON => ':',
self::TOKEN_OPEN_PHPDOC => '/\\*\\*(?=\\s)\\x20?+',
self::TOKEN_CLOSE_PHPDOC => '\\*/',
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]++)*\\.[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\\\\])*+"',
self::TOKEN_WILDCARD => '\\*',
// anything but TOKEN_CLOSE_PHPDOC or TOKEN_HORIZONTAL_WS or TOKEN_EOL
self::TOKEN_OTHER => '(?:(?!\\*/)[^\\s])++',
];
foreach ($patterns as $type => &$pattern) {
$pattern = '(?:' . $pattern . ')(*MARK:' . $type . ')';
}
return '~' . implode('|', $patterns) . '~Asi';
}
}

View File

@@ -0,0 +1,269 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Parser;
use PHPStan\PhpDocParser\Ast;
use PHPStan\PhpDocParser\Lexer\Lexer;
use function str_replace;
use function strtolower;
use function substr;
class ConstExprParser
{
/** @var bool */
private $unescapeStrings;
/** @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 $this->enrichWithAttributes(
$tokens,
new Ast\ConstExpr\ConstExprFloatNode(str_replace('_', '', $value)),
$startLine,
$startIndex
);
}
if ($tokens->isCurrentTokenType(Lexer::TOKEN_INTEGER)) {
$value = $tokens->currentTokenValue();
$tokens->next();
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 = StringUnescaper::unescapeString($value);
} else {
$value = substr($value, 1, -1);
}
}
$tokens->next();
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();
$tokens->next();
switch (strtolower($identifier)) {
case 'true':
return $this->enrichWithAttributes(
$tokens,
new Ast\ConstExpr\ConstExprTrueNode(),
$startLine,
$startIndex
);
case 'false':
return $this->enrichWithAttributes(
$tokens,
new Ast\ConstExpr\ConstExprFalseNode(),
$startLine,
$startIndex
);
case 'null':
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, $startIndex);
}
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_DOUBLE_COLON)) {
$classConstantName = '';
$lastType = null;
while (true) {
if ($lastType !== Lexer::TOKEN_IDENTIFIER && $tokens->currentTokenType() === Lexer::TOKEN_IDENTIFIER) {
$classConstantName .= $tokens->currentTokenValue();
$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
$lastType = Lexer::TOKEN_IDENTIFIER;
continue;
}
if ($lastType !== Lexer::TOKEN_WILDCARD && $tokens->tryConsumeTokenType(Lexer::TOKEN_WILDCARD)) {
$classConstantName .= '*';
$lastType = Lexer::TOKEN_WILDCARD;
if ($tokens->getSkippedHorizontalWhiteSpaceIfAny() !== '') {
break;
}
continue;
}
if ($lastType === null) {
// trigger parse error if nothing valid was consumed
$tokens->consumeTokenType(Lexer::TOKEN_WILDCARD);
}
break;
}
return $this->enrichWithAttributes(
$tokens,
new Ast\ConstExpr\ConstFetchNode($identifier, $classConstantName),
$startLine,
$startIndex
);
}
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, $startIndex);
}
throw new ParserException(
$tokens->currentTokenValue(),
$tokens->currentTokenType(),
$tokens->currentTokenOffset(),
Lexer::TOKEN_IDENTIFIER,
null,
$tokens->currentTokenLine()
);
}
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);
} while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA) && !$tokens->isCurrentTokenType($endToken));
$tokens->consumeTokenType($endToken);
}
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)) {
$key = $expr;
$value = $this->parse($tokens);
} else {
$key = null;
$value = $expr;
}
return $this->enrichWithAttributes(
$tokens,
new Ast\ConstExpr\ConstExprArrayItemNode($key, $value),
$startLine,
$startIndex
);
}
/**
* @template T of Ast\ConstExpr\ConstExprNode
* @param T $node
* @return T
*/
private function enrichWithAttributes(TokenIterator $tokens, Ast\ConstExpr\ConstExprNode $node, int $startLine, int $startIndex): Ast\ConstExpr\ConstExprNode
{
$endLine = $tokens->currentTokenLine();
$endIndex = $tokens->currentTokenIndex();
if ($this->useLinesAttributes) {
$node->setAttribute(Ast\Attribute::START_LINE, $startLine);
$node->setAttribute(Ast\Attribute::END_LINE, $endLine);
}
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;
}
}

Some files were not shown because too many files have changed in this diff Show More