From fbd2b9608818b21cbda42d8fe9379234f47f9e65 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Thu, 29 May 2025 14:00:57 +0200 Subject: [PATCH 01/19] Replace get_class() calls by ::class --- Tests/Compiler/ServiceLocatorTagPassTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Compiler/ServiceLocatorTagPassTest.php b/Tests/Compiler/ServiceLocatorTagPassTest.php index 812b47c7a..7218db6de 100644 --- a/Tests/Compiler/ServiceLocatorTagPassTest.php +++ b/Tests/Compiler/ServiceLocatorTagPassTest.php @@ -83,7 +83,7 @@ public function testProcessValue() $this->assertSame(CustomDefinition::class, $locator('bar')::class); $this->assertSame(CustomDefinition::class, $locator('baz')::class); $this->assertSame(CustomDefinition::class, $locator('some.service')::class); - $this->assertSame(CustomDefinition::class, \get_class($locator('inlines.service'))); + $this->assertSame(CustomDefinition::class, $locator('inlines.service')::class); } public function testServiceWithKeyOverwritesPreviousInheritedKey() From f23b73f9373a27c23e529aefef861b2ebdf112b6 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 30 May 2025 15:05:58 +0200 Subject: [PATCH 02/19] [DependencyInjection][FrameworkBundle] Use php-serialize to dump the container for debug/lint commands --- Alias.php | 16 +++++++++ Argument/AbstractArgument.php | 2 ++ Argument/ArgumentTrait.php | 36 +++++++++++++++++++ Argument/BoundArgument.php | 14 ++++++-- Argument/IteratorArgument.php | 2 ++ Argument/ServiceClosureArgument.php | 2 ++ Argument/ServiceLocatorArgument.php | 4 +++ Argument/TaggedIteratorArgument.php | 6 ++-- Compiler/ResolveEnvPlaceholdersPass.php | 18 +++++++--- Definition.php | 16 +++++++++ Reference.php | 18 +++++++++- .../RegisterServiceSubscribersPassTest.php | 2 +- 12 files changed, 124 insertions(+), 12 deletions(-) create mode 100644 Argument/ArgumentTrait.php diff --git a/Alias.php b/Alias.php index 0ec1161f8..73d05b46e 100644 --- a/Alias.php +++ b/Alias.php @@ -103,4 +103,20 @@ public function __toString(): string { return $this->id; } + + public function __serialize(): array + { + $data = []; + foreach ((array) $this as $k => $v) { + if (!$v) { + continue; + } + if (false !== $i = strrpos($k, "\0")) { + $k = substr($k, 1 + $i); + } + $data[$k] = $v; + } + + return $data; + } } diff --git a/Argument/AbstractArgument.php b/Argument/AbstractArgument.php index b04f9b848..76f4f7411 100644 --- a/Argument/AbstractArgument.php +++ b/Argument/AbstractArgument.php @@ -16,6 +16,8 @@ */ final class AbstractArgument { + use ArgumentTrait; + private string $text; private string $context = ''; diff --git a/Argument/ArgumentTrait.php b/Argument/ArgumentTrait.php new file mode 100644 index 000000000..77b4b2333 --- /dev/null +++ b/Argument/ArgumentTrait.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Argument; + +/** + * Helps reduce the size of the dumped container when using php-serialize. + * + * @internal + */ +trait ArgumentTrait +{ + public function __serialize(): array + { + $data = []; + foreach ((array) $this as $k => $v) { + if (null === $v) { + continue; + } + if (false !== $i = strrpos($k, "\0")) { + $k = substr($k, 1 + $i); + } + $data[$k] = $v; + } + + return $data; + } +} diff --git a/Argument/BoundArgument.php b/Argument/BoundArgument.php index f704bc19a..b8b1df90c 100644 --- a/Argument/BoundArgument.php +++ b/Argument/BoundArgument.php @@ -16,21 +16,29 @@ */ final class BoundArgument implements ArgumentInterface { + use ArgumentTrait; + public const SERVICE_BINDING = 0; public const DEFAULTS_BINDING = 1; public const INSTANCEOF_BINDING = 2; private static int $sequence = 0; + private mixed $value = null; private ?int $identifier = null; private ?bool $used = null; + private int $type = 0; + private ?string $file = null; public function __construct( - private mixed $value, + mixed $value, bool $trackUsage = true, - private int $type = 0, - private ?string $file = null, + int $type = 0, + ?string $file = null, ) { + $this->value = $value; + $this->type = $type; + $this->file = $file; if ($trackUsage) { $this->identifier = ++self::$sequence; } else { diff --git a/Argument/IteratorArgument.php b/Argument/IteratorArgument.php index 1e2de6d98..fa44f22b9 100644 --- a/Argument/IteratorArgument.php +++ b/Argument/IteratorArgument.php @@ -18,6 +18,8 @@ */ class IteratorArgument implements ArgumentInterface { + use ArgumentTrait; + private array $values; public function __construct(array $values) diff --git a/Argument/ServiceClosureArgument.php b/Argument/ServiceClosureArgument.php index 3537540ed..7fc2d3081 100644 --- a/Argument/ServiceClosureArgument.php +++ b/Argument/ServiceClosureArgument.php @@ -20,6 +20,8 @@ */ class ServiceClosureArgument implements ArgumentInterface { + use ArgumentTrait; + private array $values; public function __construct(mixed $value) diff --git a/Argument/ServiceLocatorArgument.php b/Argument/ServiceLocatorArgument.php index 555d14689..4983d83ac 100644 --- a/Argument/ServiceLocatorArgument.php +++ b/Argument/ServiceLocatorArgument.php @@ -11,6 +11,8 @@ namespace Symfony\Component\DependencyInjection\Argument; +use Symfony\Component\DependencyInjection\Loader\Configurator\Traits\ArgumentTrait; + /** * Represents a closure acting as a service locator. * @@ -18,6 +20,8 @@ */ class ServiceLocatorArgument implements ArgumentInterface { + use ArgumentTrait; + private array $values; private ?TaggedIteratorArgument $taggedIteratorArgument = null; diff --git a/Argument/TaggedIteratorArgument.php b/Argument/TaggedIteratorArgument.php index 396cdf144..2a9fdd72b 100644 --- a/Argument/TaggedIteratorArgument.php +++ b/Argument/TaggedIteratorArgument.php @@ -18,9 +18,9 @@ */ class TaggedIteratorArgument extends IteratorArgument { - private mixed $indexAttribute; - private ?string $defaultIndexMethod; - private ?string $defaultPriorityMethod; + private mixed $indexAttribute = null; + private ?string $defaultIndexMethod = null; + private ?string $defaultPriorityMethod = null; /** * @param string $tag The name of the tag identifying the target services diff --git a/Compiler/ResolveEnvPlaceholdersPass.php b/Compiler/ResolveEnvPlaceholdersPass.php index ea077cba9..87927ed24 100644 --- a/Compiler/ResolveEnvPlaceholdersPass.php +++ b/Compiler/ResolveEnvPlaceholdersPass.php @@ -20,25 +20,35 @@ class ResolveEnvPlaceholdersPass extends AbstractRecursivePass { protected bool $skipScalars = false; + /** + * @param string|true|null $format A sprintf() format returning the replacement for each env var name or + * null to resolve back to the original "%env(VAR)%" format or + * true to resolve to the actual values of the referenced env vars + */ + public function __construct( + private string|bool|null $format = true, + ) { + } + protected function processValue(mixed $value, bool $isRoot = false): mixed { if (\is_string($value)) { - return $this->container->resolveEnvPlaceholders($value, true); + return $this->container->resolveEnvPlaceholders($value, $this->format); } if ($value instanceof Definition) { $changes = $value->getChanges(); if (isset($changes['class'])) { - $value->setClass($this->container->resolveEnvPlaceholders($value->getClass(), true)); + $value->setClass($this->container->resolveEnvPlaceholders($value->getClass(), $this->format)); } if (isset($changes['file'])) { - $value->setFile($this->container->resolveEnvPlaceholders($value->getFile(), true)); + $value->setFile($this->container->resolveEnvPlaceholders($value->getFile(), $this->format)); } } $value = parent::processValue($value, $isRoot); if ($value && \is_array($value) && !$isRoot) { - $value = array_combine($this->container->resolveEnvPlaceholders(array_keys($value), true), $value); + $value = array_combine($this->container->resolveEnvPlaceholders(array_keys($value), $this->format), $value); } return $value; diff --git a/Definition.php b/Definition.php index 61cc0b9d6..b410d0220 100644 --- a/Definition.php +++ b/Definition.php @@ -820,4 +820,20 @@ public function hasErrors(): bool { return (bool) $this->errors; } + + public function __serialize(): array + { + $data = []; + foreach ((array) $this as $k => $v) { + if (false !== $i = strrpos($k, "\0")) { + $k = substr($k, 1 + $i); + } + if (!$v xor 'shared' === $k) { + continue; + } + $data[$k] = $v; + } + + return $data; + } } diff --git a/Reference.php b/Reference.php index df7d173c5..9a9d83fb1 100644 --- a/Reference.php +++ b/Reference.php @@ -34,6 +34,22 @@ public function __toString(): string */ public function getInvalidBehavior(): int { - return $this->invalidBehavior; + return $this->invalidBehavior ??= ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; + } + + public function __serialize(): array + { + $data = []; + foreach ((array) $this as $k => $v) { + if (false !== $i = strrpos($k, "\0")) { + $k = substr($k, 1 + $i); + } + if ('invalidBehavior' === $k && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $v) { + continue; + } + $data[$k] = $v; + } + + return $data; } } diff --git a/Tests/Compiler/RegisterServiceSubscribersPassTest.php b/Tests/Compiler/RegisterServiceSubscribersPassTest.php index e7b36d3ce..ffbdc180f 100644 --- a/Tests/Compiler/RegisterServiceSubscribersPassTest.php +++ b/Tests/Compiler/RegisterServiceSubscribersPassTest.php @@ -452,7 +452,7 @@ public static function getSubscribedServices(): array 'autowired' => new ServiceClosureArgument(new TypedReference('service.id', 'stdClass', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, 'autowired', [new Autowire(service: 'service.id')])), 'autowired.nullable' => new ServiceClosureArgument(new TypedReference('service.id', 'stdClass', ContainerInterface::IGNORE_ON_INVALID_REFERENCE, 'autowired.nullable', [new Autowire(service: 'service.id')])), 'autowired.parameter' => new ServiceClosureArgument('foobar'), - 'autowire.decorated' => new ServiceClosureArgument(new Reference('.service_locator.oNVewcO.inner', ContainerInterface::NULL_ON_INVALID_REFERENCE)), + 'autowire.decorated' => new ServiceClosureArgument(new Reference('.service_locator.Di.wrC8.inner', ContainerInterface::NULL_ON_INVALID_REFERENCE)), 'target' => new ServiceClosureArgument(new TypedReference('stdClass', 'stdClass', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, 'target', [new Target('someTarget')])), ]; $this->assertEquals($expected, $container->getDefinition((string) $locator->getFactory()[0])->getArgument(0)); From b511363099bce8ec9e0729e2daa406d197bf68f9 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 2 Jun 2025 16:08:14 +0200 Subject: [PATCH 03/19] Allow Symfony ^8.0 --- composer.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 460751088..7b1e731b7 100644 --- a/composer.json +++ b/composer.json @@ -20,12 +20,12 @@ "psr/container": "^1.1|^2.0", "symfony/deprecation-contracts": "^2.5|^3", "symfony/service-contracts": "^3.5", - "symfony/var-exporter": "^6.4.20|^7.2.5" + "symfony/var-exporter": "^6.4.20|^7.2.5|^8.0" }, "require-dev": { - "symfony/yaml": "^6.4|^7.0", - "symfony/config": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0" + "symfony/yaml": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0" }, "conflict": { "ext-psr": "<1.1|>=2", From 038a6e2039f998f0d364d9ea3ae14fd0f9200ca9 Mon Sep 17 00:00:00 2001 From: Ruud Kamphuis Date: Wed, 18 Jun 2025 12:27:01 +0200 Subject: [PATCH 04/19] [DependencyInjection] Allow extending `#[AsAlias]` attribute --- Attribute/AsAlias.php | 2 +- CHANGELOG.md | 5 ++++ Loader/FileLoader.php | 2 +- .../PrototypeAsAlias/WithCustomAsAlias.php | 25 +++++++++++++++++++ Tests/Loader/FileLoaderTest.php | 3 +++ 5 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 Tests/Fixtures/PrototypeAsAlias/WithCustomAsAlias.php diff --git a/Attribute/AsAlias.php b/Attribute/AsAlias.php index 0839afa48..c74b0923d 100644 --- a/Attribute/AsAlias.php +++ b/Attribute/AsAlias.php @@ -17,7 +17,7 @@ * @author Alan Poulain */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)] -final class AsAlias +class AsAlias { /** * @var list diff --git a/CHANGELOG.md b/CHANGELOG.md index df3486a9d..5c6c41cfd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.4 +--- + + * Allow `#[AsAlias]` to be extended + 7.3 --- diff --git a/Loader/FileLoader.php b/Loader/FileLoader.php index bc38767bc..3cf23cf98 100644 --- a/Loader/FileLoader.php +++ b/Loader/FileLoader.php @@ -216,7 +216,7 @@ public function registerClasses(Definition $prototype, string $namespace, string } $r = $this->container->getReflectionClass($class); $defaultAlias = 1 === \count($interfaces) ? $interfaces[0] : null; - foreach ($r->getAttributes(AsAlias::class) as $attr) { + foreach ($r->getAttributes(AsAlias::class, \ReflectionAttribute::IS_INSTANCEOF) as $attr) { /** @var AsAlias $attribute */ $attribute = $attr->newInstance(); $alias = $attribute->id ?? $defaultAlias; diff --git a/Tests/Fixtures/PrototypeAsAlias/WithCustomAsAlias.php b/Tests/Fixtures/PrototypeAsAlias/WithCustomAsAlias.php new file mode 100644 index 000000000..4f1419098 --- /dev/null +++ b/Tests/Fixtures/PrototypeAsAlias/WithCustomAsAlias.php @@ -0,0 +1,25 @@ + ['PrototypeAsAlias/{WithAsAlias,AliasFooInterface}.php', [AliasFooInterface::class => new Alias(WithAsAlias::class)]]; + yield 'PrivateCustomAlias' => ['PrototypeAsAlias/{WithCustomAsAlias,AliasFooInterface}.php', [AliasFooInterface::class => new Alias(WithCustomAsAlias::class)], 'prod']; + yield 'PrivateCustomAliasNoMatch' => ['PrototypeAsAlias/{WithCustomAsAlias,AliasFooInterface}.php', [], 'dev']; yield 'Interface' => ['PrototypeAsAlias/{WithAsAliasInterface,AliasFooInterface}.php', [AliasFooInterface::class => new Alias(WithAsAliasInterface::class)]]; yield 'Multiple' => ['PrototypeAsAlias/{WithAsAliasMultiple,AliasFooInterface}.php', [ AliasFooInterface::class => new Alias(WithAsAliasMultiple::class, true), From c1ad45335cef60a4a85f0edfb8f829b5b31fc756 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 26 Jun 2025 12:09:09 +0200 Subject: [PATCH 05/19] [DependencyInjection] Add argument `$target` to `ContainerBuilder::registerAliasForArgument()` --- CHANGELOG.md | 1 + Compiler/AutowirePass.php | 57 +++++++++++------------------ Compiler/ResolveBindingsPass.php | 3 +- ContainerBuilder.php | 9 +++-- Tests/Compiler/AutowirePassTest.php | 28 ++++++++++++++ Tests/ContainerBuilderTest.php | 4 ++ 6 files changed, 62 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c6c41cfd..9451c0f76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG --- * Allow `#[AsAlias]` to be extended + * Add argument `$target` to `ContainerBuilder::registerAliasForArgument()` 7.3 --- diff --git a/Compiler/AutowirePass.php b/Compiler/AutowirePass.php index e394cf105..19daa1e64 100644 --- a/Compiler/AutowirePass.php +++ b/Compiler/AutowirePass.php @@ -459,30 +459,14 @@ private function getAutowiredReference(TypedReference $reference, bool $filterTy $name = $target = (array_filter($reference->getAttributes(), static fn ($a) => $a instanceof Target)[0] ?? null)?->name; if (null !== $name ??= $reference->getName()) { - if ($this->container->has($alias = $type.' $'.$name) && !$this->container->findDefinition($alias)->isAbstract()) { + if (null !== ($alias = $this->getCombinedAlias($type, $name, $target)) && !$this->container->findDefinition($alias)->isAbstract()) { return new TypedReference($alias, $type, $reference->getInvalidBehavior()); } - if (null !== ($alias = $this->getCombinedAlias($type, $name)) && !$this->container->findDefinition($alias)->isAbstract()) { - return new TypedReference($alias, $type, $reference->getInvalidBehavior()); - } - - $parsedName = (new Target($name))->getParsedName(); - - if ($this->container->has($alias = $type.' $'.$parsedName) && !$this->container->findDefinition($alias)->isAbstract()) { - return new TypedReference($alias, $type, $reference->getInvalidBehavior()); - } - - if (null !== ($alias = $this->getCombinedAlias($type, $parsedName)) && !$this->container->findDefinition($alias)->isAbstract()) { - return new TypedReference($alias, $type, $reference->getInvalidBehavior()); - } - - if (($this->container->has($n = $name) && !$this->container->findDefinition($n)->isAbstract()) - || ($this->container->has($n = $parsedName) && !$this->container->findDefinition($n)->isAbstract()) - ) { + if ($this->container->has($name) && !$this->container->findDefinition($name)->isAbstract()) { foreach ($this->container->getAliases() as $id => $alias) { - if ($n === (string) $alias && str_starts_with($id, $type.' $')) { - return new TypedReference($n, $type, $reference->getInvalidBehavior()); + if ($name === (string) $alias && str_starts_with($id, $type.' $')) { + return new TypedReference($name, $type, $reference->getInvalidBehavior()); } } } @@ -492,10 +476,6 @@ private function getAutowiredReference(TypedReference $reference, bool $filterTy } } - if ($this->container->has($type) && !$this->container->findDefinition($type)->isAbstract()) { - return new TypedReference($type, $type, $reference->getInvalidBehavior()); - } - if (null !== ($alias = $this->getCombinedAlias($type)) && !$this->container->findDefinition($alias)->isAbstract()) { return new TypedReference($alias, $type, $reference->getInvalidBehavior()); } @@ -710,38 +690,43 @@ private function populateAutowiringAlias(string $id, ?string $target = null): vo $name = $m[3] ?? ''; if (class_exists($type, false) || interface_exists($type, false)) { - if (null !== $target && str_starts_with($target, '.'.$type.' $') - && (new Target($target = substr($target, \strlen($type) + 3)))->getParsedName() === $name - ) { - $name = $target; + if (null !== $target && str_starts_with($target, '.'.$type.' $')) { + $name = substr($target, \strlen($type) + 3); } $this->autowiringAliases[$type][$name] = $name; } } - private function getCombinedAlias(string $type, ?string $name = null): ?string + private function getCombinedAlias(string $type, ?string $name = null, ?string $target = null): ?string { + $prefix = $target && $name ? '.' : ''; + $suffix = $name ? ' $'.($target ?? $name) : ''; + $parsedName = $target ?? ($name ? (new Target($name))->getParsedName() : null); + + if ($this->container->has($alias = $prefix.$type.$suffix) && !$this->container->findDefinition($alias)->isAbstract()) { + return $alias; + } + if (str_contains($type, '&')) { $types = explode('&', $type); } elseif (str_contains($type, '|')) { $types = explode('|', $type); } else { - return null; + return $prefix || $name !== $parsedName && ($name = $parsedName) ? $this->getCombinedAlias($type, $name) : null; } $alias = null; - $suffix = $name ? ' $'.$name : ''; foreach ($types as $type) { - if (!$this->container->hasAlias($type.$suffix)) { - return null; + if (!$this->container->hasAlias($prefix.$type.$suffix)) { + return $prefix || $name !== $parsedName && ($name = $parsedName) ? $this->getCombinedAlias($type, $name) : null; } if (null === $alias) { - $alias = (string) $this->container->getAlias($type.$suffix); - } elseif ((string) $this->container->getAlias($type.$suffix) !== $alias) { - return null; + $alias = (string) $this->container->getAlias($prefix.$type.$suffix); + } elseif ((string) $this->container->getAlias($prefix.$type.$suffix) !== $alias) { + return $prefix || $name !== $parsedName && ($name = $parsedName) ? $this->getCombinedAlias($type, $name) : null; } } diff --git a/Compiler/ResolveBindingsPass.php b/Compiler/ResolveBindingsPass.php index b2c6f6ef7..30ecda0de 100644 --- a/Compiler/ResolveBindingsPass.php +++ b/Compiler/ResolveBindingsPass.php @@ -189,7 +189,8 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed if ( $value->isAutowired() && !$value->hasTag('container.ignore_attributes') - && $parameter->getAttributes(Autowire::class, \ReflectionAttribute::IS_INSTANCEOF) + && ($parameter->getAttributes(Autowire::class, \ReflectionAttribute::IS_INSTANCEOF) + || $parameter->getAttributes(Target::class, \ReflectionAttribute::IS_INSTANCEOF)) ) { continue; } diff --git a/ContainerBuilder.php b/ContainerBuilder.php index 38208124d..e2e90b538 100644 --- a/ContainerBuilder.php +++ b/ContainerBuilder.php @@ -1459,10 +1459,13 @@ public function registerAttributeForAutoconfiguration(string $attributeClass, ca * using camel case: "foo.bar" or "foo_bar" creates an alias bound to * "$fooBar"-named arguments with $type as type-hint. Such arguments will * receive the service $id when autowiring is used. + * + * @param ?string $target */ - public function registerAliasForArgument(string $id, string $type, ?string $name = null): Alias + public function registerAliasForArgument(string $id, string $type, ?string $name = null/*, ?string $target = null */): Alias { $parsedName = (new Target($name ??= $id))->getParsedName(); + $target = (\func_num_args() > 3 ? func_get_arg(3) : null) ?? $name; if (!preg_match('/^[a-zA-Z_\x7f-\xff]/', $parsedName)) { if ($id !== $name) { @@ -1472,8 +1475,8 @@ public function registerAliasForArgument(string $id, string $type, ?string $name throw new InvalidArgumentException(\sprintf('Invalid argument name "%s"'.$id.': the first character must be a letter.', $name)); } - if ($parsedName !== $name) { - $this->setAlias('.'.$type.' $'.$name, $type.' $'.$parsedName); + if ($parsedName !== $target) { + $this->setAlias('.'.$type.' $'.$target, $type.' $'.$parsedName); } return $this->setAlias($type.' $'.$parsedName, $id); diff --git a/Tests/Compiler/AutowirePassTest.php b/Tests/Compiler/AutowirePassTest.php index 114d514ad..a73bdb01c 100644 --- a/Tests/Compiler/AutowirePassTest.php +++ b/Tests/Compiler/AutowirePassTest.php @@ -1121,6 +1121,20 @@ public function testArgumentWithTarget() { $container = new ContainerBuilder(); + $container->register(BarInterface::class, BarInterface::class); + $container->register('.'.BarInterface::class.' $image.storage', BarInterface::class); + $container->register('with_target', WithTarget::class) + ->setAutowired(true); + + (new AutowirePass())->process($container); + + $this->assertSame('.'.BarInterface::class.' $image.storage', (string) $container->getDefinition('with_target')->getArgument(0)); + } + + public function testArgumentWithParsedTarget() + { + $container = new ContainerBuilder(); + $container->register(BarInterface::class, BarInterface::class); $container->register(BarInterface::class.' $imageStorage', BarInterface::class); $container->register('with_target', WithTarget::class) @@ -1161,6 +1175,20 @@ public function testArgumentWithTypoTargetAnonymous() (new AutowirePass())->process($container); } + public function testArgumentWithIdTarget() + { + $container = new ContainerBuilder(); + + $container->register('image.storage', BarInterface::class); + $container->registerAliasForArgument('image.storage', BarInterface::class, 'image'); + $container->register('with_target', WithTarget::class) + ->setAutowired(true); + + (new AutowirePass())->process($container); + + $this->assertSame('image.storage', (string) $container->getDefinition('with_target')->getArgument(0)); + } + public function testDecorationWithServiceAndAliasedInterface() { $container = new ContainerBuilder(); diff --git a/Tests/ContainerBuilderTest.php b/Tests/ContainerBuilderTest.php index 5e08e47ab..93fa8a329 100644 --- a/Tests/ContainerBuilderTest.php +++ b/Tests/ContainerBuilderTest.php @@ -1779,6 +1779,10 @@ public function testRegisterAliasForArgument() $container->registerAliasForArgument('Foo.bar_baz', 'Some\FooInterface', 'Bar_baz.foo'); $this->assertEquals(new Alias('Foo.bar_baz'), $container->getAlias('Some\FooInterface $barBazFoo')); $this->assertEquals(new Alias('Some\FooInterface $barBazFoo'), $container->getAlias('.Some\FooInterface $Bar_baz.foo')); + + $container->registerAliasForArgument('Foo.bar_baz', 'Some\FooInterface', 'Bar_baz.foo', 'foo'); + $this->assertEquals(new Alias('Foo.bar_baz'), $container->getAlias('Some\FooInterface $barBazFoo')); + $this->assertEquals(new Alias('Some\FooInterface $barBazFoo'), $container->getAlias('.Some\FooInterface $foo')); } public function testCaseSensitivity() From d648e5ddf97c37ee5587a7e0537519d45291f8b7 Mon Sep 17 00:00:00 2001 From: Santiago San Martin Date: Sat, 28 Jun 2025 18:24:36 -0300 Subject: [PATCH 06/19] [FrameworkBundle] fix `lint:container` command --- Alias.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Alias.php b/Alias.php index 73d05b46e..f07ce25c2 100644 --- a/Alias.php +++ b/Alias.php @@ -17,12 +17,14 @@ class Alias { private const DEFAULT_DEPRECATION_TEMPLATE = 'The "%alias_id%" service alias is deprecated. You should stop using it, as it will be removed in the future.'; + private bool $public = false; private array $deprecation = []; public function __construct( private string $id, - private bool $public = false, + bool $public = false, ) { + $this->public = $public; } /** From 24a42b337b94f14512ea1e59955c433518d1ca2c Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 8 Jul 2025 11:08:29 +0200 Subject: [PATCH 07/19] Various CS fixes --- Argument/LazyClosure.php | 1 - Compiler/AttributeAutoconfigurationPass.php | 8 ++++---- Compiler/ResolveInstanceofConditionalsPass.php | 2 +- ContainerBuilder.php | 6 +++--- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Argument/LazyClosure.php b/Argument/LazyClosure.php index 3e8718643..3dcc34e4f 100644 --- a/Argument/LazyClosure.php +++ b/Argument/LazyClosure.php @@ -12,7 +12,6 @@ namespace Symfony\Component\DependencyInjection\Argument; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Reference; diff --git a/Compiler/AttributeAutoconfigurationPass.php b/Compiler/AttributeAutoconfigurationPass.php index 9c0eec543..bbf341913 100644 --- a/Compiler/AttributeAutoconfigurationPass.php +++ b/Compiler/AttributeAutoconfigurationPass.php @@ -67,14 +67,14 @@ public function process(ContainerBuilder $container): void foreach (['class', 'method', 'property', 'parameter'] as $symbol) { if (['Reflector'] !== $types) { - if (!\in_array('Reflection' . ucfirst($symbol), $types, true)) { + if (!\in_array('Reflection'.ucfirst($symbol), $types, true)) { continue; } - if (!($targets & \constant('Attribute::TARGET_' . strtoupper($symbol)))) { - throw new LogicException(\sprintf('Invalid type "Reflection%s" on argument "$%s": attribute "%s" cannot target a ' . $symbol . ' in "%s" on line "%d".', ucfirst($symbol), $reflectorParameter->getName(), $attributeName, $callableReflector->getFileName(), $callableReflector->getStartLine())); + if (!($targets & \constant('Attribute::TARGET_'.strtoupper($symbol)))) { + throw new LogicException(\sprintf('Invalid type "Reflection%s" on argument "$%s": attribute "%s" cannot target a '.$symbol.' in "%s" on line "%d".', ucfirst($symbol), $reflectorParameter->getName(), $attributeName, $callableReflector->getFileName(), $callableReflector->getStartLine())); } } - $this->{$symbol . 'AttributeConfigurators'}[$attributeName][] = $callable; + $this->{$symbol.'AttributeConfigurators'}[$attributeName][] = $callable; } } } diff --git a/Compiler/ResolveInstanceofConditionalsPass.php b/Compiler/ResolveInstanceofConditionalsPass.php index 52dc56c0f..1c1b0dcf9 100644 --- a/Compiler/ResolveInstanceofConditionalsPass.php +++ b/Compiler/ResolveInstanceofConditionalsPass.php @@ -113,7 +113,7 @@ private function processDefinition(ContainerBuilder $container, string $id, Defi $definition = substr_replace($definition, 'Child', 44, 0); } $definition = unserialize($definition); - /** @var ChildDefinition $definition */ + /* @var ChildDefinition $definition */ $definition->setParent($parent); if (null !== $shared && !isset($definition->getChanges()['shared'])) { diff --git a/ContainerBuilder.php b/ContainerBuilder.php index e2e90b538..9ac34147e 100644 --- a/ContainerBuilder.php +++ b/ContainerBuilder.php @@ -1462,7 +1462,7 @@ public function registerAttributeForAutoconfiguration(string $attributeClass, ca * * @param ?string $target */ - public function registerAliasForArgument(string $id, string $type, ?string $name = null/*, ?string $target = null */): Alias + public function registerAliasForArgument(string $id, string $type, ?string $name = null/* , ?string $target = null */): Alias { $parsedName = (new Target($name ??= $id))->getParsedName(); $target = (\func_num_args() > 3 ? func_get_arg(3) : null) ?? $name; @@ -1503,8 +1503,8 @@ public function getAutoconfiguredAttributes(): array $autoconfiguredAttributes = []; foreach ($this->autoconfiguredAttributes as $attribute => $configurators) { - if (count($configurators) > 1) { - throw new LogicException(\sprintf('The "%s" attribute has %d configurators. Use "getAttributeAutoconfigurators()" to get all of them.', $attribute, count($configurators))); + if (\count($configurators) > 1) { + throw new LogicException(\sprintf('The "%s" attribute has %d configurators. Use "getAttributeAutoconfigurators()" to get all of them.', $attribute, \count($configurators))); } $autoconfiguredAttributes[$attribute] = $configurators[0]; From 10c50631970abe2f5d4180121462370992773a3c Mon Sep 17 00:00:00 2001 From: Gregor Harlan Date: Sat, 12 Jul 2025 15:55:19 +0200 Subject: [PATCH 08/19] optimize `in_array` calls --- Dumper/PhpDumper.php | 2 +- Dumper/YamlDumper.php | 2 +- Loader/XmlFileLoader.php | 2 +- Loader/YamlFileLoader.php | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Dumper/PhpDumper.php b/Dumper/PhpDumper.php index 9568ad26b..d8ddb7a20 100644 --- a/Dumper/PhpDumper.php +++ b/Dumper/PhpDumper.php @@ -2393,7 +2393,7 @@ private static function stripComments(string $source): string // replace multiple new lines with a single newline $rawChunk .= preg_replace(['/\n{2,}/S'], "\n", $token[1]); - } elseif (\in_array($token[0], [\T_COMMENT, \T_DOC_COMMENT])) { + } elseif (\in_array($token[0], [\T_COMMENT, \T_DOC_COMMENT], true)) { if (!\in_array($rawChunk[\strlen($rawChunk) - 1], [' ', "\n", "\r", "\t"], true)) { $rawChunk .= ' '; } diff --git a/Dumper/YamlDumper.php b/Dumper/YamlDumper.php index d79e7b904..f5501260a 100644 --- a/Dumper/YamlDumper.php +++ b/Dumper/YamlDumper.php @@ -146,7 +146,7 @@ private function addService(string $id, Definition $definition): string } $decorationOnInvalid = $decoratedService[3] ?? ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; - if (\in_array($decorationOnInvalid, [ContainerInterface::IGNORE_ON_INVALID_REFERENCE, ContainerInterface::NULL_ON_INVALID_REFERENCE])) { + if (\in_array($decorationOnInvalid, [ContainerInterface::IGNORE_ON_INVALID_REFERENCE, ContainerInterface::NULL_ON_INVALID_REFERENCE], true)) { $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE === $decorationOnInvalid ? 'null' : 'ignore'; $code .= \sprintf(" decoration_on_invalid: %s\n", $invalidBehavior); } diff --git a/Loader/XmlFileLoader.php b/Loader/XmlFileLoader.php index f59698066..1115fe643 100644 --- a/Loader/XmlFileLoader.php +++ b/Loader/XmlFileLoader.php @@ -823,7 +823,7 @@ private function shouldEnableEntityLoader(): bool private function validateAlias(\DOMElement $alias, string $file): void { foreach ($alias->attributes as $name => $node) { - if (!\in_array($name, ['alias', 'id', 'public'])) { + if (!\in_array($name, ['alias', 'id', 'public'], true)) { throw new InvalidArgumentException(\sprintf('Invalid attribute "%s" defined for alias "%s" in "%s".', $name, $alias->getAttribute('id'), $file)); } } diff --git a/Loader/YamlFileLoader.php b/Loader/YamlFileLoader.php index c3b1bf255..8d2b677fa 100644 --- a/Loader/YamlFileLoader.php +++ b/Loader/YamlFileLoader.php @@ -433,7 +433,7 @@ private function parseDefinition(string $id, array|string|null $service, string } foreach ($service as $key => $value) { - if (!\in_array($key, ['alias', 'public', 'deprecated'])) { + if (!\in_array($key, ['alias', 'public', 'deprecated'], true)) { throw new InvalidArgumentException(\sprintf('The configuration key "%s" is unsupported for the service "%s" which is defined as an alias in "%s". Allowed configuration keys for service aliases are "alias", "public" and "deprecated".', $key, $id, $file)); } @@ -805,7 +805,7 @@ private function validate(mixed $content, string $file): ?array } foreach ($content as $namespace => $data) { - if (\in_array($namespace, ['imports', 'parameters', 'services']) || str_starts_with($namespace, 'when@')) { + if (\in_array($namespace, ['imports', 'parameters', 'services'], true) || str_starts_with($namespace, 'when@')) { continue; } @@ -953,7 +953,7 @@ private function resolveServices(mixed $value, string $file, bool $isParameter = private function loadFromExtensions(array $content): void { foreach ($content as $namespace => $values) { - if (\in_array($namespace, ['imports', 'parameters', 'services']) || str_starts_with($namespace, 'when@')) { + if (\in_array($namespace, ['imports', 'parameters', 'services'], true) || str_starts_with($namespace, 'when@')) { continue; } From e58da973e98fd3460813a02203d9987fa664863c Mon Sep 17 00:00:00 2001 From: Gary PEGEOT Date: Wed, 23 Jul 2025 11:32:32 +0200 Subject: [PATCH 09/19] [DependencyInjection] Update `ResolveClassPass` to check class existence --- Compiler/ResolveClassPass.php | 9 ++- Tests/Compiler/ResolveClassPassTest.php | 19 ++++-- Tests/ContainerBuilderTest.php | 2 - Tests/Dumper/PhpDumperTest.php | 3 +- Tests/Fixtures/containers/container33.php | 4 +- .../containers/container_inline_requires.php | 2 +- .../includes/fixture_app_services.php | 63 +++++++++++++++++++ Tests/Fixtures/includes/foo.php | 10 +++ Tests/Loader/PhpFileLoaderTest.php | 1 + 9 files changed, 100 insertions(+), 13 deletions(-) create mode 100644 Tests/Fixtures/includes/fixture_app_services.php diff --git a/Compiler/ResolveClassPass.php b/Compiler/ResolveClassPass.php index 4b7a2bb40..138cca0df 100644 --- a/Compiler/ResolveClassPass.php +++ b/Compiler/ResolveClassPass.php @@ -27,9 +27,14 @@ public function process(ContainerBuilder $container): void continue; } if (preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)++$/', $id)) { - if ($definition instanceof ChildDefinition && !class_exists($id)) { - throw new InvalidArgumentException(\sprintf('Service definition "%s" has a parent but no class, and its name looks like an FQCN. Either the class is missing or you want to inherit it from the parent service. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class.', $id)); + if (!class_exists($id) && !interface_exists($id)) { + $error = $definition instanceof ChildDefinition ? + 'has a parent but no class, and its name looks like a FQCN. Either the class is missing or you want to inherit it from the parent service' : + 'name looks like a FQCN but the class does not exist'; + + throw new InvalidArgumentException("Service definition \"{$id}\" {$error}. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class."); } + $definition->setClass($id); } } diff --git a/Tests/Compiler/ResolveClassPassTest.php b/Tests/Compiler/ResolveClassPassTest.php index 914115f28..aed2dc321 100644 --- a/Tests/Compiler/ResolveClassPassTest.php +++ b/Tests/Compiler/ResolveClassPassTest.php @@ -35,7 +35,6 @@ public function testResolveClassFromId($serviceId) public static function provideValidClassId() { - yield ['Acme\UnknownClass']; yield [CaseSensitiveClass::class]; } @@ -62,7 +61,7 @@ public static function provideInvalidClassId() public function testNonFqcnChildDefinition() { $container = new ContainerBuilder(); - $parent = $container->register('App\Foo', null); + $parent = $container->register('App\Foo.parent', 'App\Foo'); $child = $container->setDefinition('App\Foo.child', new ChildDefinition('App\Foo')); (new ResolveClassPass())->process($container); @@ -74,7 +73,7 @@ public function testNonFqcnChildDefinition() public function testClassFoundChildDefinition() { $container = new ContainerBuilder(); - $parent = $container->register('App\Foo', null); + $parent = $container->register('foo.parent', 'App\Foo'); $child = $container->setDefinition(self::class, new ChildDefinition('App\Foo')); (new ResolveClassPass())->process($container); @@ -86,11 +85,21 @@ public function testClassFoundChildDefinition() public function testAmbiguousChildDefinition() { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Service definition "App\Foo\Child" has a parent but no class, and its name looks like an FQCN. Either the class is missing or you want to inherit it from the parent service. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class.'); + $this->expectExceptionMessage('Service definition "App\Foo\Child" has a parent but no class, and its name looks like a FQCN. Either the class is missing or you want to inherit it from the parent service. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class.'); $container = new ContainerBuilder(); - $container->register('App\Foo', null); + $container->register('app.foo', 'App\Foo'); $container->setDefinition('App\Foo\Child', new ChildDefinition('App\Foo')); (new ResolveClassPass())->process($container); } + + public function testInvalidClassNameDefinition() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Service definition "Acme\UnknownClass" name looks like a FQCN but the class does not exist. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class.'); + $container = new ContainerBuilder(); + $container->register('Acme\UnknownClass'); + + (new ResolveClassPass())->process($container); + } } diff --git a/Tests/ContainerBuilderTest.php b/Tests/ContainerBuilderTest.php index f87c8ced4..aa932d083 100644 --- a/Tests/ContainerBuilderTest.php +++ b/Tests/ContainerBuilderTest.php @@ -1546,11 +1546,9 @@ public function testClassFromId() { $container = new ContainerBuilder(); - $unknown = $container->register('Acme\UnknownClass'); $autoloadClass = $container->register(CaseSensitiveClass::class); $container->compile(); - $this->assertSame('Acme\UnknownClass', $unknown->getClass()); $this->assertEquals(CaseSensitiveClass::class, $autoloadClass->getClass()); } diff --git a/Tests/Dumper/PhpDumperTest.php b/Tests/Dumper/PhpDumperTest.php index a117a69a0..dbba8d282 100644 --- a/Tests/Dumper/PhpDumperTest.php +++ b/Tests/Dumper/PhpDumperTest.php @@ -75,6 +75,7 @@ require_once __DIR__.'/../Fixtures/includes/classes.php'; require_once __DIR__.'/../Fixtures/includes/foo.php'; require_once __DIR__.'/../Fixtures/includes/foo_lazy.php'; +require_once __DIR__.'/../Fixtures/includes/fixture_app_services.php'; class PhpDumperTest extends TestCase { @@ -1317,7 +1318,7 @@ public function testInlineSelfRef() ->setProperty('bar', $bar) ->addArgument($bar); - $container->register('App\Foo') + $container->register('App\Foo', 'App\Foo') ->setPublic(true) ->addArgument($baz); diff --git a/Tests/Fixtures/containers/container33.php b/Tests/Fixtures/containers/container33.php index 673abe204..a5f94494e 100644 --- a/Tests/Fixtures/containers/container33.php +++ b/Tests/Fixtures/containers/container33.php @@ -6,7 +6,7 @@ $container = new ContainerBuilder(); -$container->register(\Foo\Foo::class)->setPublic(true); -$container->register(\Bar\Foo::class)->setPublic(true); +$container->register('Foo\Foo', \Foo\Foo::class)->setPublic(true); +$container->register('Bar\Foo', \Bar\Foo::class)->setPublic(true); return $container; diff --git a/Tests/Fixtures/containers/container_inline_requires.php b/Tests/Fixtures/containers/container_inline_requires.php index d94a67057..0517daef8 100644 --- a/Tests/Fixtures/containers/container_inline_requires.php +++ b/Tests/Fixtures/containers/container_inline_requires.php @@ -12,6 +12,6 @@ $container->register(HotPath\C1::class)->addTag('container.hot_path')->setPublic(true); $container->register(HotPath\C2::class)->addArgument(new Reference(HotPath\C3::class))->setPublic(true); $container->register(HotPath\C3::class); -$container->register(ParentNotExists::class)->setPublic(true); +$container->register(ParentNotExists::class, ParentNotExists::class)->setPublic(true); return $container; diff --git a/Tests/Fixtures/includes/fixture_app_services.php b/Tests/Fixtures/includes/fixture_app_services.php new file mode 100644 index 000000000..7e0bf5433 --- /dev/null +++ b/Tests/Fixtures/includes/fixture_app_services.php @@ -0,0 +1,63 @@ +otherInstances = $otherInstances; } } + +namespace Acme; + +class Foo +{ +} + +class WithShortCutArgs +{ +} diff --git a/Tests/Loader/PhpFileLoaderTest.php b/Tests/Loader/PhpFileLoaderTest.php index 72ededfd0..9ca0104ea 100644 --- a/Tests/Loader/PhpFileLoaderTest.php +++ b/Tests/Loader/PhpFileLoaderTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Loader; require_once __DIR__.'/../Fixtures/includes/AcmeExtension.php'; +require_once __DIR__.'/../Fixtures/includes/fixture_app_services.php'; use PHPUnit\Framework\TestCase; use Symfony\Component\Config\Builder\ConfigBuilderGenerator; From 0e209ce1ecb1a0fab7e8d93d06a65ce6371fc127 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 30 Jul 2025 12:06:23 +0200 Subject: [PATCH 10/19] [DependencyInjection] Deprecate registering a service without a class when its id is a non-existing FQCN --- CHANGELOG.md | 1 + Compiler/ResolveClassPass.php | 23 +++++++++++++---------- Loader/Configurator/Traits/BindTrait.php | 2 +- Tests/Compiler/ResolveClassPassTest.php | 11 +++++++++-- 4 files changed, 24 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9451c0f76..6fa6bf4f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ CHANGELOG * Allow `#[AsAlias]` to be extended * Add argument `$target` to `ContainerBuilder::registerAliasForArgument()` + * Deprecate registering a service without a class when its id is a non-existing FQCN 7.3 --- diff --git a/Compiler/ResolveClassPass.php b/Compiler/ResolveClassPass.php index 138cca0df..952b02441 100644 --- a/Compiler/ResolveClassPass.php +++ b/Compiler/ResolveClassPass.php @@ -23,20 +23,23 @@ class ResolveClassPass implements CompilerPassInterface public function process(ContainerBuilder $container): void { foreach ($container->getDefinitions() as $id => $definition) { - if ($definition->isSynthetic() || null !== $definition->getClass()) { + if ($definition->isSynthetic() + || null !== $definition->getClass() + || !preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)++$/', $id) + ) { continue; } - if (preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)++$/', $id)) { - if (!class_exists($id) && !interface_exists($id)) { - $error = $definition instanceof ChildDefinition ? - 'has a parent but no class, and its name looks like a FQCN. Either the class is missing or you want to inherit it from the parent service' : - 'name looks like a FQCN but the class does not exist'; - - throw new InvalidArgumentException("Service definition \"{$id}\" {$error}. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class."); - } - + if (class_exists($id) || interface_exists($id, false)) { $definition->setClass($id); + continue; } + if ($definition instanceof ChildDefinition) { + throw new InvalidArgumentException(\sprintf('Service definition "%s" has a parent but no class, and its name looks like a FQCN. Either the class is missing or you want to inherit it from the parent service. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class.', $id)); + } + + trigger_deprecation('symfony/dependency-injection', '7.4', 'Service id "%s" looks like a FQCN but no corresponding class or interface exists. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class or interface.', $id); + // throw new InvalidArgumentException(\sprintf('Service id "%s" looks like a FQCN but no corresponding class or interface exists. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class or interface.'), $id); + $definition->setClass($id); } } } diff --git a/Loader/Configurator/Traits/BindTrait.php b/Loader/Configurator/Traits/BindTrait.php index 6bf6b6f43..a0501d86d 100644 --- a/Loader/Configurator/Traits/BindTrait.php +++ b/Loader/Configurator/Traits/BindTrait.php @@ -24,7 +24,7 @@ trait BindTrait * injected in the matching parameters (of the constructor, of methods * called and of controller actions). * - * @param string $nameOrFqcn A parameter name with its "$" prefix, or an FQCN + * @param string $nameOrFqcn A parameter name with its "$" prefix, or a FQCN * @param mixed $valueOrRef The value or reference to bind * * @return $this diff --git a/Tests/Compiler/ResolveClassPassTest.php b/Tests/Compiler/ResolveClassPassTest.php index aed2dc321..4094af811 100644 --- a/Tests/Compiler/ResolveClassPassTest.php +++ b/Tests/Compiler/ResolveClassPassTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler; use PHPUnit\Framework\TestCase; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -20,6 +21,8 @@ class ResolveClassPassTest extends TestCase { + use ExpectDeprecationTrait; + /** * @dataProvider provideValidClassId */ @@ -93,10 +96,14 @@ public function testAmbiguousChildDefinition() (new ResolveClassPass())->process($container); } + /** + * @group legacy + */ public function testInvalidClassNameDefinition() { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Service definition "Acme\UnknownClass" name looks like a FQCN but the class does not exist. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class.'); + // $this->expectException(InvalidArgumentException::class); + // $this->expectExceptionMessage('Service id "Acme\UnknownClass" looks like a FQCN but no corresponding class or interface exists. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class or interface.'); + $this->expectDeprecation('Since symfony/dependency-injection 7.4: Service id "Acme\UnknownClass" looks like a FQCN but no corresponding class or interface exists. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class or interface.'); $container = new ContainerBuilder(); $container->register('Acme\UnknownClass'); From 60797b1d2011d7b09c2fa4a3ad60565400933175 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 30 Jul 2025 18:59:27 +0200 Subject: [PATCH 11/19] [DependencyInjection] Dump XML using plain PHP, no DOM needed --- Dumper/XmlDumper.php | 431 ++++++++++++++++++++++--------------------- 1 file changed, 220 insertions(+), 211 deletions(-) diff --git a/Dumper/XmlDumper.php b/Dumper/XmlDumper.php index 7c38455b5..2036b3231 100644 --- a/Dumper/XmlDumper.php +++ b/Dumper/XmlDumper.php @@ -13,6 +13,7 @@ use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Argument\AbstractArgument; +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; @@ -32,34 +33,31 @@ */ class XmlDumper extends Dumper { - private \DOMDocument $document; - /** * Dumps the service container as an XML string. */ public function dump(array $options = []): string { - $this->document = new \DOMDocument('1.0', 'utf-8'); - $this->document->formatOutput = true; - - $container = $this->document->createElementNS('http://symfony.com/schema/dic/services', 'container'); - $container->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); - $container->setAttribute('xsi:schemaLocation', 'http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd'); + $xml = << + + EOXML; - $this->addParameters($container); - $this->addServices($container); + foreach ($this->addParameters() as $line) { + $xml .= "\n ".$line; + } + foreach ($this->addServices() as $line) { + $xml .= "\n ".$line; + } - $this->document->appendChild($container); - $xml = $this->document->saveXML(); - unset($this->document); + $xml .= "\n\n"; return $this->container->resolveEnvPlaceholders($xml); } - private function addParameters(\DOMElement $parent): void + private function addParameters(): iterable { - $data = $this->container->getParameterBag()->all(); - if (!$data) { + if (!$data = $this->container->getParameterBag()->all()) { return; } @@ -67,201 +65,205 @@ private function addParameters(\DOMElement $parent): void $data = $this->escape($data); } - $parameters = $this->document->createElement('parameters'); - $parent->appendChild($parameters); - $this->convertParameters($data, 'parameter', $parameters); + yield ''; + foreach ($this->convertParameters($data, 'parameter') as $line) { + yield ' '.$line; + } + yield ''; } - private function addMethodCalls(array $methodcalls, \DOMElement $parent): void + private function addMethodCalls(array $methodcalls): iterable { foreach ($methodcalls as $methodcall) { - $call = $this->document->createElement('call'); - $call->setAttribute('method', $methodcall[0]); - if (\count($methodcall[1])) { - $this->convertParameters($methodcall[1], 'argument', $call); - } - if ($methodcall[2] ?? false) { - $call->setAttribute('returns-clone', 'true'); + $xmlAttr = \sprintf(' method="%s"%s', $this->encode($methodcall[0]), ($methodcall[2] ?? false) ? ' returns-clone="true"' : ''); + + if ($methodcall[1]) { + yield \sprintf('', $xmlAttr); + foreach ($this->convertParameters($methodcall[1], 'argument') as $line) { + yield ' '.$line; + } + yield ''; + } else { + yield \sprintf('', $xmlAttr); } - $parent->appendChild($call); } } - private function addService(Definition $definition, ?string $id, \DOMElement $parent): void + private function addService(Definition $definition, ?string $id): iterable { - $service = $this->document->createElement('service'); + $xmlAttr = ''; if (null !== $id) { - $service->setAttribute('id', $id); + $xmlAttr .= \sprintf(' id="%s"', $this->encode($id)); } if ($class = $definition->getClass()) { if (str_starts_with($class, '\\')) { $class = substr($class, 1); } - $service->setAttribute('class', $class); + $xmlAttr .= \sprintf(' class="%s"', $this->encode($class)); } if (!$definition->isShared()) { - $service->setAttribute('shared', 'false'); + $xmlAttr .= ' shared="false"'; } if ($definition->isPublic()) { - $service->setAttribute('public', 'true'); + $xmlAttr .= ' public="true"'; } if ($definition->isSynthetic()) { - $service->setAttribute('synthetic', 'true'); + $xmlAttr .= ' synthetic="true"'; } if ($definition->isLazy()) { - $service->setAttribute('lazy', 'true'); + $xmlAttr .= ' lazy="true"'; } if (null !== $decoratedService = $definition->getDecoratedService()) { [$decorated, $renamedId, $priority] = $decoratedService; - $service->setAttribute('decorates', $decorated); + $xmlAttr .= \sprintf(' decorates="%s"', $this->encode($decorated)); $decorationOnInvalid = $decoratedService[3] ?? ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; if (\in_array($decorationOnInvalid, [ContainerInterface::IGNORE_ON_INVALID_REFERENCE, ContainerInterface::NULL_ON_INVALID_REFERENCE], true)) { $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE === $decorationOnInvalid ? 'null' : 'ignore'; - $service->setAttribute('decoration-on-invalid', $invalidBehavior); + $xmlAttr .= \sprintf(' decoration-on-invalid="%s"', $invalidBehavior); } if (null !== $renamedId) { - $service->setAttribute('decoration-inner-name', $renamedId); + $xmlAttr .= \sprintf(' decoration-inner-name="%s"', $this->encode($renamedId)); } if (0 !== $priority) { - $service->setAttribute('decoration-priority', $priority); + $xmlAttr .= \sprintf(' decoration-priority="%d"', $priority); } } + $xml = []; + $tags = $definition->getTags(); $tags['container.error'] = array_map(fn ($e) => ['message' => $e], $definition->getErrors()); foreach ($tags as $name => $tags) { foreach ($tags as $attributes) { - $tag = $this->document->createElement('tag'); - // Check if we have recursive attributes if (array_filter($attributes, \is_array(...))) { - $tag->setAttribute('name', $name); - $this->addTagRecursiveAttributes($tag, $attributes); - } else { - if (!\array_key_exists('name', $attributes)) { - $tag->setAttribute('name', $name); - } else { - $tag->appendChild($this->document->createTextNode($name)); + $xml[] = \sprintf(' ', $this->encode($name)); + foreach ($this->addTagRecursiveAttributes($attributes) as $line) { + $xml[] = ' '.$line; } + $xml[] = ' '; + } else { + $hasNameAttr = \array_key_exists('name', $attributes); + $attr = \sprintf(' name="%s"', $this->encode($hasNameAttr ? $attributes['name'] : $name)); foreach ($attributes as $key => $value) { - $tag->setAttribute($key, $value ?? ''); + if ('name' !== $key) { + $attr .= \sprintf(' %s="%s"', $this->encode($key), $this->encode(self::phpToXml($value ?? ''))); + } + } + if ($hasNameAttr) { + $xml[] = \sprintf(' %s', $attr, $this->encode($name, 0)); + } else { + $xml[] = \sprintf(' ', $attr); } } - $service->appendChild($tag); } } if ($definition->getFile()) { - $file = $this->document->createElement('file'); - $file->appendChild($this->document->createTextNode($definition->getFile())); - $service->appendChild($file); + $xml[] = \sprintf(' %s', $this->encode($definition->getFile(), 0)); } - if ($parameters = $definition->getArguments()) { - $this->convertParameters($parameters, 'argument', $service); + foreach ($this->convertParameters($definition->getArguments(), 'argument') as $line) { + $xml[] = ' '.$line; } - if ($parameters = $definition->getProperties()) { - $this->convertParameters($parameters, 'property', $service, 'name'); + foreach ($this->convertParameters($definition->getProperties(), 'property', 'name') as $line) { + $xml[] = ' '.$line; } - $this->addMethodCalls($definition->getMethodCalls(), $service); + foreach ($this->addMethodCalls($definition->getMethodCalls()) as $line) { + $xml[] = ' '.$line; + } if ($callable = $definition->getFactory()) { if (\is_array($callable) && ['Closure', 'fromCallable'] !== $callable && $definition->getClass() === $callable[0]) { - $service->setAttribute('constructor', $callable[1]); + $xmlAttr .= \sprintf(' constructor="%s"', $this->encode($callable[1])); } else { - $factory = $this->document->createElement('factory'); - if (\is_array($callable) && $callable[0] instanceof Definition) { - $this->addService($callable[0], null, $factory); - $factory->setAttribute('method', $callable[1]); + $xml[] = \sprintf(' ', $this->encode($callable[1])); + foreach ($this->addService($callable[0], null) as $line) { + $xml[] = ' '.$line; + } + $xml[] = ' '; } elseif (\is_array($callable)) { if (null !== $callable[0]) { - $factory->setAttribute($callable[0] instanceof Reference ? 'service' : 'class', $callable[0]); + $xml[] = \sprintf(' ', $callable[0] instanceof Reference ? 'service' : 'class', $this->encode($callable[0]), $this->encode($callable[1])); + } else { + $xml[] = \sprintf(' ', $this->encode($callable[1])); } - $factory->setAttribute('method', $callable[1]); } else { - $factory->setAttribute('function', $callable); + $xml[] = \sprintf(' ', $this->encode($callable)); } - $service->appendChild($factory); } } if ($definition->isDeprecated()) { $deprecation = $definition->getDeprecation('%service_id%'); - $deprecated = $this->document->createElement('deprecated'); - $deprecated->appendChild($this->document->createTextNode($definition->getDeprecation('%service_id%')['message'])); - $deprecated->setAttribute('package', $deprecation['package']); - $deprecated->setAttribute('version', $deprecation['version']); - - $service->appendChild($deprecated); + $xml[] = \sprintf(' %s', $this->encode($deprecation['package']), $this->encode($deprecation['version']), $this->encode($deprecation['message'], 0)); } if ($definition->isAutowired()) { - $service->setAttribute('autowire', 'true'); + $xmlAttr .= ' autowire="true"'; } if ($definition->isAutoconfigured()) { - $service->setAttribute('autoconfigure', 'true'); + $xmlAttr .= ' autoconfigure="true"'; } if ($definition->isAbstract()) { - $service->setAttribute('abstract', 'true'); + $xmlAttr .= ' abstract="true"'; } if ($callable = $definition->getConfigurator()) { - $configurator = $this->document->createElement('configurator'); - if (\is_array($callable) && $callable[0] instanceof Definition) { - $this->addService($callable[0], null, $configurator); - $configurator->setAttribute('method', $callable[1]); + $xml[] = \sprintf(' ', $this->encode($callable[1])); + foreach ($this->addService($callable[0], null) as $line) { + $xml[] = ' '.$line; + } + $xml[] = ' '; } elseif (\is_array($callable)) { - $configurator->setAttribute($callable[0] instanceof Reference ? 'service' : 'class', $callable[0]); - $configurator->setAttribute('method', $callable[1]); + $xml[] = \sprintf(' ', $callable[0] instanceof Reference ? 'service' : 'class', $this->encode($callable[0]), $this->encode($callable[1])); } else { - $configurator->setAttribute('function', $callable); + $xml[] = \sprintf(' ', $this->encode($callable)); } - $service->appendChild($configurator); } - $parent->appendChild($service); + if (!$xml) { + yield \sprintf('', $xmlAttr); + } else { + yield \sprintf('', $xmlAttr); + yield from $xml; + yield ''; + } } - private function addServiceAlias(string $alias, Alias $id, \DOMElement $parent): void + private function addServiceAlias(string $alias, Alias $id): iterable { - $service = $this->document->createElement('service'); - $service->setAttribute('id', $alias); - $service->setAttribute('alias', $id); - if ($id->isPublic()) { - $service->setAttribute('public', 'true'); - } + $xmlAttr = \sprintf(' id="%s" alias="%s"%s', $this->encode($alias), $this->encode($id), $id->isPublic() ? ' public="true"' : ''); if ($id->isDeprecated()) { $deprecation = $id->getDeprecation('%alias_id%'); - $deprecated = $this->document->createElement('deprecated'); - $deprecated->appendChild($this->document->createTextNode($deprecation['message'])); - $deprecated->setAttribute('package', $deprecation['package']); - $deprecated->setAttribute('version', $deprecation['version']); - - $service->appendChild($deprecated); + yield \sprintf('', $xmlAttr); + yield \sprintf(' %s', $this->encode($deprecation['package']), $this->encode($deprecation['version']), $this->encode($deprecation['message'], 0)); + yield ''; + } else { + yield \sprintf('', $xmlAttr); } - - $parent->appendChild($service); } - private function addServices(\DOMElement $parent): void + private function addServices(): iterable { - $definitions = $this->container->getDefinitions(); - if (!$definitions) { + if (!$definitions = $this->container->getDefinitions()) { return; } - $services = $this->document->createElement('services'); + yield ''; foreach ($definitions as $id => $definition) { - $this->addService($definition, $id, $services); + foreach ($this->addService($definition, $id) as $line) { + yield ' '.$line; + } } $aliases = $this->container->getAliases(); @@ -269,137 +271,150 @@ private function addServices(\DOMElement $parent): void while (isset($aliases[(string) $id])) { $id = $aliases[(string) $id]; } - $this->addServiceAlias($alias, $id, $services); + foreach ($this->addServiceAlias($alias, $id) as $line) { + yield ' '.$line; + } } - $parent->appendChild($services); + yield ''; } - private function addTagRecursiveAttributes(\DOMElement $parent, array $attributes): void + private function addTagRecursiveAttributes(array $attributes): iterable { foreach ($attributes as $name => $value) { - $attribute = $this->document->createElement('attribute'); - $attribute->setAttribute('name', $name); - if (\is_array($value)) { - $this->addTagRecursiveAttributes($attribute, $value); - } else { - $attribute->appendChild($this->document->createTextNode($value)); + yield \sprintf('', $this->encode($name)); + foreach ($this->addTagRecursiveAttributes($value) as $line) { + yield ' '.$line; + } + yield ''; + } elseif ('' !== $value = self::phpToXml($value ?? '')) { + yield \sprintf('%s', $this->encode($name), $this->encode($value, 0)); } - - $parent->appendChild($attribute); } } - private function convertParameters(array $parameters, string $type, \DOMElement $parent, string $keyAttribute = 'key'): void + private function convertParameters(array $parameters, string $type, string $keyAttribute = 'key'): iterable { $withKeys = !array_is_list($parameters); foreach ($parameters as $key => $value) { - $element = $this->document->createElement($type); - if ($withKeys) { - $element->setAttribute($keyAttribute, $key); - } + $xmlAttr = $withKeys ? \sprintf(' %s="%s"', $keyAttribute, $this->encode($key)) : ''; - if (\is_array($tag = $value)) { - $element->setAttribute('type', 'collection'); - $this->convertParameters($value, $type, $element, 'key'); - } elseif ($value instanceof TaggedIteratorArgument || ($value instanceof ServiceLocatorArgument && $tag = $value->getTaggedIteratorArgument())) { - $element->setAttribute('type', $value instanceof TaggedIteratorArgument ? 'tagged_iterator' : 'tagged_locator'); - $element->setAttribute('tag', $tag->getTag()); + if (($value instanceof TaggedIteratorArgument && $tag = $value) + || ($value instanceof ServiceLocatorArgument && $tag = $value->getTaggedIteratorArgument()) + ) { + $xmlAttr .= \sprintf(' type="%s"', $value instanceof TaggedIteratorArgument ? 'tagged_iterator' : 'tagged_locator'); + $xmlAttr .= \sprintf(' tag="%s"', $this->encode($tag->getTag())); if (null !== $tag->getIndexAttribute()) { - $element->setAttribute('index-by', $tag->getIndexAttribute()); + $xmlAttr .= \sprintf(' index-by="%s"', $this->encode($tag->getIndexAttribute())); if (null !== $tag->getDefaultIndexMethod()) { - $element->setAttribute('default-index-method', $tag->getDefaultIndexMethod()); + $xmlAttr .= \sprintf(' default-index-method="%s"', $this->encode($tag->getDefaultIndexMethod())); } if (null !== $tag->getDefaultPriorityMethod()) { - $element->setAttribute('default-priority-method', $tag->getDefaultPriorityMethod()); + $xmlAttr .= \sprintf(' default-priority-method="%s"', $this->encode($tag->getDefaultPriorityMethod())); } } - if ($excludes = $tag->getExclude()) { - if (1 === \count($excludes)) { - $element->setAttribute('exclude', $excludes[0]); - } else { - foreach ($excludes as $exclude) { - $element->appendChild($this->document->createElement('exclude', $exclude)); - } - } + if (1 === \count($excludes = $tag->getExclude())) { + $xmlAttr .= \sprintf(' exclude="%s"', $this->encode($excludes[0])); } if (!$tag->excludeSelf()) { - $element->setAttribute('exclude-self', 'false'); + $xmlAttr .= ' exclude-self="false"'; + } + + if (1 < \count($excludes)) { + yield \sprintf('<%s%s>', $type, $xmlAttr); + foreach ($excludes as $exclude) { + yield \sprintf(' %s', $this->encode($exclude, 0)); + } + yield \sprintf('', $type); + } else { + yield \sprintf('<%s%s/>', $type, $xmlAttr); + } + } elseif (match (true) { + \is_array($value) && $xmlAttr .= ' type="collection"' => true, + $value instanceof IteratorArgument && $xmlAttr .= ' type="iterator"' => true, + $value instanceof ServiceLocatorArgument && $xmlAttr .= ' type="service_locator"' => true, + $value instanceof ServiceClosureArgument && !$value->getValues()[0] instanceof Reference && $xmlAttr .= ' type="service_closure"' => true, + default => false, + }) { + if ($value instanceof ArgumentInterface) { + $value = $value->getValues(); + } + if ($value) { + yield \sprintf('<%s%s>', $type, $xmlAttr); + foreach ($this->convertParameters($value, $type, 'key') as $line) { + yield ' '.$line; + } + yield \sprintf('', $type); + } else { + yield \sprintf('<%s%s/>', $type, $xmlAttr); } - } elseif ($value instanceof IteratorArgument) { - $element->setAttribute('type', 'iterator'); - $this->convertParameters($value->getValues(), $type, $element, 'key'); - } elseif ($value instanceof ServiceLocatorArgument) { - $element->setAttribute('type', 'service_locator'); - $this->convertParameters($value->getValues(), $type, $element, 'key'); - } elseif ($value instanceof ServiceClosureArgument && !$value->getValues()[0] instanceof Reference) { - $element->setAttribute('type', 'service_closure'); - $this->convertParameters($value->getValues(), $type, $element, 'key'); } elseif ($value instanceof Reference || $value instanceof ServiceClosureArgument) { - $element->setAttribute('type', 'service'); if ($value instanceof ServiceClosureArgument) { - $element->setAttribute('type', 'service_closure'); + $xmlAttr .= ' type="service_closure"'; $value = $value->getValues()[0]; + } else { + $xmlAttr .= ' type="service"'; } - $element->setAttribute('id', (string) $value); - $behavior = $value->getInvalidBehavior(); - if (ContainerInterface::NULL_ON_INVALID_REFERENCE == $behavior) { - $element->setAttribute('on-invalid', 'null'); - } elseif (ContainerInterface::IGNORE_ON_INVALID_REFERENCE == $behavior) { - $element->setAttribute('on-invalid', 'ignore'); - } elseif (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE == $behavior) { - $element->setAttribute('on-invalid', 'ignore_uninitialized'); - } + $xmlAttr .= \sprintf(' id="%s"', $this->encode((string) $value)); + $xmlAttr .= match ($value->getInvalidBehavior()) { + ContainerInterface::NULL_ON_INVALID_REFERENCE => ' on-invalid="null"', + ContainerInterface::IGNORE_ON_INVALID_REFERENCE => ' on-invalid="ignore"', + ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE => ' on-invalid="ignore_uninitialized"', + default => '', + }; + + yield \sprintf('<%s%s/>', $type, $xmlAttr); } elseif ($value instanceof Definition) { - $element->setAttribute('type', 'service'); - $this->addService($value, null, $element); - } elseif ($value instanceof Expression) { - $element->setAttribute('type', 'expression'); - $text = $this->document->createTextNode(self::phpToXml((string) $value)); - $element->appendChild($text); - } elseif (\is_string($value) && !preg_match('/^[^\x00-\x08\x0B\x0C\x0E-\x1F\x7F]*+$/u', $value)) { - $element->setAttribute('type', 'binary'); - $text = $this->document->createTextNode(self::phpToXml(base64_encode($value))); - $element->appendChild($text); - } elseif ($value instanceof \UnitEnum) { - $element->setAttribute('type', 'constant'); - $element->appendChild($this->document->createTextNode(self::phpToXml($value))); - } elseif ($value instanceof AbstractArgument) { - $element->setAttribute('type', 'abstract'); - $text = $this->document->createTextNode(self::phpToXml($value->getText())); - $element->appendChild($text); + $xmlAttr .= ' type="service"'; + + yield \sprintf('<%s%s>', $type, $xmlAttr); + foreach ($this->addService($value, null) as $line) { + yield ' '.$line; + } + yield \sprintf('', $type); } else { - if (\in_array($value, ['null', 'true', 'false'], true)) { - $element->setAttribute('type', 'string'); + if ($value instanceof Expression) { + $xmlAttr .= ' type="expression"'; + $value = (string) $value; + } elseif (\is_string($value) && !preg_match('/^[^\x00-\x08\x0B\x0C\x0E-\x1F\x7F]*+$/u', $value)) { + $xmlAttr .= ' type="binary"'; + $value = base64_encode($value); + } elseif ($value instanceof \UnitEnum) { + $xmlAttr .= ' type="constant"'; + } elseif ($value instanceof AbstractArgument) { + $xmlAttr .= ' type="abstract"'; + $value = $value->getText(); + } elseif (\in_array($value, ['null', 'true', 'false'], true)) { + $xmlAttr .= ' type="string"'; + } elseif (\is_string($value) && (is_numeric($value) || preg_match('/^0b[01]*$/', $value) || preg_match('/^0x[0-9a-f]++$/i', $value))) { + $xmlAttr .= ' type="string"'; } - if (\is_string($value) && (is_numeric($value) || preg_match('/^0b[01]*$/', $value) || preg_match('/^0x[0-9a-f]++$/i', $value))) { - $element->setAttribute('type', 'string'); + if ('' === $value = self::phpToXml($value)) { + yield \sprintf('<%s%s/>', $type, $xmlAttr); + } else { + yield \sprintf('<%s%s>%s', $type, $xmlAttr, $this->encode($value, 0)); } - - $text = $this->document->createTextNode(self::phpToXml($value)); - $element->appendChild($text); } - $parent->appendChild($element); } } - /** - * Escapes arguments. - */ + private function encode(string $value, int $flags = \ENT_COMPAT): string + { + return htmlspecialchars($value, \ENT_XML1 | \ENT_SUBSTITUTE | $flags, 'UTF-8'); + } + private function escape(array $arguments): array { $args = []; foreach ($arguments as $k => $v) { - if (\is_array($v)) { - $args[$k] = $this->escape($v); - } elseif (\is_string($v)) { - $args[$k] = str_replace('%', '%%', $v); - } else { - $args[$k] = $v; - } + $args[$k] = match (true) { + \is_array($v) => $this->escape($v), + \is_string($v) => str_replace('%', '%%', $v), + default => $v, + }; } return $args; @@ -412,21 +427,15 @@ private function escape(array $arguments): array */ public static function phpToXml(mixed $value): string { - switch (true) { - case null === $value: - return 'null'; - case true === $value: - return 'true'; - case false === $value: - return 'false'; - case $value instanceof Parameter: - return '%'.$value.'%'; - case $value instanceof \UnitEnum: - return \sprintf('%s::%s', $value::class, $value->name); - case \is_object($value) || \is_resource($value): - throw new RuntimeException(\sprintf('Unable to dump a service container if a parameter is an object or a resource, got "%s".', get_debug_type($value))); - default: - return (string) $value; - } + return match (true) { + null === $value => 'null', + true === $value => 'true', + false === $value => 'false', + $value instanceof Parameter => '%'.$value.'%', + $value instanceof \UnitEnum => \sprintf('%s::%s', $value::class, $value->name), + \is_object($value), + \is_resource($value) => throw new RuntimeException(\sprintf('Unable to dump a service container if a parameter is an object or a resource, got "%s".', get_debug_type($value))), + default => (string) $value, + }; } } From 46ebc8e2565de4f84193189b3659ee668242dcde Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 9 Oct 2024 11:06:51 +0200 Subject: [PATCH 12/19] run tests using PHPUnit 11.5 --- Tests/Compiler/AutowirePassTest.php | 7 +++-- .../RegisterServiceSubscribersPassTest.php | 7 +++-- Tests/Compiler/ResolveClassPassTest.php | 9 +++--- .../ResolveReferencesToAliasesPassTest.php | 17 ++++++----- Tests/ContainerBuilderTest.php | 28 +++++++++---------- Tests/Dumper/PhpDumperTest.php | 23 ++++++++------- Tests/Loader/XmlFileLoaderTest.php | 10 +++---- Tests/Loader/YamlFileLoaderTest.php | 10 +++---- Tests/ParameterBag/FrozenParameterBagTest.php | 11 ++++---- Tests/ParameterBag/ParameterBagTest.php | 23 ++++++++------- composer.json | 2 +- phpunit.xml.dist | 11 ++++++-- 12 files changed, 78 insertions(+), 80 deletions(-) diff --git a/Tests/Compiler/AutowirePassTest.php b/Tests/Compiler/AutowirePassTest.php index a73bdb01c..20b97cad2 100644 --- a/Tests/Compiler/AutowirePassTest.php +++ b/Tests/Compiler/AutowirePassTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; @@ -400,9 +402,8 @@ public function testResolveParameter() $this->assertEquals(Foo::class, $container->getDefinition('bar')->getArgument(0)); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testOptionalParameter() { $container = new ContainerBuilder(); diff --git a/Tests/Compiler/RegisterServiceSubscribersPassTest.php b/Tests/Compiler/RegisterServiceSubscribersPassTest.php index ffbdc180f..c82815fa5 100644 --- a/Tests/Compiler/RegisterServiceSubscribersPassTest.php +++ b/Tests/Compiler/RegisterServiceSubscribersPassTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface as PsrContainerInterface; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; @@ -458,9 +460,8 @@ public static function getSubscribedServices(): array $this->assertEquals($expected, $container->getDefinition((string) $locator->getFactory()[0])->getArgument(0)); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testSubscribedServiceWithLegacyAttributes() { $container = new ContainerBuilder(); diff --git a/Tests/Compiler/ResolveClassPassTest.php b/Tests/Compiler/ResolveClassPassTest.php index 4094af811..995ec4fd7 100644 --- a/Tests/Compiler/ResolveClassPassTest.php +++ b/Tests/Compiler/ResolveClassPassTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\DependencyInjection\ChildDefinition; @@ -96,14 +98,13 @@ public function testAmbiguousChildDefinition() (new ResolveClassPass())->process($container); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testInvalidClassNameDefinition() { // $this->expectException(InvalidArgumentException::class); // $this->expectExceptionMessage('Service id "Acme\UnknownClass" looks like a FQCN but no corresponding class or interface exists. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class or interface.'); - $this->expectDeprecation('Since symfony/dependency-injection 7.4: Service id "Acme\UnknownClass" looks like a FQCN but no corresponding class or interface exists. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class or interface.'); + $this->expectUserDeprecationMessage('Since symfony/dependency-injection 7.4: Service id "Acme\UnknownClass" looks like a FQCN but no corresponding class or interface exists. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class or interface.'); $container = new ContainerBuilder(); $container->register('Acme\UnknownClass'); diff --git a/Tests/Compiler/ResolveReferencesToAliasesPassTest.php b/Tests/Compiler/ResolveReferencesToAliasesPassTest.php index aedf7fa54..1efe1fb0d 100644 --- a/Tests/Compiler/ResolveReferencesToAliasesPassTest.php +++ b/Tests/Compiler/ResolveReferencesToAliasesPassTest.php @@ -11,8 +11,9 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ExpectUserDeprecationMessageTrait; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Compiler\ResolveReferencesToAliasesPass; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -22,8 +23,6 @@ class ResolveReferencesToAliasesPassTest extends TestCase { - use ExpectUserDeprecationMessageTrait; - public function testProcess() { $container = new ContainerBuilder(); @@ -86,10 +85,10 @@ public function testResolveFactory() } /** - * The test should be kept in the group as it always expects a deprecation. - * - * @group legacy + * The test must be marked as ignoring deprecations as it always expects a deprecation. */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testDeprecationNoticeWhenReferencedByAlias() { $this->expectUserDeprecationMessage('Since foobar 1.2.3.4: The "deprecated_foo_alias" service alias is deprecated. You should stop using it, as it will be removed in the future. It is being referenced by the "alias" alias.'); @@ -108,10 +107,10 @@ public function testDeprecationNoticeWhenReferencedByAlias() } /** - * The test should be kept in the group as it always expects a deprecation. - * - * @group legacy + * The test must be marked as ignoring deprecations as it always expects a deprecation. */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testDeprecationNoticeWhenReferencedByDefinition() { $this->expectUserDeprecationMessage('Since foobar 1.2.3.4: The "foo_aliased" service alias is deprecated. You should stop using it, as it will be removed in the future. It is being referenced by the "definition" service.'); diff --git a/Tests/ContainerBuilderTest.php b/Tests/ContainerBuilderTest.php index aa932d083..3d74324a6 100644 --- a/Tests/ContainerBuilderTest.php +++ b/Tests/ContainerBuilderTest.php @@ -15,8 +15,9 @@ require_once __DIR__.'/Fixtures/includes/classes.php'; require_once __DIR__.'/Fixtures/includes/ProjectExtension.php'; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ExpectUserDeprecationMessageTrait; use Symfony\Component\Config\Resource\DirectoryResource; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Config\Resource\ResourceInterface; @@ -65,8 +66,6 @@ class ContainerBuilderTest extends TestCase { - use ExpectUserDeprecationMessageTrait; - public function testDefaultRegisteredDefinitions() { $builder = new ContainerBuilder(); @@ -108,10 +107,10 @@ public function testDefinitions() } /** - * The test should be kept in the group as it always expects a deprecation. - * - * @group legacy + * The test must be marked as ignoring deprecations as it always expects a deprecation. */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testDeprecateParameter() { $builder = new ContainerBuilder(); @@ -125,10 +124,10 @@ public function testDeprecateParameter() } /** - * The test should be kept in the group as it always expects a deprecation. - * - * @group legacy + * The test must be marked as ignoring deprecations as it always expects a deprecation. */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testParameterDeprecationIsTrgiggeredWhenCompiled() { $builder = new ContainerBuilder(); @@ -856,9 +855,8 @@ public function testMergeAttributeAutoconfiguration() $this->assertSame([AsTaggedItem::class => [$c1, $c2]], $container->getAttributeAutoconfigurators()); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testGetAutoconfiguredAttributes() { $container = new ContainerBuilder(); @@ -2011,10 +2009,10 @@ public function testAutoAliasing() } /** - * The test should be kept in the group as it always expects a deprecation. - * - * @group legacy + * The test must be marked as ignoring deprecations as it always expects a deprecation. */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testDirectlyAccessingDeprecatedPublicService() { $this->expectUserDeprecationMessage('Since foo/bar 3.8: Accessing the "Symfony\Component\DependencyInjection\Tests\A" service directly from the container is deprecated, use dependency injection instead.'); diff --git a/Tests/Dumper/PhpDumperTest.php b/Tests/Dumper/PhpDumperTest.php index dbba8d282..1eedf4a37 100644 --- a/Tests/Dumper/PhpDumperTest.php +++ b/Tests/Dumper/PhpDumperTest.php @@ -11,9 +11,10 @@ namespace Symfony\Component\DependencyInjection\Tests\Dumper; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; -use Symfony\Bridge\PhpUnit\ExpectUserDeprecationMessageTrait; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\Argument\AbstractArgument; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; @@ -79,8 +80,6 @@ class PhpDumperTest extends TestCase { - use ExpectUserDeprecationMessageTrait; - protected static string $fixturesPath; public static function setUpBeforeClass(): void @@ -480,10 +479,10 @@ public function testDumpAutowireData() } /** - * The test should be kept in the group as it always expects a deprecation. - * - * @group legacy + * The test must be marked as ignoring deprecations as it always expects a deprecation. */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testDeprecatedParameters() { $container = include self::$fixturesPath.'/containers/container_deprecated_parameters.php'; @@ -497,10 +496,10 @@ public function testDeprecatedParameters() } /** - * The test should be kept in the group as it always expects a deprecation. - * - * @group legacy + * The test must be marked as ignoring deprecations as it always expects a deprecation. */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testDeprecatedParametersAsFiles() { $container = include self::$fixturesPath.'/containers/container_deprecated_parameters.php'; @@ -1801,10 +1800,10 @@ public function testDumpServiceWithAbstractArgument() } /** - * The test should be kept in the group as it always expects a deprecation. - * - * @group legacy + * The test must be marked as ignoring deprecations as it always expects a deprecation. */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testDirectlyAccessingDeprecatedPublicService() { $this->expectUserDeprecationMessage('Since foo/bar 3.8: Accessing the "bar" service directly from the container is deprecated, use dependency injection instead.'); diff --git a/Tests/Loader/XmlFileLoaderTest.php b/Tests/Loader/XmlFileLoaderTest.php index f962fa106..f6ff7b8fe 100644 --- a/Tests/Loader/XmlFileLoaderTest.php +++ b/Tests/Loader/XmlFileLoaderTest.php @@ -11,8 +11,9 @@ namespace Symfony\Component\DependencyInjection\Tests\Loader; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ExpectUserDeprecationMessageTrait; use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; use Symfony\Component\Config\Exception\LoaderLoadException; use Symfony\Component\Config\FileLocator; @@ -52,8 +53,6 @@ class XmlFileLoaderTest extends TestCase { - use ExpectUserDeprecationMessageTrait; - protected static string $fixturesPath; public static function setUpBeforeClass(): void @@ -1335,9 +1334,8 @@ public function testUnknownConstantAsKey() $loader->load('key_type_wrong_constant.xml'); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testDeprecatedTagged() { $container = new ContainerBuilder(); diff --git a/Tests/Loader/YamlFileLoaderTest.php b/Tests/Loader/YamlFileLoaderTest.php index 54900e4c3..d4ae212b5 100644 --- a/Tests/Loader/YamlFileLoaderTest.php +++ b/Tests/Loader/YamlFileLoaderTest.php @@ -11,8 +11,9 @@ namespace Symfony\Component\DependencyInjection\Tests\Loader; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ExpectUserDeprecationMessageTrait; use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; use Symfony\Component\Config\Exception\LoaderLoadException; use Symfony\Component\Config\FileLocator; @@ -48,8 +49,6 @@ class YamlFileLoaderTest extends TestCase { - use ExpectUserDeprecationMessageTrait; - protected static string $fixturesPath; public static function setUpBeforeClass(): void @@ -1212,9 +1211,8 @@ public function testStaticConstructor() $this->assertEquals((new Definition('stdClass'))->setFactory([null, 'create']), $definition); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testDeprecatedTagged() { $container = new ContainerBuilder(); diff --git a/Tests/ParameterBag/FrozenParameterBagTest.php b/Tests/ParameterBag/FrozenParameterBagTest.php index 2a4040182..9d7e1ba00 100644 --- a/Tests/ParameterBag/FrozenParameterBagTest.php +++ b/Tests/ParameterBag/FrozenParameterBagTest.php @@ -11,14 +11,13 @@ namespace Symfony\Component\DependencyInjection\Tests\ParameterBag; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ExpectUserDeprecationMessageTrait; use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag; class FrozenParameterBagTest extends TestCase { - use ExpectUserDeprecationMessageTrait; - public function testConstructor() { $parameters = [ @@ -65,10 +64,10 @@ public function testDeprecate() } /** - * The test should be kept in the group as it always expects a deprecation. - * - * @group legacy + * The test must be marked as ignoring deprecations as it always expects a deprecation. */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testGetDeprecated() { $bag = new FrozenParameterBag( diff --git a/Tests/ParameterBag/ParameterBagTest.php b/Tests/ParameterBag/ParameterBagTest.php index db5c58a06..38ef6fcd1 100644 --- a/Tests/ParameterBag/ParameterBagTest.php +++ b/Tests/ParameterBag/ParameterBagTest.php @@ -11,8 +11,9 @@ namespace Symfony\Component\DependencyInjection\Tests\ParameterBag; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ExpectUserDeprecationMessageTrait; use Symfony\Component\DependencyInjection\Exception\EmptyParameterValueException; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException; @@ -22,8 +23,6 @@ class ParameterBagTest extends TestCase { - use ExpectUserDeprecationMessageTrait; - public function testConstructor() { $bag = new ParameterBag($parameters = [ @@ -140,10 +139,10 @@ public static function provideGetThrowParameterNotFoundExceptionData() } /** - * The test should be kept in the group as it always expects a deprecation. - * - * @group legacy + * The test must be marked as ignoring deprecations as it always expects a deprecation. */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testDeprecate() { $bag = new ParameterBag(['foo' => 'bar']); @@ -156,10 +155,10 @@ public function testDeprecate() } /** - * The test should be kept in the group as it always expects a deprecation. - * - * @group legacy + * The test must be marked as ignoring deprecations as it always expects a deprecation. */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testDeprecateWithMessage() { $bag = new ParameterBag(['foo' => 'bar']); @@ -172,10 +171,10 @@ public function testDeprecateWithMessage() } /** - * The test should be kept in the group as it always expects a deprecation. - * - * @group legacy + * The test must be marked as ignoring deprecations as it always expects a deprecation. */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testDeprecationIsTriggeredWhenResolved() { $bag = new ParameterBag(['foo' => '%bar%', 'bar' => 'baz']); diff --git a/composer.json b/composer.json index 7b1e731b7..0a6963357 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ "php": ">=8.2", "psr/container": "^1.1|^2.0", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/service-contracts": "^3.5", + "symfony/service-contracts": "^3.6", "symfony/var-exporter": "^6.4.20|^7.2.5|^8.0" }, "require-dev": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index da20ea70a..9bd36f7de 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,10 +1,11 @@ @@ -18,7 +19,7 @@ - + ./ @@ -27,5 +28,9 @@ ./Tests ./vendor - + + + + + From b4763d4dcbe91ced0e34f0dbf2a0dd4af84dd92c Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 31 Jul 2025 14:36:46 +0200 Subject: [PATCH 13/19] replace PHPUnit annotations with attributes --- Tests/AliasTest.php | 5 +- Tests/Argument/TaggedIteratorArgumentTest.php | 9 +- Tests/Attribute/AutowireInlineTest.php | 21 +-- Tests/Attribute/AutowireTest.php | 5 +- Tests/ChildDefinitionTest.php | 5 +- .../AliasDeprecatedPublicServicesPassTest.php | 5 +- Tests/Compiler/AutowirePassTest.php | 5 +- .../CheckArgumentsValidityPassTest.php | 5 +- .../CheckDefinitionValidityPassTest.php | 5 +- ...tionOnInvalidReferenceBehaviorPassTest.php | 13 +- Tests/Compiler/IntegrationTest.php | 5 +- .../PriorityTaggedServiceTraitTest.php | 5 +- Tests/Compiler/ResolveClassPassTest.php | 9 +- .../Compiler/ResolveFactoryClassPassTest.php | 5 +- ...ContainerParametersResourceCheckerTest.php | 5 +- Tests/ContainerBuilderTest.php | 13 +- Tests/ContainerTest.php | 9 +- Tests/CrossCheckTest.php | 5 +- Tests/DefinitionTest.php | 5 +- Tests/Dumper/PhpDumperTest.php | 27 ++-- Tests/Dumper/XmlDumperTest.php | 13 +- Tests/Dumper/YamlDumperTest.php | 5 +- Tests/EnvVarProcessorTest.php | 126 ++++++------------ .../InvalidParameterTypeExceptionTest.php | 5 +- Tests/Extension/ExtensionTest.php | 5 +- .../PhpDumper/LazyServiceDumperTest.php | 5 +- .../Configurator/EnvConfiguratorTest.php | 5 +- Tests/Loader/FileLoaderTest.php | 34 ++--- Tests/Loader/IniFileLoaderTest.php | 9 +- Tests/Loader/LoaderResolverTest.php | 5 +- Tests/Loader/PhpFileLoaderTest.php | 5 +- Tests/Loader/XmlFileLoaderTest.php | 16 +-- Tests/Loader/YamlFileLoaderTest.php | 9 +- Tests/ParameterBag/ParameterBagTest.php | 22 ++- 34 files changed, 152 insertions(+), 278 deletions(-) diff --git a/Tests/AliasTest.php b/Tests/AliasTest.php index b1c8a4dbd..f4e2c4c99 100644 --- a/Tests/AliasTest.php +++ b/Tests/AliasTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; @@ -69,9 +70,7 @@ public function testReturnsCorrectDeprecation() $this->assertEquals('1.1', $deprecation['version']); } - /** - * @dataProvider invalidDeprecationMessageProvider - */ + #[DataProvider('invalidDeprecationMessageProvider')] public function testCannotDeprecateWithAnInvalidTemplate($message) { $def = new Alias('foo'); diff --git a/Tests/Argument/TaggedIteratorArgumentTest.php b/Tests/Argument/TaggedIteratorArgumentTest.php index dcc38dde7..df7d393c0 100644 --- a/Tests/Argument/TaggedIteratorArgumentTest.php +++ b/Tests/Argument/TaggedIteratorArgumentTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Argument; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; @@ -57,9 +58,7 @@ public function testOnlyTagWithNeedsIndexesAndDotsTag() $this->assertSame('getDefaultQuxPriority', $taggedIteratorArgument->getDefaultPriorityMethod()); } - /** - * @dataProvider defaultIndexMethodProvider - */ + #[DataProvider('defaultIndexMethodProvider')] public function testDefaultIndexMethod(?string $indexAttribute, ?string $defaultIndexMethod, ?string $expectedDefaultIndexMethod) { $taggedIteratorArgument = new TaggedIteratorArgument('foo', $indexAttribute, $defaultIndexMethod); @@ -106,9 +105,7 @@ public static function defaultIndexMethodProvider() ]; } - /** - * @dataProvider defaultPriorityMethodProvider - */ + #[DataProvider('defaultPriorityMethodProvider')] public function testDefaultPriorityIndexMethod(?string $indexAttribute, ?string $defaultPriorityMethod, ?string $expectedDefaultPriorityMethod) { $taggedIteratorArgument = new TaggedIteratorArgument('foo', $indexAttribute, null, false, $defaultPriorityMethod); diff --git a/Tests/Attribute/AutowireInlineTest.php b/Tests/Attribute/AutowireInlineTest.php index a9ae1fb25..26d2e7b13 100644 --- a/Tests/Attribute/AutowireInlineTest.php +++ b/Tests/Attribute/AutowireInlineTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Attribute; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Attribute\AutowireInline; use Symfony\Component\DependencyInjection\Reference; @@ -24,9 +25,7 @@ public function testInvalidFactoryArray() self::assertSame([123, 456], $autowireInline->value['factory']); } - /** - * @dataProvider provideInvalidCalls - */ + #[DataProvider('provideInvalidCalls')] public function testInvalidCallsArray(array $calls) { $autowireInline = new AutowireInline('someClass', calls: $calls); @@ -86,9 +85,7 @@ public function testClassAndParamsLazy() self::assertTrue($attribute->lazy); } - /** - * @dataProvider provideFactories - */ + #[DataProvider('provideFactories')] public function testFactory(string|array $factory, string|array $expectedResult) { $attribute = new AutowireInline($factory); @@ -101,9 +98,7 @@ public function testFactory(string|array $factory, string|array $expectedResult) self::assertFalse($attribute->lazy); } - /** - * @dataProvider provideFactories - */ + #[DataProvider('provideFactories')] public function testFactoryAndParams(string|array $factory, string|array $expectedResult) { $attribute = new AutowireInline($factory, ['someParam']); @@ -116,9 +111,7 @@ public function testFactoryAndParams(string|array $factory, string|array $expect self::assertFalse($attribute->lazy); } - /** - * @dataProvider provideFactories - */ + #[DataProvider('provideFactories')] public function testFactoryAndParamsLazy(string|array $factory, string|array $expectedResult) { $attribute = new AutowireInline($factory, ['someParam'], lazy: true); @@ -143,9 +136,7 @@ public static function provideFactories(): iterable yield '@reference with method' => [['@someClass', 'someMethod'], [new Reference('someClass'), 'someMethod']]; } - /** - * @dataProvider provideCalls - */ + #[DataProvider('provideCalls')] public function testCalls(string|array $calls, array $expectedResult) { $attribute = new AutowireInline('someClass', calls: $calls); diff --git a/Tests/Attribute/AutowireTest.php b/Tests/Attribute/AutowireTest.php index aac42b0d2..6a3150c59 100644 --- a/Tests/Attribute/AutowireTest.php +++ b/Tests/Attribute/AutowireTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Attribute; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Attribute\Autowire; use Symfony\Component\DependencyInjection\Exception\LogicException; @@ -19,9 +20,7 @@ class AutowireTest extends TestCase { - /** - * @dataProvider provideMultipleParameters - */ + #[DataProvider('provideMultipleParameters')] public function testCanOnlySetOneParameter(array $parameters) { $this->expectException(LogicException::class); diff --git a/Tests/ChildDefinitionTest.php b/Tests/ChildDefinitionTest.php index 39c96f8c5..109d8a720 100644 --- a/Tests/ChildDefinitionTest.php +++ b/Tests/ChildDefinitionTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ChildDefinition; @@ -24,9 +25,7 @@ public function testConstructor() $this->assertSame([], $def->getChanges()); } - /** - * @dataProvider getPropertyTests - */ + #[DataProvider('getPropertyTests')] public function testSetProperty($property, $changeKey) { $def = new ChildDefinition('foo'); diff --git a/Tests/Compiler/AliasDeprecatedPublicServicesPassTest.php b/Tests/Compiler/AliasDeprecatedPublicServicesPassTest.php index df8f939f5..a720a0020 100644 --- a/Tests/Compiler/AliasDeprecatedPublicServicesPassTest.php +++ b/Tests/Compiler/AliasDeprecatedPublicServicesPassTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Compiler\AliasDeprecatedPublicServicesPass; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -42,9 +43,7 @@ public function testProcess() ], $alias->getDeprecation('foo')); } - /** - * @dataProvider processWithMissingAttributeProvider - */ + #[DataProvider('processWithMissingAttributeProvider')] public function testProcessWithMissingAttribute(string $attribute, array $attributes) { $container = new ContainerBuilder(); diff --git a/Tests/Compiler/AutowirePassTest.php b/Tests/Compiler/AutowirePassTest.php index 20b97cad2..9d51751b4 100644 --- a/Tests/Compiler/AutowirePassTest.php +++ b/Tests/Compiler/AutowirePassTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; @@ -875,9 +876,7 @@ public function testWithFactory() $this->assertEquals([new TypedReference(Foo::class, Foo::class)], $definition->getArguments()); } - /** - * @dataProvider provideNotWireableCalls - */ + #[DataProvider('provideNotWireableCalls')] public function testNotWireableCalls($method, $expectedMsg) { $container = new ContainerBuilder(); diff --git a/Tests/Compiler/CheckArgumentsValidityPassTest.php b/Tests/Compiler/CheckArgumentsValidityPassTest.php index 6d0a2edd0..d7d0f8e8a 100644 --- a/Tests/Compiler/CheckArgumentsValidityPassTest.php +++ b/Tests/Compiler/CheckArgumentsValidityPassTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Compiler\CheckArgumentsValidityPass; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -41,9 +42,7 @@ public function testProcess() ], $container->getDefinition('foo')->getMethodCalls()); } - /** - * @dataProvider definitionProvider - */ + #[DataProvider('definitionProvider')] public function testException(array $arguments, array $methodCalls) { $container = new ContainerBuilder(); diff --git a/Tests/Compiler/CheckDefinitionValidityPassTest.php b/Tests/Compiler/CheckDefinitionValidityPassTest.php index df7dd03d0..bf32e4832 100644 --- a/Tests/Compiler/CheckDefinitionValidityPassTest.php +++ b/Tests/Compiler/CheckDefinitionValidityPassTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Compiler\CheckDefinitionValidityPass; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -89,9 +90,7 @@ public function testValidTags() $this->addToAssertionCount(1); } - /** - * @dataProvider provideInvalidTags - */ + #[DataProvider('provideInvalidTags')] public function testInvalidTags(string $name, array $attributes, string $message) { $this->expectExceptionMessage($message); diff --git a/Tests/Compiler/CheckExceptionOnInvalidReferenceBehaviorPassTest.php b/Tests/Compiler/CheckExceptionOnInvalidReferenceBehaviorPassTest.php index 04a121d63..73539acb5 100644 --- a/Tests/Compiler/CheckExceptionOnInvalidReferenceBehaviorPassTest.php +++ b/Tests/Compiler/CheckExceptionOnInvalidReferenceBehaviorPassTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler; +use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Argument\BoundArgument; use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass; @@ -84,10 +85,8 @@ public function testProcessDefinitionWithBindings() $this->addToAssertionCount(1); } - /** - * @testWith [true] - * [false] - */ + #[TestWith([true])] + #[TestWith([false])] public function testWithErroredServiceLocator(bool $inline) { $container = new ContainerBuilder(); @@ -105,10 +104,8 @@ public function testWithErroredServiceLocator(bool $inline) $this->process($container); } - /** - * @testWith [true] - * [false] - */ + #[TestWith([true])] + #[TestWith([false])] public function testWithErroredHiddenService(bool $inline) { $container = new ContainerBuilder(); diff --git a/Tests/Compiler/IntegrationTest.php b/Tests/Compiler/IntegrationTest.php index 3f1855565..bf61835f0 100644 --- a/Tests/Compiler/IntegrationTest.php +++ b/Tests/Compiler/IntegrationTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; use Symfony\Component\Config\FileLocator; @@ -245,9 +246,7 @@ public function testAliasDecoratedService() $this->assertSame($container->get('service'), $container->get('decorator')); } - /** - * @dataProvider getYamlCompileTests - */ + #[DataProvider('getYamlCompileTests')] public function testYamlContainerCompiles($directory, $actualServiceId, $expectedServiceId, ?ContainerBuilder $mainContainer = null) { // allow a container to be passed in, which might have autoconfigure settings diff --git a/Tests/Compiler/PriorityTaggedServiceTraitTest.php b/Tests/Compiler/PriorityTaggedServiceTraitTest.php index 3f767257d..fc0656b2e 100644 --- a/Tests/Compiler/PriorityTaggedServiceTraitTest.php +++ b/Tests/Compiler/PriorityTaggedServiceTraitTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; use Symfony\Component\DependencyInjection\Attribute\AsTaggedItem; @@ -171,9 +172,7 @@ public function testTheIndexedTagsByDefaultIndexMethod() $this->assertEquals($expected, $priorityTaggedServiceTraitImplementation->test($tag, $container)); } - /** - * @dataProvider provideInvalidDefaultMethods - */ + #[DataProvider('provideInvalidDefaultMethods')] public function testTheIndexedTagsByDefaultIndexMethodFailure(string $defaultIndexMethod, ?string $indexAttribute, string $expectedExceptionMessage) { $this->expectException(InvalidArgumentException::class); diff --git a/Tests/Compiler/ResolveClassPassTest.php b/Tests/Compiler/ResolveClassPassTest.php index 995ec4fd7..43760f0d9 100644 --- a/Tests/Compiler/ResolveClassPassTest.php +++ b/Tests/Compiler/ResolveClassPassTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; @@ -25,9 +26,7 @@ class ResolveClassPassTest extends TestCase { use ExpectDeprecationTrait; - /** - * @dataProvider provideValidClassId - */ + #[DataProvider('provideValidClassId')] public function testResolveClassFromId($serviceId) { $container = new ContainerBuilder(); @@ -43,9 +42,7 @@ public static function provideValidClassId() yield [CaseSensitiveClass::class]; } - /** - * @dataProvider provideInvalidClassId - */ + #[DataProvider('provideInvalidClassId')] public function testWontResolveClassFromId($serviceId) { $container = new ContainerBuilder(); diff --git a/Tests/Compiler/ResolveFactoryClassPassTest.php b/Tests/Compiler/ResolveFactoryClassPassTest.php index 8d93eeb9c..13aeb9b57 100644 --- a/Tests/Compiler/ResolveFactoryClassPassTest.php +++ b/Tests/Compiler/ResolveFactoryClassPassTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Compiler\ResolveFactoryClassPass; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -55,9 +56,7 @@ public static function provideFulfilledFactories() ]; } - /** - * @dataProvider provideFulfilledFactories - */ + #[DataProvider('provideFulfilledFactories')] public function testIgnoresFulfilledFactories($factory) { $container = new ContainerBuilder(); diff --git a/Tests/Config/ContainerParametersResourceCheckerTest.php b/Tests/Config/ContainerParametersResourceCheckerTest.php index 7749e1963..713cd0f99 100644 --- a/Tests/Config/ContainerParametersResourceCheckerTest.php +++ b/Tests/Config/ContainerParametersResourceCheckerTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Config; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Config\ContainerParametersResource; use Symfony\Component\DependencyInjection\Config\ContainerParametersResourceChecker; @@ -34,9 +35,7 @@ public function testSupports() $this->assertTrue($this->resourceChecker->supports($this->resource)); } - /** - * @dataProvider isFreshProvider - */ + #[DataProvider('isFreshProvider')] public function testIsFresh(callable $mockContainer, $expected) { $mockContainer($this->container, $this); diff --git a/Tests/ContainerBuilderTest.php b/Tests/ContainerBuilderTest.php index 3d74324a6..a3002e59e 100644 --- a/Tests/ContainerBuilderTest.php +++ b/Tests/ContainerBuilderTest.php @@ -15,6 +15,7 @@ require_once __DIR__.'/Fixtures/includes/classes.php'; require_once __DIR__.'/Fixtures/includes/ProjectExtension.php'; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; @@ -312,18 +313,14 @@ public function testNonSharedServicesReturnsDifferentInstances() $this->assertNotSame($builder->get('bar'), $builder->get('bar')); } - /** - * @dataProvider provideBadId - */ + #[DataProvider('provideBadId')] public function testBadAliasId($id) { $this->expectException(InvalidArgumentException::class); (new ContainerBuilder())->setAlias($id, 'foo'); } - /** - * @dataProvider provideBadId - */ + #[DataProvider('provideBadId')] public function testBadDefinitionId($id) { $this->expectException(InvalidArgumentException::class); @@ -1697,9 +1694,7 @@ public function testUninitializedReference() $this->assertEquals(['foo1' => new \stdClass(), 'foo3' => new \stdClass()], iterator_to_array($bar->iter)); } - /** - * @dataProvider provideAlmostCircular - */ + #[DataProvider('provideAlmostCircular')] public function testAlmostCircular($visibility) { $container = include __DIR__.'/Fixtures/containers/container_almost_circular.php'; diff --git a/Tests/ContainerTest.php b/Tests/ContainerTest.php index 5ccb1c75d..27b1547ce 100644 --- a/Tests/ContainerTest.php +++ b/Tests/ContainerTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -34,9 +35,7 @@ public function testConstructor() $this->assertEquals(['foo' => 'bar'], $sc->getParameterBag()->all(), '__construct() takes an array of parameters as its first argument'); } - /** - * @dataProvider dataForTestCamelize - */ + #[DataProvider('dataForTestCamelize')] public function testCamelize($id, $expected) { $this->assertEquals($expected, Container::camelize($id), \sprintf('Container::camelize("%s")', $id)); @@ -58,9 +57,7 @@ public static function dataForTestCamelize() ]; } - /** - * @dataProvider dataForTestUnderscore - */ + #[DataProvider('dataForTestUnderscore')] public function testUnderscore($id, $expected) { $this->assertEquals($expected, Container::underscore($id), \sprintf('Container::underscore("%s")', $id)); diff --git a/Tests/CrossCheckTest.php b/Tests/CrossCheckTest.php index db17da408..b172a3105 100644 --- a/Tests/CrossCheckTest.php +++ b/Tests/CrossCheckTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -27,9 +28,7 @@ public static function setUpBeforeClass(): void require_once self::$fixturesPath.'/includes/foo.php'; } - /** - * @dataProvider crossCheckLoadersDumpers - */ + #[DataProvider('crossCheckLoadersDumpers')] public function testCrossCheck($fixture, $type) { $loaderClass = 'Symfony\\Component\\DependencyInjection\\Loader\\'.ucfirst($type).'FileLoader'; diff --git a/Tests/DefinitionTest.php b/Tests/DefinitionTest.php index 459e566d2..08ae1325a 100644 --- a/Tests/DefinitionTest.php +++ b/Tests/DefinitionTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; @@ -186,9 +187,7 @@ public function testSetIsDeprecated() $this->assertSame('1.1', $deprecation['version']); } - /** - * @dataProvider invalidDeprecationMessageProvider - */ + #[DataProvider('invalidDeprecationMessageProvider')] public function testSetDeprecatedWithInvalidDeprecationTemplate($message) { $def = new Definition('stdClass'); diff --git a/Tests/Dumper/PhpDumperTest.php b/Tests/Dumper/PhpDumperTest.php index 1eedf4a37..5c2c2bab0 100644 --- a/Tests/Dumper/PhpDumperTest.php +++ b/Tests/Dumper/PhpDumperTest.php @@ -11,8 +11,11 @@ namespace Symfony\Component\DependencyInjection\Tests\Dumper; +use Bar\FooLazyClass; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\IgnoreDeprecations; +use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; use Symfony\Component\Config\FileLocator; @@ -327,7 +330,7 @@ public function testDumpAsFilesWithLazyFactoriesInlined() $container->setParameter('container.dumper.inline_class_loader', true); $container->register('lazy_foo', \Bar\FooClass::class) - ->addArgument(new Definition(\Bar\FooLazyClass::class)) + ->addArgument(new Definition(FooLazyClass::class)) ->setPublic(true) ->setLazy(true); @@ -403,9 +406,7 @@ public function testConflictingMethodsWithParent() $this->assertTrue(method_exists($class, 'getFoobar2Service')); } - /** - * @dataProvider provideInvalidFactories - */ + #[DataProvider('provideInvalidFactories')] public function testInvalidFactories($factory) { $this->expectException(RuntimeException::class); @@ -779,7 +780,7 @@ public function testNonSharedLazy() $container = new ContainerBuilder(); $container - ->register('foo', \Bar\FooLazyClass::class) + ->register('foo', FooLazyClass::class) ->setFile(realpath(self::$fixturesPath.'/includes/foo_lazy.php')) ->setShared(false) ->setLazy(true) @@ -823,7 +824,7 @@ public function testNonSharedLazyAsFiles() $container = new ContainerBuilder(); $container - ->register('non_shared_foo', \Bar\FooLazyClass::class) + ->register('non_shared_foo', FooLazyClass::class) ->setFile(realpath(self::$fixturesPath.'/includes/foo_lazy.php')) ->setShared(false) ->setLazy(true) @@ -868,10 +869,8 @@ public function testNonSharedLazyAsFiles() $this->assertNotSame($foo1, $foo2); } - /** - * @testWith [false] - * [true] - */ + #[TestWith([false])] + #[TestWith([true])] public function testNonSharedLazyDefinitionReferences(bool $asGhostObject) { $container = new ContainerBuilder(); @@ -1219,9 +1218,7 @@ public function testUninitializedReference() $this->assertEquals(['foo1' => new \stdClass(), 'foo3' => new \stdClass()], iterator_to_array($bar->iter)); } - /** - * @dataProvider provideAlmostCircular - */ + #[DataProvider('provideAlmostCircular')] public function testAlmostCircular($visibility) { $container = include self::$fixturesPath.'/containers/container_almost_circular.php'; @@ -2113,9 +2110,7 @@ public function testInlineAdapterConsumer() $this->assertNotSame($fooService->factoredFromServiceWithParam, $barService->factoredFromServiceWithParam); } - /** - * @dataProvider getStripCommentsCodes - */ + #[DataProvider('getStripCommentsCodes')] public function testStripComments(string $source, string $expected) { $reflection = new \ReflectionClass(PhpDumper::class); diff --git a/Tests/Dumper/XmlDumperTest.php b/Tests/Dumper/XmlDumperTest.php index 548e5a18b..a5a21a4df 100644 --- a/Tests/Dumper/XmlDumperTest.php +++ b/Tests/Dumper/XmlDumperTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Dumper; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\Argument\AbstractArgument; @@ -110,9 +111,7 @@ public function testDumpEntities() ", $dumper->dump()); } - /** - * @dataProvider provideDecoratedServicesData - */ + #[DataProvider('provideDecoratedServicesData')] public function testDumpDecoratedServices($expectedXmlDump, $container) { $dumper = new XmlDumper($container); @@ -151,9 +150,7 @@ public static function provideDecoratedServicesData() ]; } - /** - * @dataProvider provideCompiledContainerData - */ + #[DataProvider('provideCompiledContainerData')] public function testCompiledContainerCanBeDumped($containerFile) { $fixturesPath = __DIR__.'/../Fixtures'; @@ -282,9 +279,7 @@ public function testDumpHandlesEnumeration() $this->assertEquals(file_get_contents(self::$fixturesPath.'/xml/services_with_enumeration.xml'), $dumper->dump()); } - /** - * @dataProvider provideDefaultClasses - */ + #[DataProvider('provideDefaultClasses')] public function testDumpHandlesDefaultAttribute($class, $expectedFile) { $container = new ContainerBuilder(); diff --git a/Tests/Dumper/YamlDumperTest.php b/Tests/Dumper/YamlDumperTest.php index 3a21d7aa9..131642ac7 100644 --- a/Tests/Dumper/YamlDumperTest.php +++ b/Tests/Dumper/YamlDumperTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Dumper; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\Argument\AbstractArgument; @@ -170,9 +171,7 @@ public function testDumpHandlesEnumeration() } } - /** - * @dataProvider provideDefaultClasses - */ + #[DataProvider('provideDefaultClasses')] public function testDumpHandlesDefaultAttribute($class, $expectedFile) { $container = new ContainerBuilder(); diff --git a/Tests/EnvVarProcessorTest.php b/Tests/EnvVarProcessorTest.php index e5875c628..08ea88f13 100644 --- a/Tests/EnvVarProcessorTest.php +++ b/Tests/EnvVarProcessorTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\DependencyInjection\Tests; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; use Symfony\Component\DependencyInjection\Container; @@ -27,9 +29,7 @@ class EnvVarProcessorTest extends TestCase { public const TEST_CONST = 'test'; - /** - * @dataProvider validStrings - */ + #[DataProvider('validStrings')] public function testGetEnvString($value, $processed) { $container = new ContainerBuilder(); @@ -59,9 +59,7 @@ public static function validStrings() ]; } - /** - * @dataProvider validRealEnvValues - */ + #[DataProvider('validRealEnvValues')] public function testGetEnvRealEnv($value, $processed) { $_ENV['FOO'] = $value; @@ -120,9 +118,7 @@ public function testGetEnvRealEnvNonScalar() unset($_ENV['FOO']); } - /** - * @dataProvider validBools - */ + #[DataProvider('validBools')] public function testGetEnvBool($value, $processed) { $processor = new EnvVarProcessor(new Container()); @@ -198,9 +194,7 @@ public function loadEnvVars(): array unset($_ENV['FOO'], $GLOBALS['ENV_FOO']); } - /** - * @dataProvider validBools - */ + #[DataProvider('validBools')] public function testGetEnvNot($value, $processed) { $processor = new EnvVarProcessor(new Container()); @@ -228,9 +222,7 @@ public static function validBools() ]; } - /** - * @dataProvider validInts - */ + #[DataProvider('validInts')] public function testGetEnvInt($value, $processed) { $processor = new EnvVarProcessor(new Container()); @@ -253,9 +245,7 @@ public static function validInts() ]; } - /** - * @dataProvider invalidInts - */ + #[DataProvider('invalidInts')] public function testGetEnvIntInvalid($value) { $processor = new EnvVarProcessor(new Container()); @@ -279,9 +269,7 @@ public static function invalidInts() ]; } - /** - * @dataProvider validFloats - */ + #[DataProvider('validFloats')] public function testGetEnvFloat($value, $processed) { $processor = new EnvVarProcessor(new Container()); @@ -304,9 +292,7 @@ public static function validFloats() ]; } - /** - * @dataProvider invalidFloats - */ + #[DataProvider('invalidFloats')] public function testGetEnvFloatInvalid($value) { $processor = new EnvVarProcessor(new Container()); @@ -330,9 +316,7 @@ public static function invalidFloats() ]; } - /** - * @dataProvider validConsts - */ + #[DataProvider('validConsts')] public function testGetEnvConst($value, $processed) { $processor = new EnvVarProcessor(new Container()); @@ -354,9 +338,7 @@ public static function validConsts() ]; } - /** - * @dataProvider invalidConsts - */ + #[DataProvider('invalidConsts')] public function testGetEnvConstInvalid($value) { $processor = new EnvVarProcessor(new Container()); @@ -411,9 +393,7 @@ public function testGetEnvTrim() $this->assertSame('hello', $result); } - /** - * @dataProvider validJson - */ + #[DataProvider('validJson')] public function testGetEnvJson($value, $processed) { $processor = new EnvVarProcessor(new Container()); @@ -450,9 +430,7 @@ public function testGetEnvInvalidJson() }); } - /** - * @dataProvider otherJsonValues - */ + #[DataProvider('otherJsonValues')] public function testGetEnvJsonOther($value) { $processor = new EnvVarProcessor(new Container()); @@ -504,9 +482,7 @@ public function testGetEnvKeyInvalidKey() }); } - /** - * @dataProvider noArrayValues - */ + #[DataProvider('noArrayValues')] public function testGetEnvKeyNoArrayResult($value) { $processor = new EnvVarProcessor(new Container()); @@ -531,9 +507,7 @@ public static function noArrayValues() ]; } - /** - * @dataProvider invalidArrayValues - */ + #[DataProvider('invalidArrayValues')] public function testGetEnvKeyArrayKeyNotFound($value) { $processor = new EnvVarProcessor(new Container()); @@ -557,9 +531,7 @@ public static function invalidArrayValues() ]; } - /** - * @dataProvider arrayValues - */ + #[DataProvider('arrayValues')] public function testGetEnvKey($value) { $processor = new EnvVarProcessor(new Container()); @@ -599,9 +571,7 @@ public function testGetEnvKeyChained() })); } - /** - * @dataProvider provideGetEnvEnum - */ + #[DataProvider('provideGetEnvEnum')] public function testGetEnvEnum(\BackedEnum $backedEnum) { $processor = new EnvVarProcessor(new Container()); @@ -665,9 +635,7 @@ public function testGetEnvEnumInvalidBackedValue() $processor->getEnv('enum', StringBackedEnum::class.':foo', fn () => 'bogus'); } - /** - * @dataProvider validNullables - */ + #[DataProvider('validNullables')] public function testGetEnvNullable($value, $processed) { $processor = new EnvVarProcessor(new Container()); @@ -715,9 +683,7 @@ public function testRequireFile() $this->assertEquals('foo', $result); } - /** - * @dataProvider validResolve - */ + #[DataProvider('validResolve')] public function testGetEnvResolve($value, $processed) { $container = new ContainerBuilder(); @@ -751,9 +717,7 @@ public function testGetEnvResolveNoMatch() $this->assertSame('%', $result); } - /** - * @dataProvider notScalarResolve - */ + #[DataProvider('notScalarResolve')] public function testGetEnvResolveNotScalar($value) { $container = new ContainerBuilder(); @@ -808,9 +772,7 @@ public function testGetEnvResolveNestedRealEnv() unset($_ENV['BAR']); } - /** - * @dataProvider validCsv - */ + #[DataProvider('validCsv')] public function testGetEnvCsv($value, $processed) { $processor = new EnvVarProcessor(new Container()); @@ -976,9 +938,7 @@ public function testGetEnvInvalidPrefixWithDefault() }); } - /** - * @dataProvider provideGetEnvUrlPath - */ + #[DataProvider('provideGetEnvUrlPath')] public function testGetEnvUrlPath(?string $expected, string $url) { $this->assertSame($expected, (new EnvVarProcessor(new Container()))->getEnv('url', 'foo', static fn (): string => $url)['path']); @@ -996,18 +956,16 @@ public static function provideGetEnvUrlPath() ]; } - /** - * @testWith ["http://foo.com\\bar"] - * ["\\\\foo.com/bar"] - * ["a\rb"] - * ["a\nb"] - * ["a\tb"] - * ["\u0000foo"] - * ["foo\u0000"] - * [" foo"] - * ["foo "] - * [":"] - */ + #[TestWith(['http://foo.com\\bar'])] + #[TestWith(['\\\\foo.com/bar'])] + #[TestWith(["a\rb"])] + #[TestWith(["a\nb"])] + #[TestWith(["a\tb"])] + #[TestWith(["\u0000foo"])] + #[TestWith(["foo\u0000"])] + #[TestWith([' foo'])] + #[TestWith(['foo '])] + #[TestWith([':'])] public function testGetEnvBadUrl(string $url) { $this->expectException(RuntimeException::class); @@ -1017,14 +975,12 @@ public function testGetEnvBadUrl(string $url) }); } - /** - * @testWith ["", "string"] - * [null, ""] - * [false, "bool"] - * [true, "not"] - * [0, "int"] - * [0.0, "float"] - */ + #[TestWith(['', 'string'])] + #[TestWith([null, ''])] + #[TestWith([false, 'bool'])] + #[TestWith([true, 'not'])] + #[TestWith([0, 'int'])] + #[TestWith([0.0, 'float'])] public function testGetEnvCastsNullBehavior($expected, string $prefix) { $processor = new EnvVarProcessor(new Container()); @@ -1049,9 +1005,7 @@ public function testGetEnvWithEmptyStringPrefixCastsToString() } } - /** - * @dataProvider provideGetEnvDefined - */ + #[DataProvider('provideGetEnvDefined')] public function testGetEnvDefined(bool $expected, callable $callback) { $this->assertSame($expected, (new EnvVarProcessor(new Container()))->getEnv('defined', 'NO_SOMETHING', $callback)); diff --git a/Tests/Exception/InvalidParameterTypeExceptionTest.php b/Tests/Exception/InvalidParameterTypeExceptionTest.php index ef88c71ca..3f3178d44 100644 --- a/Tests/Exception/InvalidParameterTypeExceptionTest.php +++ b/Tests/Exception/InvalidParameterTypeExceptionTest.php @@ -11,14 +11,13 @@ namespace Symfony\Component\DependencyInjection\Tests\Exception; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Exception\InvalidParameterTypeException; final class InvalidParameterTypeExceptionTest extends TestCase { - /** - * @dataProvider provideReflectionParameters - */ + #[DataProvider('provideReflectionParameters')] public function testExceptionMessage(\ReflectionParameter $parameter, string $expectedMessage) { $exception = new InvalidParameterTypeException('my_service', 'int', $parameter); diff --git a/Tests/Extension/ExtensionTest.php b/Tests/Extension/ExtensionTest.php index d2b5128c0..8a35ebb8f 100644 --- a/Tests/Extension/ExtensionTest.php +++ b/Tests/Extension/ExtensionTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Extension; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; @@ -23,9 +24,7 @@ class ExtensionTest extends TestCase { - /** - * @dataProvider getResolvedEnabledFixtures - */ + #[DataProvider('getResolvedEnabledFixtures')] public function testIsConfigEnabledReturnsTheResolvedValue($enabled) { $extension = new EnableableExtension(); diff --git a/Tests/LazyProxy/PhpDumper/LazyServiceDumperTest.php b/Tests/LazyProxy/PhpDumper/LazyServiceDumperTest.php index 1d5e9b6bf..ba5a0a5e8 100644 --- a/Tests/LazyProxy/PhpDumper/LazyServiceDumperTest.php +++ b/Tests/LazyProxy/PhpDumper/LazyServiceDumperTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests\LazyProxy\PhpDumper; +use PHPUnit\Framework\Attributes\RequiresPhp; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; @@ -65,9 +66,7 @@ public function testInvalidClass() $dumper->getProxyCode($definition); } - /** - * @requires PHP 8.3 - */ + #[RequiresPhp('8.3')] public function testReadonlyClass() { $dumper = new LazyServiceDumper(); diff --git a/Tests/Loader/Configurator/EnvConfiguratorTest.php b/Tests/Loader/Configurator/EnvConfiguratorTest.php index 75ddca1e6..fecc7b8e9 100644 --- a/Tests/Loader/Configurator/EnvConfiguratorTest.php +++ b/Tests/Loader/Configurator/EnvConfiguratorTest.php @@ -11,15 +11,14 @@ namespace Symfony\Component\DependencyInjection\Tests\Loader\Configurator; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Loader\Configurator\EnvConfigurator; use Symfony\Component\DependencyInjection\Tests\Fixtures\StringBackedEnum; final class EnvConfiguratorTest extends TestCase { - /** - * @dataProvider provide - */ + #[DataProvider('provide')] public function test(string $expected, EnvConfigurator $envConfigurator) { $this->assertSame($expected, (string) $envConfigurator); diff --git a/Tests/Loader/FileLoaderTest.php b/Tests/Loader/FileLoaderTest.php index 17bc228c7..9cff05941 100644 --- a/Tests/Loader/FileLoaderTest.php +++ b/Tests/Loader/FileLoaderTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\DependencyInjection\Tests\Loader; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\Loader\LoaderResolver; @@ -152,10 +154,8 @@ public function testRegisterClassesWithExclude() ); } - /** - * @testWith [true] - * [false] - */ + #[TestWith([true])] + #[TestWith([false])] public function testRegisterClassesWithExcludeAttribute(bool $autoconfigure) { $container = new ContainerBuilder(); @@ -260,9 +260,7 @@ public function testRegisterClassesWithIncompatibleExclude() ); } - /** - * @dataProvider excludeTrailingSlashConsistencyProvider - */ + #[DataProvider('excludeTrailingSlashConsistencyProvider')] public function testExcludeTrailingSlashConsistency(string $exclude, string $excludedId) { $container = new ContainerBuilder(); @@ -290,12 +288,10 @@ public static function excludeTrailingSlashConsistencyProvider(): iterable yield ['Prototype/OtherDir/AnotherSub/DeeperBaz.php', DeeperBaz::class]; } - /** - * @testWith ["prod", false] - * ["dev", false] - * ["bar", true] - * [null, false] - */ + #[TestWith(['prod', false])] + #[TestWith(['dev', false])] + #[TestWith(['bar', true])] + #[TestWith([null, false])] public function testRegisterClassesWithWhenEnv(?string $env, bool $expected) { $container = new ContainerBuilder(); @@ -309,9 +305,7 @@ public function testRegisterClassesWithWhenEnv(?string $env, bool $expected) $this->assertSame($expected, $container->getDefinition(Foo::class)->hasTag('container.excluded')); } - /** - * @dataProvider provideEnvAndExpectedExclusions - */ + #[DataProvider('provideEnvAndExpectedExclusions')] public function testRegisterWithNotWhenAttributes(string $env, bool $expectedNotFooExclusion) { $container = new ContainerBuilder(); @@ -350,9 +344,7 @@ public function testRegisterThrowsWithBothWhenAndNotWhenAttribute() ); } - /** - * @dataProvider provideResourcesWithAsAliasAttributes - */ + #[DataProvider('provideResourcesWithAsAliasAttributes')] public function testRegisterClassesWithAsAlias(string $resource, array $expectedAliases, ?string $env = null) { $container = new ContainerBuilder(); @@ -391,9 +383,7 @@ public static function provideResourcesWithAsAliasAttributes(): iterable yield 'Test-env specific' => ['PrototypeAsAlias/WithAsAlias*Env.php', [], 'test']; } - /** - * @dataProvider provideResourcesWithDuplicatedAsAliasAttributes - */ + #[DataProvider('provideResourcesWithDuplicatedAsAliasAttributes')] public function testRegisterClassesWithDuplicatedAsAlias(string $resource, string $expectedExceptionMessage) { $container = new ContainerBuilder(); diff --git a/Tests/Loader/IniFileLoaderTest.php b/Tests/Loader/IniFileLoaderTest.php index 1c757eea5..c3825fbb9 100644 --- a/Tests/Loader/IniFileLoaderTest.php +++ b/Tests/Loader/IniFileLoaderTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Loader; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -34,9 +35,7 @@ public function testIniFileCanBeLoaded() $this->assertEquals(['foo' => 'bar', 'bar' => '%foo%'], $this->container->getParameterBag()->all(), '->load() takes a single file name as its first argument'); } - /** - * @dataProvider getTypeConversions - */ + #[DataProvider('getTypeConversions')] public function testTypeConversions($key, $value, $supported) { $this->loader->load('types.ini'); @@ -45,9 +44,9 @@ public function testTypeConversions($key, $value, $supported) } /** - * @dataProvider getTypeConversions - * This test illustrates where our conversions differs from INI_SCANNER_TYPED introduced in PHP 5.6.1 + * This test illustrates where our conversions differs from INI_SCANNER_TYPED introduced in PHP 5.6.1. */ + #[DataProvider('getTypeConversions')] public function testTypeConversionsWithNativePhp($key, $value, $supported) { if (!$supported) { diff --git a/Tests/Loader/LoaderResolverTest.php b/Tests/Loader/LoaderResolverTest.php index 996cc5241..3b3622c8b 100644 --- a/Tests/Loader/LoaderResolverTest.php +++ b/Tests/Loader/LoaderResolverTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Loader; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\Loader\LoaderResolver; @@ -51,9 +52,7 @@ public static function provideResourcesToLoad() ]; } - /** - * @dataProvider provideResourcesToLoad - */ + #[DataProvider('provideResourcesToLoad')] public function testResolvesForcedType($resource, $type, $expectedClass) { $this->assertInstanceOf($expectedClass, $this->resolver->resolve($resource, $type)); diff --git a/Tests/Loader/PhpFileLoaderTest.php b/Tests/Loader/PhpFileLoaderTest.php index 9ca0104ea..c54f31f77 100644 --- a/Tests/Loader/PhpFileLoaderTest.php +++ b/Tests/Loader/PhpFileLoaderTest.php @@ -14,6 +14,7 @@ require_once __DIR__.'/../Fixtures/includes/AcmeExtension.php'; require_once __DIR__.'/../Fixtures/includes/fixture_app_services.php'; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Config\Builder\ConfigBuilderGenerator; use Symfony\Component\Config\FileLocator; @@ -104,9 +105,7 @@ public function testConfigServiceClosure() $this->assertStringEqualsFile($fixtures.'/php/services_closure_argument_compiled.php', $dumper->dump()); } - /** - * @dataProvider provideConfig - */ + #[DataProvider('provideConfig')] public function testConfig($file) { $fixtures = realpath(__DIR__.'/../Fixtures'); diff --git a/Tests/Loader/XmlFileLoaderTest.php b/Tests/Loader/XmlFileLoaderTest.php index f6ff7b8fe..0079ccfcf 100644 --- a/Tests/Loader/XmlFileLoaderTest.php +++ b/Tests/Loader/XmlFileLoaderTest.php @@ -11,8 +11,10 @@ namespace Symfony\Component\DependencyInjection\Tests\Loader; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\IgnoreDeprecations; +use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; use Symfony\Component\Config\Exception\LoaderLoadException; @@ -378,10 +380,8 @@ public function testParsesIteratorArgument() $this->assertEquals([new IteratorArgument(['k1' => new Reference('foo.baz'), 'k2' => new Reference('service_container')]), new IteratorArgument([])], $lazyDefinition->getArguments(), '->load() parses lazy arguments'); } - /** - * @testWith ["foo_tag"] - * ["bar_tag"] - */ + #[TestWith(['foo_tag'])] + #[TestWith(['bar_tag'])] public function testParsesTags(string $tag) { $container = new ContainerBuilder(); @@ -814,9 +814,7 @@ public function testPrototype() $this->assertContains('reflection.Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Sub\Bar', $resources); } - /** - * @dataProvider prototypeExcludeWithArrayDataProvider - */ + #[DataProvider('prototypeExcludeWithArrayDataProvider')] public function testPrototypeExcludeWithArray(string $fileName) { $container = new ContainerBuilder(); @@ -1218,9 +1216,7 @@ public function testClosure() $this->assertEquals((new Definition('Closure'))->setFactory(['Closure', 'fromCallable'])->addArgument(new Reference('bar')), $definition); } - /** - * @dataProvider dataForBindingsAndInnerCollections - */ + #[DataProvider('dataForBindingsAndInnerCollections')] public function testBindingsAndInnerCollections($bindName, $expected) { $container = new ContainerBuilder(); diff --git a/Tests/Loader/YamlFileLoaderTest.php b/Tests/Loader/YamlFileLoaderTest.php index d4ae212b5..bf142e59f 100644 --- a/Tests/Loader/YamlFileLoaderTest.php +++ b/Tests/Loader/YamlFileLoaderTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Loader; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; @@ -81,9 +82,7 @@ public function testLoadInvalidYamlFile() $m->invoke($loader, $path.'/parameters.ini'); } - /** - * @dataProvider provideInvalidFiles - */ + #[DataProvider('provideInvalidFiles')] public function testLoadInvalidFile($file) { $this->expectException(InvalidArgumentException::class); @@ -587,9 +586,7 @@ public function testPrototype() $this->assertContains('reflection.Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Sub\Bar', $resources); } - /** - * @dataProvider prototypeWithNullOrEmptyNodeDataProvider - */ + #[DataProvider('prototypeWithNullOrEmptyNodeDataProvider')] public function testPrototypeWithNullOrEmptyNode(string $fileName) { $this->expectException(InvalidArgumentException::class); diff --git a/Tests/ParameterBag/ParameterBagTest.php b/Tests/ParameterBag/ParameterBagTest.php index 38ef6fcd1..d0066737f 100644 --- a/Tests/ParameterBag/ParameterBagTest.php +++ b/Tests/ParameterBag/ParameterBagTest.php @@ -11,8 +11,10 @@ namespace Symfony\Component\DependencyInjection\Tests\ParameterBag; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\IgnoreDeprecations; +use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Exception\EmptyParameterValueException; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; @@ -82,10 +84,8 @@ public function testGetSet() } } - /** - * @testWith [1001] - * [10.0] - */ + #[TestWith([1001])] + #[TestWith([10.0])] public function testSetNumericName(int|float $name) { $bag = new ParameterBag(); @@ -96,10 +96,8 @@ public function testSetNumericName(int|float $name) $bag->set($name, 'foo'); } - /** - * @testWith [1001] - * [10.0] - */ + #[TestWith([1001])] + #[TestWith([10.0])] public function testConstructorNumericName(int|float $name) { $this->expectException(InvalidArgumentException::class); @@ -108,9 +106,7 @@ public function testConstructorNumericName(int|float $name) new ParameterBag([$name => 'foo']); } - /** - * @dataProvider provideGetThrowParameterNotFoundExceptionData - */ + #[DataProvider('provideGetThrowParameterNotFoundExceptionData')] public function testGetThrowParameterNotFoundException($parameterKey, $exceptionMessage) { $bag = new ParameterBag([ @@ -379,9 +375,7 @@ public function testEscapeValue() $this->assertEquals(['bar' => ['ding' => 'I\'m a bar %%foo %%bar', 'zero' => null]], $bag->get('foo'), '->escapeValue() escapes % by doubling it'); } - /** - * @dataProvider stringsWithSpacesProvider - */ + #[DataProvider('stringsWithSpacesProvider')] public function testResolveStringWithSpacesReturnsString($expected, $test, $description) { $bag = new ParameterBag(['foo' => 'bar']); From a55938a4680979a89e4fed344a1cd5a5d6550c25 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Tue, 24 Jun 2025 13:39:09 +0100 Subject: [PATCH 14/19] Remove some implicit bool type juggling --- Tests/AliasTest.php | 1 - Tests/DefinitionTest.php | 1 - 2 files changed, 2 deletions(-) diff --git a/Tests/AliasTest.php b/Tests/AliasTest.php index f4e2c4c99..acfffc8ef 100644 --- a/Tests/AliasTest.php +++ b/Tests/AliasTest.php @@ -87,7 +87,6 @@ public static function invalidDeprecationMessageProvider(): array "With \ns" => ["invalid \n message %alias_id%"], 'With */s' => ['invalid */ message %alias_id%'], 'message not containing required %alias_id% variable' => ['this is deprecated'], - 'template not containing required %alias_id% variable' => [true], ]; } } diff --git a/Tests/DefinitionTest.php b/Tests/DefinitionTest.php index 08ae1325a..e1f292d54 100644 --- a/Tests/DefinitionTest.php +++ b/Tests/DefinitionTest.php @@ -204,7 +204,6 @@ public static function invalidDeprecationMessageProvider(): array "With \ns" => ["invalid \n message %service_id%"], 'With */s' => ['invalid */ message %service_id%'], 'message not containing require %service_id% variable' => ['this is deprecated'], - 'template not containing require %service_id% variable' => [true], ]; } From fb0cc16c8c11b41b1f63285035da1f47ea43c1f6 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Sun, 10 Aug 2025 00:28:14 +0200 Subject: [PATCH 15/19] chore: heredoc indentation as of PHP 7.3 https://www.php.net/manual/en/language.types.string.php#language.types.string.syntax.heredoc --- Dumper/PhpDumper.php | 392 +++++++++++----------- LazyProxy/PhpDumper/LazyServiceDumper.php | 32 +- Loader/XmlFileLoader.php | 20 +- Tests/Dumper/PhpDumperTest.php | 124 +++---- Tests/EnvVarProcessorTest.php | 4 +- 5 files changed, 286 insertions(+), 286 deletions(-) diff --git a/Dumper/PhpDumper.php b/Dumper/PhpDumper.php index 7c7c85358..665b60817 100644 --- a/Dumper/PhpDumper.php +++ b/Dumper/PhpDumper.php @@ -240,19 +240,19 @@ public function dump(array $options = []): string|array if ($this->asFiles) { $fileTemplate = <<docStar} - * @internal This class has been auto-generated by the Symfony Dependency Injection Component. - */ -class %s extends {$options['class']} -{%s} + /*{$this->docStar} + * @internal This class has been auto-generated by the Symfony Dependency Injection Component. + */ + class %s extends {$options['class']} + {%s} -EOF; + EOF; $files = []; $preloadedFiles = []; $ids = $this->container->getRemovedIds(); @@ -319,23 +319,23 @@ class %s extends {$options['class']} } $code[$options['class'].'.preload.php'] = <<= 7.4 when preloading is desired + // This file has been auto-generated by the Symfony Dependency Injection Component + // You can reference it in the "opcache.preload" php.ini setting on PHP >= 7.4 when preloading is desired -use Symfony\Component\DependencyInjection\Dumper\Preloader; + use Symfony\Component\DependencyInjection\Dumper\Preloader; -if (in_array(PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) { - return; -} + if (in_array(PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) { + return; + } -require $autoloadFile; -(require __DIR__.'/{$options['class']}.php')->set(\\Container{$hash}\\{$options['class']}::class, null); -$preloadedFiles -\$classes = []; + require $autoloadFile; + (require __DIR__.'/{$options['class']}.php')->set(\\Container{$hash}\\{$options['class']}::class, null); + $preloadedFiles + \$classes = []; -EOF; + EOF; foreach ($this->preload as $class) { if (!$class || str_contains($class, '$') || \in_array($class, ['int', 'float', 'string', 'bool', 'resource', 'object', 'array', 'null', 'callable', 'iterable', 'mixed', 'void', 'never'], true)) { @@ -348,36 +348,36 @@ class %s extends {$options['class']} $code[$options['class'].'.preload.php'] .= <<<'EOF' -$preloaded = Preloader::preload($classes); + $preloaded = Preloader::preload($classes); -EOF; + EOF; } $code[$options['class'].'.php'] = << '$hash', - 'container.build_id' => '$id', - 'container.build_time' => $time, - 'container.runtime_mode' => \\in_array(\\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true) ? 'web=0' : 'web=1', -], __DIR__.\\DIRECTORY_SEPARATOR.'Container{$hash}'); + return new \\Container{$hash}\\{$options['class']}([ + 'container.build_hash' => '$hash', + 'container.build_id' => '$id', + 'container.build_time' => $time, + 'container.runtime_mode' => \\in_array(\\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true) ? 'web=0' : 'web=1', + ], __DIR__.\\DIRECTORY_SEPARATOR.'Container{$hash}'); -EOF; + EOF; } else { $code .= $this->endClass(); foreach ($proxyClasses as $c) { @@ -868,18 +868,18 @@ private function addService(string $id, Definition $definition): array $code = <<docStar} - * Gets the $public '$id'$shared$autowired service. - * - * $return -EOF; + /*{$this->docStar} + * Gets the $public '$id'$shared$autowired service. + * + * $return + EOF; $code = str_replace('*/', ' ', $code).<<%s[%s])) { - return $container->%1$s[%2$s]; - } + if (isset($container->%s[%s])) { + return $container->%1$s[%2$s]; + } -EOTXT + EOTXT , $this->container->getDefinition($id)->isPublic() ? 'services' : 'privates', $this->doExport($id) @@ -1237,32 +1237,32 @@ private function startClass(string $class, string $baseClass, bool $hasProxyClas $namespaceLine = !$this->asFiles && $this->namespace ? "\nnamespace {$this->namespace};\n" : ''; $code = <<docStar} - * @internal This class has been auto-generated by the Symfony Dependency Injection Component. - */ -class $class extends $baseClass -{ - private const DEPRECATED_PARAMETERS = []; - - private const NONEMPTY_PARAMETERS = []; - - protected \$parameters = []; - - public function __construct() - { - -EOF; + docStar} + * @internal This class has been auto-generated by the Symfony Dependency Injection Component. + */ + class $class extends $baseClass + { + private const DEPRECATED_PARAMETERS = []; + + private const NONEMPTY_PARAMETERS = []; + + protected \$parameters = []; + + public function __construct() + { + + EOF; $code = str_replace(" private const DEPRECATED_PARAMETERS = [];\n\n", $this->addDeprecatedParameters(), $code); $code = str_replace(" private const NONEMPTY_PARAMETERS = [];\n\n", $this->addNonEmptyParameters(), $code); @@ -1298,42 +1298,42 @@ public function __construct() $code .= $this->addAliases(); $code .= $this->addInlineRequires($hasProxyClasses); $code .= <<addRemovedIds(); if ($this->asFiles && !$this->inlineFactories) { $code .= <<<'EOF' - protected function load($file, $lazyLoad = true): mixed - { - if (class_exists($class = __NAMESPACE__.'\\'.$file, false)) { - return $class::do($this, $lazyLoad); - } + protected function load($file, $lazyLoad = true): mixed + { + if (class_exists($class = __NAMESPACE__.'\\'.$file, false)) { + return $class::do($this, $lazyLoad); + } - if ('.' === $file[-4]) { - $class = substr($class, 0, -4); - } else { - $file .= '.php'; - } + if ('.' === $file[-4]) { + $class = substr($class, 0, -4); + } else { + $file .= '.php'; + } - $service = require $this->containerDir.\DIRECTORY_SEPARATOR.$file; + $service = require $this->containerDir.\DIRECTORY_SEPARATOR.$file; - return class_exists($class, false) ? $class::do($this, $lazyLoad) : $service; - } + return class_exists($class, false) ? $class::do($this, $lazyLoad) : $service; + } -EOF; + EOF; } foreach ($this->container->getDefinitions() as $definition) { @@ -1349,12 +1349,12 @@ protected function load($file, $lazyLoad = true): mixed $code .= <<export($deprecation['message']); $code .= <<docStar} - * Gets the $public '$alias' alias. - * - * @return object The "$id" service. - */ - protected static function {$methodNameAlias}(\$container) - { - trigger_deprecation($packageExported, $versionExported, $messageExported); + /*{$this->docStar} + * Gets the $public '$alias' alias. + * + * @return object The "$id" service. + */ + protected static function {$methodNameAlias}(\$container) + { + trigger_deprecation($packageExported, $versionExported, $messageExported); - return \$container->get($idExported); - } + return \$container->get($idExported); + } -EOF; + EOF; } return $code; @@ -1610,64 +1610,64 @@ private function addDefaultParametersMethod(): string $code = <<<'EOF' - public function getParameter(string $name): array|bool|string|int|float|\UnitEnum|null - { - if (isset(self::DEPRECATED_PARAMETERS[$name])) { - trigger_deprecation(...self::DEPRECATED_PARAMETERS[$name]); - } + public function getParameter(string $name): array|bool|string|int|float|\UnitEnum|null + { + if (isset(self::DEPRECATED_PARAMETERS[$name])) { + trigger_deprecation(...self::DEPRECATED_PARAMETERS[$name]); + } - if (isset($this->buildParameters[$name])) { - return $this->buildParameters[$name]; - } + if (isset($this->buildParameters[$name])) { + return $this->buildParameters[$name]; + } - if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) { - throw new ParameterNotFoundException($name, extraMessage: self::NONEMPTY_PARAMETERS[$name] ?? null); - } + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) { + throw new ParameterNotFoundException($name, extraMessage: self::NONEMPTY_PARAMETERS[$name] ?? null); + } - if (isset($this->loadedDynamicParameters[$name])) { - $value = $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); - } else { - $value = $this->parameters[$name]; - } + if (isset($this->loadedDynamicParameters[$name])) { + $value = $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } else { + $value = $this->parameters[$name]; + } - if (isset(self::NONEMPTY_PARAMETERS[$name]) && (null === $value || '' === $value || [] === $value)) { - throw new \Symfony\Component\DependencyInjection\Exception\EmptyParameterValueException(self::NONEMPTY_PARAMETERS[$name]); - } + if (isset(self::NONEMPTY_PARAMETERS[$name]) && (null === $value || '' === $value || [] === $value)) { + throw new \Symfony\Component\DependencyInjection\Exception\EmptyParameterValueException(self::NONEMPTY_PARAMETERS[$name]); + } - return $value; - } + return $value; + } - public function hasParameter(string $name): bool - { - if (isset($this->buildParameters[$name])) { - return true; - } + public function hasParameter(string $name): bool + { + if (isset($this->buildParameters[$name])) { + return true; + } - return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters); - } + return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters); + } - public function setParameter(string $name, $value): void - { - throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); - } + public function setParameter(string $name, $value): void + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } - public function getParameterBag(): ParameterBagInterface - { - if (!isset($this->parameterBag)) { - $parameters = $this->parameters; - foreach ($this->loadedDynamicParameters as $name => $loaded) { - $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); - } - foreach ($this->buildParameters as $name => $value) { - $parameters[$name] = $value; - } - $this->parameterBag = new FrozenParameterBag($parameters, self::DEPRECATED_PARAMETERS, self::NONEMPTY_PARAMETERS); - } + public function getParameterBag(): ParameterBagInterface + { + if (!isset($this->parameterBag)) { + $parameters = $this->parameters; + foreach ($this->loadedDynamicParameters as $name => $loaded) { + $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + foreach ($this->buildParameters as $name => $value) { + $parameters[$name] = $value; + } + $this->parameterBag = new FrozenParameterBag($parameters, self::DEPRECATED_PARAMETERS, self::NONEMPTY_PARAMETERS); + } - return $this->parameterBag; - } + return $this->parameterBag; + } -EOF; + EOF; if (!$this->asFiles) { $code = preg_replace('/^.*buildParameters.*\n.*\n.*\n\n?/m', '', $code); @@ -1687,15 +1687,15 @@ public function getParameterBag(): ParameterBagInterface if ($dynamicPhp) { $loadedDynamicParameters = $this->exportParameters(array_combine(array_keys($dynamicPhp), array_fill(0, \count($dynamicPhp), false)), '', 8); $getDynamicParameter = <<<'EOF' - $container = $this; - $value = match ($name) { -%s - default => throw new ParameterNotFoundException($name), - }; - $this->loadedDynamicParameters[$name] = true; - - return $this->dynamicParameters[$name] = $value; -EOF; + $container = $this; + $value = match ($name) { + %s + default => throw new ParameterNotFoundException($name), + }; + $this->loadedDynamicParameters[$name] = true; + + return $this->dynamicParameters[$name] = $value; + EOF; $getDynamicParameter = \sprintf($getDynamicParameter, implode("\n", $dynamicPhp)); } else { $loadedDynamicParameters = '[]'; @@ -1704,20 +1704,20 @@ public function getParameterBag(): ParameterBagInterface return $code.<<getClass() === $proxyClass) { return <<newLazyProxy(static fn () => $factoryCode); - } + if (true === \$lazyLoad) { + $instantiation new \ReflectionClass('$proxyClass')->newLazyProxy(static fn () => $factoryCode); + } - EOF; + EOF; } return <<createProxy('$proxyClass', static fn () => \\$proxyClass::createLazyProxy(static fn () => $factoryCode)); - } + if (true === \$lazyLoad) { + $instantiation \$container->createProxy('$proxyClass', static fn () => \\$proxyClass::createLazyProxy(static fn () => $factoryCode)); + } - EOF; + EOF; } if (\PHP_VERSION_ID < 80400) { $factoryCode = \sprintf('static fn ($proxy) => %s', $factoryCode); return <<createProxy('$proxyClass', static fn () => \\$proxyClass::createLazyGhost($factoryCode)); - } + if (true === \$lazyLoad) { + $instantiation \$container->createProxy('$proxyClass', static fn () => \\$proxyClass::createLazyGhost($factoryCode)); + } - EOF; + EOF; } $factoryCode = \sprintf('static function ($proxy) use ($container) { %s; }', $factoryCode); return <<newLazyGhost($factoryCode); - } + if (true === \$lazyLoad) { + $instantiation new \ReflectionClass('$proxyClass')->newLazyGhost($factoryCode); + } - EOF; + EOF; } public function getProxyCode(Definition $definition, ?string $id = null): string diff --git a/Loader/XmlFileLoader.php b/Loader/XmlFileLoader.php index 069d16d4f..1b3f73b84 100644 --- a/Loader/XmlFileLoader.php +++ b/Loader/XmlFileLoader.php @@ -773,16 +773,16 @@ public function validateSchema(\DOMDocument $dom): bool } $source = << - - - -$imports - -EOF + + + + + $imports + + EOF ; if ($this->shouldEnableEntityLoader()) { diff --git a/Tests/Dumper/PhpDumperTest.php b/Tests/Dumper/PhpDumperTest.php index 337bf2664..aceba6d71 100644 --- a/Tests/Dumper/PhpDumperTest.php +++ b/Tests/Dumper/PhpDumperTest.php @@ -1421,25 +1421,25 @@ public function testDumpHandlesEnumeration() $this->assertSame(FooUnitEnum::BAR, $container->getParameter('unit_enum')); $this->assertSame([FooUnitEnum::BAR, FooUnitEnum::FOO], $container->getParameter('enum_array')); $this->assertStringMatchesFormat(<<<'PHP' -%A - protected static function getBarService($container) - { - return $container->services['bar'] = new \stdClass(\Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum::BAR, $container->getParameter('enum_array')); - } -%A - private function getDynamicParameter(string $name) - { - $container = $this; - $value = match ($name) { - 'unit_enum' => \Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum::BAR, - 'enum_array' => [ - 0 => \Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum::BAR, - 1 => \Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum::FOO, - ], - default => throw new ParameterNotFoundException($name), - }; -%A -PHP + %A + protected static function getBarService($container) + { + return $container->services['bar'] = new \stdClass(\Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum::BAR, $container->getParameter('enum_array')); + } + %A + private function getDynamicParameter(string $name) + { + $container = $this; + $value = match ($name) { + 'unit_enum' => \Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum::BAR, + 'enum_array' => [ + 0 => \Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum::BAR, + 1 => \Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum::FOO, + ], + default => throw new ParameterNotFoundException($name), + }; + %A + PHP , $dumpedContainer ); } @@ -2140,74 +2140,74 @@ public static function getStripCommentsCodes(): array [' Date: Tue, 12 Aug 2025 15:30:12 +0200 Subject: [PATCH 16/19] Fix tests --- Dumper/XmlDumper.php | 2 +- Tests/CrossCheckTest.php | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Dumper/XmlDumper.php b/Dumper/XmlDumper.php index 2036b3231..ca579b1e5 100644 --- a/Dumper/XmlDumper.php +++ b/Dumper/XmlDumper.php @@ -403,7 +403,7 @@ private function convertParameters(array $parameters, string $type, string $keyA private function encode(string $value, int $flags = \ENT_COMPAT): string { - return htmlspecialchars($value, \ENT_XML1 | \ENT_SUBSTITUTE | $flags, 'UTF-8'); + return str_replace("\r", ' ', htmlspecialchars($value, \ENT_XML1 | \ENT_SUBSTITUTE | $flags, 'UTF-8')); } private function escape(array $arguments): array diff --git a/Tests/CrossCheckTest.php b/Tests/CrossCheckTest.php index b172a3105..f4df7e249 100644 --- a/Tests/CrossCheckTest.php +++ b/Tests/CrossCheckTest.php @@ -51,10 +51,10 @@ public function testCrossCheck($fixture, $type) unlink($tmp); - $this->assertEquals($container2->getAliases(), $container1->getAliases(), 'loading a dump from a previously loaded container returns the same container'); - $this->assertEquals($container2->getDefinitions(), $container1->getDefinitions(), 'loading a dump from a previously loaded container returns the same container'); - $this->assertEquals($container2->getParameterBag()->all(), $container1->getParameterBag()->all(), '->getParameterBag() returns the same value for both containers'); - $this->assertEquals(serialize($container2), serialize($container1), 'loading a dump from a previously loaded container returns the same container'); + $this->assertEquals($container1->getAliases(), $container2->getAliases(), 'loading a dump from a previously loaded container returns the same container'); + $this->assertEquals($container1->getDefinitions(), $container2->getDefinitions(), 'loading a dump from a previously loaded container returns the same container'); + $this->assertEquals($container1->getParameterBag()->all(), $container2->getParameterBag()->all(), '->getParameterBag() returns the same value for both containers'); + $this->assertEquals(serialize($container1), serialize($container2), 'loading a dump from a previously loaded container returns the same container'); $services1 = []; foreach ($container1 as $id => $service) { @@ -67,7 +67,7 @@ public function testCrossCheck($fixture, $type) unset($services1['service_container'], $services2['service_container']); - $this->assertEquals($services2, $services1, 'Iterator on the containers returns the same services'); + $this->assertEquals($services1, $services2, 'Iterator on the containers returns the same services'); } public static function crossCheckLoadersDumpers() From 5265d527ff26dbcdbb715e0029507a0b335843e3 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Sat, 9 Aug 2025 23:58:04 +0200 Subject: [PATCH 17/19] chore: PHP CS Fixer - restore PHP / PHPUnit rulesets --- Tests/Loader/IniFileLoaderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Loader/IniFileLoaderTest.php b/Tests/Loader/IniFileLoaderTest.php index c3825fbb9..a02d72665 100644 --- a/Tests/Loader/IniFileLoaderTest.php +++ b/Tests/Loader/IniFileLoaderTest.php @@ -81,7 +81,7 @@ public static function getTypeConversions() ['zero', 0, true], ['0b0110_byte_string', bindec('0b0110'), false], // not supported by INI_SCANNER_TYPED ['11112222333344445555_great_number', '1111,2222,3333,4444,5555', true], - ['0777_number_starting_with_0', 0777, false], // not supported by INI_SCANNER_TYPED + ['0777_number_starting_with_0', 0o777, false], // not supported by INI_SCANNER_TYPED ['255_hexadecimal', 0xFF, false], // not supported by INI_SCANNER_TYPED ['100.0_exponential', 1e2, false], // not supported by INI_SCANNER_TYPED ['-120.0_exponential', -1.2E2, false], // not supported by INI_SCANNER_TYPED From e5e2747a1591cfa14acdc238a1794ef481d5b3b8 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Sun, 10 Aug 2025 00:12:49 +0200 Subject: [PATCH 18/19] chore: PHP CS Fixer - update heredoc handling --- Dumper/PhpDumper.php | 3 +- Tests/Dumper/PhpDumperTest.php | 53 +++++++++++++++++----------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/Dumper/PhpDumper.php b/Dumper/PhpDumper.php index 665b60817..a18049085 100644 --- a/Dumper/PhpDumper.php +++ b/Dumper/PhpDumper.php @@ -1026,8 +1026,7 @@ private function addInlineReference(string $id, Definition $definition, string $ return $container->%1$s[%2$s]; } - EOTXT - , + EOTXT, $this->container->getDefinition($id)->isPublic() ? 'services' : 'privates', $this->doExport($id) ); diff --git a/Tests/Dumper/PhpDumperTest.php b/Tests/Dumper/PhpDumperTest.php index aceba6d71..ec29d5612 100644 --- a/Tests/Dumper/PhpDumperTest.php +++ b/Tests/Dumper/PhpDumperTest.php @@ -1439,8 +1439,8 @@ private function getDynamicParameter(string $name) default => throw new ParameterNotFoundException($name), }; %A - PHP - , $dumpedContainer + PHP, + $dumpedContainer ); } @@ -2139,48 +2139,49 @@ public static function getStripCommentsCodes(): array [' Date: Thu, 14 Aug 2025 09:36:33 +0200 Subject: [PATCH 19/19] Replace __sleep/wakeup() by __(un)serialize() when BC isn't a concern --- Loader/Configurator/AbstractConfigurator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Loader/Configurator/AbstractConfigurator.php b/Loader/Configurator/AbstractConfigurator.php index 524667e68..eea329e94 100644 --- a/Loader/Configurator/AbstractConfigurator.php +++ b/Loader/Configurator/AbstractConfigurator.php @@ -43,12 +43,12 @@ public function __call(string $method, array $args): mixed throw new \BadMethodCallException(\sprintf('Call to undefined method "%s::%s()".', static::class, $method)); } - public function __sleep(): array + public function __serialize(): array { throw new \BadMethodCallException('Cannot serialize '.__CLASS__); } - public function __wakeup(): void + public function __unserialize(array $data): void { throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); }