Skip to content

Commit

Permalink
[DependencyInjection] fixes validation of non-scalar attribute values
Browse files Browse the repository at this point in the history
  • Loading branch information
ju1ius authored and nicolas-grekas committed Jan 13, 2023
1 parent be8072b commit 7867301
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 20 deletions.
18 changes: 13 additions & 5 deletions Compiler/CheckDefinitionValidityPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,7 @@ public function process(ContainerBuilder $container)
// tag attribute values must be scalars
foreach ($definition->getTags() as $name => $tags) {
foreach ($tags as $attributes) {
foreach ($attributes as $attribute => $value) {
if (!\is_scalar($value) && null !== $value) {
throw new RuntimeException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s".', $id, $name, $attribute));
}
}
$this->validateAttributes($id, $name, $attributes);
}
}

Expand All @@ -87,4 +83,16 @@ public function process(ContainerBuilder $container)
}
}
}

private function validateAttributes(string $id, string $tag, array $attributes, array $path = []): void
{
foreach ($attributes as $name => $value) {
if (\is_array($value)) {
$this->validateAttributes($id, $tag, $value, [...$path, $name]);
} elseif (!\is_scalar($value) && null !== $value) {
$name = implode('.', [...$path, $name]);
throw new RuntimeException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s".', $id, $tag, $name));
}
}
}
}
9 changes: 5 additions & 4 deletions Loader/Configurator/DefaultsConfigurator.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,14 @@ final public function instanceof(string $fqcn): InstanceofConfigurator
return $this->parent->instanceof($fqcn);
}

private function validateAttributes(string $tagName, array $attributes, string $prefix = ''): void
private function validateAttributes(string $tag, array $attributes, array $path = []): void
{
foreach ($attributes as $attribute => $value) {
foreach ($attributes as $name => $value) {
if (\is_array($value)) {
$this->validateAttributes($tagName, $value, $attribute.'.');
$this->validateAttributes($tag, $value, [...$path, $name]);
} elseif (!\is_scalar($value ?? '')) {
throw new InvalidArgumentException(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type or an array of scalar-type.', $tagName, $prefix.$attribute));
$name = implode('.', [...$path, $name]);
throw new InvalidArgumentException(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type or an array of scalar-type.', $tag, $name));
}
}
}
Expand Down
9 changes: 5 additions & 4 deletions Loader/Configurator/Traits/TagTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,14 @@ final public function tag(string $name, array $attributes = []): static
return $this;
}

private function validateAttributes(string $tagName, array $attributes, string $prefix = ''): void
private function validateAttributes(string $tag, array $attributes, array $path = []): void
{
foreach ($attributes as $attribute => $value) {
foreach ($attributes as $name => $value) {
if (\is_array($value)) {
$this->validateAttributes($tagName, $value, $attribute.'.');
$this->validateAttributes($tag, $value, [...$path, $name]);
} elseif (!\is_scalar($value ?? '')) {
throw new InvalidArgumentException(sprintf('A tag attribute must be of a scalar-type or an array of scalar-types for service "%s", tag "%s", attribute "%s".', $this->id, $tagName, $prefix.$attribute));
$name = implode('.', [...$path, $name]);
throw new InvalidArgumentException(sprintf('A tag attribute must be of a scalar-type or an array of scalar-types for service "%s", tag "%s", attribute "%s".', $this->id, $tag, $name));
}
}
}
Expand Down
9 changes: 5 additions & 4 deletions Loader/YamlFileLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -937,13 +937,14 @@ private function checkDefinition(string $id, array $definition, string $file)
}
}

private function validateAttributes(string $message, array $attributes, string $prefix = ''): void
private function validateAttributes(string $message, array $attributes, array $path = []): void
{
foreach ($attributes as $attribute => $value) {
foreach ($attributes as $name => $value) {
if (\is_array($value)) {
$this->validateAttributes($message, $value, $attribute.'.');
$this->validateAttributes($message, $value, [...$path, $name]);
} elseif (!\is_scalar($value ?? '')) {
throw new InvalidArgumentException(sprintf($message, $prefix.$attribute));
$name = implode('.', [...$path, $name]);
throw new InvalidArgumentException(sprintf($message, $name));
}
}
}
Expand Down
31 changes: 28 additions & 3 deletions Tests/Compiler/CheckDefinitionValidityPassTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,21 +80,46 @@ public function testValidTags()
$container->register('b', 'class')->addTag('foo', ['bar' => null]);
$container->register('c', 'class')->addTag('foo', ['bar' => 1]);
$container->register('d', 'class')->addTag('foo', ['bar' => 1.1]);
$container->register('d', 'class')->addTag('foo', ['bar' => ['baz' => 'baz']]);
$container->register('e', 'class')->addTag('foo', ['deep' => ['foo' => ['bar' => 'baz']]]);

$this->process($container);

$this->addToAssertionCount(1);
}

public function testInvalidTags()
/**
* @dataProvider provideInvalidTags
*/
public function testInvalidTags(string $name, array $attributes, string $message)
{
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage($message);
$container = new ContainerBuilder();
$container->register('a', 'class')->addTag('foo', ['bar' => ['baz' => 'baz']]);

$container->register('a', 'class')->addTag($name, $attributes);
$this->process($container);
}

public static function provideInvalidTags(): iterable
{
$message = 'A "tags" attribute must be of a scalar-type for service "a", tag "%s", attribute "%s".';
yield 'object attribute value' => [
'foo',
['bar' => new class() {}],
sprintf($message, 'foo', 'bar'),
];
yield 'nested object attribute value' => [
'foo',
['bar' => ['baz' => new class() {}]],
sprintf($message, 'foo', 'bar.baz'),
];
yield 'deeply nested object attribute value' => [
'foo',
['bar' => ['baz' => ['qux' => new class() {}]]],
sprintf($message, 'foo', 'bar.baz.qux'),
];
}

public function testDynamicPublicServiceName()
{
$this->expectException(EnvParameterException::class);
Expand Down

0 comments on commit 7867301

Please sign in to comment.