diff --git a/src/Metadata/Parameters.php b/src/Metadata/Parameters.php index c576a113af..83add5323f 100644 --- a/src/Metadata/Parameters.php +++ b/src/Metadata/Parameters.php @@ -105,15 +105,13 @@ private static function propertyDiscriminator(Parameter $parameter): string */ public function remove(string $key, string $parameterClass = QueryParameter::class): self { - foreach ($this->parameters as $i => [$parameterName, $parameter]) { - if ($parameterName === $key && $parameterClass === $parameter::class) { - unset($this->parameters[$i]); - - return $this; - } + if (null === $i = $this->indexOf($key, $parameterClass)) { + throw new RuntimeException(\sprintf('Could not remove parameter "%s".', $key)); } - throw new RuntimeException(\sprintf('Could not remove parameter "%s".', $key)); + unset($this->parameters[$i]); + + return $this; } /** @@ -125,13 +123,11 @@ public function remove(string $key, string $parameterClass = QueryParameter::cla */ public function get(string $key, string $parameterClass = QueryParameter::class): ?Parameter { - foreach ($this->parameters as [$parameterName, $parameter]) { - if ($parameterName === $key && $parameterClass === $parameter::class) { - return $parameter; - } + if (null === $i = $this->indexOf($key, $parameterClass)) { + return null; } - return null; + return $this->parameters[$i][1]; } /** @@ -141,13 +137,27 @@ public function get(string $key, string $parameterClass = QueryParameter::class) */ public function has(string $key, string $parameterClass = QueryParameter::class): bool { - foreach ($this->parameters as [$parameterName, $parameter]) { - if ($parameterName === $key && $parameterClass === $parameter::class) { - return true; + return null !== $this->indexOf($key, $parameterClass); + } + + private function indexOf(string $key, string $parameterClass): ?int + { + $subclassMatch = null; + foreach ($this->parameters as $i => [$parameterName, $parameter]) { + if ($parameterName !== $key) { + continue; + } + + if ($parameterClass === $parameter::class) { + return $i; + } + + if (null === $subclassMatch && $parameter instanceof $parameterClass) { + $subclassMatch = $i; } } - return false; + return $subclassMatch; } public function count(): int diff --git a/src/Metadata/Tests/ParametersTest.php b/src/Metadata/Tests/ParametersTest.php index 966212e6db..ca61564561 100644 --- a/src/Metadata/Tests/ParametersTest.php +++ b/src/Metadata/Tests/ParametersTest.php @@ -16,6 +16,7 @@ use ApiPlatform\Metadata\HeaderParameter; use ApiPlatform\Metadata\Parameters; use ApiPlatform\Metadata\QueryParameter; +use ApiPlatform\Metadata\ScalarHeaderParameter; use PHPUnit\Framework\TestCase; class ParametersTest extends TestCase @@ -77,4 +78,31 @@ public function testPropertyPlaceholderKeysAreNotDeduplicatedViaAdd(): void $this->assertCount(2, $parameters); } + + public function testLookupMatchesSubclass(): void + { + $scalar = new ScalarHeaderParameter(key: 'Foo'); + $parameters = new Parameters([$scalar]); + + $this->assertSame($scalar, $parameters->get('Foo', HeaderParameter::class)); + $this->assertTrue($parameters->has('Foo', HeaderParameter::class)); + $this->assertFalse($parameters->has('Foo')); + + $parameters->remove('Foo', HeaderParameter::class); + $this->assertCount(0, $parameters); + } + + public function testLookupPrefersExactClassOverSubclass(): void + { + $scalar = new ScalarHeaderParameter(key: 'Foo'); + $header = new HeaderParameter(key: 'Foo'); + $parameters = new Parameters([$scalar, $header]); + + $this->assertSame($header, $parameters->get('Foo', HeaderParameter::class)); + $this->assertSame($scalar, $parameters->get('Foo', ScalarHeaderParameter::class)); + + $parameters->remove('Foo', HeaderParameter::class); + $this->assertCount(1, $parameters); + $this->assertSame($scalar, $parameters->get('Foo', HeaderParameter::class)); + } }