From 4c7da776675a252f720ba8ed6e85acdcd2dffd55 Mon Sep 17 00:00:00 2001 From: Jaapio Date: Fri, 23 Jan 2026 16:45:26 +0100 Subject: [PATCH 1/6] Files did not get any context when processed Files without namespace did not get a type context, this resulted in unknown scope of the types to be resolved. The other fix in this commit is to make sure the FQSN coming from the php-parser is correctly translated to a FQSEN object, this was not the case for some situations. --- psalm-baseline.xml | 3 - .../Php/Expression/ExpressionPrinter.php | 5 +- .../Reflection/Php/Factory/File.php | 7 ++- .../Reflection/Types/BaseToContext.php | 59 +++++++++++++++++++ .../Reflection/Types/FileToContext.php | 19 ++++++ .../Types/NamespaceNodeToContext.php | 46 +-------------- tests/integration/EnumTest.php | 9 ++- tests/integration/ProjectCreationTest.php | 33 +++++++++++ .../GlobalFiles/function_constant_default.php | 7 +++ .../Reflection/Php/Factory/FileTest.php | 2 +- 10 files changed, 134 insertions(+), 56 deletions(-) create mode 100644 src/phpDocumentor/Reflection/Types/BaseToContext.php create mode 100644 src/phpDocumentor/Reflection/Types/FileToContext.php create mode 100644 tests/integration/data/GlobalFiles/function_constant_default.php diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 9dd1e5bf..4a60c177 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -225,8 +225,5 @@ - - aliasesToFullyQualifiedNames($namespace)]]> - diff --git a/src/phpDocumentor/Reflection/Php/Expression/ExpressionPrinter.php b/src/phpDocumentor/Reflection/Php/Expression/ExpressionPrinter.php index 5fe66ae7..5e9162ec 100644 --- a/src/phpDocumentor/Reflection/Php/Expression/ExpressionPrinter.php +++ b/src/phpDocumentor/Reflection/Php/Expression/ExpressionPrinter.php @@ -86,7 +86,10 @@ protected function pExpr_ClassConstFetch(Expr\ClassConstFetch $node): string { $renderedName = parent::pObjectProperty($node->name); - if ($node->class instanceof Name) { + if ($node->class instanceof Name\FullyQualified) { + $className = parent::pName_FullyQualified($node->class); + $className = $this->typeResolver->resolve($className, $this->context); + } elseif ($node->class instanceof Name) { $className = parent::pName($node->class); $className = $this->typeResolver->resolve($className, $this->context); } else { diff --git a/src/phpDocumentor/Reflection/Php/Factory/File.php b/src/phpDocumentor/Reflection/Php/Factory/File.php index 46fba96d..22b95e7b 100644 --- a/src/phpDocumentor/Reflection/Php/Factory/File.php +++ b/src/phpDocumentor/Reflection/Php/Factory/File.php @@ -24,6 +24,7 @@ use phpDocumentor\Reflection\Php\NodesFactory; use phpDocumentor\Reflection\Php\StrategyContainer; use phpDocumentor\Reflection\Types\Context; +use phpDocumentor\Reflection\Types\FileToContext; use PhpParser\Comment\Doc; use PhpParser\Node; use PhpParser\Node\Stmt\Class_ as ClassNode; @@ -106,7 +107,9 @@ private function createFile(CreateCommand $command): FileElement $code = $file->getContents(); $nodes = $this->nodesFactory->create($code); - $docBlock = $this->createFileDocBlock(null, $nodes); + $fileToContext = new FileToContext(); + $typeContext = $fileToContext($nodes); + $docBlock = $this->createFileDocBlock($typeContext, $nodes); $result = new FileElement( $file->md5(), @@ -115,7 +118,7 @@ private function createFile(CreateCommand $command): FileElement $docBlock, ); - $this->createElements($command->getContext()->push($result), $nodes, $command->getStrategies()); + $this->createElements($command->getContext()->push($result)->withTypeContext($typeContext), $nodes, $command->getStrategies()); return $result; } diff --git a/src/phpDocumentor/Reflection/Types/BaseToContext.php b/src/phpDocumentor/Reflection/Types/BaseToContext.php new file mode 100644 index 00000000..26b5acbe --- /dev/null +++ b/src/phpDocumentor/Reflection/Types/BaseToContext.php @@ -0,0 +1,59 @@ + + */ + protected static function flattenUsage(array $usages): array + { + return array_merge([], ...array_merge([], ...array_map( + static fn ($use): array => array_map( + static function (Node\UseItem|UseUse $useUse) use ($use): array { + if ($use instanceof GroupUse) { + return [ + (string) $useUse->getAlias() => $use->prefix->toString() . '\\' . $useUse->name->toString(), + ]; + } + + return [(string) $useUse->getAlias() => $useUse->name->toString()]; + }, + $use->uses, + ), + $usages, + ))); + } + + /** + * @param Node[] $nodes + * + * @return Use_[]|GroupUse[] + */ + protected static function filterUsage(array $nodes): array + { + return array_filter( + $nodes, + static fn (Node $node): bool => ( + $node instanceof Use_ + || $node instanceof GroupUse + ) && in_array($node->type, [Use_::TYPE_UNKNOWN, Use_::TYPE_NORMAL], true), + ); + } +} diff --git a/src/phpDocumentor/Reflection/Types/FileToContext.php b/src/phpDocumentor/Reflection/Types/FileToContext.php new file mode 100644 index 00000000..7a45e7e8 --- /dev/null +++ b/src/phpDocumentor/Reflection/Types/FileToContext.php @@ -0,0 +1,19 @@ +name ? $namespace->name->toString() : '', - $this->aliasesToFullyQualifiedNames($namespace), - ); - } - - /** @return string[] indexed by alias */ - private function aliasesToFullyQualifiedNames(Namespace_ $namespace): array - { - // flatten(flatten(map(stuff))) - return array_merge([], ...array_merge([], ...array_map( - static fn ($use): array => array_map( - static function (Node\UseItem|UseUse $useUse) use ($use): array { - if ($use instanceof GroupUse) { - return [ - (string) $useUse->getAlias() => $use->prefix->toString() . '\\' . $useUse->name->toString(), - ]; - } - - return [(string) $useUse->getAlias() => $useUse->name->toString()]; - }, - $use->uses, - ), - $this->classAlikeUses($namespace), - ))); - } - - /** @return Use_[]|GroupUse[] */ - private function classAlikeUses(Namespace_ $namespace): array - { - return array_filter( - $namespace->stmts, - static fn (Node $node): bool => ( - $node instanceof Use_ - || $node instanceof GroupUse - ) && in_array($node->type, [Use_::TYPE_UNKNOWN, Use_::TYPE_NORMAL], true), + self::flattenUsage(self::filterUsage($namespace->stmts)), ); } } diff --git a/tests/integration/EnumTest.php b/tests/integration/EnumTest.php index 03e2c30f..bf3d12e6 100644 --- a/tests/integration/EnumTest.php +++ b/tests/integration/EnumTest.php @@ -115,10 +115,9 @@ public function testEnumSupportInMethod(): void $method->getArguments()[0]->getType() ); - //This should be fixed in #219 -// self::assertEquals( -// '\MyNamespace\MyEnum::VALUE1', -// $method->getArguments()[0]->getDefault() -// ); + self::assertEquals( + '\MyNamespace\MyEnum::VALUE1', + $method->getArguments()[0]->getDefault() + ); } } diff --git a/tests/integration/ProjectCreationTest.php b/tests/integration/ProjectCreationTest.php index 7cd1701a..63558a71 100644 --- a/tests/integration/ProjectCreationTest.php +++ b/tests/integration/ProjectCreationTest.php @@ -17,6 +17,7 @@ use phpDocumentor\Reflection\DocBlock\Tags\Param; use phpDocumentor\Reflection\File\LocalFile; use phpDocumentor\Reflection\Php\Class_; +use phpDocumentor\Reflection\Php\Expression; use phpDocumentor\Reflection\Php\Function_; use phpDocumentor\Reflection\Php\ProjectFactory; use phpDocumentor\Reflection\Types\Integer; @@ -250,4 +251,36 @@ public function testMethodReturnType() : void $this->assertEquals(new String_(), $interface->getMethods()['\Packing::getName()']->getReturnType()); } + + public function testFunctionContantDefaultIsResolved() : void + { + $fileName = __DIR__ . '/data/GlobalFiles/function_constant_default.php'; + $project = $this->fixture->create( + 'MyProject', + [new LocalFile($fileName)] + ); + + $this->assertArrayHasKey($fileName, $project->getFiles()); + $functions = $project->getFiles()[$fileName]->getFunctions(); + + self::assertEquals( + new Expression( + '{{ PHPDOCa2f2ed4f8ebc2cbb4c21a29dc40ab61d }}', + [ + '{{ PHPDOCa2f2ed4f8ebc2cbb4c21a29dc40ab61d }}' => new Fqsen('\Acme\Plugin::class'), + ], + ), + $functions['\foo()']->getArguments()[0]->getDefault(false) + ); + + self::assertEquals( + new Expression( + '{{ PHPDOCa8cfde6331bd59eb2ac96f8911c4b666 }}', + [ + '{{ PHPDOCa8cfde6331bd59eb2ac96f8911c4b666 }}' => new Object_(), + ], + ), + $functions['\bar()']->getArguments()[0]->getDefault(false) + ); + } } diff --git a/tests/integration/data/GlobalFiles/function_constant_default.php b/tests/integration/data/GlobalFiles/function_constant_default.php new file mode 100644 index 00000000..685a0d03 --- /dev/null +++ b/tests/integration/data/GlobalFiles/function_constant_default.php @@ -0,0 +1,7 @@ +nodesFactoryMock->create(file_get_contents(__FILE__))->willReturn([$node]); - $this->docBlockFactory->create('Text', null)->willReturn($docblock); + $this->docBlockFactory->create('Text', Argument::any())->willReturn($docblock); $strategies = $this->prophesize(StrategyContainer::class); $strategies->findMatching(Argument::type(ContextStack::class), $node)->willReturn( From 1c1fd0d8571fb80fbaec2d233e46ece86ca1ae25 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Jan 2026 20:30:15 +0000 Subject: [PATCH 2/6] Bump dependabot/fetch-metadata from 2.4.0 to 2.5.0 Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 2.4.0 to 2.5.0. - [Release notes](https://github.com/dependabot/fetch-metadata/releases) - [Commits](https://github.com/dependabot/fetch-metadata/compare/v2.4.0...v2.5.0) --- updated-dependencies: - dependency-name: dependabot/fetch-metadata dependency-version: 2.5.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/dependabot-auto-merge.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml index d0c1fb55..12b371ca 100644 --- a/.github/workflows/dependabot-auto-merge.yml +++ b/.github/workflows/dependabot-auto-merge.yml @@ -13,7 +13,7 @@ jobs: steps: - name: "Dependabot metadata" id: "metadata" - uses: "dependabot/fetch-metadata@v2.4.0" + uses: "dependabot/fetch-metadata@v2.5.0" with: github-token: "${{ secrets.GITHUB_TOKEN }}" - name: "Enable auto-merge for Dependabot PRs" From 8b34fee977fb1b6da2dd10b330fb9b810c41e904 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 01:31:32 +0000 Subject: [PATCH 3/6] Bump phpunit/phpunit from 10.5.58 to 10.5.62 Bumps [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) from 10.5.58 to 10.5.62. - [Release notes](https://github.com/sebastianbergmann/phpunit/releases) - [Changelog](https://github.com/sebastianbergmann/phpunit/blob/10.5.62/ChangeLog-10.5.md) - [Commits](https://github.com/sebastianbergmann/phpunit/compare/10.5.58...10.5.62) --- updated-dependencies: - dependency-name: phpunit/phpunit dependency-version: 10.5.62 dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- composer.lock | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/composer.lock b/composer.lock index 5e78026d..e8791042 100644 --- a/composer.lock +++ b/composer.lock @@ -56,16 +56,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.6.2", + "version": "v5.7.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "3a454ca033b9e06b63282ce19562e892747449bb" + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb", - "reference": "3a454ca033b9e06b63282ce19562e892747449bb", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", "shasum": "" }, "require": { @@ -108,9 +108,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" }, - "time": "2025-10-21T19:32:17+00:00" + "time": "2025-12-06T11:56:16+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -1723,16 +1723,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.58", + "version": "10.5.62", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "e24fb46da450d8e6a5788670513c1af1424f16ca" + "reference": "3f7dd5066ebde5809296a81f0b19e8b00e5aab49" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e24fb46da450d8e6a5788670513c1af1424f16ca", - "reference": "e24fb46da450d8e6a5788670513c1af1424f16ca", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3f7dd5066ebde5809296a81f0b19e8b00e5aab49", + "reference": "3f7dd5066ebde5809296a81f0b19e8b00e5aab49", "shasum": "" }, "require": { @@ -1753,7 +1753,7 @@ "phpunit/php-timer": "^6.0.0", "sebastian/cli-parser": "^2.0.1", "sebastian/code-unit": "^2.0.0", - "sebastian/comparator": "^5.0.4", + "sebastian/comparator": "^5.0.5", "sebastian/diff": "^5.1.1", "sebastian/environment": "^6.1.0", "sebastian/exporter": "^5.1.4", @@ -1804,7 +1804,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.58" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.62" }, "funding": [ { @@ -1828,7 +1828,7 @@ "type": "tidelift" } ], - "time": "2025-09-28T12:04:46+00:00" + "time": "2026-01-27T05:32:38+00:00" }, { "name": "psalm/phar", @@ -2094,16 +2094,16 @@ }, { "name": "sebastian/comparator", - "version": "5.0.4", + "version": "5.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "e8e53097718d2b53cfb2aa859b06a41abf58c62e" + "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e8e53097718d2b53cfb2aa859b06a41abf58c62e", - "reference": "e8e53097718d2b53cfb2aa859b06a41abf58c62e", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55dfef806eb7dfeb6e7a6935601fef866f8ca48d", + "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d", "shasum": "" }, "require": { @@ -2159,7 +2159,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.4" + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.5" }, "funding": [ { @@ -2179,7 +2179,7 @@ "type": "tidelift" } ], - "time": "2025-09-07T05:25:07+00:00" + "time": "2026-01-24T09:25:16+00:00" }, { "name": "sebastian/complexity", @@ -3023,16 +3023,16 @@ }, { "name": "theseer/tokenizer", - "version": "1.3.0", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "d74205c497bfbca49f34d4bc4c19c17e22db4ebb" + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/d74205c497bfbca49f34d4bc4c19c17e22db4ebb", - "reference": "d74205c497bfbca49f34d4bc4c19c17e22db4ebb", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", "shasum": "" }, "require": { @@ -3061,7 +3061,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.3.0" + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" }, "funding": [ { @@ -3069,7 +3069,7 @@ "type": "github" } ], - "time": "2025-11-13T13:44:09+00:00" + "time": "2025-11-17T20:03:58+00:00" } ], "aliases": [], From da58e103a4fd1d903c3565ebeaf128db282a3c92 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Feb 2026 08:42:21 +0000 Subject: [PATCH 4/6] Bump phpspec/prophecy-phpunit from 2.4.0 to 2.5.0 Bumps [phpspec/prophecy-phpunit](https://github.com/phpspec/prophecy-phpunit) from 2.4.0 to 2.5.0. - [Release notes](https://github.com/phpspec/prophecy-phpunit/releases) - [Changelog](https://github.com/phpspec/prophecy-phpunit/blob/master/CHANGES.md) - [Commits](https://github.com/phpspec/prophecy-phpunit/compare/v2.4.0...v2.5.0) --- updated-dependencies: - dependency-name: phpspec/prophecy-phpunit dependency-version: 2.5.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- composer.lock | 72 +++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/composer.lock b/composer.lock index e8791042..eff0f0d0 100644 --- a/composer.lock +++ b/composer.lock @@ -8,29 +8,29 @@ "packages": [ { "name": "doctrine/deprecations", - "version": "1.1.5", + "version": "1.1.6", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38" + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "conflict": { - "phpunit/phpunit": "<=7.5 || >=13" + "phpunit/phpunit": "<=7.5 || >=14" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^12 || ^13", - "phpstan/phpstan": "1.4.10 || 2.1.11", + "doctrine/coding-standard": "^9 || ^12 || ^14", + "phpstan/phpstan": "1.4.10 || 2.1.30", "phpstan/phpstan-phpunit": "^1.0 || ^2", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", "psr/log": "^1 || ^2 || ^3" }, "suggest": { @@ -50,9 +50,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.5" + "source": "https://github.com/doctrine/deprecations/tree/1.1.6" }, - "time": "2025-04-07T20:06:18+00:00" + "time": "2026-02-07T07:09:04+00:00" }, { "name": "nikic/php-parser", @@ -167,16 +167,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.6.4", + "version": "5.6.6", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "90a04bcbf03784066f16038e87e23a0a83cee3c2" + "reference": "5cee1d3dfc2d2aa6599834520911d246f656bcb8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/90a04bcbf03784066f16038e87e23a0a83cee3c2", - "reference": "90a04bcbf03784066f16038e87e23a0a83cee3c2", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/5cee1d3dfc2d2aa6599834520911d246f656bcb8", + "reference": "5cee1d3dfc2d2aa6599834520911d246f656bcb8", "shasum": "" }, "require": { @@ -186,7 +186,7 @@ "phpdocumentor/reflection-common": "^2.2", "phpdocumentor/type-resolver": "^1.7", "phpstan/phpdoc-parser": "^1.7|^2.0", - "webmozart/assert": "^1.9.1" + "webmozart/assert": "^1.9.1 || ^2" }, "require-dev": { "mockery/mockery": "~1.3.5 || ~1.6.0", @@ -225,9 +225,9 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.4" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.6" }, - "time": "2025-11-17T21:13:10+00:00" + "time": "2025-12-22T21:13:58+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -289,16 +289,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "2.3.0", + "version": "2.3.2", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495" + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/1e0cd5370df5dd2e556a36b9c62f62e555870495", - "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a", "shasum": "" }, "require": { @@ -330,9 +330,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2" }, - "time": "2025-08-30T15:50:23+00:00" + "time": "2026-01-25T14:56:51+00:00" }, { "name": "symfony/polyfill-php80", @@ -1190,22 +1190,22 @@ }, { "name": "phpspec/prophecy-phpunit", - "version": "v2.4.0", + "version": "v2.5.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy-phpunit.git", - "reference": "d3c28041d9390c9bca325a08c5b2993ac855bded" + "reference": "89f91b01d0640b7820e427e02a007bc6489d8a26" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy-phpunit/zipball/d3c28041d9390c9bca325a08c5b2993ac855bded", - "reference": "d3c28041d9390c9bca325a08c5b2993ac855bded", + "url": "https://api.github.com/repos/phpspec/prophecy-phpunit/zipball/89f91b01d0640b7820e427e02a007bc6489d8a26", + "reference": "89f91b01d0640b7820e427e02a007bc6489d8a26", "shasum": "" }, "require": { "php": "^7.3 || ^8", "phpspec/prophecy": "^1.18", - "phpunit/phpunit": "^9.1 || ^10.1 || ^11.0 || ^12.0" + "phpunit/phpunit": "^9.1 || ^10.1 || ^11.0 || ^12.0 || ^13.0" }, "require-dev": { "phpstan/phpstan": "^1.10" @@ -1239,9 +1239,9 @@ ], "support": { "issues": "https://github.com/phpspec/prophecy-phpunit/issues", - "source": "https://github.com/phpspec/prophecy-phpunit/tree/v2.4.0" + "source": "https://github.com/phpspec/prophecy-phpunit/tree/v2.5.0" }, - "time": "2025-05-13T13:52:32+00:00" + "time": "2026-02-09T15:40:55+00:00" }, { "name": "phpstan/extension-installer", @@ -1723,16 +1723,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.62", + "version": "10.5.63", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "3f7dd5066ebde5809296a81f0b19e8b00e5aab49" + "reference": "33198268dad71e926626b618f3ec3966661e4d90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3f7dd5066ebde5809296a81f0b19e8b00e5aab49", - "reference": "3f7dd5066ebde5809296a81f0b19e8b00e5aab49", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/33198268dad71e926626b618f3ec3966661e4d90", + "reference": "33198268dad71e926626b618f3ec3966661e4d90", "shasum": "" }, "require": { @@ -1804,7 +1804,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.62" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.63" }, "funding": [ { @@ -1828,7 +1828,7 @@ "type": "tidelift" } ], - "time": "2026-01-27T05:32:38+00:00" + "time": "2026-01-27T05:48:37+00:00" }, { "name": "psalm/phar", From 48344f312b94d5f2f856dee924939ecae7f1cf2d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 08:50:46 +0000 Subject: [PATCH 5/6] Bump actions/upload-artifact from 5 to 7 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 7. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v5...v7) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/documentation.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/documentation.yaml b/.github/workflows/documentation.yaml index 19975936..32ac9bbd 100644 --- a/.github/workflows/documentation.yaml +++ b/.github/workflows/documentation.yaml @@ -19,7 +19,7 @@ jobs: - name: "Deploy" if: "${{ github.event_name == 'push' && github.ref == 'refs/heads/6.x' }}" - uses: "actions/upload-artifact@v5" + uses: "actions/upload-artifact@v7" with: name: "documentation" path: "build/docs" From 5d8c68c75712242eab15be9953bdad417aeac6e9 Mon Sep 17 00:00:00 2001 From: lacatoire Date: Thu, 9 Apr 2026 20:21:34 +0200 Subject: [PATCH 6/6] Add support for PHP 8.3 typed class constants The Constant class now accepts and exposes a Type parameter, following the same pattern used by Property. The ClassConstantIterator reads the type from the AST node, and the ClassConstant factory converts it via the Type helper before passing it to the Constant constructor. --- src/phpDocumentor/Reflection/Php/Constant.php | 7 ++++ .../Reflection/Php/Factory/ClassConstant.php | 1 + .../Php/Factory/ClassConstantIterator.php | 11 ++++++ tests/integration/TypedConstantTest.php | 34 +++++++++++++++++++ .../integration/data/PHP83/TypedConstants.php | 14 ++++++++ .../Reflection/Php/ConstantTest.php | 23 +++++++++++++ .../Php/Factory/ClassConstantIteratorTest.php | 27 +++++++++++++++ .../Php/Factory/ClassConstantTest.php | 25 ++++++++++++++ 8 files changed, 142 insertions(+) create mode 100644 tests/integration/TypedConstantTest.php create mode 100644 tests/integration/data/PHP83/TypedConstants.php diff --git a/src/phpDocumentor/Reflection/Php/Constant.php b/src/phpDocumentor/Reflection/Php/Constant.php index 1b334758..736dcdd0 100644 --- a/src/phpDocumentor/Reflection/Php/Constant.php +++ b/src/phpDocumentor/Reflection/Php/Constant.php @@ -19,6 +19,7 @@ use phpDocumentor\Reflection\Fqsen; use phpDocumentor\Reflection\Location; use phpDocumentor\Reflection\Metadata\MetaDataContainer as MetaDataContainerInterface; +use phpDocumentor\Reflection\Type; use function is_string; use function trigger_error; @@ -52,6 +53,7 @@ public function __construct( Location|null $endLocation = null, Visibility|null $visibility = null, private readonly bool $final = false, + private readonly Type|null $type = null, ) { $this->location = $location ?: new Location(-1); $this->endLocation = $endLocation ?: new Location(-1); @@ -135,4 +137,9 @@ public function isFinal(): bool { return $this->final; } + + public function getType(): Type|null + { + return $this->type; + } } diff --git a/src/phpDocumentor/Reflection/Php/Factory/ClassConstant.php b/src/phpDocumentor/Reflection/Php/Factory/ClassConstant.php index 7d6ed2b9..e49bd2be 100644 --- a/src/phpDocumentor/Reflection/Php/Factory/ClassConstant.php +++ b/src/phpDocumentor/Reflection/Php/Factory/ClassConstant.php @@ -93,6 +93,7 @@ protected function doCreate( new Location($const->getEndLine()), $this->buildVisibility($const), $const->isFinal(), + (new Type())->fromPhpParser($const->getType(), $context->getTypeContext()), ); foreach ($this->reducers as $reducer) { diff --git a/src/phpDocumentor/Reflection/Php/Factory/ClassConstantIterator.php b/src/phpDocumentor/Reflection/Php/Factory/ClassConstantIterator.php index 93e1f23c..278d21ae 100644 --- a/src/phpDocumentor/Reflection/Php/Factory/ClassConstantIterator.php +++ b/src/phpDocumentor/Reflection/Php/Factory/ClassConstantIterator.php @@ -17,7 +17,10 @@ use Override; use phpDocumentor\Reflection\Fqsen; use PhpParser\Comment\Doc; +use PhpParser\Node\ComplexType; use PhpParser\Node\Expr; +use PhpParser\Node\Identifier; +use PhpParser\Node\Name; use PhpParser\Node\Stmt\ClassConst; /** @@ -123,6 +126,14 @@ public function isFinal(): bool return $this->classConstants->isFinal(); } + /** + * Gets the type of the constant. + */ + public function getType(): Identifier|Name|ComplexType|null + { + return $this->classConstants->type; + } + /** @link http://php.net/manual/en/iterator.current.php */ #[Override] public function current(): self diff --git a/tests/integration/TypedConstantTest.php b/tests/integration/TypedConstantTest.php new file mode 100644 index 00000000..f4575749 --- /dev/null +++ b/tests/integration/TypedConstantTest.php @@ -0,0 +1,34 @@ +create('My project', [new LocalFile($file)]); + + $class = $project->getFiles()[$file]->getClasses()['\PHP83\TypedConstants']; + $constants = $class->getConstants(); + + $this->assertEquals(new String_(), $constants['\PHP83\TypedConstants::NAME']->getType()); + $this->assertEquals(new Integer(), $constants['\PHP83\TypedConstants::COUNT']->getType()); + $this->assertEquals(new Compound([new String_(), new Integer()]), $constants['\PHP83\TypedConstants::UNION']->getType()); + $this->assertEquals(new Nullable(new String_()), $constants['\PHP83\TypedConstants::NULLABLE']->getType()); + $this->assertNull($constants['\PHP83\TypedConstants::UNTYPED']->getType()); + } +} diff --git a/tests/integration/data/PHP83/TypedConstants.php b/tests/integration/data/PHP83/TypedConstants.php new file mode 100644 index 00000000..2f1d893c --- /dev/null +++ b/tests/integration/data/PHP83/TypedConstants.php @@ -0,0 +1,14 @@ +fixture->getVisibility()); } + public function testGetTypeReturnsNullByDefault(): void + { + self::assertNull($this->fixture->getType()); + } + + public function testGetTypeReturnsTypeWhenProvided(): void + { + $type = new String_(); + $fixture = new Constant( + $this->fqsen, + $this->docBlock, + new Expression($this->value), + null, + null, + null, + false, + $type, + ); + + self::assertSame($type, $fixture->getType()); + } + public function testLineAndColumnNumberIsReturnedWhenALocationIsProvided(): void { $fixture = new Constant($this->fqsen, $this->docBlock, null, new Location(100, 20), new Location(101, 20)); diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/ClassConstantIteratorTest.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/ClassConstantIteratorTest.php index 61f5cf7c..e83c4d7f 100644 --- a/tests/unit/phpDocumentor/Reflection/Php/Factory/ClassConstantIteratorTest.php +++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/ClassConstantIteratorTest.php @@ -19,6 +19,8 @@ use PhpParser\Comment\Doc; use PhpParser\Node\Const_; use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Identifier; +use PhpParser\Node\Scalar\String_; use PhpParser\Node\Stmt\ClassConst; use PHPUnit\Framework\Attributes\CoversClass; @@ -78,6 +80,31 @@ public function testGetDocCommentPropFirst(): void $this->assertEquals('test', $fixture->getDocComment()->getText()); } + public function testGetTypeReturnsNullWhenUntyped(): void + { + $const = new Const_('\Space\MyClass::MY_CONST1', new String_('a')); + $const->setAttribute('fqsen', new Fqsen((string) $const->name)); + + $classConstantNode = new ClassConst([$const]); + + $fixture = new ClassConstantIterator($classConstantNode); + + $this->assertNull($fixture->getType()); + } + + public function testGetTypeReturnsTypeWhenTyped(): void + { + $const = new Const_('\Space\MyClass::MY_CONST1', new String_('a')); + $const->setAttribute('fqsen', new Fqsen((string) $const->name)); + + $classConstantNode = new ClassConst([$const], 0, [], [], new Identifier('string')); + + $fixture = new ClassConstantIterator($classConstantNode); + + $this->assertInstanceOf(Identifier::class, $fixture->getType()); + $this->assertEquals('string', $fixture->getType()->toString()); + } + public function testGetDocComment(): void { $const = m::mock(Const_::class); diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/ClassConstantTest.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/ClassConstantTest.php index dec052dc..b0388675 100644 --- a/tests/unit/phpDocumentor/Reflection/Php/Factory/ClassConstantTest.php +++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/ClassConstantTest.php @@ -24,8 +24,10 @@ use phpDocumentor\Reflection\Php\ProjectFactoryStrategies; use phpDocumentor\Reflection\Php\Trait_ as TraitElement; use phpDocumentor\Reflection\Types\Null_; +use phpDocumentor\Reflection\Types\String_ as StringType; use PhpParser\Comment\Doc; use PhpParser\Node\Const_; +use PhpParser\Node\Identifier; use PhpParser\Node\Scalar\String_; use PhpParser\Node\Stmt\Class_ as ClassNode; use PhpParser\Node\Stmt\ClassConst; @@ -138,6 +140,29 @@ public function testCreateForEnum(): void self::assertInstanceOf(ConstantDescriptor::class, current($result->getConstants())); } + public function testCreateWithTypedConstant(): void + { + $const = new Const_('\Space\MyClass::MY_CONST1', new String_('a')); + $const->setAttribute('fqsen', new Fqsen((string) $const->name)); + $constantStub = new ClassConst([$const], ClassNode::MODIFIER_PUBLIC, [], [], new Identifier('string')); + + $class = $this->performCreate($constantStub); + + $constant = current($class->getConstants()); + $this->assertInstanceOf(ConstantDescriptor::class, $constant); + $this->assertEquals(new StringType(), $constant->getType()); + } + + public function testCreateWithUntypedConstantHasNullType(): void + { + $constantStub = $this->buildConstantIteratorStub(); + + $class = $this->performCreate($constantStub); + + $constant = current($class->getConstants()); + $this->assertNull($constant->getType()); + } + public function testCreateWithDocBlock(): void { $doc = new Doc('text');