From b19779adef2887592b9eb2aa3c85983eeb27cd4b Mon Sep 17 00:00:00 2001 From: lacatoire Date: Fri, 10 Apr 2026 20:20:14 +0200 Subject: [PATCH 1/2] Add support for PHP 8.4 interface properties PHP 8.4 allows declaring properties in interfaces using property hooks. Add getProperties() and addProperty() to Interface_, and allow the Property factory to accept Interface_ as a valid container. Fixes phpDocumentor/phpDocumentor#4061 --- .../Reflection/Php/Factory/Property.php | 2 + .../Reflection/Php/Interface_.php | 21 ++++++++++ tests/integration/InterfacePropertyTest.php | 42 +++++++++++++++++++ .../data/PHP84/InterfaceProperties.php | 15 +++++++ .../Reflection/Php/Interface_Test.php | 13 ++++++ 5 files changed, 93 insertions(+) create mode 100644 tests/integration/InterfacePropertyTest.php create mode 100644 tests/integration/data/PHP84/InterfaceProperties.php diff --git a/src/phpDocumentor/Reflection/Php/Factory/Property.php b/src/phpDocumentor/Reflection/Php/Factory/Property.php index 5b32c179..8232dcf5 100644 --- a/src/phpDocumentor/Reflection/Php/Factory/Property.php +++ b/src/phpDocumentor/Reflection/Php/Factory/Property.php @@ -18,6 +18,7 @@ use phpDocumentor\Reflection\Location; use phpDocumentor\Reflection\Php\Class_; use phpDocumentor\Reflection\Php\Factory\Reducer\Reducer; +use phpDocumentor\Reflection\Php\Interface_; use phpDocumentor\Reflection\Php\Property as PropertyDescriptor; use phpDocumentor\Reflection\Php\StrategyContainer; use phpDocumentor\Reflection\Php\Trait_; @@ -69,6 +70,7 @@ protected function doCreate( [ Class_::class, Trait_::class, + Interface_::class, ], ); diff --git a/src/phpDocumentor/Reflection/Php/Interface_.php b/src/phpDocumentor/Reflection/Php/Interface_.php index a38ca067..64ce1ac6 100644 --- a/src/phpDocumentor/Reflection/Php/Interface_.php +++ b/src/phpDocumentor/Reflection/Php/Interface_.php @@ -37,6 +37,9 @@ final class Interface_ implements Element, MetaDataContainerInterface, Attribute /** @var Method[] */ private array $methods = []; + /** @var Property[] */ + private array $properties = []; + private readonly Location $location; private readonly Location $endLocation; @@ -95,6 +98,24 @@ public function addMethod(Method $method): void $this->methods[(string) $method->getFqsen()] = $method; } + /** + * Returns the properties of this interface. + * + * @return Property[] + */ + public function getProperties(): array + { + return $this->properties; + } + + /** + * Add a property to this interface. + */ + public function addProperty(Property $property): void + { + $this->properties[(string) $property->getFqsen()] = $property; + } + /** * Returns the Fqsen of the element. */ diff --git a/tests/integration/InterfacePropertyTest.php b/tests/integration/InterfacePropertyTest.php new file mode 100644 index 00000000..47393c23 --- /dev/null +++ b/tests/integration/InterfacePropertyTest.php @@ -0,0 +1,42 @@ +create('My project', [new LocalFile($file)]); + + $interfaces = $project->getFiles()[$file]->getInterfaces(); + + $hasId = $interfaces['\PHP84\HasId']; + $properties = $hasId->getProperties(); + $this->assertCount(1, $properties); + $idProperty = $properties['\PHP84\HasId::$id']; + $this->assertEquals(new Integer(), $idProperty->getType()); + $this->assertEquals(new Visibility(Visibility::PUBLIC_), $idProperty->getVisibility()); + $this->assertCount(1, $idProperty->getHooks()); + $this->assertEquals('get', $idProperty->getHooks()[0]->getName()); + + $hasName = $interfaces['\PHP84\HasName']; + $properties = $hasName->getProperties(); + $this->assertCount(1, $properties); + $nameProperty = $properties['\PHP84\HasName::$name']; + $this->assertEquals(new String_(), $nameProperty->getType()); + $this->assertCount(2, $nameProperty->getHooks()); + } +} diff --git a/tests/integration/data/PHP84/InterfaceProperties.php b/tests/integration/data/PHP84/InterfaceProperties.php new file mode 100644 index 00000000..4ba761dd --- /dev/null +++ b/tests/integration/data/PHP84/InterfaceProperties.php @@ -0,0 +1,15 @@ +assertEquals(['\MySpace\MyInterface::myMethod()' => $method], $this->fixture->getMethods()); } + public function testSettingAndGettingProperties(): void + { + $this->assertEquals([], $this->fixture->getProperties()); + + $property = new Property(new Fqsen('\MySpace\MyInterface::$myProperty')); + + $this->fixture->addProperty($property); + + $this->assertEquals(['\MySpace\MyInterface::$myProperty' => $property], $this->fixture->getProperties()); + } + public function testReturningTheParentsOfThisInterface(): void { $this->assertSame($this->exampleParents, $this->fixture->getParents()); From fea99bbab43998424c8a625266b3a993d8854462 Mon Sep 17 00:00:00 2001 From: lacatoire Date: Fri, 10 Apr 2026 20:51:35 +0200 Subject: [PATCH 2/2] Fix coding standards and skip test on old php-parser - Remove same-namespace import of Property in Interface_Test - Add RequiresPackage constraint for nikic/php-parser >= 5.2 on the integration test, as older versions cannot parse interface property syntax --- tests/integration/InterfacePropertyTest.php | 2 ++ tests/unit/phpDocumentor/Reflection/Php/Interface_Test.php | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/integration/InterfacePropertyTest.php b/tests/integration/InterfacePropertyTest.php index 47393c23..f7760f69 100644 --- a/tests/integration/InterfacePropertyTest.php +++ b/tests/integration/InterfacePropertyTest.php @@ -4,6 +4,7 @@ namespace integration; +use EliasHaeussler\PHPUnitAttributes\Attribute\RequiresPackage; use phpDocumentor\Reflection\File\LocalFile; use phpDocumentor\Reflection\Php\ProjectFactory; use phpDocumentor\Reflection\Php\Visibility; @@ -12,6 +13,7 @@ use PHPUnit\Framework\Attributes\CoversNothing; use PHPUnit\Framework\TestCase; +#[RequiresPackage('nikic/php-parser', '>= 5.2')] #[CoversNothing] final class InterfacePropertyTest extends TestCase { diff --git a/tests/unit/phpDocumentor/Reflection/Php/Interface_Test.php b/tests/unit/phpDocumentor/Reflection/Php/Interface_Test.php index c7b65ec6..05edaba7 100644 --- a/tests/unit/phpDocumentor/Reflection/Php/Interface_Test.php +++ b/tests/unit/phpDocumentor/Reflection/Php/Interface_Test.php @@ -18,7 +18,6 @@ use phpDocumentor\Reflection\Fqsen; use phpDocumentor\Reflection\Location; use phpDocumentor\Reflection\Metadata\MetaDataContainer as MetaDataContainerInterface; -use phpDocumentor\Reflection\Php\Property; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\UsesClass;