Skip to content

Commit e157ef3

Browse files
committed
Merge branch '1.14.x' into 2.0.x
* 1.14.x: Allow annotation classes with a variadic parameter (#479)
2 parents 508c71c + fb0d71a commit e157ef3

File tree

2 files changed

+161
-0
lines changed

2 files changed

+161
-0
lines changed

lib/Doctrine/Common/Annotations/DocParser.php

+21
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,10 @@ class_exists(NamedArgumentConstructor::class);
601601
$metadata['default_property'] = reset($metadata['properties']);
602602
} elseif ($metadata['has_named_argument_constructor']) {
603603
foreach ($constructor->getParameters() as $parameter) {
604+
if ($parameter->isVariadic()) {
605+
break;
606+
}
607+
604608
$metadata['constructor_args'][$parameter->getName()] = [
605609
'position' => $parameter->getPosition(),
606610
'default' => $parameter->isOptional() ? $parameter->getDefaultValue() : null,
@@ -930,6 +934,23 @@ private function Annotation()
930934

931935
if (self::$annotationMetadata[$name]['has_named_argument_constructor']) {
932936
if (PHP_VERSION_ID >= 80000) {
937+
foreach ($values as $property => $value) {
938+
if (! isset(self::$annotationMetadata[$name]['constructor_args'][$property])) {
939+
throw AnnotationException::creationError(sprintf(
940+
<<<'EXCEPTION'
941+
The annotation @%s declared on %s does not have a property named "%s"
942+
that can be set through its named arguments constructor.
943+
Available named arguments: %s
944+
EXCEPTION
945+
,
946+
$originalName,
947+
$this->context,
948+
$property,
949+
implode(', ', array_keys(self::$annotationMetadata[$name]['constructor_args']))
950+
));
951+
}
952+
}
953+
933954
return $this->instantiateAnnotiation($originalName, $this->context, $name, $values);
934955
}
935956

tests/Doctrine/Tests/Common/Annotations/DocParserTest.php

+140
Original file line numberDiff line numberDiff line change
@@ -1649,6 +1649,18 @@ public function testNamedArgumentsConstructorAnnotationWithDefaultProperty(): vo
16491649
self::assertSame(1234, $result[0]->getBar());
16501650
}
16511651

1652+
public function testNamedArgumentsConstructorAnnotationWithExtraArguments(): void
1653+
{
1654+
$docParser = $this->createTestParser();
1655+
1656+
$this->expectException(AnnotationException::class);
1657+
$this->expectExceptionMessageMatches(
1658+
'/does not have a property named "invalid"\s.*\sAvailable named arguments: foo, bar/'
1659+
);
1660+
1661+
$docParser->parse('/** @AnotherNamedAnnotation(foo="baz", invalid="uh oh") */');
1662+
}
1663+
16521664
public function testNamedArgumentsConstructorAnnotationWithDefaultPropertyAsArray(): void
16531665
{
16541666
$result = $this
@@ -1701,6 +1713,115 @@ public function testNamedArgumentsConstructorAnnotationWithWrongArgumentType():
17011713
}
17021714
}
17031715

1716+
public function testAnnotationWithConstructorWithVariadicParamAndExtraNamedArguments(): void
1717+
{
1718+
$parser = $this->createTestParser();
1719+
$docblock = <<<'DOCBLOCK'
1720+
/**
1721+
* @SomeAnnotationWithConstructorWithVariadicParam(name = "Some data", foo = "Foo", bar = "Bar")
1722+
*/
1723+
DOCBLOCK;
1724+
1725+
$this->expectException(AnnotationException::class);
1726+
$this->expectExceptionMessageMatches(
1727+
'/does not have a property named "foo"\s.*\sAvailable named arguments: name/'
1728+
);
1729+
1730+
$parser->parse($docblock);
1731+
}
1732+
1733+
public function testAnnotationWithConstructorWithVariadicParamAndExtraNamedArgumentsShuffled(): void
1734+
{
1735+
$parser = $this->createTestParser();
1736+
$docblock = <<<'DOCBLOCK'
1737+
/**
1738+
* @SomeAnnotationWithConstructorWithVariadicParam(foo = "Foo", name = "Some data", bar = "Bar")
1739+
*/
1740+
DOCBLOCK;
1741+
1742+
$this->expectException(AnnotationException::class);
1743+
$this->expectExceptionMessageMatches(
1744+
'/does not have a property named "foo"\s.*\sAvailable named arguments: name/'
1745+
);
1746+
1747+
$parser->parse($docblock);
1748+
}
1749+
1750+
public function testAnnotationWithConstructorWithVariadicParamAndCombinedNamedAndPositionalArguments(): void
1751+
{
1752+
$parser = $this->createTestParser();
1753+
$docblock = <<<'DOCBLOCK'
1754+
/**
1755+
* @SomeAnnotationWithConstructorWithVariadicParam("Some data", "Foo", bar = "Bar")
1756+
*/
1757+
DOCBLOCK;
1758+
1759+
$this->expectException(AnnotationException::class);
1760+
$this->expectExceptionMessageMatches(
1761+
'/does not have a property named "bar"\s.*\sAvailable named arguments: name/'
1762+
);
1763+
1764+
$parser->parse($docblock);
1765+
}
1766+
1767+
public function testAnnotationWithConstructorWithVariadicParamPassOneNamedArgument(): void
1768+
{
1769+
$parser = $this->createTestParser();
1770+
$docblock = <<<'DOCBLOCK'
1771+
/**
1772+
* @SomeAnnotationWithConstructorWithVariadicParam(name = "Some data", data = "Foo")
1773+
*/
1774+
DOCBLOCK;
1775+
1776+
$this->expectException(AnnotationException::class);
1777+
$this->expectExceptionMessageMatches(
1778+
'/does not have a property named "data"\s.*\sAvailable named arguments: name/'
1779+
);
1780+
1781+
$parser->parse($docblock);
1782+
}
1783+
1784+
public function testAnnotationWithConstructorWithVariadicParamPassPositionalArguments(): void
1785+
{
1786+
$parser = $this->createTestParser();
1787+
$docblock = <<<'DOCBLOCK'
1788+
/**
1789+
* @SomeAnnotationWithConstructorWithVariadicParam("Some data", "Foo", "Bar")
1790+
*/
1791+
DOCBLOCK;
1792+
1793+
$result = $parser->parse($docblock);
1794+
self::assertCount(1, $result);
1795+
$annot = $result[0];
1796+
1797+
self::assertInstanceOf(SomeAnnotationWithConstructorWithVariadicParam::class, $annot);
1798+
1799+
self::assertSame('Some data', $annot->name);
1800+
// Positional extra arguments will be ignored
1801+
self::assertSame([], $annot->data);
1802+
}
1803+
1804+
public function testAnnotationWithConstructorWithVariadicParamNoArgs(): void
1805+
{
1806+
$parser = $this->createTestParser();
1807+
1808+
// Without variadic arguments
1809+
$docblock = <<<'DOCBLOCK'
1810+
/**
1811+
* @SomeAnnotationWithConstructorWithVariadicParam("Some data")
1812+
*/
1813+
DOCBLOCK;
1814+
1815+
$result = $parser->parse($docblock);
1816+
self::assertCount(1, $result);
1817+
$annot = $result[0];
1818+
1819+
self::assertInstanceOf(SomeAnnotationWithConstructorWithVariadicParam::class, $annot);
1820+
1821+
self::assertSame('Some data', $annot->name);
1822+
self::assertSame([], $annot->data);
1823+
}
1824+
17041825
/**
17051826
* Override for BC with PHPUnit <8
17061827
*/
@@ -1782,6 +1903,25 @@ public function getBar(): int
17821903
}
17831904
}
17841905

1906+
/**
1907+
* @Annotation
1908+
* @NamedArgumentConstructor
1909+
*/
1910+
class SomeAnnotationWithConstructorWithVariadicParam
1911+
{
1912+
public function __construct(string $name, string ...$data)
1913+
{
1914+
$this->name = $name;
1915+
$this->data = $data;
1916+
}
1917+
1918+
/** @var string[] */
1919+
public $data;
1920+
1921+
/** @var string */
1922+
public $name;
1923+
}
1924+
17851925
/** @Annotation */
17861926
class SettingsAnnotation
17871927
{

0 commit comments

Comments
 (0)