diff --git a/src/Symfony/Bundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/DependencyInjection/Configuration.php index cc7326d2e6..34156be729 100644 --- a/src/Symfony/Bundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/DependencyInjection/Configuration.php @@ -674,6 +674,18 @@ private function addDefaultsSection(ArrayNodeDefinition $rootNode): void ->arrayNode('parameters') ->info('Global parameters applied to all resources and operations.') ->useAttributeAsKey('parameter_class') + ->validate() + ->always(static function (array $parameters): array { + foreach ($parameters as $name => $parameter) { + $class = $parameter['class'] ?? $name; + if ((isset($parameter['class']) || str_contains((string) $name, '\\')) && !is_subclass_of($class, Parameter::class)) { + throw new InvalidConfigurationException(\sprintf('The default parameter "%s" must reference a class extending "%s", "%s" given.', $name, Parameter::class, $class)); + } + } + + return $parameters; + }) + ->end() ->prototype('array') ->ignoreExtraKeys(false) ->children() diff --git a/tests/Symfony/Bundle/DependencyInjection/ConfigurationTest.php b/tests/Symfony/Bundle/DependencyInjection/ConfigurationTest.php index 24c952b015..e294d98a0f 100644 --- a/tests/Symfony/Bundle/DependencyInjection/ConfigurationTest.php +++ b/tests/Symfony/Bundle/DependencyInjection/ConfigurationTest.php @@ -14,6 +14,7 @@ namespace ApiPlatform\Tests\Symfony\Bundle\DependencyInjection; use ApiPlatform\Metadata\Exception\InvalidArgumentException; +use ApiPlatform\Metadata\HeaderParameter; use ApiPlatform\Symfony\Bundle\DependencyInjection\Configuration; use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use Doctrine\ORM\OptimisticLockException; @@ -540,4 +541,43 @@ public function testOauthScopeNaming(): void $this->assertEquals(['resource:read-write' => 'Read and write resource data'], $config['oauth']['scopes']); } + + public function testDefaultParameters(): void + { + $config = $this->processor->processConfiguration($this->configuration, [ + 'api_platform' => [ + 'defaults' => [ + 'parameters' => [ + HeaderParameter::class => ['key' => 'API-Token'], + 'request_id' => ['class' => HeaderParameter::class, 'key' => 'Request-ID'], + 'api_version' => ['key' => 'api_version'], + ], + ], + ], + ]); + + $this->assertCount(3, $config['defaults']['parameters']); + } + + public static function invalidDefaultParameterProvider(): iterable + { + yield 'unknown class option' => ['api_token', ['class' => 'ApiPlatform\Metadata\HeaderParamter'], 'ApiPlatform\Metadata\HeaderParamter']; + yield 'unknown class as key' => ['ApiPlatform\Metadata\HeaderParamter', ['key' => 'API-Token'], 'ApiPlatform\Metadata\HeaderParamter']; + yield 'not a parameter' => ['api_token', ['class' => \stdClass::class], \stdClass::class]; + } + + #[DataProvider('invalidDefaultParameterProvider')] + public function testDefaultParametersWithInvalidClass(string $name, array $parameter, string $class): void + { + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage(\sprintf('The default parameter "%s" must reference a class extending "ApiPlatform\Metadata\Parameter", "%s" given.', $name, $class)); + + $this->processor->processConfiguration($this->configuration, [ + 'api_platform' => [ + 'defaults' => [ + 'parameters' => [$name => $parameter], + ], + ], + ]); + } }