diff --git a/Alias.php b/Alias.php index 0ec1161f8..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; } /** @@ -103,4 +105,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/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..6fa6bf4f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ CHANGELOG ========= +7.4 +--- + + * 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/AutowirePass.php b/Compiler/AutowirePass.php index ef5642c3a..4c7cda611 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->canDefinitionBeAutowired($alias)) { + if (null !== ($alias = $this->getCombinedAlias($type, $name, $target)) && $this->canDefinitionBeAutowired($alias)) { return new TypedReference($alias, $type, $reference->getInvalidBehavior()); } - if (null !== ($alias = $this->getCombinedAlias($type, $name)) && $this->canDefinitionBeAutowired($alias)) { - return new TypedReference($alias, $type, $reference->getInvalidBehavior()); - } - - $parsedName = (new Target($name))->getParsedName(); - - if ($this->container->has($alias = $type.' $'.$parsedName) && $this->canDefinitionBeAutowired($alias)) { - return new TypedReference($alias, $type, $reference->getInvalidBehavior()); - } - - if (null !== ($alias = $this->getCombinedAlias($type, $parsedName)) && $this->canDefinitionBeAutowired($alias)) { - return new TypedReference($alias, $type, $reference->getInvalidBehavior()); - } - - if (($this->container->has($n = $name) && $this->canDefinitionBeAutowired($n)) - || ($this->container->has($n = $parsedName) && $this->canDefinitionBeAutowired($n)) - ) { + if ($this->container->has($name) && $this->canDefinitionBeAutowired($name)) { 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->canDefinitionBeAutowired($type)) { - return new TypedReference($type, $type, $reference->getInvalidBehavior()); - } - if (null !== ($alias = $this->getCombinedAlias($type)) && $this->canDefinitionBeAutowired($alias)) { return new TypedReference($alias, $type, $reference->getInvalidBehavior()); } @@ -686,7 +666,7 @@ private function getAliasesSuggestionForType(ContainerBuilder $container, string { $aliases = []; foreach (class_parents($type) + class_implements($type) as $parent) { - if ($container->has($parent) && !$container->findDefinition($parent)->isAbstract()) { + if ($container->has($parent) && $this->canDefinitionBeAutowired($parent)) { $aliases[] = $parent; } } @@ -717,38 +697,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->canDefinitionBeAutowired($alias)) { + 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/Compiler/ResolveClassPass.php b/Compiler/ResolveClassPass.php index 4b7a2bb40..952b02441 100644 --- a/Compiler/ResolveClassPass.php +++ b/Compiler/ResolveClassPass.php @@ -23,15 +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 ($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, 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/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/ContainerBuilder.php b/ContainerBuilder.php index 4d1765154..5a0931930 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/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/Dumper/PhpDumper.php b/Dumper/PhpDumper.php index 90050ef97..1b2f3dd41 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 +1236,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 +1297,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 +1348,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 +1609,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 +1686,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 +1703,20 @@ public function getParameterBag(): ParameterBagInterface return $code.<<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 str_replace("\r", ' ', 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, + }; } } 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/LazyProxy/PhpDumper/LazyServiceDumper.php b/LazyProxy/PhpDumper/LazyServiceDumper.php index c534630e3..6563c0258 100644 --- a/LazyProxy/PhpDumper/LazyServiceDumper.php +++ b/LazyProxy/PhpDumper/LazyServiceDumper.php @@ -90,44 +90,44 @@ public function getProxyFactoryCode(Definition $definition, string $id, string $ if (!$asGhostObject) { if ($definition->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/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__); } 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/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/Loader/XmlFileLoader.php b/Loader/XmlFileLoader.php index eed874cd9..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()) { @@ -827,7 +827,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; } 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/AliasTest.php b/Tests/AliasTest.php index b1c8a4dbd..acfffc8ef 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'); @@ -88,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/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 d6bbbc70f..458cc91e4 100644 --- a/Tests/Compiler/AutowirePassTest.php +++ b/Tests/Compiler/AutowirePassTest.php @@ -11,6 +11,9 @@ 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; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; @@ -400,9 +403,8 @@ public function testResolveParameter() $this->assertEquals(Foo::class, $container->getDefinition('bar')->getArgument(0)); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testOptionalParameter() { $container = new ContainerBuilder(); @@ -874,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(); @@ -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/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/RegisterServiceSubscribersPassTest.php b/Tests/Compiler/RegisterServiceSubscribersPassTest.php index e7b36d3ce..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; @@ -452,15 +454,14 @@ 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)); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testSubscribedServiceWithLegacyAttributes() { $container = new ContainerBuilder(); diff --git a/Tests/Compiler/ResolveClassPassTest.php b/Tests/Compiler/ResolveClassPassTest.php index 914115f28..43760f0d9 100644 --- a/Tests/Compiler/ResolveClassPassTest.php +++ b/Tests/Compiler/ResolveClassPassTest.php @@ -11,7 +11,11 @@ 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; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -20,9 +24,9 @@ class ResolveClassPassTest extends TestCase { - /** - * @dataProvider provideValidClassId - */ + use ExpectDeprecationTrait; + + #[DataProvider('provideValidClassId')] public function testResolveClassFromId($serviceId) { $container = new ContainerBuilder(); @@ -35,13 +39,10 @@ public function testResolveClassFromId($serviceId) public static function provideValidClassId() { - yield ['Acme\UnknownClass']; yield [CaseSensitiveClass::class]; } - /** - * @dataProvider provideInvalidClassId - */ + #[DataProvider('provideInvalidClassId')] public function testWontResolveClassFromId($serviceId) { $container = new ContainerBuilder(); @@ -62,7 +63,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 +75,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 +87,24 @@ 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); } + + #[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->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'); + + (new ResolveClassPass())->process($container); + } } 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/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/Compiler/ServiceLocatorTagPassTest.php b/Tests/Compiler/ServiceLocatorTagPassTest.php index 9a9306775..ad9c62d9b 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 testServiceListIsOrdered() 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 dc33ddc41..a24900875 100644 --- a/Tests/ContainerBuilderTest.php +++ b/Tests/ContainerBuilderTest.php @@ -15,8 +15,10 @@ 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; -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 +67,6 @@ class ContainerBuilderTest extends TestCase { - use ExpectUserDeprecationMessageTrait; - public function testDefaultRegisteredDefinitions() { $builder = new ContainerBuilder(); @@ -108,10 +108,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 +125,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(); @@ -313,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); @@ -856,9 +852,8 @@ public function testMergeAttributeAutoconfiguration() $this->assertSame([AsTaggedItem::class => [$c1, $c2]], $container->getAttributeAutoconfigurators()); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testGetAutoconfiguredAttributes() { $container = new ContainerBuilder(); @@ -1547,11 +1542,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()); } @@ -1702,9 +1695,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'; @@ -1780,6 +1771,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() @@ -2010,10 +2005,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/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..f4df7e249 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'; @@ -52,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) { @@ -68,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() diff --git a/Tests/DefinitionTest.php b/Tests/DefinitionTest.php index 459e566d2..e1f292d54 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'); @@ -205,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], ]; } diff --git a/Tests/Dumper/PhpDumperTest.php b/Tests/Dumper/PhpDumperTest.php index f9302e818..ec29d5612 100644 --- a/Tests/Dumper/PhpDumperTest.php +++ b/Tests/Dumper/PhpDumperTest.php @@ -11,9 +11,13 @@ 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\Bridge\PhpUnit\ExpectUserDeprecationMessageTrait; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\Argument\AbstractArgument; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; @@ -75,11 +79,10 @@ 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 { - use ExpectUserDeprecationMessageTrait; - protected static string $fixturesPath; public static function setUpBeforeClass(): void @@ -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); @@ -479,10 +480,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'; @@ -496,10 +497,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'; @@ -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'; @@ -1317,7 +1314,7 @@ public function testInlineSelfRef() ->setProperty('bar', $bar) ->addArgument($bar); - $container->register('App\Foo') + $container->register('App\Foo', 'App\Foo') ->setPublic(true) ->addArgument($baz); @@ -1424,26 +1421,26 @@ 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 - , $dumpedContainer + %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 ); } @@ -1800,10 +1797,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.'); @@ -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); @@ -2144,75 +2139,76 @@ public static function getStripCommentsCodes(): array ['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..8a6f7093c 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()); @@ -844,8 +806,8 @@ public function testGetEnvShuffleInvalid() public static function validCsv() { $complex = <<<'CSV' -,"""","foo""","\""",\,foo\ -CSV; + ,"""","foo""","\""",\,foo\ + CSV; return [ ['', []], @@ -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/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 @@ +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/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 0ad1b363c..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; @@ -45,6 +47,7 @@ use Symfony\Component\DependencyInjection\Tests\Fixtures\PrototypeAsAlias\WithAsAliasInterface; use Symfony\Component\DependencyInjection\Tests\Fixtures\PrototypeAsAlias\WithAsAliasMultiple; use Symfony\Component\DependencyInjection\Tests\Fixtures\PrototypeAsAlias\WithAsAliasProdEnv; +use Symfony\Component\DependencyInjection\Tests\Fixtures\PrototypeAsAlias\WithCustomAsAlias; use Symfony\Component\DependencyInjection\Tests\Fixtures\Utils\NotAService; class FileLoaderTest extends TestCase @@ -151,10 +154,8 @@ public function testRegisterClassesWithExclude() ); } - /** - * @testWith [true] - * [false] - */ + #[TestWith([true])] + #[TestWith([false])] public function testRegisterClassesWithExcludeAttribute(bool $autoconfigure) { $container = new ContainerBuilder(); @@ -259,9 +260,7 @@ public function testRegisterClassesWithIncompatibleExclude() ); } - /** - * @dataProvider excludeTrailingSlashConsistencyProvider - */ + #[DataProvider('excludeTrailingSlashConsistencyProvider')] public function testExcludeTrailingSlashConsistency(string $exclude, string $excludedId) { $container = new ContainerBuilder(); @@ -289,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(); @@ -308,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(); @@ -349,9 +344,7 @@ public function testRegisterThrowsWithBothWhenAndNotWhenAttribute() ); } - /** - * @dataProvider provideResourcesWithAsAliasAttributes - */ + #[DataProvider('provideResourcesWithAsAliasAttributes')] public function testRegisterClassesWithAsAlias(string $resource, array $expectedAliases, ?string $env = null) { $container = new ContainerBuilder(); @@ -368,6 +361,8 @@ public function testRegisterClassesWithAsAlias(string $resource, array $expected public static function provideResourcesWithAsAliasAttributes(): iterable { yield 'Private' => ['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), @@ -388,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..a02d72665 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) { @@ -82,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 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 72ededfd0..c54f31f77 100644 --- a/Tests/Loader/PhpFileLoaderTest.php +++ b/Tests/Loader/PhpFileLoaderTest.php @@ -12,7 +12,9 @@ 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\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Config\Builder\ConfigBuilderGenerator; use Symfony\Component\Config\FileLocator; @@ -103,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 f962fa106..0079ccfcf 100644 --- a/Tests/Loader/XmlFileLoaderTest.php +++ b/Tests/Loader/XmlFileLoaderTest.php @@ -11,8 +11,11 @@ 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\Bridge\PhpUnit\ExpectUserDeprecationMessageTrait; use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; use Symfony\Component\Config\Exception\LoaderLoadException; use Symfony\Component\Config\FileLocator; @@ -52,8 +55,6 @@ class XmlFileLoaderTest extends TestCase { - use ExpectUserDeprecationMessageTrait; - protected static string $fixturesPath; public static function setUpBeforeClass(): void @@ -379,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(); @@ -815,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(); @@ -1219,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(); @@ -1335,9 +1330,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..bf142e59f 100644 --- a/Tests/Loader/YamlFileLoaderTest.php +++ b/Tests/Loader/YamlFileLoaderTest.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\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 +50,6 @@ class YamlFileLoaderTest extends TestCase { - use ExpectUserDeprecationMessageTrait; - protected static string $fixturesPath; public static function setUpBeforeClass(): void @@ -82,9 +82,7 @@ public function testLoadInvalidYamlFile() $m->invoke($loader, $path.'/parameters.ini'); } - /** - * @dataProvider provideInvalidFiles - */ + #[DataProvider('provideInvalidFiles')] public function testLoadInvalidFile($file) { $this->expectException(InvalidArgumentException::class); @@ -588,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); @@ -1212,9 +1208,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..d0066737f 100644 --- a/Tests/ParameterBag/ParameterBagTest.php +++ b/Tests/ParameterBag/ParameterBagTest.php @@ -11,8 +11,11 @@ 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\Bridge\PhpUnit\ExpectUserDeprecationMessageTrait; use Symfony\Component\DependencyInjection\Exception\EmptyParameterValueException; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException; @@ -22,8 +25,6 @@ class ParameterBagTest extends TestCase { - use ExpectUserDeprecationMessageTrait; - public function testConstructor() { $bag = new ParameterBag($parameters = [ @@ -83,10 +84,8 @@ public function testGetSet() } } - /** - * @testWith [1001] - * [10.0] - */ + #[TestWith([1001])] + #[TestWith([10.0])] public function testSetNumericName(int|float $name) { $bag = new ParameterBag(); @@ -97,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); @@ -109,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([ @@ -140,10 +135,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 +151,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 +167,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']); @@ -380,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']); diff --git a/composer.json b/composer.json index 460751088..0a6963357 100644 --- a/composer.json +++ b/composer.json @@ -19,13 +19,13 @@ "php": ">=8.2", "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/service-contracts": "^3.6", + "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", 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 - + + + + +