N°8910 - Upgrade Symfony packages (#811)

This commit is contained in:
Benjamin Dalsass
2026-02-23 06:54:26 +01:00
committed by GitHub
parent b91e6c384a
commit 4853ca444e
224 changed files with 4758 additions and 1778 deletions

View File

@@ -52,7 +52,7 @@ class Autowire
if (null !== $value && null !== $service) {
throw new LogicException('#[Autowire] attribute cannot declare $value and $service at the same time.');
}
} elseif (!(null !== $value xor null !== $service xor null !== $expression xor null !== $env xor null !== $param)) {
} elseif (1 !== (null !== $value) + (null !== $service) + (null !== $expression) + (null !== $env) + (null !== $param)) {
throw new LogicException('#[Autowire] attribute must declare exactly one of $service, $expression, $env, $param or $value.');
}

View File

@@ -41,6 +41,7 @@ class AnalyzeServiceReferencesPass extends AbstractRecursivePass
private bool $lazy;
private bool $byConstructor;
private bool $byFactory;
private bool $byMultiUseArgument;
private array $definitions;
private array $aliases;
@@ -67,6 +68,7 @@ class AnalyzeServiceReferencesPass extends AbstractRecursivePass
$this->lazy = false;
$this->byConstructor = false;
$this->byFactory = false;
$this->byMultiUseArgument = false;
$this->definitions = $container->getDefinitions();
$this->aliases = $container->getAliases();
@@ -89,7 +91,12 @@ class AnalyzeServiceReferencesPass extends AbstractRecursivePass
if ($value instanceof ArgumentInterface) {
$this->lazy = !$this->byFactory || !$value instanceof IteratorArgument;
$byMultiUseArgument = $this->byMultiUseArgument;
if ($value instanceof IteratorArgument) {
$this->byMultiUseArgument = true;
}
parent::processValue($value->getValues());
$this->byMultiUseArgument = $byMultiUseArgument;
$this->lazy = $lazy;
return $value;
@@ -106,7 +113,8 @@ class AnalyzeServiceReferencesPass extends AbstractRecursivePass
$value,
$this->lazy || ($this->hasProxyDumper && $targetDefinition?->isLazy()),
ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior(),
$this->byConstructor
$this->byConstructor,
$this->byMultiUseArgument
);
if ($inExpression) {
@@ -117,7 +125,9 @@ class AnalyzeServiceReferencesPass extends AbstractRecursivePass
$targetDefinition,
$value,
$this->lazy || $targetDefinition?->isLazy(),
true
true,
$this->byConstructor,
$this->byMultiUseArgument
);
}

View File

@@ -40,6 +40,10 @@ class CheckDefinitionValidityPass implements CompilerPassInterface
public function process(ContainerBuilder $container)
{
foreach ($container->getDefinitions() as $id => $definition) {
if ($definition->hasErrors()) {
continue;
}
// synthetic service is public
if ($definition->isSynthetic() && !$definition->isPublic()) {
throw new RuntimeException(\sprintf('A synthetic service ("%s") must be public.', $id));

View File

@@ -79,7 +79,7 @@ final class CheckTypeDeclarationsPass extends AbstractRecursivePass
protected function processValue(mixed $value, bool $isRoot = false): mixed
{
if (isset($this->skippedIds[$this->currentId])) {
if (isset($this->skippedIds[$this->currentId ?? ''])) {
return $value;
}
@@ -142,13 +142,14 @@ final class CheckTypeDeclarationsPass extends AbstractRecursivePass
if (!$p->hasType() || $p->isVariadic()) {
continue;
}
$key = $i;
if (\array_key_exists($p->name, $values)) {
$i = $p->name;
$key = $p->name;
} elseif (!\array_key_exists($i, $values)) {
continue;
}
$this->checkType($checkedDefinition, $values[$i], $p, $envPlaceholderUniquePrefix);
$this->checkType($checkedDefinition, $values[$key], $p, $envPlaceholderUniquePrefix);
}
if ($reflectionFunction->isVariadic() && ($lastParameter = end($reflectionParameters))->hasType()) {

View File

@@ -69,9 +69,9 @@ class DefinitionErrorExceptionPass extends AbstractRecursivePass
ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE === $value->getInvalidBehavior()
|| ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior()
) {
$this->sourceReferences[$targetId][$this->currentId] ??= true;
$this->sourceReferences[$targetId][$this->currentId ?? ''] ??= true;
} else {
$this->sourceReferences[$targetId][$this->currentId] = false;
$this->sourceReferences[$targetId][$this->currentId ?? ''] = false;
}
return $value;
@@ -81,7 +81,7 @@ class DefinitionErrorExceptionPass extends AbstractRecursivePass
return parent::processValue($value, $isRoot);
}
$this->erroredDefinitions[$this->currentId] = $value;
$this->erroredDefinitions[$this->currentId ?? ''] = $value;
return parent::processValue($value);
}

View File

@@ -146,7 +146,7 @@ class InlineServiceDefinitionsPass extends AbstractRecursivePass
$this->container->log($this, \sprintf('Inlined service "%s" to "%s".', $id, $this->currentId));
$this->inlinedIds[$id] = $definition->isPublic() || !$definition->isShared();
$this->notInlinedIds[$this->currentId] = true;
$this->notInlinedIds[$this->currentId ?? ''] = true;
if ($definition->isShared()) {
return $definition;

View File

@@ -97,7 +97,8 @@ class PassConfig
new AliasDeprecatedPublicServicesPass(),
],
// Let build parameters be available as late as possible
-2048 => [new RemoveBuildParametersPass()],
// Don't remove array parameters since ResolveParameterPlaceHoldersPass doesn't resolve them
-2048 => [new RemoveBuildParametersPass(true)],
];
}

View File

@@ -20,6 +20,11 @@ class RemoveBuildParametersPass implements CompilerPassInterface
*/
private array $removedParameters = [];
public function __construct(
private bool $preserveArrays = false,
) {
}
/**
* @return void
*/
@@ -29,7 +34,7 @@ class RemoveBuildParametersPass implements CompilerPassInterface
$this->removedParameters = [];
foreach ($parameterBag->all() as $name => $value) {
if ('.' === ($name[0] ?? '')) {
if ('.' === ($name[0] ?? '') && (!$this->preserveArrays || !\is_array($value))) {
$this->removedParameters[$name] = $value;
$parameterBag->remove($name);

View File

@@ -41,7 +41,15 @@ class ReplaceAliasByActualDefinitionPass extends AbstractRecursivePass
$seenAliasTargets = [];
$replacements = [];
foreach ($container->getAliases() as $definitionId => $target) {
// Sort aliases so non-deprecated ones come first. This ensures that when
// multiple aliases point to the same private definition, non-deprecated
// aliases get priority for renaming. Otherwise, the definition might be
// renamed to a deprecated alias ID, causing the original service ID to
// become an alias to the deprecated one (inverting the alias chain).
$aliases = $container->getAliases();
uasort($aliases, static fn ($a, $b) => $a->isDeprecated() <=> $b->isDeprecated());
foreach ($aliases as $definitionId => $target) {
$targetId = (string) $target;
// Special case: leave this target alone
if ('service_container' === $targetId) {

View File

@@ -121,10 +121,10 @@ class ResolveBindingsPass extends AbstractRecursivePass
foreach ($bindings as $key => $binding) {
[$bindingValue, $bindingId, $used, $bindingType, $file] = $binding->getValues();
if ($used) {
$this->usedBindings[$bindingId] = true;
unset($this->unusedBindings[$bindingId]);
} elseif (!isset($this->usedBindings[$bindingId])) {
$this->unusedBindings[$bindingId] = [$key, $this->currentId, $bindingType, $file];
$this->usedBindings[$bindingId ?? ''] = true;
unset($this->unusedBindings[$bindingId ?? '']);
} elseif (!isset($this->usedBindings[$bindingId ?? ''])) {
$this->unusedBindings[$bindingId ?? ''] = [$key, $this->currentId, $bindingType, $file];
}
if (preg_match('/^(?:(?:array|bool|float|int|string|iterable|([^ $]++)) )\$/', $key, $m)) {
@@ -263,8 +263,8 @@ class ResolveBindingsPass extends AbstractRecursivePass
{
[$bindingValue, $bindingId] = $binding->getValues();
$this->usedBindings[$bindingId] = true;
unset($this->unusedBindings[$bindingId]);
$this->usedBindings[$bindingId ?? ''] = true;
unset($this->unusedBindings[$bindingId ?? '']);
return $bindingValue;
}

View File

@@ -26,15 +26,17 @@ class ResolveClassPass implements CompilerPassInterface
public function process(ContainerBuilder $container)
{
foreach ($container->getDefinitions() as $id => $definition) {
if ($definition->isSynthetic() || null !== $definition->getClass()) {
if ($definition->isSynthetic()
|| $definition->hasErrors()
|| 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));
}
$definition->setClass($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));
}
$definition->setClass($id);
}
}
}

View File

@@ -51,7 +51,7 @@ class ResolveHotPathPass extends AbstractRecursivePass
return $value->clearTag('container.hot_path');
}
$this->resolvedIds[$this->currentId] = true;
$this->resolvedIds[$this->currentId ?? ''] = true;
if (!$value->hasTag('container.hot_path')) {
return $value;

View File

@@ -36,7 +36,11 @@ class ResolveReferencesToAliasesPass extends AbstractRecursivePass
$this->currentId = $id;
if ($aliasId !== $defId = $this->getDefinitionId($aliasId, $container)) {
$container->setAlias($id, $defId)->setPublic($alias->isPublic());
$newAlias = $container->setAlias($id, $defId)->setPublic($alias->isPublic());
if ($alias->isDeprecated()) {
$newAlias->setDeprecated(...array_values($alias->getDeprecation('%alias_id%')));
}
}
}
}

View File

@@ -74,7 +74,7 @@ class ServiceReferenceGraph
/**
* Connects 2 nodes together in the Graph.
*/
public function connect(?string $sourceId, mixed $sourceValue, ?string $destId, mixed $destValue = null, ?Reference $reference = null, bool $lazy = false, bool $weak = false, bool $byConstructor = false): void
public function connect(?string $sourceId, mixed $sourceValue, ?string $destId, mixed $destValue = null, ?Reference $reference = null, bool $lazy = false, bool $weak = false, bool $byConstructor = false, bool $byMultiUseArgument = false): void
{
if (null === $sourceId || null === $destId) {
return;
@@ -82,7 +82,7 @@ class ServiceReferenceGraph
$sourceNode = $this->createNode($sourceId, $sourceValue);
$destNode = $this->createNode($destId, $destValue);
$edge = new ServiceReferenceGraphEdge($sourceNode, $destNode, $reference, $lazy, $weak, $byConstructor);
$edge = new ServiceReferenceGraphEdge($sourceNode, $destNode, $reference, $lazy, $weak, $byConstructor, $byMultiUseArgument);
$sourceNode->addOutEdge($edge);
$destNode->addInEdge($edge);

View File

@@ -26,8 +26,9 @@ class ServiceReferenceGraphEdge
private bool $lazy;
private bool $weak;
private bool $byConstructor;
private bool $byMultiUseArgument;
public function __construct(ServiceReferenceGraphNode $sourceNode, ServiceReferenceGraphNode $destNode, mixed $value = null, bool $lazy = false, bool $weak = false, bool $byConstructor = false)
public function __construct(ServiceReferenceGraphNode $sourceNode, ServiceReferenceGraphNode $destNode, mixed $value = null, bool $lazy = false, bool $weak = false, bool $byConstructor = false, bool $byMultiUseArgument = false)
{
$this->sourceNode = $sourceNode;
$this->destNode = $destNode;
@@ -35,6 +36,7 @@ class ServiceReferenceGraphEdge
$this->lazy = $lazy;
$this->weak = $weak;
$this->byConstructor = $byConstructor;
$this->byMultiUseArgument = $byMultiUseArgument;
}
/**
@@ -84,4 +86,9 @@ class ServiceReferenceGraphEdge
{
return $this->byConstructor;
}
public function isFromMultiUseArgument(): bool
{
return $this->byMultiUseArgument;
}
}

View File

@@ -207,7 +207,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
$this->extensions[$extension->getAlias()] = $extension;
if (false !== $extension->getNamespace()) {
$this->extensionsByNs[$extension->getNamespace()] = $extension;
$this->extensionsByNs[$extension->getNamespace() ?? ''] = $extension;
}
}

View File

@@ -458,7 +458,7 @@ EOF;
foreach ($edges as $edge) {
$node = $edge->getDestNode();
$id = $node->getId();
if ($sourceId === $id || !$node->getValue() instanceof Definition || $edge->isWeak()) {
if (($sourceId === $id && !$edge->isLazy()) || !$node->getValue() instanceof Definition || $edge->isWeak()) {
continue;
}
@@ -699,7 +699,6 @@ EOF;
$asGhostObject = false;
$isProxyCandidate = $this->isProxyCandidate($definition, $asGhostObject, $id);
$instantiation = '';
$lastWitherIndex = null;
foreach ($definition->getMethodCalls() as $k => $call) {
@@ -708,20 +707,26 @@ EOF;
}
}
if (!$isProxyCandidate && $definition->isShared() && !isset($this->singleUsePrivateIds[$id]) && null === $lastWitherIndex) {
$instantiation = \sprintf('$container->%s[%s] = %s', $this->container->getDefinition($id)->isPublic() ? 'services' : 'privates', $this->doExport($id), $isSimpleInstance ? '' : '$instance');
} elseif (!$isSimpleInstance) {
$instantiation = '$instance';
$shouldShareInline = !$isProxyCandidate && $definition->isShared() && !isset($this->singleUsePrivateIds[$id]) && null === $lastWitherIndex;
$serviceAccessor = \sprintf('$container->%s[%s]', $this->container->getDefinition($id)->isPublic() ? 'services' : 'privates', $this->doExport($id));
$return = match (true) {
$shouldShareInline && !isset($this->circularReferences[$id]) && $isSimpleInstance => 'return '.$serviceAccessor.' = ',
$shouldShareInline && !isset($this->circularReferences[$id]) => $serviceAccessor.' = $instance = ',
$shouldShareInline || !$isSimpleInstance => '$instance = ',
default => 'return ',
};
$code = $this->addNewInstance($definition, ' '.$return, $id, $asGhostObject);
if ($shouldShareInline && isset($this->circularReferences[$id])) {
$code .= \sprintf(
"\n if (isset(%s)) {\n return %1\$s;\n }\n\n %s%1\$s = \$instance;\n",
$serviceAccessor,
$isSimpleInstance ? 'return ' : ''
);
}
$return = '';
if ($isSimpleInstance) {
$return = 'return ';
} else {
$instantiation .= ' = ';
}
return $this->addNewInstance($definition, ' '.$return.$instantiation, $id, $asGhostObject);
return $code;
}
private function isTrivialInstance(Definition $definition): bool
@@ -1055,7 +1060,7 @@ EOTXT
$code = '';
if ($isSimpleInstance = $isRootInstance = null === $inlineDef) {
foreach ($this->serviceCalls as $targetId => [$callCount, $behavior, $byConstructor]) {
foreach ($this->serviceCalls as $targetId => [, , $byConstructor]) {
if ($byConstructor && isset($this->circularReferences[$id][$targetId]) && !$this->circularReferences[$id][$targetId] && !($this->hasProxyDumper && $definition->isLazy())) {
$code .= $this->addInlineReference($id, $definition, $targetId, $forConstructor);
}
@@ -1208,6 +1213,7 @@ EOTXT
|| ($callable[0] instanceof Definition && !$this->definitionVariables->offsetExists($callable[0]))
))) {
$initializer = 'fn () => '.$this->dumpValue($callable[0]);
$this->preload[LazyClosure::class] = LazyClosure::class;
return $return.LazyClosure::getCode($initializer, $callable, $class, $this->container, $id).$tail;
}
@@ -1610,27 +1616,28 @@ EOF;
trigger_deprecation(...self::DEPRECATED_PARAMETERS[$name]);
}
if (isset($this->buildParameters[$name])) {
if (\array_key_exists($name, $this->buildParameters)) {
return $this->buildParameters[$name];
}
if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) {
throw new ParameterNotFoundException($name);
}
if (isset($this->loadedDynamicParameters[$name])) {
return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name);
}
if (!\array_key_exists($name, $this->parameters) || '.' === ($name[0] ?? '')) {
throw new ParameterNotFoundException($name);
}
return $this->parameters[$name];
}
public function hasParameter(string $name): bool
{
if (isset($this->buildParameters[$name])) {
if (\array_key_exists($name, $this->buildParameters)) {
return true;
}
return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters);
return \array_key_exists($name, $this->parameters) || isset($this->loadedDynamicParameters[$name]);
}
public function setParameter(string $name, $value): void
@@ -1843,7 +1850,15 @@ EOF;
$returnedType = '';
if ($value instanceof TypedReference) {
$returnedType = \sprintf(': %s\%s', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE >= $value->getInvalidBehavior() ? '' : '?', str_replace(['|', '&'], ['|\\', '&\\'], $value->getType()));
$type = $value->getType();
$nullable = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE >= $value->getInvalidBehavior() ? '' : '?';
if ('?' === ($type[0] ?? '')) {
$type = substr($type, 1);
$nullable = '?';
}
$returnedType = \sprintf(': %s\%s', $nullable, str_replace(['|', '&'], ['|\\', '&\\'], $type));
}
$attribute = '';
@@ -2180,7 +2195,7 @@ EOF;
if (!$value = $edge->getSourceNode()->getValue()) {
continue;
}
if ($edge->isLazy() || !$value instanceof Definition || !$value->isShared()) {
if ($edge->isLazy() || !$value instanceof Definition || !$value->isShared() || $edge->isFromMultiUseArgument()) {
return false;
}

View File

@@ -325,7 +325,7 @@ class EnvVarProcessor implements EnvVarProcessorInterface
}
if ('query_string' === $prefix) {
$queryString = parse_url($env, \PHP_URL_QUERY) ?: $env;
$queryString = parse_url($env, \PHP_URL_QUERY) ?: (parse_url($env, \PHP_URL_SCHEME) ? '' : $env);
parse_str($queryString, $result);
return $result;

View File

@@ -46,15 +46,12 @@ abstract class AbstractConfigurator
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__);
}
/**
* @return void
*/
public function __wakeup()
public function __unserialize(array $data): void
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}

View File

@@ -166,12 +166,12 @@ abstract class FileLoader extends BaseFileLoader
$this->interfaces[] = $class;
} else {
$this->setDefinition($class, $definition = $getPrototype());
$definition->setClass($class);
if (null !== $errorMessage) {
$definition->addError($errorMessage);
continue;
}
$definition->setClass($class);
$interfaces = [];
foreach (class_implements($class, false) as $interface) {

View File

@@ -57,6 +57,9 @@ class PhpFileLoader extends FileLoader
return include $path;
}, $this, ProtectedPhpFileLoader::class);
$instanceof = $this->instanceof;
$this->instanceof = [];
try {
$callback = $load($path, $this->env);
@@ -64,7 +67,7 @@ class PhpFileLoader extends FileLoader
$this->executeCallback($callback, new ContainerConfigurator($this->container, $this, $this->instanceof, $path, $resource, $this->env), $path);
}
} finally {
$this->instanceof = [];
$this->instanceof = $instanceof;
$this->registerAliasesForSinglyImplementedInterfaces();
}