diff -Nru php-doctrine-annotations-2.0.0/debian/changelog php-doctrine-annotations-2.0.1/debian/changelog --- php-doctrine-annotations-2.0.0/debian/changelog 2023-01-01 09:15:03.000000000 +0000 +++ php-doctrine-annotations-2.0.1/debian/changelog 2023-02-03 04:28:39.000000000 +0000 @@ -1,3 +1,10 @@ +php-doctrine-annotations (2.0.1-1) unstable; urgency=medium + + [ Alexander M. Turek ] + * Allow annotation classes with a variadic parameter (#479) + + -- David Prévot Fri, 03 Feb 2023 05:28:39 +0100 + php-doctrine-annotations (2.0.0-3) unstable; urgency=medium * Mark package as Multi-Arch: foreign diff -Nru php-doctrine-annotations-2.0.0/.doctrine-project.json php-doctrine-annotations-2.0.1/.doctrine-project.json --- php-doctrine-annotations-2.0.0/.doctrine-project.json 2022-12-19 18:17:20.000000000 +0000 +++ php-doctrine-annotations-2.0.1/.doctrine-project.json 2023-02-02 22:02:53.000000000 +0000 @@ -5,21 +5,25 @@ "docsSlug": "doctrine-annotations", "versions": [ { + "name": "2.0", + "branchName": "2.0.x", + "aliases": [ + "current", + "stable" + ], + "current": true, + "maintained": true + }, + { "name": "1.14", "branchName": "1.14.x", - "slug": "latest", - "upcoming": true + "maintained": true }, { "name": "1.13", "branchName": "1.13.x", "slug": "1.13", - "aliases": [ - "current", - "stable" - ], - "current": true, - "maintained": true + "maintained": false }, { "name": "1.12", diff -Nru php-doctrine-annotations-2.0.0/lib/Doctrine/Common/Annotations/DocParser.php php-doctrine-annotations-2.0.1/lib/Doctrine/Common/Annotations/DocParser.php --- php-doctrine-annotations-2.0.0/lib/Doctrine/Common/Annotations/DocParser.php 2022-12-19 18:17:20.000000000 +0000 +++ php-doctrine-annotations-2.0.1/lib/Doctrine/Common/Annotations/DocParser.php 2023-02-02 22:02:53.000000000 +0000 @@ -601,6 +601,10 @@ $metadata['default_property'] = reset($metadata['properties']); } elseif ($metadata['has_named_argument_constructor']) { foreach ($constructor->getParameters() as $parameter) { + if ($parameter->isVariadic()) { + break; + } + $metadata['constructor_args'][$parameter->getName()] = [ 'position' => $parameter->getPosition(), 'default' => $parameter->isOptional() ? $parameter->getDefaultValue() : null, @@ -930,6 +934,23 @@ if (self::$annotationMetadata[$name]['has_named_argument_constructor']) { if (PHP_VERSION_ID >= 80000) { + foreach ($values as $property => $value) { + if (! isset(self::$annotationMetadata[$name]['constructor_args'][$property])) { + throw AnnotationException::creationError(sprintf( + <<<'EXCEPTION' +The annotation @%s declared on %s does not have a property named "%s" +that can be set through its named arguments constructor. +Available named arguments: %s +EXCEPTION + , + $originalName, + $this->context, + $property, + implode(', ', array_keys(self::$annotationMetadata[$name]['constructor_args'])) + )); + } + } + return $this->instantiateAnnotiation($originalName, $this->context, $name, $values); } diff -Nru php-doctrine-annotations-2.0.0/tests/Doctrine/Tests/Common/Annotations/DocParserTest.php php-doctrine-annotations-2.0.1/tests/Doctrine/Tests/Common/Annotations/DocParserTest.php --- php-doctrine-annotations-2.0.0/tests/Doctrine/Tests/Common/Annotations/DocParserTest.php 2022-12-19 18:17:20.000000000 +0000 +++ php-doctrine-annotations-2.0.1/tests/Doctrine/Tests/Common/Annotations/DocParserTest.php 2023-02-02 22:02:53.000000000 +0000 @@ -1649,6 +1649,18 @@ self::assertSame(1234, $result[0]->getBar()); } + public function testNamedArgumentsConstructorAnnotationWithExtraArguments(): void + { + $docParser = $this->createTestParser(); + + $this->expectException(AnnotationException::class); + $this->expectExceptionMessageMatches( + '/does not have a property named "invalid"\s.*\sAvailable named arguments: foo, bar/' + ); + + $docParser->parse('/** @AnotherNamedAnnotation(foo="baz", invalid="uh oh") */'); + } + public function testNamedArgumentsConstructorAnnotationWithDefaultPropertyAsArray(): void { $result = $this @@ -1701,6 +1713,115 @@ } } + public function testAnnotationWithConstructorWithVariadicParamAndExtraNamedArguments(): void + { + $parser = $this->createTestParser(); + $docblock = <<<'DOCBLOCK' +/** + * @SomeAnnotationWithConstructorWithVariadicParam(name = "Some data", foo = "Foo", bar = "Bar") + */ +DOCBLOCK; + + $this->expectException(AnnotationException::class); + $this->expectExceptionMessageMatches( + '/does not have a property named "foo"\s.*\sAvailable named arguments: name/' + ); + + $parser->parse($docblock); + } + + public function testAnnotationWithConstructorWithVariadicParamAndExtraNamedArgumentsShuffled(): void + { + $parser = $this->createTestParser(); + $docblock = <<<'DOCBLOCK' +/** + * @SomeAnnotationWithConstructorWithVariadicParam(foo = "Foo", name = "Some data", bar = "Bar") + */ +DOCBLOCK; + + $this->expectException(AnnotationException::class); + $this->expectExceptionMessageMatches( + '/does not have a property named "foo"\s.*\sAvailable named arguments: name/' + ); + + $parser->parse($docblock); + } + + public function testAnnotationWithConstructorWithVariadicParamAndCombinedNamedAndPositionalArguments(): void + { + $parser = $this->createTestParser(); + $docblock = <<<'DOCBLOCK' +/** + * @SomeAnnotationWithConstructorWithVariadicParam("Some data", "Foo", bar = "Bar") + */ +DOCBLOCK; + + $this->expectException(AnnotationException::class); + $this->expectExceptionMessageMatches( + '/does not have a property named "bar"\s.*\sAvailable named arguments: name/' + ); + + $parser->parse($docblock); + } + + public function testAnnotationWithConstructorWithVariadicParamPassOneNamedArgument(): void + { + $parser = $this->createTestParser(); + $docblock = <<<'DOCBLOCK' +/** + * @SomeAnnotationWithConstructorWithVariadicParam(name = "Some data", data = "Foo") + */ +DOCBLOCK; + + $this->expectException(AnnotationException::class); + $this->expectExceptionMessageMatches( + '/does not have a property named "data"\s.*\sAvailable named arguments: name/' + ); + + $parser->parse($docblock); + } + + public function testAnnotationWithConstructorWithVariadicParamPassPositionalArguments(): void + { + $parser = $this->createTestParser(); + $docblock = <<<'DOCBLOCK' +/** + * @SomeAnnotationWithConstructorWithVariadicParam("Some data", "Foo", "Bar") + */ +DOCBLOCK; + + $result = $parser->parse($docblock); + self::assertCount(1, $result); + $annot = $result[0]; + + self::assertInstanceOf(SomeAnnotationWithConstructorWithVariadicParam::class, $annot); + + self::assertSame('Some data', $annot->name); + // Positional extra arguments will be ignored + self::assertSame([], $annot->data); + } + + public function testAnnotationWithConstructorWithVariadicParamNoArgs(): void + { + $parser = $this->createTestParser(); + + // Without variadic arguments + $docblock = <<<'DOCBLOCK' +/** + * @SomeAnnotationWithConstructorWithVariadicParam("Some data") + */ +DOCBLOCK; + + $result = $parser->parse($docblock); + self::assertCount(1, $result); + $annot = $result[0]; + + self::assertInstanceOf(SomeAnnotationWithConstructorWithVariadicParam::class, $annot); + + self::assertSame('Some data', $annot->name); + self::assertSame([], $annot->data); + } + /** * Override for BC with PHPUnit <8 */ @@ -1782,6 +1903,25 @@ } } +/** + * @Annotation + * @NamedArgumentConstructor + */ +class SomeAnnotationWithConstructorWithVariadicParam +{ + public function __construct(string $name, string ...$data) + { + $this->name = $name; + $this->data = $data; + } + + /** @var string[] */ + public $data; + + /** @var string */ + public $name; +} + /** @Annotation */ class SettingsAnnotation {