From fe1ddd3a99d35e9b7779bf13526d8fa1d016b43f Mon Sep 17 00:00:00 2001 From: maxbeckers Date: Thu, 3 Nov 2022 08:29:05 +0100 Subject: [PATCH 01/58] [Console] Fix clear line with question in section --- .../Console/Output/ConsoleSectionOutput.php | 8 +++++ .../Component/Console/Style/SymfonyStyle.php | 6 ++++ .../Console/Tests/Style/SymfonyStyleTest.php | 36 +++++++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/src/Symfony/Component/Console/Output/ConsoleSectionOutput.php b/src/Symfony/Component/Console/Output/ConsoleSectionOutput.php index d4c2f20c71741..527c1a224f8b2 100644 --- a/src/Symfony/Component/Console/Output/ConsoleSectionOutput.php +++ b/src/Symfony/Component/Console/Output/ConsoleSectionOutput.php @@ -87,6 +87,14 @@ public function addContent(string $input) } } + /** + * @internal + */ + public function incrementLines() + { + ++$this->lines; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Console/Style/SymfonyStyle.php b/src/Symfony/Component/Console/Style/SymfonyStyle.php index f13c313d3a5c2..1de3b552f333d 100644 --- a/src/Symfony/Component/Console/Style/SymfonyStyle.php +++ b/src/Symfony/Component/Console/Style/SymfonyStyle.php @@ -22,6 +22,7 @@ use Symfony\Component\Console\Helper\TableSeparator; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\ConsoleSectionOutput; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\TrimmedBufferOutput; use Symfony\Component\Console\Question\ChoiceQuestion; @@ -350,6 +351,11 @@ public function askQuestion(Question $question): mixed if ($this->input->isInteractive()) { $this->newLine(); $this->bufferedOutput->write("\n"); + if ($this->output instanceof ConsoleSectionOutput) { + // add one line more to the ConsoleSectionOutput because of the `return` to submit the input + // this is relevant when a `ConsoleSectionOutput::clear` is called. + $this->output->incrementLines(); + } } return $answer; diff --git a/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php b/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php index 74c24034095b1..3441449da9c60 100644 --- a/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php +++ b/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php @@ -16,11 +16,13 @@ use Symfony\Component\Console\Exception\RuntimeException; use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\Input; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\ConsoleSectionOutput; use Symfony\Component\Console\Output\NullOutput; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\StreamOutput; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Console\Tester\CommandTester; @@ -181,4 +183,38 @@ public function testMemoryConsumption() $this->assertSame(0, memory_get_usage() - $start); } + + public function testAskAndClearExpectFullSectionCleared() + { + $answer = 'Answer'; + $inputStream = fopen('php://memory', 'r+'); + fwrite($inputStream, $answer.\PHP_EOL); + rewind($inputStream); + $input = $this->createMock(Input::class); + $sections = []; + $output = new ConsoleSectionOutput(fopen('php://memory', 'r+', false), $sections, StreamOutput::VERBOSITY_NORMAL, true, new OutputFormatter()); + $input + ->method('isInteractive') + ->willReturn(true); + $input + ->method('getStream') + ->willReturn($inputStream); + + $style = new SymfonyStyle($input, $output); + + $style->write('foo'); + $givenAnswer = $style->ask('Dummy question?'); + $output->write('bar'); + $output->clear(); + + rewind($output->getStream()); + $this->assertEquals($answer, $givenAnswer); + $this->assertEquals( + 'foo'.\PHP_EOL. // write foo + \PHP_EOL.\PHP_EOL.\PHP_EOL." \033[32mDummy question?\033[39m:".\PHP_EOL.' > '.\PHP_EOL.\PHP_EOL.\PHP_EOL. // question + 'bar'.\PHP_EOL. // write bar + "\033[10A\033[0J", // clear 10 lines (9 output lines and one from the answer input return) + stream_get_contents($output->getStream()) + ); + } } From cbc616aa89a2a59c6b8b88063119fe2ce82aeaa1 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 25 Nov 2022 16:00:04 +0100 Subject: [PATCH 02/58] fix dumping top-level tagged values --- src/Symfony/Component/Yaml/Dumper.php | 26 +++++++++++++++++++ .../Component/Yaml/Tests/DumperTest.php | 13 +++++----- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/Yaml/Dumper.php b/src/Symfony/Component/Yaml/Dumper.php index 711840c109ec0..679d533cb77aa 100644 --- a/src/Symfony/Component/Yaml/Dumper.php +++ b/src/Symfony/Component/Yaml/Dumper.php @@ -58,6 +58,8 @@ public function dump($input, int $inline = 0, int $indent = 0, int $flags = 0): if ($inline <= 0 || (!\is_array($input) && !$input instanceof TaggedValue && $dumpObjectAsInlineMap) || empty($input)) { $output .= $prefix.Inline::dump($input, $flags); + } elseif ($input instanceof TaggedValue) { + $output .= $this->dumpTaggedValue($input, $inline, $indent, $flags, $prefix); } else { $dumpAsMap = Inline::isHash($input); @@ -137,4 +139,28 @@ public function dump($input, int $inline = 0, int $indent = 0, int $flags = 0): return $output; } + + private function dumpTaggedValue(TaggedValue $value, int $inline, int $indent, int $flags, string $prefix): string + { + $output = sprintf('%s!%s', $prefix ? $prefix.' ' : '', $value->getTag()); + + if (Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && \is_string($value->getValue()) && false !== strpos($value->getValue(), "\n") && false === strpos($value->getValue(), "\r\n")) { + // If the first line starts with a space character, the spec requires a blockIndicationIndicator + // http://www.yaml.org/spec/1.2/spec.html#id2793979 + $blockIndentationIndicator = (' ' === substr($value->getValue(), 0, 1)) ? (string) $this->indentation : ''; + $output .= sprintf(' |%s', $blockIndentationIndicator); + + foreach (explode("\n", $value->getValue()) as $row) { + $output .= sprintf("\n%s%s%s", $prefix, str_repeat(' ', $this->indentation), $row); + } + + return $output; + } + + if ($inline - 1 <= 0 || null === $value->getValue() || \is_scalar($value->getValue())) { + return $output.' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n"; + } + + return $output."\n".$this->dump($value->getValue(), $inline - 1, $indent, $flags); + } } diff --git a/src/Symfony/Component/Yaml/Tests/DumperTest.php b/src/Symfony/Component/Yaml/Tests/DumperTest.php index 59a47a8130f31..ae5051d6af3c5 100644 --- a/src/Symfony/Component/Yaml/Tests/DumperTest.php +++ b/src/Symfony/Component/Yaml/Tests/DumperTest.php @@ -444,8 +444,11 @@ public function testDumpingTaggedValueTopLevelAssoc() { $data = new TaggedValue('user', ['name' => 'jane']); - // @todo Fix the dumper, the output should not be ''. - $expected = ''; + $expected = <<<'YAML' +!user +name: jane + +YAML; $yaml = $this->dumper->dump($data, 2); $this->assertSame($expected, $yaml); } @@ -454,9 +457,7 @@ public function testDumpingTaggedValueTopLevelMultiLine() { $data = new TaggedValue('text', "a\nb\n"); - // @todo Fix the dumper, the output should not be ''. - $expected = ''; - $this->assertSame($expected, $this->dumper->dump($data, 2, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK)); + $this->assertSame("!text |\n a\n b\n ", $this->dumper->dump($data, 2, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK)); } public function testDumpingTaggedValueSpecialCharsInTag() @@ -696,7 +697,7 @@ public function testDumpMultiLineStringAsScalarBlock() nested_inlined_multi_line_string: { inlined_multi_line: "foo\nbar\r\nempty line:\n\nbaz" } YAML -); + ); $this->assertSame($expected, $yml); $this->assertSame($data, $this->parser->parse($yml)); } From cc5ab1422bd8462b073283f11eb0e488c4fb792d Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Mon, 28 Nov 2022 12:50:21 +0100 Subject: [PATCH 03/58] [VarDumper] Ignore \Error in __debugInfo() --- src/Symfony/Component/VarDumper/Caster/Caster.php | 2 +- .../Component/VarDumper/Tests/Caster/CasterTest.php | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/VarDumper/Caster/Caster.php b/src/Symfony/Component/VarDumper/Caster/Caster.php index 53f4461d0df80..890f531063760 100644 --- a/src/Symfony/Component/VarDumper/Caster/Caster.php +++ b/src/Symfony/Component/VarDumper/Caster/Caster.php @@ -47,7 +47,7 @@ public static function castObject(object $obj, string $class, bool $hasDebugInfo if ($hasDebugInfo) { try { $debugInfo = $obj->__debugInfo(); - } catch (\Exception $e) { + } catch (\Throwable $e) { // ignore failing __debugInfo() $hasDebugInfo = false; } diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/CasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/CasterTest.php index c39e82cf6adb0..f4be025c351fe 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/CasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/CasterTest.php @@ -175,4 +175,14 @@ public function testAnonymousClass() , $c ); } + + public function testTypeErrorInDebugInfo() + { + $this->assertDumpMatchesFormat('class@anonymous {}', new class() { + public function __debugInfo(): array + { + return ['class' => \get_class(null)]; + } + }); + } } From 2030c265adb5893831fd76a131413ae7afced303 Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Sat, 3 Dec 2022 10:03:15 +0100 Subject: [PATCH 04/58] [Translation] Fix extraction when dealing with VariadicPlaceholder parameters --- .../Extractor/Visitor/AbstractVisitor.php | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/Symfony/Component/Translation/Extractor/Visitor/AbstractVisitor.php b/src/Symfony/Component/Translation/Extractor/Visitor/AbstractVisitor.php index 8d5bd8e64eb4b..764f8f708881f 100644 --- a/src/Symfony/Component/Translation/Extractor/Visitor/AbstractVisitor.php +++ b/src/Symfony/Component/Translation/Extractor/Visitor/AbstractVisitor.php @@ -42,32 +42,31 @@ protected function addMessageToCatalogue(string $message, ?string $domain, int $ protected function getStringArguments(Node\Expr\CallLike|Node\Attribute|Node\Expr\New_ $node, int|string $index, bool $indexIsRegex = false): array { - $args = $node instanceof Node\Expr\CallLike ? $node->getArgs() : $node->args; - if (\is_string($index)) { return $this->getStringNamedArguments($node, $index, $indexIsRegex); } + $args = $node instanceof Node\Expr\CallLike ? $node->getRawArgs() : $node->args; + if (\count($args) < $index) { return []; } - /** @var Node\Arg $arg */ - $arg = $args[$index]; - if (!$result = $this->getStringValue($arg->value)) { - return []; + if (($arg = $args[$index]) instanceof Node\Arg) { + if ($result = $this->getStringValue($arg->value)) { + return [$result]; + } } - return [$result]; + return []; } protected function hasNodeNamedArguments(Node\Expr\CallLike|Node\Attribute|Node\Expr\New_ $node): bool { - $args = $node instanceof Node\Expr\CallLike ? $node->getArgs() : $node->args; + $args = $node instanceof Node\Expr\CallLike ? $node->getRawArgs() : $node->args; - /** @var Node\Arg $arg */ foreach ($args as $arg) { - if (null !== $arg->name) { + if ($arg instanceof Node\Arg && null !== $arg->name) { return true; } } From bd2780a23724dc042c13d7e97c88fef4417d5cad Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 5 Dec 2022 14:10:54 +0100 Subject: [PATCH 05/58] Revert "bug #48027 [DependencyInjection] Don't autoconfigure tag when it's already set with attributes (nicolas-grekas)" This reverts commit 67e0e872529b24086d53d7fac7da9a505b2c2e96, reversing changes made to f9eaefa677b8b6bf35eecb11487b3f730d3e1c91. --- .../Compiler/ResolveInstanceofConditionalsPass.php | 2 +- .../DependencyInjection/Tests/Compiler/IntegrationTest.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php index 426fe651a6ada..b211b84e1336d 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php @@ -129,7 +129,7 @@ private function processDefinition(ContainerBuilder $container, string $id, Defi foreach ($instanceofTags[$i] as $k => $v) { if (null === $definition->getDecoratedService() || \in_array($k, $tagsToKeep, true)) { foreach ($v as $v) { - if ($definition->hasTag($k) && (!$v || \in_array($v, $definition->getTag($k)))) { + if ($definition->hasTag($k) && \in_array($v, $definition->getTag($k))) { continue; } $definition->addTag($k, $v); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php index 89ad99d987603..0c1fe4d141958 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php @@ -883,7 +883,6 @@ static function (ChildDefinition $definition, CustomAutoconfiguration $attribute $definition->addTag('app.custom_tag', get_object_vars($attribute) + ['class' => $reflector->getName()]); } ); - $container->registerForAutoconfiguration(TaggedService1::class)->addTag('app.custom_tag'); $container->register('one', TaggedService1::class) ->setPublic(true) From 9b0d717b662036a24165918d5a930ce082855f5c Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 5 Dec 2022 18:55:46 +0100 Subject: [PATCH 06/58] [DoctrineBridge] Skip resolving entities when the corresponding request attribute is already an object --- .../ArgumentResolver/EntityValueResolver.php | 16 +++++++--------- .../ArgumentResolver/EntityValueResolverTest.php | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/ArgumentResolver/EntityValueResolver.php b/src/Symfony/Bridge/Doctrine/ArgumentResolver/EntityValueResolver.php index 5794d9814215d..b531857c1422c 100644 --- a/src/Symfony/Bridge/Doctrine/ArgumentResolver/EntityValueResolver.php +++ b/src/Symfony/Bridge/Doctrine/ArgumentResolver/EntityValueResolver.php @@ -40,7 +40,13 @@ public function __construct( public function resolve(Request $request, ArgumentMetadata $argument): array { - $options = $this->getMapEntityAttribute($argument); + if (\is_object($request->attributes->get($argument->getName()))) { + return []; + } + + $options = $argument->getAttributes(MapEntity::class, ArgumentMetadata::IS_INSTANCEOF); + $options = ($options[0] ?? $this->defaults)->withDefaults($this->defaults, $argument->getType()); + if (!$options->class || $options->disabled) { return []; } @@ -201,12 +207,4 @@ private function findViaExpression(ObjectManager $manager, Request $request, Map return null; } } - - private function getMapEntityAttribute(ArgumentMetadata $argument): MapEntity - { - /** @var MapEntity $options */ - $options = $argument->getAttributes(MapEntity::class, ArgumentMetadata::IS_INSTANCEOF)[0] ?? $this->defaults; - - return $options->withDefaults($this->defaults, $argument->getType()); - } } diff --git a/src/Symfony/Bridge/Doctrine/Tests/ArgumentResolver/EntityValueResolverTest.php b/src/Symfony/Bridge/Doctrine/Tests/ArgumentResolver/EntityValueResolverTest.php index 629a37256c0f2..3dc814acaa047 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/ArgumentResolver/EntityValueResolverTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/ArgumentResolver/EntityValueResolverTest.php @@ -373,6 +373,20 @@ public function testExpressionSyntaxErrorThrowsException() $resolver->resolve($request, $argument); } + public function testAlreadyResolved() + { + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); + $resolver = new EntityValueResolver($registry); + + $request = new Request(); + $request->attributes->set('arg', new \stdClass()); + + $argument = $this->createArgument('stdClass', name: 'arg'); + + $this->assertSame([], $resolver->resolve($request, $argument)); + } + private function createArgument(string $class = null, MapEntity $entity = null, string $name = 'arg', bool $isNullable = false): ArgumentMetadata { return new ArgumentMetadata($name, $class ?? \stdClass::class, false, false, null, $isNullable, $entity ? [$entity] : []); From 3c1afda201e46dc6d4ba2812d4cb737d70cd7961 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 6 Dec 2022 18:38:18 +0100 Subject: [PATCH 07/58] Bump Symfony version to 6.2.2 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 0e99228e0937f..a8f6fdade9589 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -75,12 +75,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '6.2.1'; - public const VERSION_ID = 60201; + public const VERSION = '6.2.2-DEV'; + public const VERSION_ID = 60202; public const MAJOR_VERSION = 6; public const MINOR_VERSION = 2; - public const RELEASE_VERSION = 1; - public const EXTRA_VERSION = ''; + public const RELEASE_VERSION = 2; + public const EXTRA_VERSION = 'DEV'; public const END_OF_MAINTENANCE = '07/2023'; public const END_OF_LIFE = '07/2023'; From b60c2b8b274e47bb69954d82e8387bf012baaf2d Mon Sep 17 00:00:00 2001 From: Romain Monteil Date: Tue, 6 Dec 2022 18:57:02 +0100 Subject: [PATCH 08/58] [FrameworkBundle] Remove check of undefined service in mailer assertions --- .../Bundle/FrameworkBundle/Test/MailerAssertionsTrait.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/MailerAssertionsTrait.php b/src/Symfony/Bundle/FrameworkBundle/Test/MailerAssertionsTrait.php index 1a629d6255fbe..d6b29d2b5a0c6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/MailerAssertionsTrait.php +++ b/src/Symfony/Bundle/FrameworkBundle/Test/MailerAssertionsTrait.php @@ -123,10 +123,6 @@ private static function getMessageMailerEvents(): MessageEvents return $container->get('mailer.message_logger_listener')->getEvents(); } - if ($container->has('mailer.logger_message_listener')) { - return $container->get('mailer.logger_message_listener')->getEvents(); - } - static::fail('A client must have Mailer enabled to make email assertions. Did you forget to require symfony/mailer?'); } } From df69810cdb31b3fa44f5988e6cfae3aadee28e0b Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Tue, 6 Dec 2022 13:43:04 -0500 Subject: [PATCH 09/58] [FrameworkBundle] fix removing commands if console not available --- .../DependencyInjection/FrameworkExtension.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 9fbfa7a6a3d07..7ae60ecf89fd2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -390,7 +390,7 @@ public function load(array $configs, ContainerBuilder $container) if ($this->readConfigEnabled('mailer', $container, $config['mailer'])) { $this->registerMailerConfiguration($config['mailer'], $container, $loader); - if (!class_exists(MailerTestCommand::class) || !$this->hasConsole()) { + if (!$this->hasConsole() || !class_exists(MailerTestCommand::class)) { $container->removeDefinition('console.command.mailer_test'); } } @@ -1971,7 +1971,7 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder throw new LogicException('Messenger support cannot be enabled as the Messenger component is not installed. Try running "composer require symfony/messenger".'); } - if (!class_exists(StatsCommand::class)) { + if (!$this->hasConsole() || !class_exists(StatsCommand::class)) { $container->removeDefinition('console.command.messenger_stats'); } From c5da2eb3861f8f7f3bfe8f826550d68ab424dacc Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 6 Dec 2022 20:54:54 +0100 Subject: [PATCH 10/58] [DependencyInjection] Generate different classes for ghost objects and virtual proxies --- .../DependencyInjection/Dumper/PhpDumper.php | 4 +- .../Instantiator/LazyServiceInstantiator.php | 10 ++- .../LazyProxy/PhpDumper/LazyServiceDumper.php | 13 ++-- .../Tests/Dumper/PhpDumperTest.php | 14 ++-- .../php/services9_lazy_inlined_factories.txt | 8 +-- ...lazy_ghost.php => services_dedup_lazy.php} | 51 +++++++++++++- .../php/services_dedup_lazy_proxy.php | 70 ------------------- .../php/services_non_shared_lazy_as_files.txt | 12 ++-- .../php/services_non_shared_lazy_ghost.php | 4 +- .../Fixtures/php/services_wither_lazy.php | 4 +- 10 files changed, 83 insertions(+), 107 deletions(-) rename src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/{services_dedup_lazy_ghost.php => services_dedup_lazy.php} (55%) delete mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dedup_lazy_proxy.php diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 4d827d53e28b7..e930724810ae9 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -545,10 +545,10 @@ private function generateProxyClasses(): array if (!$definition = $this->isProxyCandidate($definition, $asGhostObject, $id)) { continue; } - if (isset($alreadyGenerated[$class = $definition->getClass()])) { + if (isset($alreadyGenerated[$asGhostObject][$class = $definition->getClass()])) { continue; } - $alreadyGenerated[$class] = true; + $alreadyGenerated[$asGhostObject][$class] = true; // register class' reflector for resource tracking $this->container->getReflectionClass($class); if ("\n" === $proxyCode = "\n".$proxyDumper->getProxyCode($definition, $id)) { diff --git a/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/LazyServiceInstantiator.php b/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/LazyServiceInstantiator.php index cb846445de994..3de3d0361d775 100644 --- a/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/LazyServiceInstantiator.php +++ b/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/LazyServiceInstantiator.php @@ -13,8 +13,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\LazyServiceDumper; -use Symfony\Component\VarExporter\LazyGhostTrait; /** * @author Nicolas Grekas @@ -25,10 +25,14 @@ public function instantiateProxy(ContainerInterface $container, Definition $defi { $dumper = new LazyServiceDumper(); - if (!class_exists($proxyClass = $dumper->getProxyClass($definition, $class), false)) { + if (!$dumper->isProxyCandidate($definition, $asGhostObject, $id)) { + throw new InvalidArgumentException(sprintf('Cannot instantiate lazy proxy for service "%s".', $id)); + } + + if (!class_exists($proxyClass = $dumper->getProxyClass($definition, $asGhostObject, $class), false)) { eval($dumper->getProxyCode($definition, $id)); } - return isset(class_uses($proxyClass)[LazyGhostTrait::class]) ? $proxyClass::createLazyGhost($realInstantiator) : $proxyClass::createLazyProxy($realInstantiator); + return $asGhostObject ? $proxyClass::createLazyGhost($realInstantiator) : $proxyClass::createLazyProxy($realInstantiator); } } diff --git a/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/LazyServiceDumper.php b/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/LazyServiceDumper.php index 8f69da33929e9..eeef902b0f889 100644 --- a/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/LazyServiceDumper.php +++ b/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/LazyServiceDumper.php @@ -72,9 +72,10 @@ public function getProxyFactoryCode(Definition $definition, string $id, string $ $instantiation .= sprintf(' $this->%s[%s] =', $definition->isPublic() && !$definition->isPrivate() ? 'services' : 'privates', var_export($id, true)); } - $proxyClass = $this->getProxyClass($definition); + $asGhostObject = str_contains($factoryCode, '$proxy'); + $proxyClass = $this->getProxyClass($definition, $asGhostObject); - if (!str_contains($factoryCode, '$proxy')) { + if (!$asGhostObject) { return <<createProxy('$proxyClass', fn () => \\$proxyClass::createLazyProxy(fn () => $factoryCode)); @@ -104,7 +105,7 @@ public function getProxyCode(Definition $definition, string $id = null): string if (!$this->isProxyCandidate($definition, $asGhostObject, $id)) { throw new InvalidArgumentException(sprintf('Cannot instantiate lazy proxy for service "%s".', $id ?? $definition->getClass())); } - $proxyClass = $this->getProxyClass($definition, $class); + $proxyClass = $this->getProxyClass($definition, $asGhostObject, $class); if ($asGhostObject) { try { @@ -142,10 +143,12 @@ public function getProxyCode(Definition $definition, string $id = null): string } } - public function getProxyClass(Definition $definition, \ReflectionClass &$class = null): string + public function getProxyClass(Definition $definition, bool $asGhostObject, \ReflectionClass &$class = null): string { $class = new \ReflectionClass($definition->getClass()); - return preg_replace('/^.*\\\\/', '', $class->name).'_'.substr(hash('sha256', $this->salt.'+'.$class->name), -7); + return preg_replace('/^.*\\\\/', '', $class->name) + .($asGhostObject ? 'Ghost' : 'Proxy') + .ucfirst(substr(hash('sha256', $this->salt.'+'.$class->name), -7)); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 12d5b80f8baf3..870d448fc0c76 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -772,24 +772,18 @@ public function testCircularReferenceAllowanceForLazyServices() $dumper->dump(); } - /** - * @testWith [false] - * [true] - */ - public function testDedupLazyProxy(bool $asGhostObject) + public function testDedupLazyProxy() { $container = new ContainerBuilder(); $container->register('foo', 'stdClass')->setLazy(true)->setPublic(true); $container->register('bar', 'stdClass')->setLazy(true)->setPublic(true); + $container->register('baz', 'stdClass')->setLazy(true)->setPublic(true)->setFactory('foo_bar'); + $container->register('buz', 'stdClass')->setLazy(true)->setPublic(true)->setFactory('foo_bar'); $container->compile(); $dumper = new PhpDumper($container); - if (!$asGhostObject) { - $dumper->setProxyDumper(new \DummyProxyDumper()); - } - - $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_dedup_lazy'.($asGhostObject ? '_ghost' : '_proxy').'.php', $dumper->dump()); + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_dedup_lazy.php', $dumper->dump()); } public function testLazyArgumentProvideGenerator() diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt index 7af59984549ae..b606736e93f02 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt @@ -6,11 +6,11 @@ namespace Container%s; include_once $this->targetDir.''.'/Fixtures/includes/foo.php'; -class FooClass_2b16075 extends \Bar\FooClass implements \Symfony\Component\VarExporter\LazyObjectInterface +class FooClassGhost2b16075 extends \Bar\FooClass implements \Symfony\Component\VarExporter\LazyObjectInterface %A -if (!\class_exists('FooClass_%s', false)) { - \class_alias(__NAMESPACE__.'\\FooClass_%s', 'FooClass_%s', false); +if (!\class_exists('FooClassGhost2b16075', false)) { + \class_alias(__NAMESPACE__.'\\FooClassGhost2b16075', 'FooClassGhost2b16075', false); } [Container%s/ProjectServiceContainer.php] => services['lazy_foo'] = $this->createProxy('FooClass_2b16075', fn () => \FooClass_2b16075::createLazyGhost($this->getLazyFooService(...))); + return $this->services['lazy_foo'] = $this->createProxy('FooClassGhost2b16075', fn () => \FooClassGhost2b16075::createLazyGhost($this->getLazyFooService(...))); } include_once $this->targetDir.''.'/Fixtures/includes/foo_lazy.php'; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dedup_lazy_ghost.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dedup_lazy.php similarity index 55% rename from src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dedup_lazy_ghost.php rename to src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dedup_lazy.php index a7972921fadba..867cda7e7a289 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dedup_lazy_ghost.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dedup_lazy.php @@ -21,6 +21,8 @@ public function __construct() $this->services = $this->privates = []; $this->methodMap = [ 'bar' => 'getBarService', + 'baz' => 'getBazService', + 'buz' => 'getBuzService', 'foo' => 'getFooService', ]; @@ -50,12 +52,40 @@ protected function createProxy($class, \Closure $factory) protected function getBarService($lazyLoad = true) { if (true === $lazyLoad) { - return $this->services['bar'] = $this->createProxy('stdClass_5a8a5eb', fn () => \stdClass_5a8a5eb::createLazyGhost($this->getBarService(...))); + return $this->services['bar'] = $this->createProxy('stdClassGhost5a8a5eb', fn () => \stdClassGhost5a8a5eb::createLazyGhost($this->getBarService(...))); } return $lazyLoad; } + /** + * Gets the public 'baz' shared service. + * + * @return \stdClass + */ + protected function getBazService($lazyLoad = true) + { + if (true === $lazyLoad) { + return $this->services['baz'] = $this->createProxy('stdClassProxy5a8a5eb', fn () => \stdClassProxy5a8a5eb::createLazyProxy(fn () => $this->getBazService(false))); + } + + return \foo_bar(); + } + + /** + * Gets the public 'buz' shared service. + * + * @return \stdClass + */ + protected function getBuzService($lazyLoad = true) + { + if (true === $lazyLoad) { + return $this->services['buz'] = $this->createProxy('stdClassProxy5a8a5eb', fn () => \stdClassProxy5a8a5eb::createLazyProxy(fn () => $this->getBuzService(false))); + } + + return \foo_bar(); + } + /** * Gets the public 'foo' shared service. * @@ -64,14 +94,14 @@ protected function getBarService($lazyLoad = true) protected function getFooService($lazyLoad = true) { if (true === $lazyLoad) { - return $this->services['foo'] = $this->createProxy('stdClass_5a8a5eb', fn () => \stdClass_5a8a5eb::createLazyGhost($this->getFooService(...))); + return $this->services['foo'] = $this->createProxy('stdClassGhost5a8a5eb', fn () => \stdClassGhost5a8a5eb::createLazyGhost($this->getFooService(...))); } return $lazyLoad; } } -class stdClass_5a8a5eb extends \stdClass implements \Symfony\Component\VarExporter\LazyObjectInterface +class stdClassGhost5a8a5eb extends \stdClass implements \Symfony\Component\VarExporter\LazyObjectInterface { use \Symfony\Component\VarExporter\LazyGhostTrait; @@ -82,3 +112,18 @@ class stdClass_5a8a5eb extends \stdClass implements \Symfony\Component\VarExport class_exists(\Symfony\Component\VarExporter\Internal\Hydrator::class); class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectRegistry::class); class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class); + +class stdClassProxy5a8a5eb extends \stdClass implements \Symfony\Component\VarExporter\LazyObjectInterface +{ + use \Symfony\Component\VarExporter\LazyProxyTrait; + + private const LAZY_OBJECT_PROPERTY_SCOPES = [ + 'lazyObjectReal' => [self::class, 'lazyObjectReal', null], + "\0".self::class."\0lazyObjectReal" => [self::class, 'lazyObjectReal', null], + ]; +} + +// Help opcache.preload discover always-needed symbols +class_exists(\Symfony\Component\VarExporter\Internal\Hydrator::class); +class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectRegistry::class); +class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dedup_lazy_proxy.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dedup_lazy_proxy.php deleted file mode 100644 index 841c892216f68..0000000000000 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dedup_lazy_proxy.php +++ /dev/null @@ -1,70 +0,0 @@ -services = $this->privates = []; - $this->methodMap = [ - 'bar' => 'getBarService', - 'foo' => 'getFooService', - ]; - - $this->aliases = []; - } - - public function compile(): void - { - throw new LogicException('You cannot compile a dumped container that was already compiled.'); - } - - public function isCompiled(): bool - { - return true; - } - - protected function createProxy($class, \Closure $factory) - { - return $factory(); - } - - /** - * Gets the public 'bar' shared service. - * - * @return \stdClass - */ - protected function getBarService($lazyLoad = true) - { - // lazy factory for stdClass - - return new \stdClass(); - } - - /** - * Gets the public 'foo' shared service. - * - * @return \stdClass - */ - protected function getFooService($lazyLoad = true) - { - // lazy factory for stdClass - - return new \stdClass(); - } -} - -// proxy code for stdClass diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_as_files.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_as_files.txt index a52a32a27dadd..a4e8807c71039 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_as_files.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_as_files.txt @@ -22,7 +22,7 @@ class getNonSharedFooService extends ProjectServiceContainer $container->factories['non_shared_foo'] ??= fn () => self::do($container); if (true === $lazyLoad) { - return $container->createProxy('FooLazyClass_f814e3a', fn () => \FooLazyClass_f814e3a::createLazyGhost(fn ($proxy) => self::do($container, $proxy))); + return $container->createProxy('FooLazyClassGhostF814e3a', fn () => \FooLazyClassGhostF814e3a::createLazyGhost(fn ($proxy) => self::do($container, $proxy))); } static $include = true; @@ -37,11 +37,11 @@ class getNonSharedFooService extends ProjectServiceContainer } } - [Container%s/FooLazyClass_f814e3a.php] => set(\Container%s\ProjectServiceContainer::class, null); -require __DIR__.'/Container%s/FooLazyClass_f814e3a.php'; +require __DIR__.'/Container%s/FooLazyClassGhostF814e3a.php'; require __DIR__.'/Container%s/getNonSharedFooService.php'; $classes = []; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_ghost.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_ghost.php index c304c9461fd1c..7d584eef41063 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_ghost.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_ghost.php @@ -68,14 +68,14 @@ protected function getFooService($lazyLoad = true) $this->factories['service_container']['foo'] ??= $this->getFooService(...); if (true === $lazyLoad) { - return $this->createProxy('stdClass_5a8a5eb', fn () => \stdClass_5a8a5eb::createLazyGhost($this->getFooService(...))); + return $this->createProxy('stdClassGhost5a8a5eb', fn () => \stdClassGhost5a8a5eb::createLazyGhost($this->getFooService(...))); } return $lazyLoad; } } -class stdClass_5a8a5eb extends \stdClass implements \Symfony\Component\VarExporter\LazyObjectInterface +class stdClassGhost5a8a5eb extends \stdClass implements \Symfony\Component\VarExporter\LazyObjectInterface { use \Symfony\Component\VarExporter\LazyGhostTrait; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_wither_lazy.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_wither_lazy.php index 5de8772d6746e..b0bbc7b640be8 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_wither_lazy.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_wither_lazy.php @@ -56,7 +56,7 @@ protected function createProxy($class, \Closure $factory) protected function getWitherService($lazyLoad = true) { if (true === $lazyLoad) { - return $this->services['wither'] = $this->createProxy('Wither_94fa281', fn () => \Wither_94fa281::createLazyProxy(fn () => $this->getWitherService(false))); + return $this->services['wither'] = $this->createProxy('WitherProxy94fa281', fn () => \WitherProxy94fa281::createLazyProxy(fn () => $this->getWitherService(false))); } $instance = new \Symfony\Component\DependencyInjection\Tests\Compiler\Wither(); @@ -71,7 +71,7 @@ protected function getWitherService($lazyLoad = true) } } -class Wither_94fa281 extends \Symfony\Component\DependencyInjection\Tests\Compiler\Wither implements \Symfony\Component\VarExporter\LazyObjectInterface +class WitherProxy94fa281 extends \Symfony\Component\DependencyInjection\Tests\Compiler\Wither implements \Symfony\Component\VarExporter\LazyObjectInterface { use \Symfony\Component\VarExporter\LazyProxyTrait; From f9d211dd2463423ce65036aa8907ecd7bdf77d9f Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Wed, 7 Dec 2022 15:46:19 +0100 Subject: [PATCH 11/58] Fix generated changelog for v6.2.1 --- CHANGELOG-6.2.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/CHANGELOG-6.2.md b/CHANGELOG-6.2.md index 4789556444de9..a0cc3772c78ac 100644 --- a/CHANGELOG-6.2.md +++ b/CHANGELOG-6.2.md @@ -22,9 +22,6 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * bug #48046 [WebProfilerBundle] Remove redundant code from logger template (HypeMC) * bug #48428 Fixed undefined variable error (Kevin Meijer) * bug #48416 [FrameworkBundle] don't register the MailerTestCommand symfony/console is not installed (xabbuh) - * bug #48395 [String] Fix AsciiSlugger with emojis (fancyweb) - * bug #48385 [Security] Reuse `AbstractFactory`'s config tree in `AccessTokenFactory` (chalasr) - * bug #48292 [Security] [LoginLink] Throw InvalidLoginLinkException on missing parameter (MatTheCat) * 6.2.0 (2022-11-30) From 1661c7e661bc91a7cf5f119559bcdc53b855ca5c Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Wed, 7 Dec 2022 10:06:16 -0500 Subject: [PATCH 12/58] [FrameworkBundle] add `kernel.locale_aware` tag to `LocaleSwitcher` --- .../FrameworkBundle/Resources/config/translation.php | 3 ++- .../Tests/DependencyInjection/FrameworkExtensionTest.php | 9 ++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.php index fadaff6d2b596..e40f1aae4d619 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.php @@ -168,10 +168,11 @@ ->set('translation.locale_switcher', LocaleSwitcher::class) ->args([ param('kernel.default_locale'), - tagged_iterator('kernel.locale_aware'), + tagged_iterator('kernel.locale_aware', exclude: 'translation.locale_switcher'), service('router.request_context')->ignoreOnInvalid(), ]) ->tag('kernel.reset', ['method' => 'reset']) + ->tag('kernel.locale_aware') ->alias(LocaleAwareInterface::class, 'translation.locale_switcher') ->alias(LocaleSwitcher::class, 'translation.locale_switcher') ; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index 3743acfe915f9..4b643f27cbe31 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -37,6 +37,7 @@ use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Compiler\ResolveInstanceofConditionalsPass; +use Symfony\Component\DependencyInjection\Compiler\ResolveTaggedIteratorArgumentPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; @@ -2057,7 +2058,9 @@ public function testLocaleSwitcherServiceRegistered() $this->markTestSkipped('LocaleSwitcher not available.'); } - $container = $this->createContainerFromFile('full'); + $container = $this->createContainerFromFile('full', compile: false); + $container->addCompilerPass(new ResolveTaggedIteratorArgumentPass()); + $container->compile(); $this->assertTrue($container->has('translation.locale_switcher')); @@ -2067,6 +2070,10 @@ public function testLocaleSwitcherServiceRegistered() $this->assertInstanceOf(TaggedIteratorArgument::class, $switcherDef->getArgument(1)); $this->assertSame('kernel.locale_aware', $switcherDef->getArgument(1)->getTag()); $this->assertEquals(new Reference('router.request_context', ContainerBuilder::IGNORE_ON_INVALID_REFERENCE), $switcherDef->getArgument(2)); + + $localeAwareServices = array_map(fn (Reference $r) => (string) $r, $switcherDef->getArgument(1)->getValues()); + + $this->assertNotContains('translation.locale_switcher', $localeAwareServices); } public function testHtmlSanitizer() From d2a5592deb538f5c7ffe6b65f07a14ce8e320cf6 Mon Sep 17 00:00:00 2001 From: Saif Eddin Gmati <29315886+azjezz@users.noreply.github.com> Date: Thu, 8 Dec 2022 16:46:28 +0100 Subject: [PATCH 13/58] chore: fix syntax error `parent:` was getting parsed as a goto label, and `__wakeup()` as a function call. this would result in a runtime error once this code branch is reached. ref: https://twitter.com/azjezz/status/1600870101606989824 --- src/Symfony/Component/VarExporter/LazyProxyTrait.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/VarExporter/LazyProxyTrait.php b/src/Symfony/Component/VarExporter/LazyProxyTrait.php index 196da7d608e07..9b42f25c6a9fa 100644 --- a/src/Symfony/Component/VarExporter/LazyProxyTrait.php +++ b/src/Symfony/Component/VarExporter/LazyProxyTrait.php @@ -337,7 +337,7 @@ public function __unserialize(array $data): void PublicHydrator::hydrate($this, $data); if (Registry::$parentMethods[$class]['wakeup']) { - parent:__wakeup(); + parent::__wakeup(); } } } From 3fd5a40ef1198954b8f7875add17026d1481d4b5 Mon Sep 17 00:00:00 2001 From: Ion Bazan Date: Fri, 9 Dec 2022 00:19:01 +0800 Subject: [PATCH 14/58] Point `Security::*` constants to `SecurityRequestAttributes::*` ones --- src/Symfony/Bundle/SecurityBundle/Security.php | 5 +++++ src/Symfony/Component/Security/Core/Security.php | 6 ------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Bundle/SecurityBundle/Security.php b/src/Symfony/Bundle/SecurityBundle/Security.php index 05a5f7c9394eb..c894c1b926bdb 100644 --- a/src/Symfony/Bundle/SecurityBundle/Security.php +++ b/src/Symfony/Bundle/SecurityBundle/Security.php @@ -24,6 +24,7 @@ use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface; use Symfony\Component\Security\Http\Event\LogoutEvent; use Symfony\Component\Security\Http\ParameterBagUtils; +use Symfony\Component\Security\Http\SecurityRequestAttributes; use Symfony\Contracts\Service\ServiceProviderInterface; /** @@ -37,6 +38,10 @@ */ class Security extends LegacySecurity { + public const ACCESS_DENIED_ERROR = SecurityRequestAttributes::ACCESS_DENIED_ERROR; + public const AUTHENTICATION_ERROR = SecurityRequestAttributes::AUTHENTICATION_ERROR; + public const LAST_USERNAME = SecurityRequestAttributes::LAST_USERNAME; + public function __construct(private readonly ContainerInterface $container, private readonly array $authenticators = []) { parent::__construct($container, false); diff --git a/src/Symfony/Component/Security/Core/Security.php b/src/Symfony/Component/Security/Core/Security.php index f1ebf822f5bac..97f1c8ce1f568 100644 --- a/src/Symfony/Component/Security/Core/Security.php +++ b/src/Symfony/Component/Security/Core/Security.php @@ -26,22 +26,16 @@ class Security implements AuthorizationCheckerInterface { /** * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security::ACCESS_DENIED_ERROR instead - * - * In 7.0, move this constant to the NewSecurityHelper class and make it reference SecurityRequestAttributes::ACCESS_DENIED_ERROR. */ public const ACCESS_DENIED_ERROR = '_security.403_error'; /** * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security::AUTHENTICATION_ERROR instead - * - * In 7.0, move this constant to the NewSecurityHelper class and make it reference SecurityRequestAttributes::AUTHENTICATION_ERROR. */ public const AUTHENTICATION_ERROR = '_security.last_error'; /** * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security::LAST_USERNAME instead - * - * In 7.0, move this constant to the NewSecurityHelper class and make it reference SecurityRequestAttributes::LAST_USERNAME. */ public const LAST_USERNAME = '_security.last_username'; From 6ad327921e25d5d45a1b546a0c6869f2b06d89d1 Mon Sep 17 00:00:00 2001 From: Steffen Keuper Date: Mon, 5 Dec 2022 20:30:08 +0100 Subject: [PATCH 15/58] [RateLimiter] Add typecast to Reservation::wait --- src/Symfony/Component/RateLimiter/Reservation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/RateLimiter/Reservation.php b/src/Symfony/Component/RateLimiter/Reservation.php index 0a21310513809..108d672574991 100644 --- a/src/Symfony/Component/RateLimiter/Reservation.php +++ b/src/Symfony/Component/RateLimiter/Reservation.php @@ -45,6 +45,6 @@ public function getRateLimit(): RateLimit public function wait(): void { - usleep($this->getWaitDuration() * 1e6); + usleep((int) ($this->getWaitDuration() * 1e6)); } } From e85a6e84523f80475fb519a915550c3c2f42f610 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Sat, 19 Nov 2022 17:15:51 +0100 Subject: [PATCH 16/58] Minor (comment only), part 2 Second attempt of https://github.com/symfony/symfony/pull/48242 Even if you didn't like "visual", you still can't say "its" to a user ;-) --- src/Symfony/Component/Security/Core/User/UserInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Security/Core/User/UserInterface.php b/src/Symfony/Component/Security/Core/User/UserInterface.php index c9b0930b19ffc..cace8f6aed6cf 100644 --- a/src/Symfony/Component/Security/Core/User/UserInterface.php +++ b/src/Symfony/Component/Security/Core/User/UserInterface.php @@ -55,7 +55,7 @@ public function getRoles(): array; public function eraseCredentials(); /** - * Returns the identifier for this user (e.g. its username or email address). + * Returns the identifier for this user (e.g. username or email address). */ public function getUserIdentifier(): string; } From 2170d3cd710567c35bd438622293808817fbf5ac Mon Sep 17 00:00:00 2001 From: Roy de Vos Burchart Date: Thu, 1 Dec 2022 10:58:44 +0100 Subject: [PATCH 17/58] [HttpFoundation] IPv4-mapped IPv6 addresses incorrectly rejected --- src/Symfony/Component/HttpFoundation/IpUtils.php | 12 ++++++++---- .../Component/HttpFoundation/Tests/IpUtilsTest.php | 1 + 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/IpUtils.php b/src/Symfony/Component/HttpFoundation/IpUtils.php index de2112cfc7028..01d20d87a40b8 100644 --- a/src/Symfony/Component/HttpFoundation/IpUtils.php +++ b/src/Symfony/Component/HttpFoundation/IpUtils.php @@ -125,10 +125,6 @@ public static function checkIp6($requestIp, $ip) } // Check to see if we were given a IP4 $requestIp or $ip by mistake - if (str_contains($requestIp, '.') || str_contains($ip, '.')) { - return self::$checkedIps[$cacheKey] = false; - } - if (!filter_var($requestIp, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) { return self::$checkedIps[$cacheKey] = false; } @@ -136,6 +132,10 @@ public static function checkIp6($requestIp, $ip) if (str_contains($ip, '/')) { [$address, $netmask] = explode('/', $ip, 2); + if (!filter_var($address, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) { + return self::$checkedIps[$cacheKey] = false; + } + if ('0' === $netmask) { return (bool) unpack('n*', @inet_pton($address)); } @@ -144,6 +144,10 @@ public static function checkIp6($requestIp, $ip) return self::$checkedIps[$cacheKey] = false; } } else { + if (!filter_var($ip, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) { + return self::$checkedIps[$cacheKey] = false; + } + $address = $ip; $netmask = 128; } diff --git a/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php b/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php index 8de4b4d7bd472..10b40e0541e0c 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php @@ -77,6 +77,7 @@ public function getIpv6Data() [false, '0.0.0.0/8', '::1'], [false, '::1', '127.0.0.1'], [false, '::1', '0.0.0.0/8'], + [true, '::ffff:10.126.42.2', '::ffff:10.0.0.0/0'], ]; } From 94e2d533477cf8930bcb5fa6e0b8029e10ddbad0 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 9 Dec 2022 10:12:08 +0100 Subject: [PATCH 18/58] [Cache] sync Redis6Proxy with upstream --- .../Cache/Tests/Traits/RedisProxiesTest.php | 1 + .../Component/Cache/Traits/Redis6Proxy.php | 182 +++++++++++------- .../Cache/Traits/RedisCluster6Proxy.php | 131 +++++++++++-- 3 files changed, 225 insertions(+), 89 deletions(-) diff --git a/src/Symfony/Component/Cache/Tests/Traits/RedisProxiesTest.php b/src/Symfony/Component/Cache/Tests/Traits/RedisProxiesTest.php index 67b212e520243..27fabf700af8a 100644 --- a/src/Symfony/Component/Cache/Tests/Traits/RedisProxiesTest.php +++ b/src/Symfony/Component/Cache/Tests/Traits/RedisProxiesTest.php @@ -60,6 +60,7 @@ public function testRedis6Proxy($class, $stub) $stub = file_get_contents("https://raw.githubusercontent.com/phpredis/phpredis/develop/{$stub}.stub.php"); $stub = preg_replace('/^class /m', 'return; \0', $stub); $stub = preg_replace('/^return; class ([a-zA-Z]++)/m', 'interface \1StubInterface', $stub, 1); + $stub = preg_replace('/^ public const .*/m', '', $stub); eval(substr($stub, 5)); $proxy = file_get_contents(\dirname(__DIR__, 2)."/Traits/{$class}6Proxy.php"); diff --git a/src/Symfony/Component/Cache/Traits/Redis6Proxy.php b/src/Symfony/Component/Cache/Traits/Redis6Proxy.php index f5373df72f3bf..8d9ac740fd52c 100644 --- a/src/Symfony/Component/Cache/Traits/Redis6Proxy.php +++ b/src/Symfony/Component/Cache/Traits/Redis6Proxy.php @@ -44,9 +44,9 @@ public function _compress($value): string return $this->lazyObjectReal->_compress(...\func_get_args()); } - public function _pack($value): string + public function _uncompress($value): string { - return $this->lazyObjectReal->_pack(...\func_get_args()); + return $this->lazyObjectReal->_uncompress(...\func_get_args()); } public function _prefix($key): string @@ -59,19 +59,19 @@ public function _serialize($value): string return $this->lazyObjectReal->_serialize(...\func_get_args()); } - public function _uncompress($value): string + public function _unserialize($value): mixed { - return $this->lazyObjectReal->_uncompress(...\func_get_args()); + return $this->lazyObjectReal->_unserialize(...\func_get_args()); } - public function _unpack($value): mixed + public function _pack($value): string { - return $this->lazyObjectReal->_unpack(...\func_get_args()); + return $this->lazyObjectReal->_pack(...\func_get_args()); } - public function _unserialize($value): mixed + public function _unpack($value): mixed { - return $this->lazyObjectReal->_unserialize(...\func_get_args()); + return $this->lazyObjectReal->_unpack(...\func_get_args()); } public function acl($subcmd, ...$args): mixed @@ -114,12 +114,12 @@ public function bitpos($key, $bit, $start = 0, $end = -1, $bybit = false): \Redi return $this->lazyObjectReal->bitpos(...\func_get_args()); } - public function blPop($key, $timeout_or_key, ...$extra_args): \Redis|array|false|null + public function blPop($key_or_keys, $timeout_or_key, ...$extra_args): \Redis|array|false|null { return $this->lazyObjectReal->blPop(...\func_get_args()); } - public function brPop($key, $timeout_or_key, ...$extra_args): \Redis|array|false|null + public function brPop($key_or_keys, $timeout_or_key, ...$extra_args): \Redis|array|false|null { return $this->lazyObjectReal->brPop(...\func_get_args()); } @@ -159,7 +159,7 @@ public function lmpop($keys, $from, $count = 1): \Redis|array|false|null return $this->lazyObjectReal->lmpop(...\func_get_args()); } - public function clearLastError(): \Redis|bool + public function clearLastError(): bool { return $this->lazyObjectReal->clearLastError(...\func_get_args()); } @@ -174,7 +174,7 @@ public function close(): bool return $this->lazyObjectReal->close(...\func_get_args()); } - public function command($opt, $arg): mixed + public function command($opt = null, ...$args): mixed { return $this->lazyObjectReal->command(...\func_get_args()); } @@ -194,7 +194,7 @@ public function copy($src, $dst, $options = null): \Redis|bool return $this->lazyObjectReal->copy(...\func_get_args()); } - public function dbSize(): \Redis|int + public function dbSize(): \Redis|false|int { return $this->lazyObjectReal->dbSize(...\func_get_args()); } @@ -304,7 +304,7 @@ public function flushDB($sync = null): \Redis|bool return $this->lazyObjectReal->flushDB(...\func_get_args()); } - public function geoadd($key, $lng, $lat, $member, ...$other_triples): \Redis|false|int + public function geoadd($key, $lng, $lat, $member, ...$other_triples_and_options): \Redis|false|int { return $this->lazyObjectReal->geoadd(...\func_get_args()); } @@ -424,7 +424,7 @@ public function lcs($key1, $key2, $options = null): \Redis|array|false|int|strin return $this->lazyObjectReal->lcs(...\func_get_args()); } - public function getReadTimeout(): int + public function getReadTimeout(): float { return $this->lazyObjectReal->getReadTimeout(...\func_get_args()); } @@ -434,17 +434,27 @@ public function getset($key, $value): \Redis|false|string return $this->lazyObjectReal->getset(...\func_get_args()); } - public function getTimeout(): int + public function getTimeout(): false|float { return $this->lazyObjectReal->getTimeout(...\func_get_args()); } - public function hDel($key, $member, ...$other_members): \Redis|false|int + public function getTransferredBytes(): array + { + return $this->lazyObjectReal->getTransferredBytes(...\func_get_args()); + } + + public function clearTransferredBytes(): void + { + $this->lazyObjectReal->clearTransferredBytes(...\func_get_args()); + } + + public function hDel($key, $field, ...$other_fields): \Redis|false|int { return $this->lazyObjectReal->hDel(...\func_get_args()); } - public function hExists($key, $member): \Redis|bool + public function hExists($key, $field): \Redis|bool { return $this->lazyObjectReal->hExists(...\func_get_args()); } @@ -459,12 +469,12 @@ public function hGetAll($key): \Redis|array|false return $this->lazyObjectReal->hGetAll(...\func_get_args()); } - public function hIncrBy($key, $member, $value): \Redis|false|int + public function hIncrBy($key, $field, $value): \Redis|false|int { return $this->lazyObjectReal->hIncrBy(...\func_get_args()); } - public function hIncrByFloat($key, $member, $value): \Redis|false|float + public function hIncrByFloat($key, $field, $value): \Redis|false|float { return $this->lazyObjectReal->hIncrByFloat(...\func_get_args()); } @@ -479,12 +489,12 @@ public function hLen($key): \Redis|false|int return $this->lazyObjectReal->hLen(...\func_get_args()); } - public function hMget($key, $keys): \Redis|array|false + public function hMget($key, $fields): \Redis|array|false { return $this->lazyObjectReal->hMget(...\func_get_args()); } - public function hMset($key, $keyvals): \Redis|bool + public function hMset($key, $fieldvals): \Redis|bool { return $this->lazyObjectReal->hMset(...\func_get_args()); } @@ -499,12 +509,12 @@ public function hSet($key, $member, $value): \Redis|false|int return $this->lazyObjectReal->hSet(...\func_get_args()); } - public function hSetNx($key, $member, $value): \Redis|bool + public function hSetNx($key, $field, $value): \Redis|bool { return $this->lazyObjectReal->hSetNx(...\func_get_args()); } - public function hStrLen($key, $member): \Redis|false|int + public function hStrLen($key, $field): \Redis|false|int { return $this->lazyObjectReal->hStrLen(...\func_get_args()); } @@ -519,17 +529,17 @@ public function hscan($key, &$iterator, $pattern = null, $count = 0): \Redis|arr return $this->lazyObjectReal->hscan(...\func_get_args()); } - public function incr($key, $by = 1) + public function incr($key, $by = 1): \Redis|false|int { return $this->lazyObjectReal->incr(...\func_get_args()); } - public function incrBy($key, $value) + public function incrBy($key, $value): \Redis|false|int { return $this->lazyObjectReal->incrBy(...\func_get_args()); } - public function incrByFloat($key, $value) + public function incrByFloat($key, $value): \Redis|false|float { return $this->lazyObjectReal->incrByFloat(...\func_get_args()); } @@ -564,6 +574,11 @@ public function lMove($src, $dst, $wherefrom, $whereto): \Redis|false|string return $this->lazyObjectReal->lMove(...\func_get_args()); } + public function blmove($src, $dst, $wherefrom, $whereto, $timeout): \Redis|false|string + { + return $this->lazyObjectReal->blmove(...\func_get_args()); + } + public function lPop($key, $count = 0): \Redis|array|bool|string { return $this->lazyObjectReal->lPop(...\func_get_args()); @@ -574,22 +589,22 @@ public function lPos($key, $value, $options = null): \Redis|array|bool|int|null return $this->lazyObjectReal->lPos(...\func_get_args()); } - public function lPush($key, ...$elements) + public function lPush($key, ...$elements): \Redis|false|int { return $this->lazyObjectReal->lPush(...\func_get_args()); } - public function rPush($key, ...$elements) + public function rPush($key, ...$elements): \Redis|false|int { return $this->lazyObjectReal->rPush(...\func_get_args()); } - public function lPushx($key, $value) + public function lPushx($key, $value): \Redis|false|int { return $this->lazyObjectReal->lPushx(...\func_get_args()); } - public function rPushx($key, $value) + public function rPushx($key, $value): \Redis|false|int { return $this->lazyObjectReal->rPushx(...\func_get_args()); } @@ -614,7 +629,7 @@ public function lrange($key, $start, $end): \Redis|array|false return $this->lazyObjectReal->lrange(...\func_get_args()); } - public function lrem($key, $value, $count = 0) + public function lrem($key, $value, $count = 0): \Redis|false|int { return $this->lazyObjectReal->lrem(...\func_get_args()); } @@ -624,7 +639,7 @@ public function ltrim($key, $start, $end): \Redis|bool return $this->lazyObjectReal->ltrim(...\func_get_args()); } - public function mget($keys) + public function mget($keys): \Redis|array { return $this->lazyObjectReal->mget(...\func_get_args()); } @@ -634,7 +649,7 @@ public function migrate($host, $port, $key, $dstdb, $timeout, $copy = false, $re return $this->lazyObjectReal->migrate(...\func_get_args()); } - public function move($key, $index): bool + public function move($key, $index): \Redis|bool { return $this->lazyObjectReal->move(...\func_get_args()); } @@ -669,7 +684,7 @@ public function pconnect($host, $port = 6379, $timeout = 0.0, $persistent_id = n return $this->lazyObjectReal->pconnect(...\func_get_args()); } - public function persist($key): bool + public function persist($key): \Redis|bool { return $this->lazyObjectReal->persist(...\func_get_args()); } @@ -679,27 +694,27 @@ public function pexpire($key, $timeout, $mode = null): bool return $this->lazyObjectReal->pexpire(...\func_get_args()); } - public function pexpireAt($key, $timestamp, $mode = null): bool + public function pexpireAt($key, $timestamp, $mode = null): \Redis|bool { return $this->lazyObjectReal->pexpireAt(...\func_get_args()); } - public function pfadd($key, $elements): int + public function pfadd($key, $elements): \Redis|int { return $this->lazyObjectReal->pfadd(...\func_get_args()); } - public function pfcount($key): int + public function pfcount($key): \Redis|int { return $this->lazyObjectReal->pfcount(...\func_get_args()); } - public function pfmerge($dst, $keys): bool + public function pfmerge($dst, $srckeys): \Redis|bool { return $this->lazyObjectReal->pfmerge(...\func_get_args()); } - public function ping($key = null) + public function ping($message = null): \Redis|bool|string { return $this->lazyObjectReal->ping(...\func_get_args()); } @@ -714,7 +729,7 @@ public function popen($host, $port = 6379, $timeout = 0.0, $persistent_id = null return $this->lazyObjectReal->popen(...\func_get_args()); } - public function psetex($key, $expire, $value) + public function psetex($key, $expire, $value): \Redis|bool { return $this->lazyObjectReal->psetex(...\func_get_args()); } @@ -729,7 +744,7 @@ public function pttl($key): \Redis|false|int return $this->lazyObjectReal->pttl(...\func_get_args()); } - public function publish($channel, $message): mixed + public function publish($channel, $message): \Redis|false|int { return $this->lazyObjectReal->publish(...\func_get_args()); } @@ -749,7 +764,7 @@ public function rPop($key, $count = 0): \Redis|array|bool|string return $this->lazyObjectReal->rPop(...\func_get_args()); } - public function randomKey() + public function randomKey(): \Redis|false|string { return $this->lazyObjectReal->randomKey(...\func_get_args()); } @@ -759,17 +774,17 @@ public function rawcommand($command, ...$args): mixed return $this->lazyObjectReal->rawcommand(...\func_get_args()); } - public function rename($key_src, $key_dst) + public function rename($old_name, $new_name): \Redis|bool { return $this->lazyObjectReal->rename(...\func_get_args()); } - public function renameNx($key_src, $key_dst) + public function renameNx($key_src, $key_dst): \Redis|bool { return $this->lazyObjectReal->renameNx(...\func_get_args()); } - public function restore($key, $timeout, $value, $options = null): bool + public function restore($key, $ttl, $value, $options = null): \Redis|bool { return $this->lazyObjectReal->restore(...\func_get_args()); } @@ -779,7 +794,7 @@ public function role(): mixed return $this->lazyObjectReal->role(...\func_get_args()); } - public function rpoplpush($src, $dst): \Redis|false|string + public function rpoplpush($srckey, $dstkey): \Redis|false|string { return $this->lazyObjectReal->rpoplpush(...\func_get_args()); } @@ -824,7 +839,7 @@ public function sMembers($key): \Redis|array|false return $this->lazyObjectReal->sMembers(...\func_get_args()); } - public function sMisMember($key, $member, ...$other_members): array + public function sMisMember($key, $member, ...$other_members): \Redis|array|false { return $this->lazyObjectReal->sMisMember(...\func_get_args()); } @@ -854,7 +869,7 @@ public function sUnionStore($dst, $key, ...$other_keys): \Redis|false|int return $this->lazyObjectReal->sUnionStore(...\func_get_args()); } - public function save(): bool + public function save(): \Redis|bool { return $this->lazyObjectReal->save(...\func_get_args()); } @@ -879,17 +894,17 @@ public function select($db): \Redis|bool return $this->lazyObjectReal->select(...\func_get_args()); } - public function set($key, $value, $opt = null): \Redis|bool|string + public function set($key, $value, $options = null): \Redis|bool|string { return $this->lazyObjectReal->set(...\func_get_args()); } - public function setBit($key, $idx, $value) + public function setBit($key, $idx, $value): \Redis|false|int { return $this->lazyObjectReal->setBit(...\func_get_args()); } - public function setRange($key, $start, $value) + public function setRange($key, $index, $value): \Redis|false|int { return $this->lazyObjectReal->setRange(...\func_get_args()); } @@ -904,7 +919,7 @@ public function setex($key, $expire, $value) return $this->lazyObjectReal->setex(...\func_get_args()); } - public function setnx($key, $value) + public function setnx($key, $value): \Redis|bool { return $this->lazyObjectReal->setnx(...\func_get_args()); } @@ -914,11 +929,21 @@ public function sismember($key, $value): \Redis|bool return $this->lazyObjectReal->sismember(...\func_get_args()); } - public function slaveof($host = null, $port = 6379): bool + public function slaveof($host = null, $port = 6379): \Redis|bool { return $this->lazyObjectReal->slaveof(...\func_get_args()); } + public function replicaof($host = null, $port = 6379): \Redis|bool + { + return $this->lazyObjectReal->replicaof(...\func_get_args()); + } + + public function touch($key_or_array, ...$more_keys): \Redis|false|int + { + return $this->lazyObjectReal->touch(...\func_get_args()); + } + public function slowlog($operation, $length = 0): mixed { return $this->lazyObjectReal->slowlog(...\func_get_args()); @@ -929,6 +954,11 @@ public function sort($key, $options = null): mixed return $this->lazyObjectReal->sort(...\func_get_args()); } + public function sort_ro($key, $options = null): mixed + { + return $this->lazyObjectReal->sort_ro(...\func_get_args()); + } + public function sortAsc($key, $pattern = null, $get = null, $offset = -1, $count = -1, $store = null): array { return $this->lazyObjectReal->sortAsc(...\func_get_args()); @@ -959,7 +989,12 @@ public function sscan($key, &$iterator, $pattern = null, $count = 0): array|fals return $this->lazyObjectReal->sscan(...\func_get_args()); } - public function strlen($key) + public function ssubscribe($channels, $cb): bool + { + return $this->lazyObjectReal->ssubscribe(...\func_get_args()); + } + + public function strlen($key): \Redis|false|int { return $this->lazyObjectReal->strlen(...\func_get_args()); } @@ -969,12 +1004,17 @@ public function subscribe($channels, $cb): bool return $this->lazyObjectReal->subscribe(...\func_get_args()); } - public function swapdb($src, $dst): bool + public function sunsubscribe($channels): \Redis|array|bool + { + return $this->lazyObjectReal->sunsubscribe(...\func_get_args()); + } + + public function swapdb($src, $dst): \Redis|bool { return $this->lazyObjectReal->swapdb(...\func_get_args()); } - public function time(): array + public function time(): \Redis|array { return $this->lazyObjectReal->time(...\func_get_args()); } @@ -984,12 +1024,12 @@ public function ttl($key): \Redis|false|int return $this->lazyObjectReal->ttl(...\func_get_args()); } - public function type($key) + public function type($key): \Redis|false|int { return $this->lazyObjectReal->type(...\func_get_args()); } - public function unlink($key, ...$other_keys) + public function unlink($key, ...$other_keys): \Redis|false|int { return $this->lazyObjectReal->unlink(...\func_get_args()); } @@ -999,17 +1039,17 @@ public function unsubscribe($channels): \Redis|array|bool return $this->lazyObjectReal->unsubscribe(...\func_get_args()); } - public function unwatch() + public function unwatch(): \Redis|bool { return $this->lazyObjectReal->unwatch(...\func_get_args()); } - public function watch($key, ...$other_keys) + public function watch($key, ...$other_keys): \Redis|bool { return $this->lazyObjectReal->watch(...\func_get_args()); } - public function wait($count, $timeout): false|int + public function wait($numreplicas, $timeout): false|int { return $this->lazyObjectReal->wait(...\func_get_args()); } @@ -1039,7 +1079,7 @@ public function xdel($key, $ids): \Redis|false|int return $this->lazyObjectReal->xdel(...\func_get_args()); } - public function xgroup($operation, $key = null, $arg1 = null, $arg2 = null, $arg3 = false): mixed + public function xgroup($operation, $key = null, $group = null, $id_or_consumer = null, $mkstream = false, $entries_read = -2): mixed { return $this->lazyObjectReal->xgroup(...\func_get_args()); } @@ -1074,12 +1114,12 @@ public function xreadgroup($group, $consumer, $streams, $count = 1, $block = 1): return $this->lazyObjectReal->xreadgroup(...\func_get_args()); } - public function xrevrange($key, $start, $end, $count = -1): \Redis|array|bool + public function xrevrange($key, $end, $start, $count = -1): \Redis|array|bool { return $this->lazyObjectReal->xrevrange(...\func_get_args()); } - public function xtrim($key, $maxlen, $approx = false, $minid = false, $limit = -1): \Redis|false|int + public function xtrim($key, $threshold, $approx = false, $minid = false, $limit = -1): \Redis|false|int { return $this->lazyObjectReal->xtrim(...\func_get_args()); } @@ -1114,12 +1154,12 @@ public function zMscore($key, $member, ...$other_members): \Redis|array|false return $this->lazyObjectReal->zMscore(...\func_get_args()); } - public function zPopMax($key, $value = null): \Redis|array|false + public function zPopMax($key, $count = null): \Redis|array|false { return $this->lazyObjectReal->zPopMax(...\func_get_args()); } - public function zPopMin($key, $value = null): \Redis|array|false + public function zPopMin($key, $count = null): \Redis|array|false { return $this->lazyObjectReal->zPopMin(...\func_get_args()); } @@ -1179,12 +1219,12 @@ public function zRevRange($key, $start, $end, $scores = null): \Redis|array|fals return $this->lazyObjectReal->zRevRange(...\func_get_args()); } - public function zRevRangeByLex($key, $min, $max, $offset = -1, $count = -1): \Redis|array|false + public function zRevRangeByLex($key, $max, $min, $offset = -1, $count = -1): \Redis|array|false { return $this->lazyObjectReal->zRevRangeByLex(...\func_get_args()); } - public function zRevRangeByScore($key, $start, $end, $options = []): \Redis|array|false + public function zRevRangeByScore($key, $max, $min, $options = []): \Redis|array|false { return $this->lazyObjectReal->zRevRangeByScore(...\func_get_args()); } @@ -1204,7 +1244,7 @@ public function zdiff($keys, $options = null): \Redis|array|false return $this->lazyObjectReal->zdiff(...\func_get_args()); } - public function zdiffstore($dst, $keys, $options = null): \Redis|false|int + public function zdiffstore($dst, $keys): \Redis|false|int { return $this->lazyObjectReal->zdiffstore(...\func_get_args()); } @@ -1224,7 +1264,7 @@ public function zinterstore($dst, $keys, $weights = null, $aggregate = null): \R return $this->lazyObjectReal->zinterstore(...\func_get_args()); } - public function zscan($key, &$iterator, $pattern = null, $count = 0): \Redis|array|bool + public function zscan($key, &$iterator, $pattern = null, $count = 0): \Redis|array|false { return $this->lazyObjectReal->zscan(...\func_get_args()); } diff --git a/src/Symfony/Component/Cache/Traits/RedisCluster6Proxy.php b/src/Symfony/Component/Cache/Traits/RedisCluster6Proxy.php index 9cc6abf7fad94..e94fa2b36711e 100644 --- a/src/Symfony/Component/Cache/Traits/RedisCluster6Proxy.php +++ b/src/Symfony/Component/Cache/Traits/RedisCluster6Proxy.php @@ -44,44 +44,44 @@ public function _compress($value): string return $this->lazyObjectReal->_compress(...\func_get_args()); } - public function _masters(): array + public function _uncompress($value): string { - return $this->lazyObjectReal->_masters(...\func_get_args()); + return $this->lazyObjectReal->_uncompress(...\func_get_args()); } - public function _pack($value): string + public function _serialize($value): bool|string { - return $this->lazyObjectReal->_pack(...\func_get_args()); + return $this->lazyObjectReal->_serialize(...\func_get_args()); } - public function _prefix($key): bool|string + public function _unserialize($value): mixed { - return $this->lazyObjectReal->_prefix(...\func_get_args()); + return $this->lazyObjectReal->_unserialize(...\func_get_args()); } - public function _redir(): ?string + public function _pack($value): string { - return $this->lazyObjectReal->_redir(...\func_get_args()); + return $this->lazyObjectReal->_pack(...\func_get_args()); } - public function _serialize($value): bool|string + public function _unpack($value): mixed { - return $this->lazyObjectReal->_serialize(...\func_get_args()); + return $this->lazyObjectReal->_unpack(...\func_get_args()); } - public function _uncompress($value): string + public function _prefix($key): bool|string { - return $this->lazyObjectReal->_uncompress(...\func_get_args()); + return $this->lazyObjectReal->_prefix(...\func_get_args()); } - public function _unpack($value): mixed + public function _masters(): array { - return $this->lazyObjectReal->_unpack(...\func_get_args()); + return $this->lazyObjectReal->_masters(...\func_get_args()); } - public function _unserialize($value): mixed + public function _redir(): ?string { - return $this->lazyObjectReal->_unserialize(...\func_get_args()); + return $this->lazyObjectReal->_redir(...\func_get_args()); } public function acl($key_or_address, $subcmd, ...$args): mixed @@ -134,6 +134,16 @@ public function brpoplpush($srckey, $deskey, $timeout): mixed return $this->lazyObjectReal->brpoplpush(...\func_get_args()); } + public function lmove($src, $dst, $wherefrom, $whereto): \Redis|false|string + { + return $this->lazyObjectReal->lmove(...\func_get_args()); + } + + public function blmove($src, $dst, $wherefrom, $whereto, $timeout): \Redis|false|string + { + return $this->lazyObjectReal->blmove(...\func_get_args()); + } + public function bzpopmax($key, $timeout_or_key, ...$extra_args): array { return $this->lazyObjectReal->bzpopmax(...\func_get_args()); @@ -199,6 +209,11 @@ public function dbsize($key_or_address): \RedisCluster|int return $this->lazyObjectReal->dbsize(...\func_get_args()); } + public function copy($src, $dst, $options = null): \RedisCluster|bool + { + return $this->lazyObjectReal->copy(...\func_get_args()); + } + public function decr($key, $by = 1): \RedisCluster|false|int { return $this->lazyObjectReal->decr(...\func_get_args()); @@ -264,6 +279,11 @@ public function exists($key, ...$other_keys): \RedisCluster|bool|int return $this->lazyObjectReal->exists(...\func_get_args()); } + public function touch($key, ...$other_keys): \RedisCluster|bool|int + { + return $this->lazyObjectReal->touch(...\func_get_args()); + } + public function expire($key, $timeout, $mode = null): \RedisCluster|bool { return $this->lazyObjectReal->expire(...\func_get_args()); @@ -294,7 +314,7 @@ public function flushdb($key_or_address, $async = false): \RedisCluster|bool return $this->lazyObjectReal->flushdb(...\func_get_args()); } - public function geoadd($key, $lng, $lat, $member, ...$other_triples): \RedisCluster|int + public function geoadd($key, $lng, $lat, $member, ...$other_triples_and_options): \RedisCluster|false|int { return $this->lazyObjectReal->geoadd(...\func_get_args()); } @@ -334,6 +354,16 @@ public function georadiusbymember_ro($key, $member, $radius, $unit, $options = [ return $this->lazyObjectReal->georadiusbymember_ro(...\func_get_args()); } + public function geosearch($key, $position, $shape, $unit, $options = []): \RedisCluster|array + { + return $this->lazyObjectReal->geosearch(...\func_get_args()); + } + + public function geosearchstore($dst, $src, $position, $shape, $unit, $options = []): \RedisCluster|array|false|int + { + return $this->lazyObjectReal->geosearchstore(...\func_get_args()); + } + public function get($key): mixed { return $this->lazyObjectReal->get(...\func_get_args()); @@ -374,6 +404,16 @@ public function getset($key, $value): \RedisCluster|bool|string return $this->lazyObjectReal->getset(...\func_get_args()); } + public function gettransferredbytes(): array|false + { + return $this->lazyObjectReal->gettransferredbytes(...\func_get_args()); + } + + public function cleartransferredbytes(): void + { + $this->lazyObjectReal->cleartransferredbytes(...\func_get_args()); + } + public function hdel($key, $member, ...$other_members): \RedisCluster|false|int { return $this->lazyObjectReal->hdel(...\func_get_args()); @@ -429,6 +469,11 @@ public function hscan($key, &$iterator, $pattern = null, $count = 0): array|bool return $this->lazyObjectReal->hscan(...\func_get_args()); } + public function hrandfield($key, $options = null): \RedisCluster|array|string + { + return $this->lazyObjectReal->hrandfield(...\func_get_args()); + } + public function hset($key, $member, $value): \RedisCluster|false|int { return $this->lazyObjectReal->hset(...\func_get_args()); @@ -504,6 +549,11 @@ public function lpop($key, $count = 0): \RedisCluster|array|bool|string return $this->lazyObjectReal->lpop(...\func_get_args()); } + public function lpos($key, $value, $options = null): \Redis|array|bool|int|null + { + return $this->lazyObjectReal->lpos(...\func_get_args()); + } + public function lpush($key, $value, ...$other_values): \RedisCluster|bool|int { return $this->lazyObjectReal->lpush(...\func_get_args()); @@ -764,6 +814,11 @@ public function sismember($key, $value): \RedisCluster|bool return $this->lazyObjectReal->sismember(...\func_get_args()); } + public function smismember($key, $member, ...$other_members): \RedisCluster|array|false + { + return $this->lazyObjectReal->smismember(...\func_get_args()); + } + public function slowlog($key_or_address, ...$args): mixed { return $this->lazyObjectReal->slowlog(...\func_get_args()); @@ -784,6 +839,11 @@ public function sort($key, $options = null): \RedisCluster|array|bool|int|string return $this->lazyObjectReal->sort(...\func_get_args()); } + public function sort_ro($key, $options = null): \RedisCluster|array|bool|int|string + { + return $this->lazyObjectReal->sort_ro(...\func_get_args()); + } + public function spop($key, $count = 0): \RedisCluster|array|false|string { return $this->lazyObjectReal->spop(...\func_get_args()); @@ -879,11 +939,16 @@ public function xdel($key, $ids): \RedisCluster|false|int return $this->lazyObjectReal->xdel(...\func_get_args()); } - public function xgroup($operation, $key = null, $arg1 = null, $arg2 = null, $arg3 = false): mixed + public function xgroup($operation, $key = null, $group = null, $id_or_consumer = null, $mkstream = false, $entries_read = -2): mixed { return $this->lazyObjectReal->xgroup(...\func_get_args()); } + public function xautoclaim($key, $group, $consumer, $min_idle, $start, $count = -1, $justid = false): \RedisCluster|array|bool + { + return $this->lazyObjectReal->xautoclaim(...\func_get_args()); + } + public function xinfo($operation, $arg1 = null, $arg2 = null, $count = -1): mixed { return $this->lazyObjectReal->xinfo(...\func_get_args()); @@ -979,6 +1044,11 @@ public function zrangestore($dstkey, $srckey, $start, $end, $options = null): \R return $this->lazyObjectReal->zrangestore(...\func_get_args()); } + public function zrandmember($key, $options = null): \RedisCluster|array|string + { + return $this->lazyObjectReal->zrandmember(...\func_get_args()); + } + public function zrangebylex($key, $min, $max, $offset = -1, $count = -1): \RedisCluster|array|false { return $this->lazyObjectReal->zrangebylex(...\func_get_args()); @@ -1044,8 +1114,33 @@ public function zscore($key, $member): \RedisCluster|false|float return $this->lazyObjectReal->zscore(...\func_get_args()); } + public function zmscore($key, $member, ...$other_members): \Redis|array|false + { + return $this->lazyObjectReal->zmscore(...\func_get_args()); + } + public function zunionstore($dst, $keys, $weights = null, $aggregate = null): \RedisCluster|false|int { return $this->lazyObjectReal->zunionstore(...\func_get_args()); } + + public function zinter($keys, $weights = null, $options = null): \RedisCluster|array|false + { + return $this->lazyObjectReal->zinter(...\func_get_args()); + } + + public function zdiffstore($dst, $keys): \RedisCluster|false|int + { + return $this->lazyObjectReal->zdiffstore(...\func_get_args()); + } + + public function zunion($keys, $weights = null, $options = null): \RedisCluster|array|false + { + return $this->lazyObjectReal->zunion(...\func_get_args()); + } + + public function zdiff($keys, $options = null): \RedisCluster|array|false + { + return $this->lazyObjectReal->zdiff(...\func_get_args()); + } } From d73459ef2559300bd1323ff103501924bf3a7820 Mon Sep 17 00:00:00 2001 From: victor-prdh Date: Wed, 7 Dec 2022 20:37:18 +0100 Subject: [PATCH 19/58] [Clock] fix usleep deprecation warning --- src/Symfony/Component/Clock/MonotonicClock.php | 2 +- src/Symfony/Component/Clock/NativeClock.php | 2 +- src/Symfony/Component/Clock/Tests/MonotonicClockTest.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Clock/MonotonicClock.php b/src/Symfony/Component/Clock/MonotonicClock.php index ef4bfd50cb697..badb99f1daba0 100644 --- a/src/Symfony/Component/Clock/MonotonicClock.php +++ b/src/Symfony/Component/Clock/MonotonicClock.php @@ -67,7 +67,7 @@ public function sleep(float|int $seconds): void } if (0 < $us = $seconds - $s) { - usleep($us * 1E6); + usleep((int) ($us * 1E6)); } } diff --git a/src/Symfony/Component/Clock/NativeClock.php b/src/Symfony/Component/Clock/NativeClock.php index 0fcf7879a2d9d..21ade183fb435 100644 --- a/src/Symfony/Component/Clock/NativeClock.php +++ b/src/Symfony/Component/Clock/NativeClock.php @@ -41,7 +41,7 @@ public function sleep(float|int $seconds): void } if (0 < $us = $seconds - $s) { - usleep($us * 1E6); + usleep((int) ($us * 1E6)); } } diff --git a/src/Symfony/Component/Clock/Tests/MonotonicClockTest.php b/src/Symfony/Component/Clock/Tests/MonotonicClockTest.php index 3230c75371b2e..54cfe78c375cc 100644 --- a/src/Symfony/Component/Clock/Tests/MonotonicClockTest.php +++ b/src/Symfony/Component/Clock/Tests/MonotonicClockTest.php @@ -56,7 +56,7 @@ public function testSleep() usleep(10); $after = microtime(true); - $this->assertGreaterThanOrEqual($before + 1.5, $now); + $this->assertGreaterThanOrEqual($before + 1.499999, $now); $this->assertLessThan($after, $now); $this->assertLessThan(1.9, $now - $before); $this->assertSame($tz, $clock->now()->getTimezone()->getName()); From 248e79258269e5ea2a167c73eba2a169a8432904 Mon Sep 17 00:00:00 2001 From: Steffen Persch Date: Fri, 9 Dec 2022 23:27:19 +0100 Subject: [PATCH 20/58] [TwigBundle] Alias BodyRendererInterface --- src/Symfony/Bundle/TwigBundle/Resources/config/mailer.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/mailer.php b/src/Symfony/Bundle/TwigBundle/Resources/config/mailer.php index 1444481f2c0ba..e43658a7da02c 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/config/mailer.php +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/mailer.php @@ -13,6 +13,7 @@ use Symfony\Bridge\Twig\Mime\BodyRenderer; use Symfony\Component\Mailer\EventListener\MessageListener; +use Symfony\Component\Mime\BodyRendererInterface; return static function (ContainerConfigurator $container) { $container->services() @@ -22,5 +23,6 @@ ->set('twig.mime_body_renderer', BodyRenderer::class) ->args([service('twig')]) + ->alias(BodyRendererInterface::class, 'twig.mime_body_renderer') ; }; From ef8c3cdfd30e62610d47e1e27fa763ad0a9855c9 Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Sun, 11 Dec 2022 10:51:57 +0100 Subject: [PATCH 21/58] [Translator] Fix typo "internal" / "interval" --- src/Symfony/Contracts/Translation/Test/TranslatorTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Contracts/Translation/Test/TranslatorTest.php b/src/Symfony/Contracts/Translation/Test/TranslatorTest.php index 98f0267b7512a..68587f12d9817 100644 --- a/src/Symfony/Contracts/Translation/Test/TranslatorTest.php +++ b/src/Symfony/Contracts/Translation/Test/TranslatorTest.php @@ -140,7 +140,7 @@ public function getTransChoiceTests() } /** - * @dataProvider getInternal + * @dataProvider getInterval */ public function testInterval($expected, $number, $interval) { @@ -149,7 +149,7 @@ public function testInterval($expected, $number, $interval) $this->assertEquals($expected, $translator->trans($interval.' foo|[1,Inf[ bar', ['%count%' => $number])); } - public function getInternal() + public function getInterval() { return [ ['foo', 3, '{1,2, 3 ,4}'], From 97fe54d64e5ad7836c69d5f8bcfad69922aaa92f Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Sun, 11 Dec 2022 18:33:12 +0100 Subject: [PATCH 22/58] [SecurityBundle] Fix authenticator existence check in `Security::login()` --- .../Bundle/SecurityBundle/Security.php | 2 +- .../SecurityBundle/Tests/SecurityTest.php | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/SecurityBundle/Security.php b/src/Symfony/Bundle/SecurityBundle/Security.php index c894c1b926bdb..d78cfc0edd02f 100644 --- a/src/Symfony/Bundle/SecurityBundle/Security.php +++ b/src/Symfony/Bundle/SecurityBundle/Security.php @@ -116,7 +116,7 @@ public function logout(bool $validateCsrfToken = true): ?Response private function getAuthenticator(?string $authenticatorName, string $firewallName): AuthenticatorInterface { - if (!\array_key_exists($firewallName, $this->authenticators)) { + if (!isset($this->authenticators[$firewallName])) { throw new LogicException(sprintf('No authenticators found for firewall "%s".', $firewallName)); } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/SecurityTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/SecurityTest.php index 7ee7a2465263d..b69467cb95cf3 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/SecurityTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/SecurityTest.php @@ -169,6 +169,38 @@ public function testLogin() $security->login($user); } + public function testLoginWithoutAuthenticatorThrows() + { + $request = new Request(); + $authenticator = $this->createMock(AuthenticatorInterface::class); + $requestStack = $this->createMock(RequestStack::class); + $firewallMap = $this->createMock(FirewallMap::class); + $firewall = new FirewallConfig('main', 'main'); + $user = $this->createMock(UserInterface::class); + $userChecker = $this->createMock(UserCheckerInterface::class); + + $container = $this->createMock(ContainerInterface::class); + $container + ->expects($this->atLeastOnce()) + ->method('get') + ->willReturnMap([ + ['request_stack', $requestStack], + ['security.firewall.map', $firewallMap], + ['security.user_checker', $userChecker], + ]) + ; + + $requestStack->expects($this->once())->method('getCurrentRequest')->willReturn($request); + $firewallMap->expects($this->once())->method('getFirewallConfig')->willReturn($firewall); + + $security = new Security($container, ['main' => null]); + + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('No authenticators found for firewall "main".'); + + $security->login($user); + } + public function testLogout() { $request = new Request(); From 1e7202d19c69817e134941a670939450a9204e84 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Fri, 9 Dec 2022 17:02:57 +0100 Subject: [PATCH 23/58] Fix missing command not matching namespace error message --- src/Symfony/Component/Console/Application.php | 2 ++ .../Console/Tests/ApplicationTest.php | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index 99548faf858b8..d4ec1be090927 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -298,6 +298,8 @@ public function doRun(InputInterface $input, OutputInterface $output) return isset($event) ? $event->getExitCode() : 1; } + + throw $e; } catch (NamespaceNotFoundException) { throw $e; } diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index c178151688928..c82c3eb188418 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -18,6 +18,7 @@ use Symfony\Component\Console\Command\HelpCommand; use Symfony\Component\Console\Command\LazyCommand; use Symfony\Component\Console\Command\SignalableCommandInterface; +use Symfony\Component\Console\CommandLoader\CommandLoaderInterface; use Symfony\Component\Console\CommandLoader\FactoryCommandLoader; use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass; use Symfony\Component\Console\Event\ConsoleCommandEvent; @@ -1466,6 +1467,25 @@ public function testRunWithError() } } + public function testRunWithFindError() + { + $this->expectException(\Error::class); + $this->expectExceptionMessage('Find exception'); + + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + // Throws an exception when find fails + $commandLoader = $this->createMock(CommandLoaderInterface::class); + $commandLoader->method('getNames')->willThrowException(new \Error('Find exception')); + $application->setCommandLoader($commandLoader); + + // The exception should not be ignored + $tester = new ApplicationTester($application); + $tester->run(['command' => 'foo']); + } + public function testRunAllowsErrorListenersToSilenceTheException() { $dispatcher = $this->getDispatcher(); From 57c6a623b34da9f4c15978cc7a07e29f0fc2d2fc Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 12 Dec 2022 09:20:26 +0100 Subject: [PATCH 24/58] [VarExporter] Fix adding a key to an uninitialized array --- .../Component/VarExporter/LazyGhostTrait.php | 36 ++++++++++++++---- .../Component/VarExporter/LazyProxyTrait.php | 36 ++++++++++++++---- .../VarExporter/Tests/LazyGhostTraitTest.php | 38 +++++++++++++++++++ .../VarExporter/Tests/LazyProxyTraitTest.php | 12 ++++++ 4 files changed, 106 insertions(+), 16 deletions(-) diff --git a/src/Symfony/Component/VarExporter/LazyGhostTrait.php b/src/Symfony/Component/VarExporter/LazyGhostTrait.php index 21bef1cb5b495..a0dea4dd5b8ee 100644 --- a/src/Symfony/Component/VarExporter/LazyGhostTrait.php +++ b/src/Symfony/Component/VarExporter/LazyGhostTrait.php @@ -193,17 +193,37 @@ public function &__get($name): mixed get_in_scope: - if (null === $scope) { - if (null === $readonlyScope) { - return $this->$name; + try { + if (null === $scope) { + if (null === $readonlyScope) { + return $this->$name; + } + $value = $this->$name; + + return $value; } - $value = $this->$name; + $accessor = Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope); - return $value; - } - $accessor = Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope); + return $accessor['get']($this, $name, null !== $readonlyScope); + } catch (\Error $e) { + if (\Error::class !== $e::class || !str_starts_with($e->getMessage(), 'Cannot access uninitialized non-nullable property')) { + throw $e; + } + + try { + if (null === $scope) { + $this->$name = []; + + return $this->$name; + } - return $accessor['get']($this, $name, null !== $readonlyScope); + $accessor['set']($this, $name, []); + + return $accessor['get']($this, $name, null !== $readonlyScope); + } catch (\Error) { + throw $e; + } + } } public function __set($name, $value): void diff --git a/src/Symfony/Component/VarExporter/LazyProxyTrait.php b/src/Symfony/Component/VarExporter/LazyProxyTrait.php index 9b42f25c6a9fa..fa6a741fd6d40 100644 --- a/src/Symfony/Component/VarExporter/LazyProxyTrait.php +++ b/src/Symfony/Component/VarExporter/LazyProxyTrait.php @@ -130,17 +130,37 @@ public function &__get($name): mixed get_in_scope: - if (null === $scope) { - if (null === $readonlyScope && 1 !== $parent) { - return $instance->$name; + try { + if (null === $scope) { + if (null === $readonlyScope && 1 !== $parent) { + return $instance->$name; + } + $value = $instance->$name; + + return $value; } - $value = $instance->$name; + $accessor = Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope); - return $value; - } - $accessor = Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope); + return $accessor['get']($instance, $name, null !== $readonlyScope || 1 === $parent); + } catch (\Error $e) { + if (\Error::class !== $e::class || !str_starts_with($e->getMessage(), 'Cannot access uninitialized non-nullable property')) { + throw $e; + } + + try { + if (null === $scope) { + $instance->$name = []; + + return $instance->$name; + } - return $accessor['get']($instance, $name, null !== $readonlyScope || 1 === $parent); + $accessor['set']($instance, $name, []); + + return $accessor['get']($instance, $name, null !== $readonlyScope || 1 === $parent); + } catch (\Error) { + throw $e; + } + } } public function __set($name, $value): void diff --git a/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php b/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php index 25673fb073ca8..3ef2a8c95c1c2 100644 --- a/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php +++ b/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\VarExporter\Internal\LazyObjectState; +use Symfony\Component\VarExporter\ProxyHelper; use Symfony\Component\VarExporter\Tests\Fixtures\LazyGhost\ChildMagicClass; use Symfony\Component\VarExporter\Tests\Fixtures\LazyGhost\ChildStdClass; use Symfony\Component\VarExporter\Tests\Fixtures\LazyGhost\ChildTestClass; @@ -393,4 +394,41 @@ public function testFullInitializationAfterPartialInitialization() $this->assertSame(3, ((array) $instance)["\0".TestClass::class."\0private"]); $this->assertSame(1001, $counter); } + + public function testIndirectModification() + { + $obj = new class() { + public array $foo; + }; + $proxy = $this->createLazyGhost($obj::class, fn () => null); + + $proxy->foo[] = 123; + + $this->assertSame([123], $proxy->foo); + } + + /** + * @template T + * + * @param class-string $class + * + * @return T + */ + private function createLazyGhost(string $class, \Closure|array $initializer, array $skippedProperties = null): object + { + $r = new \ReflectionClass($class); + + if (str_contains($class, "\0")) { + $class = __CLASS__.'\\'.debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['function'].'_L'.$r->getStartLine(); + class_alias($r->name, $class); + } + $proxy = str_replace($r->name, $class, ProxyHelper::generateLazyGhost($r)); + $class = str_replace('\\', '_', $class).'_'.md5($proxy); + + if (!class_exists($class, false)) { + eval((\PHP_VERSION_ID >= 80200 && $r->isReadOnly() ? 'readonly ' : '').'class '.$class.' '.$proxy); + } + + return $class::createLazyGhost($initializer, $skippedProperties); + } } diff --git a/src/Symfony/Component/VarExporter/Tests/LazyProxyTraitTest.php b/src/Symfony/Component/VarExporter/Tests/LazyProxyTraitTest.php index 04497f02b34ae..074934ae17991 100644 --- a/src/Symfony/Component/VarExporter/Tests/LazyProxyTraitTest.php +++ b/src/Symfony/Component/VarExporter/Tests/LazyProxyTraitTest.php @@ -233,6 +233,18 @@ public function setFoo($foo): static $this->assertSame(234, $proxy->foo); } + public function testIndirectModification() + { + $obj = new class() { + public array $foo; + }; + $proxy = $this->createLazyProxy($obj::class, fn () => $obj); + + $proxy->foo[] = 123; + + $this->assertSame([123], $proxy->foo); + } + /** * @requires PHP 8.2 */ From bccd23d4df5b526f0d141ee0404e676811932c51 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 12 Dec 2022 16:54:21 +0100 Subject: [PATCH 25/58] Fix getting the name of closures on PHP 8.1.11+ --- .../FrameworkBundle/Console/Descriptor/JsonDescriptor.php | 2 +- .../FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php | 2 +- .../FrameworkBundle/Console/Descriptor/TextDescriptor.php | 2 +- .../Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php | 2 +- .../Bundle/SecurityBundle/Command/DebugFirewallCommand.php | 2 +- src/Symfony/Component/ErrorHandler/Internal/TentativeTypes.php | 1 + src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php | 2 +- .../HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php | 2 +- .../Component/HttpKernel/DataCollector/RequestDataCollector.php | 2 +- src/Symfony/Component/Messenger/Handler/HandlerDescriptor.php | 2 +- src/Symfony/Component/String/LazyString.php | 2 +- src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php | 2 +- 12 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php index 162a1fb806c9e..0ad063343f78c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php @@ -361,7 +361,7 @@ private function getCallableData($callable): array } $data['name'] = $r->name; - if ($class = $r->getClosureScopeClass()) { + if ($class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) { $data['class'] = $class->name; if (!$r->getClosureThis()) { $data['static'] = true; diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php index cf70089405419..a3fbabc6d2bf9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php @@ -377,7 +377,7 @@ protected function describeCallable($callable, array $options = []) } $string .= "\n".sprintf('- Name: `%s`', $r->name); - if ($class = $r->getClosureScopeClass()) { + if ($class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) { $string .= "\n".sprintf('- Class: `%s`', $class->name); if (!$r->getClosureThis()) { $string .= "\n- Static: yes"; diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php index a76d7f20744bc..e7eb18762de86 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php @@ -614,7 +614,7 @@ private function formatCallable($callable): string if (str_contains($r->name, '{closure}')) { return 'Closure()'; } - if ($class = $r->getClosureScopeClass()) { + if ($class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) { return sprintf('%s::%s()', $class->name, $r->name); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php index ef59fe3dc4fa3..350452f33cee9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php @@ -548,7 +548,7 @@ private function getCallableDocument($callable): \DOMDocument } $callableXML->setAttribute('name', $r->name); - if ($class = $r->getClosureScopeClass()) { + if ($class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) { $callableXML->setAttribute('class', $class->name); if (!$r->getClosureThis()) { $callableXML->setAttribute('static', 'true'); diff --git a/src/Symfony/Bundle/SecurityBundle/Command/DebugFirewallCommand.php b/src/Symfony/Bundle/SecurityBundle/Command/DebugFirewallCommand.php index b5fe209944ce9..3757c5657ae4a 100644 --- a/src/Symfony/Bundle/SecurityBundle/Command/DebugFirewallCommand.php +++ b/src/Symfony/Bundle/SecurityBundle/Command/DebugFirewallCommand.php @@ -255,7 +255,7 @@ private function formatCallable($callable): string if (false !== strpos($r->name, '{closure}')) { return 'Closure()'; } - if ($class = $r->getClosureScopeClass()) { + if ($class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) { return sprintf('%s::%s()', $class->name, $r->name); } diff --git a/src/Symfony/Component/ErrorHandler/Internal/TentativeTypes.php b/src/Symfony/Component/ErrorHandler/Internal/TentativeTypes.php index 2168a1c075816..1e8afe39b9a4e 100644 --- a/src/Symfony/Component/ErrorHandler/Internal/TentativeTypes.php +++ b/src/Symfony/Component/ErrorHandler/Internal/TentativeTypes.php @@ -753,6 +753,7 @@ class TentativeTypes 'isVariadic' => 'bool', 'isStatic' => 'bool', 'getClosureThis' => '?object', + 'getClosureCalledClass' => '?ReflectionClass', 'getClosureScopeClass' => '?ReflectionClass', 'getDocComment' => 'string|false', 'getEndLine' => 'int|false', diff --git a/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php b/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php index 3916716ec0bc7..86d3854b22c4e 100644 --- a/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php +++ b/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php @@ -49,7 +49,7 @@ public function __construct($listener, ?string $name, Stopwatch $stopwatch, Even $r = new \ReflectionFunction($listener); if (str_contains($r->name, '{closure}')) { $this->pretty = $this->name = 'closure'; - } elseif ($class = $r->getClosureScopeClass()) { + } elseif ($class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) { $this->name = $class->name; $this->pretty = $this->name.'::'.$r->name; } else { diff --git a/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php b/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php index e3835c057a4a3..85bb805f34bb6 100644 --- a/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php +++ b/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php @@ -33,7 +33,7 @@ public function createArgumentMetadata($controller): array $class = $reflection->class; } else { $reflection = new \ReflectionFunction($controller); - if ($class = str_contains($reflection->name, '{closure}') ? null : $reflection->getClosureScopeClass()) { + if ($class = str_contains($reflection->name, '{closure}') ? null : (\PHP_VERSION_ID >= 80111 ? $reflection->getClosureCalledClass() : $reflection->getClosureScopeClass())) { $class = $class->name; } } diff --git a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php index 951860a225668..5717000f292c7 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php @@ -479,7 +479,7 @@ private function parseController($controller) } $controller['method'] = $r->name; - if ($class = $r->getClosureScopeClass()) { + if ($class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) { $controller['class'] = $class->name; } else { return $r->name; diff --git a/src/Symfony/Component/Messenger/Handler/HandlerDescriptor.php b/src/Symfony/Component/Messenger/Handler/HandlerDescriptor.php index 6acb2c2f377bf..5957e3f13823b 100644 --- a/src/Symfony/Component/Messenger/Handler/HandlerDescriptor.php +++ b/src/Symfony/Component/Messenger/Handler/HandlerDescriptor.php @@ -37,7 +37,7 @@ public function __construct(callable $handler, array $options = []) if (str_contains($r->name, '{closure}')) { $this->name = 'Closure'; } elseif (!$handler = $r->getClosureThis()) { - $class = $r->getClosureScopeClass(); + $class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass(); $this->name = ($class ? $class->name.'::' : '').$r->name; } else { diff --git a/src/Symfony/Component/String/LazyString.php b/src/Symfony/Component/String/LazyString.php index 3b10595f3e677..9c7a9c58b659b 100644 --- a/src/Symfony/Component/String/LazyString.php +++ b/src/Symfony/Component/String/LazyString.php @@ -148,7 +148,7 @@ private static function getPrettyName(callable $callback): string } elseif ($callback instanceof \Closure) { $r = new \ReflectionFunction($callback); - if (false !== strpos($r->name, '{closure}') || !$class = $r->getClosureScopeClass()) { + if (false !== strpos($r->name, '{closure}') || !$class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) { return $r->name; } diff --git a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php index 274ee0d98f7da..5c644053ad136 100644 --- a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php @@ -197,7 +197,7 @@ public static function castFunctionAbstract(\ReflectionFunctionAbstract $c, arra self::addMap($a, $c, [ 'returnsReference' => 'returnsReference', 'returnType' => 'getReturnType', - 'class' => 'getClosureScopeClass', + 'class' => \PHP_VERSION_ID >= 80111 ? 'getClosureCalledClass' : 'getClosureScopeClass', 'this' => 'getClosureThis', ]); From e18c36ade33c5662040f881996e0c74cfdd4abf7 Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Mon, 12 Dec 2022 19:52:38 +0100 Subject: [PATCH 26/58] [ErrorHandler][DebugClassLoader] Fix some new return types support --- .../ErrorHandler/DebugClassLoader.php | 12 +++++++- .../Tests/DebugClassLoaderTest.php | 4 +++ .../Tests/Fixtures/ReturnType.php | 4 +++ .../Tests/Fixtures/ReturnTypeParent.php | 28 +++++++++++++++++++ 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/ErrorHandler/DebugClassLoader.php b/src/Symfony/Component/ErrorHandler/DebugClassLoader.php index 6e4adeab05509..746e8d44a548e 100644 --- a/src/Symfony/Component/ErrorHandler/DebugClassLoader.php +++ b/src/Symfony/Component/ErrorHandler/DebugClassLoader.php @@ -56,7 +56,7 @@ class DebugClassLoader 'null' => 'null', 'resource' => 'resource', 'boolean' => 'bool', - 'true' => 'bool', + 'true' => 'true', 'false' => 'false', 'integer' => 'int', 'array' => 'array', @@ -74,6 +74,7 @@ class DebugClassLoader '$this' => 'static', 'list' => 'array', 'class-string' => 'string', + 'never' => 'never', ]; private const BUILTIN_RETURN_TYPES = [ @@ -91,6 +92,9 @@ class DebugClassLoader 'parent' => true, 'mixed' => true, 'static' => true, + 'null' => true, + 'true' => true, + 'never' => true, ]; private const MAGIC_METHODS = [ @@ -762,6 +766,12 @@ private function setReturnType(string $types, string $class, string $method, str return; } + if ('null' === $types) { + self::$returnTypes[$class][$method] = ['null', 'null', $class, $filename]; + + return; + } + if ($nullable = 0 === strpos($types, 'null|')) { $types = substr($types, 5); } elseif ($nullable = '|null' === substr($types, -5)) { diff --git a/src/Symfony/Component/ErrorHandler/Tests/DebugClassLoaderTest.php b/src/Symfony/Component/ErrorHandler/Tests/DebugClassLoaderTest.php index 92ff96fdf83eb..83d7793d89592 100644 --- a/src/Symfony/Component/ErrorHandler/Tests/DebugClassLoaderTest.php +++ b/src/Symfony/Component/ErrorHandler/Tests/DebugClassLoaderTest.php @@ -402,6 +402,10 @@ class_exists('Test\\'.ReturnType::class, true); 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::mixed()" might add "mixed" as a native return type declaration in the future. Do the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" now to avoid errors or add an explicit @return annotation to suppress this message.', 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::nullableMixed()" might add "mixed" as a native return type declaration in the future. Do the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" now to avoid errors or add an explicit @return annotation to suppress this message.', 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::static()" might add "static" as a native return type declaration in the future. Do the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" now to avoid errors or add an explicit @return annotation to suppress this message.', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::false()" might add "false" as a native return type declaration in the future. Do the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" now to avoid errors or add an explicit @return annotation to suppress this message.', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::true()" might add "true" as a native return type declaration in the future. Do the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" now to avoid errors or add an explicit @return annotation to suppress this message.', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::never()" might add "never" as a native return type declaration in the future. Do the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" now to avoid errors or add an explicit @return annotation to suppress this message.', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::null()" might add "null" as a native return type declaration in the future. Do the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" now to avoid errors or add an explicit @return annotation to suppress this message.', ]), $deprecations); } } diff --git a/src/Symfony/Component/ErrorHandler/Tests/Fixtures/ReturnType.php b/src/Symfony/Component/ErrorHandler/Tests/Fixtures/ReturnType.php index 21c4d2012f663..1b8138001de78 100644 --- a/src/Symfony/Component/ErrorHandler/Tests/Fixtures/ReturnType.php +++ b/src/Symfony/Component/ErrorHandler/Tests/Fixtures/ReturnType.php @@ -46,5 +46,9 @@ public function this() { } public function mixed() { } public function nullableMixed() { } public function static() { } + public function false() { } + public function true() { } + public function never() { } + public function null() { } public function outsideMethod() { } } diff --git a/src/Symfony/Component/ErrorHandler/Tests/Fixtures/ReturnTypeParent.php b/src/Symfony/Component/ErrorHandler/Tests/Fixtures/ReturnTypeParent.php index c7b0b6f0fca0c..d42c7c8c02167 100644 --- a/src/Symfony/Component/ErrorHandler/Tests/Fixtures/ReturnTypeParent.php +++ b/src/Symfony/Component/ErrorHandler/Tests/Fixtures/ReturnTypeParent.php @@ -220,6 +220,34 @@ public function static() { } + /** + * @return false + */ + public function false() + { + } + + /** + * @return true + */ + public function true() + { + } + + /** + * @return never + */ + public function never() + { + } + + /** + * @return null + */ + public function null() + { + } + /** * @return int */ From ac528021f6e02f3b13abe33014b625c0139a1253 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 13 Dec 2022 10:30:20 +0100 Subject: [PATCH 27/58] [HttpKernel][ErrorHandler] Fix reading the SYMFONY_IDE env var --- phpunit.xml.dist | 1 + src/Symfony/Bundle/FrameworkBundle/phpunit.xml.dist | 1 + .../Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php | 2 +- src/Symfony/Component/ErrorHandler/phpunit.xml.dist | 1 + src/Symfony/Component/HttpKernel/Debug/FileLinkFormatter.php | 3 ++- src/Symfony/Component/HttpKernel/phpunit.xml.dist | 1 + 6 files changed, 7 insertions(+), 2 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 5f5207576f4f6..7d9901aeb3fa1 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -13,6 +13,7 @@ + diff --git a/src/Symfony/Bundle/FrameworkBundle/phpunit.xml.dist b/src/Symfony/Bundle/FrameworkBundle/phpunit.xml.dist index d00ee0f1e214e..3ea6d9e6e218e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/phpunit.xml.dist +++ b/src/Symfony/Bundle/FrameworkBundle/phpunit.xml.dist @@ -10,6 +10,7 @@ > + diff --git a/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php b/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php index b3603b261ec79..72a666b867917 100644 --- a/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php +++ b/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php @@ -50,7 +50,7 @@ public function __construct(bool|callable $debug = false, string $charset = null { $this->debug = \is_bool($debug) ? $debug : $debug(...); $this->charset = $charset ?: (\ini_get('default_charset') ?: 'UTF-8'); - $fileLinkFormat ??= $_SERVER['SYMFONY_IDE'] ?? null; + $fileLinkFormat ??= $_ENV['SYMFONY_IDE'] ?? $_SERVER['SYMFONY_IDE'] ?? null; $this->fileLinkFormat = \is_string($fileLinkFormat) ? (ErrorRendererInterface::IDE_LINK_FORMATS[$fileLinkFormat] ?? $fileLinkFormat ?: false) : ($fileLinkFormat ?: \ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format') ?: false); diff --git a/src/Symfony/Component/ErrorHandler/phpunit.xml.dist b/src/Symfony/Component/ErrorHandler/phpunit.xml.dist index b23ccab51b8a7..b5b52f5a5208f 100644 --- a/src/Symfony/Component/ErrorHandler/phpunit.xml.dist +++ b/src/Symfony/Component/ErrorHandler/phpunit.xml.dist @@ -10,6 +10,7 @@ > + diff --git a/src/Symfony/Component/HttpKernel/Debug/FileLinkFormatter.php b/src/Symfony/Component/HttpKernel/Debug/FileLinkFormatter.php index 094465274181b..4eaeeb1513713 100644 --- a/src/Symfony/Component/HttpKernel/Debug/FileLinkFormatter.php +++ b/src/Symfony/Component/HttpKernel/Debug/FileLinkFormatter.php @@ -35,7 +35,8 @@ class FileLinkFormatter */ public function __construct(string|array $fileLinkFormat = null, RequestStack $requestStack = null, string $baseDir = null, string|\Closure $urlFormat = null) { - $fileLinkFormat ??= $_SERVER['SYMFONY_IDE'] ?? ''; + $fileLinkFormat ??= $_ENV['SYMFONY_IDE'] ?? $_SERVER['SYMFONY_IDE'] ?? ''; + if (!\is_array($fileLinkFormat) && $fileLinkFormat = (ErrorRendererInterface::IDE_LINK_FORMATS[$fileLinkFormat] ?? $fileLinkFormat) ?: \ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format') ?: false) { $i = strpos($f = $fileLinkFormat, '&', max(strrpos($f, '%f'), strrpos($f, '%l'))) ?: \strlen($f); $fileLinkFormat = [substr($f, 0, $i)] + preg_split('/&([^>]++)>/', substr($f, $i), -1, \PREG_SPLIT_DELIM_CAPTURE); diff --git a/src/Symfony/Component/HttpKernel/phpunit.xml.dist b/src/Symfony/Component/HttpKernel/phpunit.xml.dist index 7e2c738f869f1..a72dcbcca2979 100644 --- a/src/Symfony/Component/HttpKernel/phpunit.xml.dist +++ b/src/Symfony/Component/HttpKernel/phpunit.xml.dist @@ -10,6 +10,7 @@ > + From 8cb094c8c20e4fbd614158ff179202ddff098f03 Mon Sep 17 00:00:00 2001 From: Jiri Barous Date: Thu, 24 Nov 2022 19:09:14 +0100 Subject: [PATCH 28/58] bug #48313 [Mime] Fix MessagePart serialization Fixes #48313 --- src/Symfony/Component/Mime/Part/MessagePart.php | 13 +++++++++++++ .../Component/Mime/Tests/Part/MessagePartTest.php | 10 ++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/Symfony/Component/Mime/Part/MessagePart.php b/src/Symfony/Component/Mime/Part/MessagePart.php index 1b5c23e2bc411..00129b4757b40 100644 --- a/src/Symfony/Component/Mime/Part/MessagePart.php +++ b/src/Symfony/Component/Mime/Part/MessagePart.php @@ -59,4 +59,17 @@ public function bodyToIterable(): iterable { return $this->message->toIterable(); } + + /** + * @return array + */ + public function __sleep() + { + return ['message']; + } + + public function __wakeup() + { + $this->__construct($this->message); + } } diff --git a/src/Symfony/Component/Mime/Tests/Part/MessagePartTest.php b/src/Symfony/Component/Mime/Tests/Part/MessagePartTest.php index 2713d5bc079c7..c01958a4b94b8 100644 --- a/src/Symfony/Component/Mime/Tests/Part/MessagePartTest.php +++ b/src/Symfony/Component/Mime/Tests/Part/MessagePartTest.php @@ -39,4 +39,14 @@ public function testHeaders() new ParameterizedHeader('Content-Disposition', 'attachment', ['name' => 'Subject.eml', 'filename' => 'Subject.eml']) ), $p->getPreparedHeaders()); } + + public function testSerialize() + { + $email = (new Email())->from('fabien@symfony.com')->to('you@example.com')->text('content'); + $email->getHeaders()->addIdHeader('Message-ID', $email->generateMessageId()); + + $p = new MessagePart($email); + $expected = clone $p; + $this->assertEquals($expected->toString(), unserialize(serialize($p))->toString()); + } } From 57e28f6bda3b382ed116ef40d19df95c5f326d40 Mon Sep 17 00:00:00 2001 From: MatTheCat Date: Sun, 20 Nov 2022 19:35:45 +0100 Subject: [PATCH 29/58] [FrameworkBundle] Allow configuring `framework.exceptions` with a config builder --- .../DependencyInjection/Configuration.php | 24 +++++----- .../Resources/config/schema/symfony-1.0.xsd | 24 ++++++++-- .../Fixtures/xml/exceptions.xml | 26 ++++++++--- .../Fixtures/xml/exceptions_legacy.xml | 16 +++++++ .../FrameworkExtensionTest.php | 44 ++++++++++++------- .../XmlFrameworkExtensionTest.php | 34 ++++++++++++++ 6 files changed, 127 insertions(+), 41 deletions(-) create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions_legacy.xml diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index a86ec0561d3bc..c70a07635843a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -1194,35 +1194,31 @@ private function addExceptionsSection(ArrayNodeDefinition $rootNode) $logLevels = (new \ReflectionClass(LogLevel::class))->getConstants(); $rootNode + ->fixXmlConfig('exception') ->children() ->arrayNode('exceptions') ->info('Exception handling configuration') + ->useAttributeAsKey('class') ->beforeNormalization() + // Handle legacy XML configuration ->ifArray() ->then(function (array $v): array { if (!\array_key_exists('exception', $v)) { return $v; } - // Fix XML normalization - $data = isset($v['exception'][0]) ? $v['exception'] : [$v['exception']]; - $exceptions = []; - foreach ($data as $exception) { - $config = []; - if (\array_key_exists('log-level', $exception)) { - $config['log_level'] = $exception['log-level']; - } - if (\array_key_exists('status-code', $exception)) { - $config['status_code'] = $exception['status-code']; - } - $exceptions[$exception['name']] = $config; + $v = $v['exception']; + unset($v['exception']); + + foreach ($v as &$exception) { + $exception['class'] = $exception['name']; + unset($exception['name']); } - return $exceptions; + return $v; }) ->end() ->prototype('array') - ->fixXmlConfig('exception') ->children() ->scalarNode('log_level') ->info('The level of log message. Null to let Symfony decide.') diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd index d5f369cb1e815..d4be9fb6f2b7f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd @@ -30,6 +30,7 @@ + @@ -361,14 +362,29 @@ - + - - + - + + + + + + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions.xml index 49878fc118b50..35c787867a93a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions.xml @@ -6,11 +6,25 @@ http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> - - - - - - + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions_legacy.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions_legacy.xml new file mode 100644 index 0000000000000..49878fc118b50 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions_legacy.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index abbe2d9a3e4c1..f212a6db89ffa 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -547,24 +547,34 @@ public function testExceptionsConfig() { $container = $this->createContainerFromFile('exceptions'); + $configuration = $container->getDefinition('exception_listener')->getArgument(3); + $this->assertSame([ - \Symfony\Component\HttpKernel\Exception\BadRequestHttpException::class => [ - 'log_level' => 'info', - 'status_code' => 422, - ], - \Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class => [ - 'log_level' => 'info', - 'status_code' => null, - ], - \Symfony\Component\HttpKernel\Exception\ConflictHttpException::class => [ - 'log_level' => 'info', - 'status_code' => null, - ], - \Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException::class => [ - 'log_level' => null, - 'status_code' => 500, - ], - ], $container->getDefinition('exception_listener')->getArgument(3)); + \Symfony\Component\HttpKernel\Exception\BadRequestHttpException::class, + \Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class, + \Symfony\Component\HttpKernel\Exception\ConflictHttpException::class, + \Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException::class, + ], array_keys($configuration)); + + $this->assertEqualsCanonicalizing([ + 'log_level' => 'info', + 'status_code' => 422, + ], $configuration[\Symfony\Component\HttpKernel\Exception\BadRequestHttpException::class]); + + $this->assertEqualsCanonicalizing([ + 'log_level' => 'info', + 'status_code' => null, + ], $configuration[\Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class]); + + $this->assertEqualsCanonicalizing([ + 'log_level' => 'info', + 'status_code' => null, + ], $configuration[\Symfony\Component\HttpKernel\Exception\ConflictHttpException::class]); + + $this->assertEqualsCanonicalizing([ + 'log_level' => null, + 'status_code' => 500, + ], $configuration[\Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException::class]); } public function testRouter() diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php index ebc37d93bed84..131bb07f0c657 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php @@ -32,4 +32,38 @@ public function testMessengerMiddlewareFactoryErroneousFormat() { $this->markTestSkipped('XML configuration will not allow erroneous format.'); } + + public function testLegacyExceptionsConfig() + { + $container = $this->createContainerFromFile('exceptions_legacy'); + + $configuration = $container->getDefinition('exception_listener')->getArgument(3); + + $this->assertSame([ + \Symfony\Component\HttpKernel\Exception\BadRequestHttpException::class, + \Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class, + \Symfony\Component\HttpKernel\Exception\ConflictHttpException::class, + \Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException::class, + ], array_keys($configuration)); + + $this->assertEqualsCanonicalizing([ + 'log_level' => 'info', + 'status_code' => 422, + ], $configuration[\Symfony\Component\HttpKernel\Exception\BadRequestHttpException::class]); + + $this->assertEqualsCanonicalizing([ + 'log_level' => 'info', + 'status_code' => null, + ], $configuration[\Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class]); + + $this->assertEqualsCanonicalizing([ + 'log_level' => 'info', + 'status_code' => null, + ], $configuration[\Symfony\Component\HttpKernel\Exception\ConflictHttpException::class]); + + $this->assertEqualsCanonicalizing([ + 'log_level' => null, + 'status_code' => 500, + ], $configuration[\Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException::class]); + } } From 90c8f735c7ddfa1cc9ca448182e866dbe27f181d Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Tue, 13 Dec 2022 11:55:00 +0100 Subject: [PATCH 30/58] Use static methods inside data providers Extracted from closed PR #48283 This is work, which needs to be done manually anyway. --- .../HttpCodeActivationStrategyTest.php | 20 +++--- .../NotFoundActivationStrategyTest.php | 14 ++-- .../Tests/Processor/DebugProcessorTest.php | 18 ++--- .../Csp/ContentSecurityPolicyHandlerTest.php | 68 +++++++++---------- .../Tests/Extractor/PhpDocExtractorTest.php | 14 ++-- 5 files changed, 67 insertions(+), 67 deletions(-) diff --git a/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/HttpCodeActivationStrategyTest.php b/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/HttpCodeActivationStrategyTest.php index 073f3eee1f86f..0afb8eb4cfbe8 100644 --- a/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/HttpCodeActivationStrategyTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/HttpCodeActivationStrategyTest.php @@ -101,19 +101,19 @@ public function isActivatedProvider(): array { return [ ['/test', ['level' => Logger::ERROR], true], - ['/400', ['level' => Logger::ERROR, 'context' => $this->getContextException(400)], true], - ['/400/a', ['level' => Logger::ERROR, 'context' => $this->getContextException(400)], false], - ['/400/b', ['level' => Logger::ERROR, 'context' => $this->getContextException(400)], false], - ['/400/c', ['level' => Logger::ERROR, 'context' => $this->getContextException(400)], true], - ['/401', ['level' => Logger::ERROR, 'context' => $this->getContextException(401)], true], - ['/403', ['level' => Logger::ERROR, 'context' => $this->getContextException(403)], false], - ['/404', ['level' => Logger::ERROR, 'context' => $this->getContextException(404)], false], - ['/405', ['level' => Logger::ERROR, 'context' => $this->getContextException(405)], false], - ['/500', ['level' => Logger::ERROR, 'context' => $this->getContextException(500)], true], + ['/400', ['level' => Logger::ERROR, 'context' => self::getContextException(400)], true], + ['/400/a', ['level' => Logger::ERROR, 'context' => self::getContextException(400)], false], + ['/400/b', ['level' => Logger::ERROR, 'context' => self::getContextException(400)], false], + ['/400/c', ['level' => Logger::ERROR, 'context' => self::getContextException(400)], true], + ['/401', ['level' => Logger::ERROR, 'context' => self::getContextException(401)], true], + ['/403', ['level' => Logger::ERROR, 'context' => self::getContextException(403)], false], + ['/404', ['level' => Logger::ERROR, 'context' => self::getContextException(404)], false], + ['/405', ['level' => Logger::ERROR, 'context' => self::getContextException(405)], false], + ['/500', ['level' => Logger::ERROR, 'context' => self::getContextException(500)], true], ]; } - private function getContextException(int $code): array + private static function getContextException(int $code): array { return ['exception' => new HttpException($code)]; } diff --git a/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.php b/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.php index a60cc450c7236..09e71e0b332ef 100644 --- a/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.php @@ -53,18 +53,18 @@ public function isActivatedProvider(): array { return [ ['/test', ['level' => Logger::DEBUG], false], - ['/foo', ['level' => Logger::DEBUG, 'context' => $this->getContextException(404)], false], - ['/baz/bar', ['level' => Logger::ERROR, 'context' => $this->getContextException(404)], false], - ['/foo', ['level' => Logger::ERROR, 'context' => $this->getContextException(404)], false], - ['/foo', ['level' => Logger::ERROR, 'context' => $this->getContextException(500)], true], + ['/foo', ['level' => Logger::DEBUG, 'context' => self::getContextException(404)], false], + ['/baz/bar', ['level' => Logger::ERROR, 'context' => self::getContextException(404)], false], + ['/foo', ['level' => Logger::ERROR, 'context' => self::getContextException(404)], false], + ['/foo', ['level' => Logger::ERROR, 'context' => self::getContextException(500)], true], ['/test', ['level' => Logger::ERROR], true], - ['/baz', ['level' => Logger::ERROR, 'context' => $this->getContextException(404)], true], - ['/baz', ['level' => Logger::ERROR, 'context' => $this->getContextException(500)], true], + ['/baz', ['level' => Logger::ERROR, 'context' => self::getContextException(404)], true], + ['/baz', ['level' => Logger::ERROR, 'context' => self::getContextException(500)], true], ]; } - protected function getContextException(int $code): array + protected static function getContextException(int $code): array { return ['exception' => new HttpException($code)]; } diff --git a/src/Symfony/Bridge/Monolog/Tests/Processor/DebugProcessorTest.php b/src/Symfony/Bridge/Monolog/Tests/Processor/DebugProcessorTest.php index c576462d0abfe..d01ca9f83ea1d 100644 --- a/src/Symfony/Bridge/Monolog/Tests/Processor/DebugProcessorTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/Processor/DebugProcessorTest.php @@ -34,7 +34,7 @@ public function testDatetimeFormat(array $record, $expectedTimestamp) public function providerDatetimeFormatTests(): array { - $record = $this->getRecord(); + $record = self::getRecord(); return [ [array_merge($record, ['datetime' => new \DateTime('2019-01-01T00:01:00+00:00')]), 1546300860], @@ -58,7 +58,7 @@ public function testDatetimeRfc3339Format(array $record, $expectedTimestamp) public function providerDatetimeRfc3339FormatTests(): array { - $record = $this->getRecord(); + $record = self::getRecord(); return [ [array_merge($record, ['datetime' => new \DateTime('2019-01-01T00:01:00+00:00')]), '2019-01-01T00:01:00.000+00:00'], @@ -70,8 +70,8 @@ public function providerDatetimeRfc3339FormatTests(): array public function testDebugProcessor() { $processor = new DebugProcessor(); - $processor($this->getRecord()); - $processor($this->getRecord(Logger::ERROR)); + $processor(self::getRecord()); + $processor(self::getRecord(Logger::ERROR)); $this->assertCount(2, $processor->getLogs()); $this->assertSame(1, $processor->countErrors()); @@ -89,8 +89,8 @@ public function testWithRequestStack() { $stack = new RequestStack(); $processor = new DebugProcessor($stack); - $processor($this->getRecord()); - $processor($this->getRecord(Logger::ERROR)); + $processor(self::getRecord()); + $processor(self::getRecord(Logger::ERROR)); $this->assertCount(2, $processor->getLogs()); $this->assertSame(1, $processor->countErrors()); @@ -98,8 +98,8 @@ public function testWithRequestStack() $request = new Request(); $stack->push($request); - $processor($this->getRecord()); - $processor($this->getRecord(Logger::ERROR)); + $processor(self::getRecord()); + $processor(self::getRecord(Logger::ERROR)); $this->assertCount(4, $processor->getLogs()); $this->assertSame(2, $processor->countErrors()); @@ -123,7 +123,7 @@ public function testInheritedClassCallCountErrorsWithoutArgument() $this->assertEquals(0, $debugProcessorChild->countErrors()); } - private function getRecord($level = Logger::WARNING, $message = 'test'): array + private static function getRecord($level = Logger::WARNING, $message = 'test'): array { return [ 'message' => $message, diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/Csp/ContentSecurityPolicyHandlerTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/Csp/ContentSecurityPolicyHandlerTest.php index fbaf2f7965d05..c345b5fbb89c8 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Tests/Csp/ContentSecurityPolicyHandlerTest.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/Csp/ContentSecurityPolicyHandlerTest.php @@ -66,10 +66,10 @@ public function provideRequestAndResponses() ]; return [ - [$nonce, ['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce], $this->createRequest(), $this->createResponse()], - [$nonce, ['csp_script_nonce' => $requestScriptNonce, 'csp_style_nonce' => $requestStyleNonce], $this->createRequest($requestNonceHeaders), $this->createResponse($responseNonceHeaders)], - [$nonce, ['csp_script_nonce' => $requestScriptNonce, 'csp_style_nonce' => $requestStyleNonce], $this->createRequest($requestNonceHeaders), $this->createResponse()], - [$nonce, ['csp_script_nonce' => $responseScriptNonce, 'csp_style_nonce' => $responseStyleNonce], $this->createRequest(), $this->createResponse($responseNonceHeaders)], + [$nonce, ['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce], self::createRequest(), self::createResponse()], + [$nonce, ['csp_script_nonce' => $requestScriptNonce, 'csp_style_nonce' => $requestStyleNonce], self::createRequest($requestNonceHeaders), self::createResponse($responseNonceHeaders)], + [$nonce, ['csp_script_nonce' => $requestScriptNonce, 'csp_style_nonce' => $requestStyleNonce], self::createRequest($requestNonceHeaders), self::createResponse()], + [$nonce, ['csp_script_nonce' => $responseScriptNonce, 'csp_style_nonce' => $responseStyleNonce], self::createRequest(), self::createResponse($responseNonceHeaders)], ]; } @@ -96,104 +96,104 @@ public function provideRequestAndResponsesForOnKernelResponse() [ $nonce, ['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce], - $this->createRequest(), - $this->createResponse(), + self::createRequest(), + self::createResponse(), ['Content-Security-Policy' => null, 'Content-Security-Policy-Report-Only' => null, 'X-Content-Security-Policy' => null], ], [ $nonce, ['csp_script_nonce' => $requestScriptNonce, 'csp_style_nonce' => $requestStyleNonce], - $this->createRequest($requestNonceHeaders), - $this->createResponse($responseNonceHeaders), + self::createRequest($requestNonceHeaders), + self::createResponse($responseNonceHeaders), ['Content-Security-Policy' => null, 'Content-Security-Policy-Report-Only' => null, 'X-Content-Security-Policy' => null], ], [ $nonce, ['csp_script_nonce' => $requestScriptNonce, 'csp_style_nonce' => $requestStyleNonce], - $this->createRequest($requestNonceHeaders), - $this->createResponse(), + self::createRequest($requestNonceHeaders), + self::createResponse(), ['Content-Security-Policy' => null, 'Content-Security-Policy-Report-Only' => null, 'X-Content-Security-Policy' => null], ], [ $nonce, ['csp_script_nonce' => $responseScriptNonce, 'csp_style_nonce' => $responseStyleNonce], - $this->createRequest(), - $this->createResponse($responseNonceHeaders), + self::createRequest(), + self::createResponse($responseNonceHeaders), ['Content-Security-Policy' => null, 'Content-Security-Policy-Report-Only' => null, 'X-Content-Security-Policy' => null], ], [ $nonce, ['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce], - $this->createRequest(), - $this->createResponse(['Content-Security-Policy' => 'frame-ancestors https: ; form-action: https:', 'Content-Security-Policy-Report-Only' => 'frame-ancestors http: ; form-action: http:']), + self::createRequest(), + self::createResponse(['Content-Security-Policy' => 'frame-ancestors https: ; form-action: https:', 'Content-Security-Policy-Report-Only' => 'frame-ancestors http: ; form-action: http:']), ['Content-Security-Policy' => 'frame-ancestors https: ; form-action: https:', 'Content-Security-Policy-Report-Only' => 'frame-ancestors http: ; form-action: http:', 'X-Content-Security-Policy' => null], ], [ $nonce, ['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce], - $this->createRequest(), - $this->createResponse(['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'']), + self::createRequest(), + self::createResponse(['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'']), ['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'; style-src \'self\' domain.com \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'; style-src \'self\' domain-report-only.com \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'X-Content-Security-Policy' => null], ], [ $nonce, ['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce], - $this->createRequest(), - $this->createResponse(['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\'; style-src \'self\' \'unsafe-inline\'; style-src-elem \'self\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\'; style-src \'self\' \'unsafe-inline\'; style-src-elem \'self\'']), + self::createRequest(), + self::createResponse(['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\'; style-src \'self\' \'unsafe-inline\'; style-src-elem \'self\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\'; style-src \'self\' \'unsafe-inline\'; style-src-elem \'self\'']), ['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src \'self\' \'unsafe-inline\'; style-src-elem \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src \'self\' \'unsafe-inline\'; style-src-elem \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'X-Content-Security-Policy' => null], ], [ $nonce, ['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce], - $this->createRequest(), - $this->createResponse(['Content-Security-Policy' => 'default-src \'none\'', 'Content-Security-Policy-Report-Only' => 'default-src \'none\'']), + self::createRequest(), + self::createResponse(['Content-Security-Policy' => 'default-src \'none\'', 'Content-Security-Policy-Report-Only' => 'default-src \'none\'']), ['Content-Security-Policy' => 'default-src \'none\'; script-src \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'Content-Security-Policy-Report-Only' => 'default-src \'none\'; script-src \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'X-Content-Security-Policy' => null], ], [ $nonce, ['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce], - $this->createRequest(), - $this->createResponse(['Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\'']), + self::createRequest(), + self::createResponse(['Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\'']), ['Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\'', 'X-Content-Security-Policy' => null], ], [ $nonce, ['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce], - $this->createRequest(), - $this->createResponse(['Content-Security-Policy' => 'script-src \'self\'; style-src \'self\'']), + self::createRequest(), + self::createResponse(['Content-Security-Policy' => 'script-src \'self\'; style-src \'self\'']), ['Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'X-Content-Security-Policy' => null], ], [ $nonce, ['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce], - $this->createRequest(), - $this->createResponse(['X-Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\'']), + self::createRequest(), + self::createResponse(['X-Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\'']), ['X-Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\'', 'Content-Security-Policy' => null], ], [ $nonce, ['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce], - $this->createRequest(), - $this->createResponse(['X-Content-Security-Policy' => 'script-src \'self\'']), + self::createRequest(), + self::createResponse(['X-Content-Security-Policy' => 'script-src \'self\'']), ['X-Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'Content-Security-Policy' => null], ], [ $nonce, ['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce], - $this->createRequest(), - $this->createResponse(['X-Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\' \'sha384-LALALALALAAL\'']), + self::createRequest(), + self::createResponse(['X-Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\' \'sha384-LALALALALAAL\'']), ['X-Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\' \'sha384-LALALALALAAL\' \'nonce-'.$nonce.'\'', 'Content-Security-Policy' => null], ], [ $nonce, ['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce], - $this->createRequest(), - $this->createResponse(['Content-Security-Policy' => 'script-src \'self\'; style-src \'self\'', 'X-Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\'; style-src \'self\'']), + self::createRequest(), + self::createResponse(['Content-Security-Policy' => 'script-src \'self\'; style-src \'self\'', 'X-Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\'; style-src \'self\'']), ['Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'X-Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\'; style-src \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\''], ], ]; } - private function createRequest(array $headers = []) + private static function createRequest(array $headers = []) { $request = new Request(); $request->headers->add($headers); @@ -201,7 +201,7 @@ private function createRequest(array $headers = []) return $request; } - private function createResponse(array $headers = []) + private static function createResponse(array $headers = []) { $response = new Response(); $response->headers->add($headers); diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php index 21020415ef58b..dec09cc70b7b4 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php @@ -58,8 +58,8 @@ public function invalidTypesProvider() return [ 'pub' => ['pub', null, null], 'stat' => ['stat', null, null], - 'foo' => ['foo', $this->isPhpDocumentorV5() ? 'Foo.' : null, null], - 'bar' => ['bar', $this->isPhpDocumentorV5() ? 'Bar.' : null, null], + 'foo' => ['foo', self::isPhpDocumentorV5() ? 'Foo.' : null, null], + 'bar' => ['bar', self::isPhpDocumentorV5() ? 'Bar.' : null, null], ]; } @@ -125,10 +125,10 @@ public function typesProvider() ['donotexist', null, null, null], ['staticGetter', null, null, null], ['staticSetter', null, null, null], - ['emptyVar', null, $this->isPhpDocumentorV5() ? 'This should not be removed.' : null, null], + ['emptyVar', null, self::isPhpDocumentorV5() ? 'This should not be removed.' : null, null], [ 'arrayWithKeys', - $this->isPhpDocumentorV5() ? [ + self::isPhpDocumentorV5() ? [ new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_STRING)), ] : null, null, @@ -136,7 +136,7 @@ public function typesProvider() ], [ 'arrayOfMixed', - $this->isPhpDocumentorV5() ? [ + self::isPhpDocumentorV5() ? [ new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_STRING), null), ] : null, null, @@ -144,7 +144,7 @@ public function typesProvider() ], [ 'listOfStrings', - $this->isPhpDocumentorV5() ? [ + self::isPhpDocumentorV5() ? [ new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING)), ] : null, null, @@ -417,7 +417,7 @@ public function testUnknownPseudoType() $this->assertEquals([new Type(Type::BUILTIN_TYPE_OBJECT, false, 'scalar')], $this->extractor->getTypes(PseudoTypeDummy::class, 'unknownPseudoType')); } - protected function isPhpDocumentorV5() + protected static function isPhpDocumentorV5() { if (class_exists(InvalidTag::class)) { return true; From a4e3b93894a91ad5646b3e708b31c0ebf30792dc Mon Sep 17 00:00:00 2001 From: Adrien Peyre Date: Tue, 11 Oct 2022 20:27:43 +0200 Subject: [PATCH 31/58] TraceableHttpClient: increase decorator's priority --- .../Component/HttpClient/DependencyInjection/HttpClientPass.php | 2 +- .../HttpClient/Tests/DependencyInjection/HttpClientPassTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpClient/DependencyInjection/HttpClientPass.php b/src/Symfony/Component/HttpClient/DependencyInjection/HttpClientPass.php index 73f88651345d3..8f3c3c5347026 100644 --- a/src/Symfony/Component/HttpClient/DependencyInjection/HttpClientPass.php +++ b/src/Symfony/Component/HttpClient/DependencyInjection/HttpClientPass.php @@ -43,7 +43,7 @@ public function process(ContainerBuilder $container) $container->register('.debug.'.$id, TraceableHttpClient::class) ->setArguments([new Reference('.debug.'.$id.'.inner'), new Reference('debug.stopwatch', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)]) ->addTag('kernel.reset', ['method' => 'reset']) - ->setDecoratedService($id); + ->setDecoratedService($id, null, 5); $container->getDefinition('data_collector.http_client') ->addMethodCall('registerClient', [$id, new Reference('.debug.'.$id)]); } diff --git a/src/Symfony/Component/HttpClient/Tests/DependencyInjection/HttpClientPassTest.php b/src/Symfony/Component/HttpClient/Tests/DependencyInjection/HttpClientPassTest.php index eb04f88226d1f..c6dcf4fcf7902 100755 --- a/src/Symfony/Component/HttpClient/Tests/DependencyInjection/HttpClientPassTest.php +++ b/src/Symfony/Component/HttpClient/Tests/DependencyInjection/HttpClientPassTest.php @@ -38,7 +38,7 @@ public function testItDecoratesHttpClientWithTraceableHttpClient() $sut->process($container); $this->assertTrue($container->hasDefinition('.debug.foo')); $this->assertSame(TraceableHttpClient::class, $container->getDefinition('.debug.foo')->getClass()); - $this->assertSame(['foo', null, 0], $container->getDefinition('.debug.foo')->getDecoratedService()); + $this->assertSame(['foo', null, 5], $container->getDefinition('.debug.foo')->getDecoratedService()); } public function testItRegistersDebugHttpClientToCollector() From 81a4e9d60d51fec444129444f2407ad2edc5ce1f Mon Sep 17 00:00:00 2001 From: HypeMC Date: Sat, 29 Oct 2022 18:44:02 +0200 Subject: [PATCH 32/58] [WebProfilerBundle] Fix dump header not being displayed --- .../Tests/Twig/WebProfilerExtensionTest.php | 61 +++++++++++++++++++ .../Twig/WebProfilerExtension.php | 14 +++-- 2 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 src/Symfony/Bundle/WebProfilerBundle/Tests/Twig/WebProfilerExtensionTest.php diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/Twig/WebProfilerExtensionTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/Twig/WebProfilerExtensionTest.php new file mode 100644 index 0000000000000..6b026bcc53385 --- /dev/null +++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/Twig/WebProfilerExtensionTest.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\WebProfilerBundle\Tests\Twig; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\WebProfilerBundle\Twig\WebProfilerExtension; +use Symfony\Component\VarDumper\Cloner\VarCloner; +use Twig\Environment; +use Twig\Extension\CoreExtension; +use Twig\Extension\EscaperExtension; + +class WebProfilerExtensionTest extends TestCase +{ + /** + * @dataProvider provideMessages + */ + public function testDumpHeaderIsDisplayed(string $message, array $context, bool $dump1HasHeader, bool $dump2HasHeader) + { + class_exists(CoreExtension::class); // Load twig_convert_encoding() + class_exists(EscaperExtension::class); // Load twig_escape_filter() + + $twigEnvironment = $this->mockTwigEnvironment(); + $varCloner = new VarCloner(); + + $webProfilerExtension = new WebProfilerExtension(); + + $needle = 'window.Sfdump'; + + $dump1 = $webProfilerExtension->dumpLog($twigEnvironment, $message, $varCloner->cloneVar($context)); + self::assertSame($dump1HasHeader, str_contains($dump1, $needle)); + + $dump2 = $webProfilerExtension->dumpData($twigEnvironment, $varCloner->cloneVar([])); + self::assertSame($dump2HasHeader, str_contains($dump2, $needle)); + } + + public function provideMessages(): iterable + { + yield ['Some message', ['foo' => 'foo', 'bar' => 'bar'], false, true]; + yield ['Some message {@see some text}', ['foo' => 'foo', 'bar' => 'bar'], false, true]; + yield ['Some message {foo}', ['foo' => 'foo', 'bar' => 'bar'], true, false]; + yield ['Some message {foo}', ['bar' => 'bar'], false, true]; + } + + private function mockTwigEnvironment() + { + $twigEnvironment = $this->createMock(Environment::class); + + $twigEnvironment->expects($this->any())->method('getCharset')->willReturn('UTF-8'); + + return $twigEnvironment; + } +} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Twig/WebProfilerExtension.php b/src/Symfony/Bundle/WebProfilerBundle/Twig/WebProfilerExtension.php index 8a8721a3a1516..b5f0f3cad2479 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Twig/WebProfilerExtension.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Twig/WebProfilerExtension.php @@ -90,13 +90,19 @@ public function dumpLog(Environment $env, string $message, Data $context = null) $message = twig_escape_filter($env, $message); $message = preg_replace('/"(.*?)"/', '"$1"', $message); - if (null === $context || !str_contains($message, '{')) { + $replacements = []; + foreach ($context ?? [] as $k => $v) { + $k = '{'.twig_escape_filter($env, $k).'}'; + if (str_contains($message, $k)) { + $replacements[$k] = $v; + } + } + + if (!$replacements) { return ''.$message.''; } - $replacements = []; - foreach ($context as $k => $v) { - $k = '{'.twig_escape_filter($env, $k).'}'; + foreach ($replacements as $k => $v) { $replacements['"'.$k.'"'] = $replacements['"'.$k.'"'] = $replacements[$k] = $this->dumpData($env, $v); } From b9337f17687e17eb34d6d000881c05bfa7f1a895 Mon Sep 17 00:00:00 2001 From: Brandon Antonio Lorenzo Date: Fri, 2 Dec 2022 17:35:26 -0600 Subject: [PATCH 33/58] [DependencyInjection] Fix bug when tag name is a text node --- .../DependencyInjection/Loader/XmlFileLoader.php | 2 +- .../DependencyInjection/Tests/Fixtures/xml/services10.xml | 4 ++++ .../Tests/Loader/XmlFileLoaderTest.php | 8 ++++++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index 76e2d1b8555a3..ab76eb9e0df08 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -336,7 +336,7 @@ private function parseDefinition(\DOMElement $service, string $file, Definition $tags = $this->getChildren($service, 'tag'); foreach ($tags as $tag) { - if ('' === $tagName = $tag->hasChildNodes() || '' === $tag->nodeValue ? $tag->getAttribute('name') : $tag->nodeValue) { + if ('' === $tagName = $tag->childElementCount || '' === $tag->nodeValue ? $tag->getAttribute('name') : $tag->nodeValue) { throw new InvalidArgumentException(sprintf('The tag name for service "%s" in "%s" must be a non-empty string.', (string) $service->getAttribute('id'), $file)); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services10.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services10.xml index 921070e1b44a0..89b96413913d2 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services10.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services10.xml @@ -11,6 +11,10 @@ other-option="lorem" an_other-option="ipsum" /> + bar_tag diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index b3de370ba370f..9207742bc25f1 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -369,13 +369,17 @@ 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'); } - public function testParsesTags() + /** + * @testWith ["foo_tag"] + * ["bar_tag"] + */ + public function testParsesTags(string $tag) { $container = new ContainerBuilder(); $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); $loader->load('services10.xml'); - $services = $container->findTaggedServiceIds('foo_tag'); + $services = $container->findTaggedServiceIds($tag); $this->assertCount(1, $services); foreach ($services as $id => $tagAttributes) { From 90eb89f4e9eda5e6d50001961d889b293f4c593e Mon Sep 17 00:00:00 2001 From: HypeMC Date: Tue, 6 Dec 2022 23:11:45 +0100 Subject: [PATCH 34/58] [HttpKernel] Fix CacheAttributeListener priority --- .../Functional/CacheAttributeListenerTest.php | 98 +++++++++++++++++++ .../app/CacheAttributeListener/bundles.php | 18 ++++ .../app/CacheAttributeListener/config.yml | 24 +++++ .../app/CacheAttributeListener/routing.yml | 3 + .../Bundle/FrameworkBundle/composer.json | 2 +- .../IsGrantedAttributeListener.php | 2 +- 6 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CacheAttributeListenerTest.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/bundles.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/config.yml create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/routing.yml diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CacheAttributeListenerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CacheAttributeListenerTest.php new file mode 100644 index 0000000000000..72b2c12266d87 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CacheAttributeListenerTest.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Attribute\Cache; +use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; +use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; +use Symfony\Component\Security\Core\User\InMemoryUser; +use Symfony\Component\Security\Http\Attribute\IsGranted; + +class CacheAttributeListenerTest extends AbstractWebTestCase +{ + public function testAnonimousUserWithEtag() + { + $client = self::createClient(['test_case' => 'CacheAttributeListener']); + + $client->request('GET', '/', server: ['HTTP_IF_NONE_MATCH' => sprintf('"%s"', hash('sha256', '12345'))]); + + self::assertTrue($client->getResponse()->isRedirect('http://localhost/login')); + } + + public function testAnonimousUserWithoutEtag() + { + $client = self::createClient(['test_case' => 'CacheAttributeListener']); + + $client->request('GET', '/'); + + self::assertTrue($client->getResponse()->isRedirect('http://localhost/login')); + } + + public function testLoggedInUserWithEtag() + { + $client = self::createClient(['test_case' => 'CacheAttributeListener']); + + $client->loginUser(new InMemoryUser('the-username', 'the-password', ['ROLE_USER'])); + $client->request('GET', '/', server: ['HTTP_IF_NONE_MATCH' => sprintf('"%s"', hash('sha256', '12345'))]); + + $response = $client->getResponse(); + + self::assertSame(304, $response->getStatusCode()); + self::assertSame('', $response->getContent()); + } + + public function testLoggedInUserWithoutEtag() + { + $client = self::createClient(['test_case' => 'CacheAttributeListener']); + + $client->loginUser(new InMemoryUser('the-username', 'the-password', ['ROLE_USER'])); + $client->request('GET', '/'); + + $response = $client->getResponse(); + + self::assertSame(200, $response->getStatusCode()); + self::assertSame('Hi there!', $response->getContent()); + } +} + +class TestEntityValueResolver implements ValueResolverInterface +{ + public function resolve(Request $request, ArgumentMetadata $argument): iterable + { + return Post::class === $argument->getType() ? [new Post()] : []; + } +} + +class Post +{ + public function getId(): int + { + return 1; + } + + public function getEtag(): string + { + return '12345'; + } +} + +class WithAttributesController +{ + #[IsGranted('ROLE_USER')] + #[Cache(etag: 'post.getEtag()')] + public function __invoke(Post $post): Response + { + return new Response('Hi there!'); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/bundles.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/bundles.php new file mode 100644 index 0000000000000..9a26fb163a77d --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\SecurityBundle\SecurityBundle; + +return [ + new FrameworkBundle(), + new SecurityBundle(), +]; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/config.yml new file mode 100644 index 0000000000000..21890451a1094 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/config.yml @@ -0,0 +1,24 @@ +imports: + - { resource: ../config/default.yml } + +services: + Symfony\Bundle\FrameworkBundle\Tests\Functional\TestEntityValueResolver: + tags: + - { name: controller.argument_value_resolver, priority: 110 } + + Symfony\Bundle\FrameworkBundle\Tests\Functional\WithAttributesController: + public: true + +security: + providers: + main: + memory: + users: + the-username: { password: the-password, roles: [ 'ROLE_USER' ] } + + firewalls: + main: + pattern: ^/ + form_login: + login_path: /login + provider: main diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/routing.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/routing.yml new file mode 100644 index 0000000000000..50c37b823fcbe --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/routing.yml @@ -0,0 +1,3 @@ +with_attributes_controller: + path: / + controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\WithAttributesController diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 4ab6b2e919e32..00986c7854f08 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -26,7 +26,7 @@ "symfony/error-handler": "^6.1", "symfony/event-dispatcher": "^5.4|^6.0", "symfony/http-foundation": "^6.2", - "symfony/http-kernel": "^6.2", + "symfony/http-kernel": "^6.2.1", "symfony/polyfill-mbstring": "~1.0", "symfony/filesystem": "^5.4|^6.0", "symfony/finder": "^5.4|^6.0", diff --git a/src/Symfony/Component/Security/Http/EventListener/IsGrantedAttributeListener.php b/src/Symfony/Component/Security/Http/EventListener/IsGrantedAttributeListener.php index ce8fbacb8c1cc..518578b511c9b 100644 --- a/src/Symfony/Component/Security/Http/EventListener/IsGrantedAttributeListener.php +++ b/src/Symfony/Component/Security/Http/EventListener/IsGrantedAttributeListener.php @@ -77,7 +77,7 @@ public function onKernelControllerArguments(ControllerArgumentsEvent $event) public static function getSubscribedEvents(): array { - return [KernelEvents::CONTROLLER_ARGUMENTS => ['onKernelControllerArguments', 10]]; + return [KernelEvents::CONTROLLER_ARGUMENTS => ['onKernelControllerArguments', 20]]; } private function getIsGrantedSubject(string|Expression $subjectRef, Request $request, array $arguments): mixed From 6c33582762527744064efea4db48b600fb052883 Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Fri, 9 Dec 2022 09:47:34 +0100 Subject: [PATCH 35/58] [ExpressionLanguage] Fix BC of cached SerializedParsedExpression containing GetAttrNode --- .../Component/ExpressionLanguage/Node/GetAttrNode.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Symfony/Component/ExpressionLanguage/Node/GetAttrNode.php b/src/Symfony/Component/ExpressionLanguage/Node/GetAttrNode.php index 1bad653328965..e9b10c3295d75 100644 --- a/src/Symfony/Component/ExpressionLanguage/Node/GetAttrNode.php +++ b/src/Symfony/Component/ExpressionLanguage/Node/GetAttrNode.php @@ -149,4 +149,15 @@ public function toArray() return [$this->nodes['node'], '[', $this->nodes['attribute'], ']']; } } + + /** + * Provides BC with instances serialized before v6.2 + */ + public function __unserialize(array $data): void + { + $this->nodes = $data['nodes']; + $this->attributes = $data['attributes']; + $this->attributes['is_null_coalesce'] ??= false; + $this->attributes['is_short_circuited'] ??= $data["\x00Symfony\Component\ExpressionLanguage\Node\GetAttrNode\x00isShortCircuited"] ?? false; + } } From 3cf4401aa68195ba91956cf014b4b00fe56c68db Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 13 Dec 2022 17:05:37 +0100 Subject: [PATCH 36/58] [HttpFoundation] Fix dumping array cookies --- src/Symfony/Component/HttpFoundation/Request.php | 4 ++-- src/Symfony/Component/HttpFoundation/Tests/RequestTest.php | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 2bf52ce4339df..10f779d279ec0 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -522,10 +522,10 @@ public function __toString() $cookies = []; foreach ($this->cookies as $k => $v) { - $cookies[] = $k.'='.$v; + $cookies[] = \is_array($v) ? http_build_query([$k => $v], '', '; ', \PHP_QUERY_RFC3986) : "$k=$v"; } - if (!empty($cookies)) { + if ($cookies) { $cookieHeader = 'Cookie: '.implode('; ', $cookies)."\r\n"; } diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index df55628c6d15d..058b5419a87f9 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -1702,6 +1702,12 @@ public function testToString() $asString = (string) $request; $this->assertStringContainsString('Cookie: Foo=Bar; Another=Cookie', $asString); + + $request->cookies->set('foo.bar', [1, 2]); + + $asString = (string) $request; + + $this->assertStringContainsString('foo.bar%5B0%5D=1; foo.bar%5B1%5D=2', $asString); } public function testIsMethod() From d4e1edd64d8d53379eb940f244739ca52c9f4b37 Mon Sep 17 00:00:00 2001 From: Klaus Silveira Date: Thu, 17 Nov 2022 17:48:28 -0500 Subject: [PATCH 37/58] [Serializer] Prevent GetSetMethodNormalizer from creating invalid magic method call --- .../Normalizer/GetSetMethodNormalizer.php | 15 ++++++--- .../Normalizer/GetSetMethodNormalizerTest.php | 31 +++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php index b38a5f6a419ba..962bf6a5b9b5e 100644 --- a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php @@ -126,17 +126,17 @@ protected function getAttributeValue(object $object, string $attribute, string $ $ucfirsted = ucfirst($attribute); $getter = 'get'.$ucfirsted; - if (\is_callable([$object, $getter])) { + if (method_exists($object, $getter) && \is_callable([$object, $getter])) { return $object->$getter(); } $isser = 'is'.$ucfirsted; - if (\is_callable([$object, $isser])) { + if (method_exists($object, $isser) && \is_callable([$object, $isser])) { return $object->$isser(); } $haser = 'has'.$ucfirsted; - if (\is_callable([$object, $haser])) { + if (method_exists($object, $haser) && \is_callable([$object, $haser])) { return $object->$haser(); } @@ -152,7 +152,14 @@ protected function setAttributeValue(object $object, string $attribute, $value, $key = \get_class($object).':'.$setter; if (!isset(self::$setterAccessibleCache[$key])) { - self::$setterAccessibleCache[$key] = \is_callable([$object, $setter]) && !(new \ReflectionMethod($object, $setter))->isStatic(); + try { + // We have to use is_callable() here since method_exists() + // does not "see" protected/private methods + self::$setterAccessibleCache[$key] = \is_callable([$object, $setter]) && !(new \ReflectionMethod($object, $setter))->isStatic(); + } catch (\ReflectionException $e) { + // Method does not exist in the class, probably a magic method + self::$setterAccessibleCache[$key] = false; + } } if (self::$setterAccessibleCache[$key]) { diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php index 6fd430bb47a43..e6f8396fe9d15 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php @@ -453,6 +453,22 @@ public function testHasGetterNormalize() ); } + public function testCallMagicMethodDenormalize() + { + $obj = $this->normalizer->denormalize(['active' => true], ObjectWithMagicMethod::class); + $this->assertTrue($obj->isActive()); + } + + public function testCallMagicMethodNormalize() + { + $obj = new ObjectWithMagicMethod(); + + $this->assertSame( + ['active' => true], + $this->normalizer->normalize($obj, 'any') + ); + } + protected function getObjectCollectionWithExpectedArray(): array { return [[ @@ -722,3 +738,18 @@ public function hasFoo() return $this->foo; } } + +class ObjectWithMagicMethod +{ + private $active = true; + + public function isActive() + { + return $this->active; + } + + public function __call($key, $value) + { + throw new \RuntimeException('__call should not be called. Called with: '.$key); + } +} From 02c879c1cf9263346943e35cad789409795a69ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Mon, 12 Dec 2022 15:07:22 +0100 Subject: [PATCH 38/58] [Messenger][Amqp] Added missing rpc_timeout option Without this option, it's not possible to set a timeout on the connection. It means, if the network between RabbitMQ and the application goes down, the code in `Connection::get()` will hand forever. And at some point it will cause more troubles. For example: * many connection/channel opened (because the consumer is not killed) ; * or, when the connexion gets back, RabbitMQ have killed the consumer anyway => Another Exception. With this patch, and with the following configuration, exception are clear on what occurs. ``` framework: messenger: transports: rabbitmq: dsn: .... options: read_timeout: 5 write_timeout: 5 connect_timeout: 5 confirm_timeout: 5 rpc_timeout: 5 [...] ``` Example of exception: ``` In AmqpReceiver.php line 56: [Symfony\Component\Messenger\Exception\TransportException] Library error: request timed out Exception trace: at /app/vendor/symfony/amqp-messenger/Transport/AmqpReceiver.php:56 Symfony\Component\Messenger\Bridge\Amqp\Transport\AmqpReceiver->getEnvelope() at /app/vendor/symfony/amqp-messenger/Transport/AmqpReceiver.php:47 Symfony\Component\Messenger\Bridge\Amqp\Transport\AmqpReceiver->getFromQueues() at /app/vendor/symfony/amqp-messenger/Transport/AmqpReceiver.php:41 Symfony\Component\Messenger\Bridge\Amqp\Transport\AmqpReceiver->get() at /app/vendor/symfony/symfony/src/Symfony/Component/Messenger/Worker.php:106 Symfony\Component\Messenger\Worker->run() at /app/vendor/symfony/symfony/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php:229 Symfony\Component\Messenger\Command\ConsumeMessagesCommand->execute() at /app/vendor/symfony/symfony/src/Symfony/Component/Console/Command/Command.php:312 Symfony\Component\Console\Command\Command->run() at /app/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:1038 Symfony\Component\Console\Application->doRunCommand() at /app/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php:88 Symfony\Bundle\FrameworkBundle\Console\Application->doRunCommand() at /app/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:312 Symfony\Component\Console\Application->doRun() at /app/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php:77 Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at /app/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:168 Symfony\Component\Console\Application->run() at /app/bin/console:29 In Connection.php line 432: [AMQPException] Library error: request timed out Exception trace: at /app/vendor/symfony/amqp-messenger/Transport/Connection.php:432 AMQPQueue->get() at /app/vendor/symfony/amqp-messenger/Transport/Connection.php:432 Symfony\Component\Messenger\Bridge\Amqp\Transport\Connection->get() at /app/vendor/symfony/amqp-messenger/Transport/AmqpReceiver.php:54 Symfony\Component\Messenger\Bridge\Amqp\Transport\AmqpReceiver->getEnvelope() at /app/vendor/symfony/amqp-messenger/Transport/AmqpReceiver.php:47 Symfony\Component\Messenger\Bridge\Amqp\Transport\AmqpReceiver->getFromQueues() at /app/vendor/symfony/amqp-messenger/Transport/AmqpReceiver.php:41 Symfony\Component\Messenger\Bridge\Amqp\Transport\AmqpReceiver->get() at /app/vendor/symfony/symfony/src/Symfony/Component/Messenger/Worker.php:106 Symfony\Component\Messenger\Worker->run() at /app/vendor/symfony/symfony/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php:229 Symfony\Component\Messenger\Command\ConsumeMessagesCommand->execute() at /app/vendor/symfony/symfony/src/Symfony/Component/Console/Command/Command.php:312 Symfony\Component\Console\Command\Command->run() at /app/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:1038 Symfony\Component\Console\Application->doRunCommand() at /app/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php:88 Symfony\Bundle\FrameworkBundle\Console\Application->doRunCommand() at /app/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:312 Symfony\Component\Console\Application->doRun() at /app/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php:77 Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at /app/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:168 Symfony\Component\Console\Application->run() at /app/bin/console:29 messenger:consume [-l|--limit LIMIT] [-f|--failure-limit FAILURE-LIMIT] [-m|--memory-limit MEMORY-LIMIT] [-t|--time-limit TIME-LIMIT] [--sleep SLEEP] [-b|--bus BUS] [--queues QUEUES] [--no-reset] [--] [...] ``` --- .../Component/Messenger/Bridge/Amqp/Transport/Connection.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Symfony/Component/Messenger/Bridge/Amqp/Transport/Connection.php b/src/Symfony/Component/Messenger/Bridge/Amqp/Transport/Connection.php index 35fa87ca54a5a..0595b573e8776 100644 --- a/src/Symfony/Component/Messenger/Bridge/Amqp/Transport/Connection.php +++ b/src/Symfony/Component/Messenger/Bridge/Amqp/Transport/Connection.php @@ -32,6 +32,9 @@ class Connection 'x-message-ttl', ]; + /** + * @see https://github.com/php-amqp/php-amqp/blob/master/amqp_connection_resource.h + */ private const AVAILABLE_OPTIONS = [ 'host', 'port', @@ -53,6 +56,7 @@ class Connection 'write_timeout', 'confirm_timeout', 'connect_timeout', + 'rpc_timeout', 'cacert', 'cert', 'key', From 4d2b176b936f0e39acfce9cf869a68ae38895790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Nogueira?= <> Date: Mon, 24 Oct 2022 18:02:31 +0100 Subject: [PATCH 39/58] [Cache] Fix dealing with ext-redis' multi/exec returning a bool --- src/Symfony/Component/Cache/Traits/RedisTrait.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Cache/Traits/RedisTrait.php b/src/Symfony/Component/Cache/Traits/RedisTrait.php index dabdde4e705ce..ac9d5e1a9c1b8 100644 --- a/src/Symfony/Component/Cache/Traits/RedisTrait.php +++ b/src/Symfony/Component/Cache/Traits/RedisTrait.php @@ -529,7 +529,11 @@ private function pipeline(\Closure $generator, $redis = null): \Generator if (!$redis instanceof \Predis\ClientInterface && 'eval' === $command && $redis->getLastError()) { $e = new \RedisException($redis->getLastError()); - $results = array_map(function ($v) use ($e) { return false === $v ? $e : $v; }, $results); + $results = array_map(function ($v) use ($e) { return false === $v ? $e : $v; }, (array) $results); + } + + if (\is_bool($results)) { + return; } foreach ($ids as $k => $id) { From c871df802e60d3d936dabc8aba9519967d718e70 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 13 Dec 2022 19:04:17 +0100 Subject: [PATCH 40/58] [Translation] add tests + fix --- .../Translation/Extractor/Visitor/AbstractVisitor.php | 10 ++-------- .../Tests/fixtures/extractor-ast/translation.html.php | 2 ++ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Component/Translation/Extractor/Visitor/AbstractVisitor.php b/src/Symfony/Component/Translation/Extractor/Visitor/AbstractVisitor.php index 764f8f708881f..62e7a3da1622b 100644 --- a/src/Symfony/Component/Translation/Extractor/Visitor/AbstractVisitor.php +++ b/src/Symfony/Component/Translation/Extractor/Visitor/AbstractVisitor.php @@ -48,17 +48,11 @@ protected function getStringArguments(Node\Expr\CallLike|Node\Attribute|Node\Exp $args = $node instanceof Node\Expr\CallLike ? $node->getRawArgs() : $node->args; - if (\count($args) < $index) { + if (!($arg = $args[$index] ?? null) instanceof Node\Arg) { return []; } - if (($arg = $args[$index]) instanceof Node\Arg) { - if ($result = $this->getStringValue($arg->value)) { - return [$result]; - } - } - - return []; + return (array) $this->getStringValue($arg->value); } protected function hasNodeNamedArguments(Node\Expr\CallLike|Node\Attribute|Node\Expr\New_ $node): bool diff --git a/src/Symfony/Component/Translation/Tests/fixtures/extractor-ast/translation.html.php b/src/Symfony/Component/Translation/Tests/fixtures/extractor-ast/translation.html.php index 781da00b64f9e..e34863f3dc660 100644 --- a/src/Symfony/Component/Translation/Tests/fixtures/extractor-ast/translation.html.php +++ b/src/Symfony/Component/Translation/Tests/fixtures/extractor-ast/translation.html.php @@ -54,3 +54,5 @@ trans('variable-assignation-inlined-in-trans-method-call3', [], $domain = 'not_messages'); ?> trans(domain: $domain = 'not_messages', message: $key = 'variable-assignation-inlined-with-named-arguments-in-trans-method', parameters: $parameters = []); ?> + +trans(...); // should not fail ?> From cb2178d4de3303a6ca1fd08894cbd371a44cd7ae Mon Sep 17 00:00:00 2001 From: Tom Van Looy Date: Tue, 13 Dec 2022 19:51:33 +0100 Subject: [PATCH 41/58] Use relative timestamps --- .../Handler/MemcachedSessionHandler.php | 17 ++++++++++++++-- .../Handler/MemcachedSessionHandlerTest.php | 20 ++++++++++++++++++- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php index 9cb841ae0d775..e0ec4d2d9984d 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php @@ -77,7 +77,7 @@ protected function doRead(string $sessionId) #[\ReturnTypeWillChange] public function updateTimestamp($sessionId, $data) { - $this->memcached->touch($this->prefix.$sessionId, time() + (int) ($this->ttl ?? \ini_get('session.gc_maxlifetime'))); + $this->memcached->touch($this->prefix.$sessionId, $this->getCompatibleTtl()); return true; } @@ -87,7 +87,20 @@ public function updateTimestamp($sessionId, $data) */ protected function doWrite(string $sessionId, string $data) { - return $this->memcached->set($this->prefix.$sessionId, $data, time() + (int) ($this->ttl ?? \ini_get('session.gc_maxlifetime'))); + return $this->memcached->set($this->prefix.$sessionId, $data, $this->getCompatibleTtl()); + } + + private function getCompatibleTtl(): int + { + $ttl = (int) ($this->ttl ?? \ini_get('session.gc_maxlifetime')); + + // If the relative TTL that is used exceeds 30 days, memcached will treat the value as Unix time. + // We have to convert it to an absolute Unix time at this point, to make sure the TTL is correct. + if ($ttl > 60 * 60 * 24 * 30) { + $ttl += time(); + } + + return $ttl; } /** diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php index d404b74c6a5f4..6abdf4eb05f5c 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php @@ -16,6 +16,7 @@ /** * @requires extension memcached + * * @group time-sensitive */ class MemcachedSessionHandlerTest extends TestCase @@ -92,13 +93,30 @@ public function testWriteSession() $this->memcached ->expects($this->once()) ->method('set') - ->with(self::PREFIX.'id', 'data', $this->equalTo(time() + self::TTL, 2)) + ->with(self::PREFIX.'id', 'data', $this->equalTo(self::TTL, 2)) ->willReturn(true) ; $this->assertTrue($this->storage->write('id', 'data')); } + public function testWriteSessionWithLargeTTL() + { + $this->memcached + ->expects($this->once()) + ->method('set') + ->with(self::PREFIX.'id', 'data', $this->equalTo(time() + self::TTL + 60 * 60 * 24 * 30, 2)) + ->willReturn(true) + ; + + $storage = new MemcachedSessionHandler( + $this->memcached, + ['prefix' => self::PREFIX, 'expiretime' => self::TTL + 60 * 60 * 24 * 30] + ); + + $this->assertTrue($storage->write('id', 'data')); + } + public function testDestroySession() { $this->memcached From 93e5160ec0c0b47633b24448404277b5173c26af Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sun, 11 Dec 2022 19:08:44 +0100 Subject: [PATCH 42/58] Fix HtmlSanitizer default configuration behavior for allowed schemes --- .../FrameworkExtension.php | 8 ++++-- .../FrameworkExtensionTest.php | 2 ++ .../Tests/TextSanitizer/UrlSanitizerTest.php | 27 +++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 788d8929627ec..44c6eef0b4b60 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -2738,10 +2738,14 @@ private function registerHtmlSanitizerConfiguration(array $config, ContainerBuil // Settings $def->addMethodCall('forceHttpsUrls', [$sanitizerConfig['force_https_urls']], true); - $def->addMethodCall('allowLinkSchemes', [$sanitizerConfig['allowed_link_schemes']], true); + if ($sanitizerConfig['allowed_link_schemes']) { + $def->addMethodCall('allowLinkSchemes', [$sanitizerConfig['allowed_link_schemes']], true); + } $def->addMethodCall('allowLinkHosts', [$sanitizerConfig['allowed_link_hosts']], true); $def->addMethodCall('allowRelativeLinks', [$sanitizerConfig['allow_relative_links']], true); - $def->addMethodCall('allowMediaSchemes', [$sanitizerConfig['allowed_media_schemes']], true); + if ($sanitizerConfig['allowed_media_schemes']) { + $def->addMethodCall('allowMediaSchemes', [$sanitizerConfig['allowed_media_schemes']], true); + } $def->addMethodCall('allowMediaHosts', [$sanitizerConfig['allowed_media_hosts']], true); $def->addMethodCall('allowRelativeMedias', [$sanitizerConfig['allow_relative_medias']], true); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index 4b643f27cbe31..d0268990e0fb4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -2140,7 +2140,9 @@ public function testHtmlSanitizerDefaultNullAllowedLinkMediaHost() $calls = $container->getDefinition('html_sanitizer.config.custom_default')->getMethodCalls(); $this->assertContains(['allowLinkHosts', [null], true], $calls); + $this->assertContains(['allowRelativeLinks', [false], true], $calls); $this->assertContains(['allowMediaHosts', [null], true], $calls); + $this->assertContains(['allowRelativeMedias', [false], true], $calls); } public function testHtmlSanitizerDefaultConfig() diff --git a/src/Symfony/Component/HtmlSanitizer/Tests/TextSanitizer/UrlSanitizerTest.php b/src/Symfony/Component/HtmlSanitizer/Tests/TextSanitizer/UrlSanitizerTest.php index 3216244e9ed10..18fec32dee43d 100644 --- a/src/Symfony/Component/HtmlSanitizer/Tests/TextSanitizer/UrlSanitizerTest.php +++ b/src/Symfony/Component/HtmlSanitizer/Tests/TextSanitizer/UrlSanitizerTest.php @@ -45,6 +45,33 @@ public function provideSanitize() 'output' => null, ]; + yield [ + 'input' => 'http://trusted.com/link.php', + 'allowedSchemes' => null, + 'allowedHosts' => null, + 'forceHttps' => false, + 'allowRelative' => false, + 'output' => 'http://trusted.com/link.php', + ]; + + yield [ + 'input' => 'https://trusted.com/link.php', + 'allowedSchemes' => null, + 'allowedHosts' => null, + 'forceHttps' => false, + 'allowRelative' => false, + 'output' => 'https://trusted.com/link.php', + ]; + + yield [ + 'input' => 'data:text/plain;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7', + 'allowedSchemes' => null, + 'allowedHosts' => null, + 'forceHttps' => false, + 'allowRelative' => false, + 'output' => 'data:text/plain;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7', + ]; + yield [ 'input' => 'https://trusted.com/link.php', 'allowedSchemes' => ['https'], From bb7ea3cb064360a776e3854dcff3bd21e7fdfb1f Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 14 Dec 2022 14:50:04 +0100 Subject: [PATCH 43/58] [Validator] Allow opt-out of EmailValidator deprecation when using Validation::createValidatorBuilder() --- .../Validator/ConstraintValidatorFactory.php | 13 +++----- .../Tests/ConstraintValidatorFactoryTest.php | 33 +++++++++++++++++++ ...ontainerConstraintValidatorFactoryTest.php | 18 ++-------- .../Tests/Fixtures/DummyConstraint.php | 22 +++++++++++++ .../Fixtures/DummyConstraintValidator.php | 22 +++++++++++++ 5 files changed, 84 insertions(+), 24 deletions(-) create mode 100644 src/Symfony/Component/Validator/Tests/ConstraintValidatorFactoryTest.php create mode 100644 src/Symfony/Component/Validator/Tests/Fixtures/DummyConstraint.php create mode 100644 src/Symfony/Component/Validator/Tests/Fixtures/DummyConstraintValidator.php diff --git a/src/Symfony/Component/Validator/ConstraintValidatorFactory.php b/src/Symfony/Component/Validator/ConstraintValidatorFactory.php index 8152ad0dece11..778e202a84bf8 100644 --- a/src/Symfony/Component/Validator/ConstraintValidatorFactory.php +++ b/src/Symfony/Component/Validator/ConstraintValidatorFactory.php @@ -26,20 +26,17 @@ class ConstraintValidatorFactory implements ConstraintValidatorFactoryInterface { protected $validators = []; - public function __construct() + public function __construct(array $validators = []) { + $this->validators = $validators; } public function getInstance(Constraint $constraint): ConstraintValidatorInterface { - $className = $constraint->validatedBy(); - - if (!isset($this->validators[$className])) { - $this->validators[$className] = 'validator.expression' === $className - ? new ExpressionValidator() - : new $className(); + if ('validator.expression' === $name = $class = $constraint->validatedBy()) { + $class = ExpressionValidator::class; } - return $this->validators[$className]; + return $this->validators[$name] ??= new $class(); } } diff --git a/src/Symfony/Component/Validator/Tests/ConstraintValidatorFactoryTest.php b/src/Symfony/Component/Validator/Tests/ConstraintValidatorFactoryTest.php new file mode 100644 index 0000000000000..d7d9b302d1bb7 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/ConstraintValidatorFactoryTest.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Validator\ConstraintValidatorFactory; +use Symfony\Component\Validator\Tests\Fixtures\DummyConstraint; +use Symfony\Component\Validator\Tests\Fixtures\DummyConstraintValidator; + +class ConstraintValidatorFactoryTest extends TestCase +{ + public function testGetInstance() + { + $factory = new ConstraintValidatorFactory(); + $this->assertInstanceOf(DummyConstraintValidator::class, $factory->getInstance(new DummyConstraint())); + } + + public function testPredefinedGetInstance() + { + $validator = new DummyConstraintValidator(); + $factory = new ConstraintValidatorFactory([DummyConstraintValidator::class => $validator]); + $this->assertSame($validator, $factory->getInstance(new DummyConstraint())); + } +} diff --git a/src/Symfony/Component/Validator/Tests/ContainerConstraintValidatorFactoryTest.php b/src/Symfony/Component/Validator/Tests/ContainerConstraintValidatorFactoryTest.php index 3999b86b4f196..63b7f6f96ae01 100644 --- a/src/Symfony/Component/Validator/Tests/ContainerConstraintValidatorFactoryTest.php +++ b/src/Symfony/Component/Validator/Tests/ContainerConstraintValidatorFactoryTest.php @@ -15,9 +15,10 @@ use Symfony\Component\DependencyInjection\Container; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\Blank as BlankConstraint; -use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\ContainerConstraintValidatorFactory; use Symfony\Component\Validator\Exception\ValidatorException; +use Symfony\Component\Validator\Tests\Fixtures\DummyConstraint; +use Symfony\Component\Validator\Tests\Fixtures\DummyConstraintValidator; class ContainerConstraintValidatorFactoryTest extends TestCase { @@ -59,18 +60,3 @@ public function testGetInstanceInvalidValidatorClass() $factory->getInstance($constraint); } } - -class DummyConstraint extends Constraint -{ - public function validatedBy(): string - { - return DummyConstraintValidator::class; - } -} - -class DummyConstraintValidator extends ConstraintValidator -{ - public function validate($value, Constraint $constraint) - { - } -} diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/DummyConstraint.php b/src/Symfony/Component/Validator/Tests/Fixtures/DummyConstraint.php new file mode 100644 index 0000000000000..c2209f135f5ce --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Fixtures/DummyConstraint.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Fixtures; + +use Symfony\Component\Validator\Constraint; + +class DummyConstraint extends Constraint +{ + public function validatedBy(): string + { + return DummyConstraintValidator::class; + } +} diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/DummyConstraintValidator.php b/src/Symfony/Component/Validator/Tests/Fixtures/DummyConstraintValidator.php new file mode 100644 index 0000000000000..3481a5e2d8c1f --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Fixtures/DummyConstraintValidator.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Fixtures; + +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidator; + +class DummyConstraintValidator extends ConstraintValidator +{ + public function validate($value, Constraint $constraint) + { + } +} From 9d2f10fd12f67c356e05a41c36ec1d07e330ffd6 Mon Sep 17 00:00:00 2001 From: Jannik Zschiesche Date: Mon, 12 Dec 2022 11:18:42 +0100 Subject: [PATCH 44/58] Hide excluded services from container debug list --- .../Bundle/FrameworkBundle/Command/ContainerDebugCommand.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php index b040f2419d1b3..293fccc20b18b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php @@ -308,6 +308,9 @@ private function findServiceIdsContaining(ContainerBuilder $builder, string $nam if (!$showHidden && str_starts_with($serviceId, '.')) { continue; } + if (!$showHidden && $builder->hasDefinition($serviceId) && $builder->getDefinition($serviceId)->hasTag('container.excluded')) { + continue; + } if (false !== stripos(str_replace('\\', '', $serviceId), $name)) { $foundServiceIdsIgnoringBackslashes[] = $serviceId; } From 6910244cd596bd2190bd23201e37f9ea693523e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Romey?= Date: Wed, 14 Dec 2022 16:04:36 +0100 Subject: [PATCH 45/58] [FrameworkBundle] fix: fix help message --- .../FrameworkBundle/Command/ConfigDumpReferenceCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php index 7a56ec5abed48..812389c92e02d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php @@ -69,7 +69,7 @@ protected function configure() For dumping a specific option, add its path as second argument (only available for the yaml format): - php %command.full_name% framework profiler.matcher + php %command.full_name% framework http_client.default_options EOF ) From 2bfbd92d2b385a19e07300b2ac4d91316beb2331 Mon Sep 17 00:00:00 2001 From: Mehrdad Date: Sun, 6 Nov 2022 19:28:53 +0330 Subject: [PATCH 46/58] [Mailer] Include all transports' debug messages in RoundRobin transport exception --- .../Transport/RoundRobinTransportTest.php | 48 +++++++++++++++++-- .../Mailer/Transport/RoundRobinTransport.php | 6 ++- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/Mailer/Tests/Transport/RoundRobinTransportTest.php b/src/Symfony/Component/Mailer/Tests/Transport/RoundRobinTransportTest.php index fff75af64b457..08edb245a0df9 100644 --- a/src/Symfony/Component/Mailer/Tests/Transport/RoundRobinTransportTest.php +++ b/src/Symfony/Component/Mailer/Tests/Transport/RoundRobinTransportTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Mailer\Exception\TransportException; +use Symfony\Component\Mailer\Exception\TransportExceptionInterface; use Symfony\Component\Mailer\Transport\RoundRobinTransport; use Symfony\Component\Mailer\Transport\TransportInterface; use Symfony\Component\Mime\RawMessage; @@ -60,10 +61,21 @@ public function testSendAllDead() $t2 = $this->createMock(TransportInterface::class); $t2->expects($this->once())->method('send')->will($this->throwException(new TransportException())); $t = new RoundRobinTransport([$t1, $t2]); - $this->expectException(TransportException::class); - $this->expectExceptionMessage('All transports failed.'); - $t->send(new RawMessage('')); - $this->assertTransports($t, 1, [$t1, $t2]); + $p = new \ReflectionProperty($t, 'cursor'); + $p->setAccessible(true); + $p->setValue($t, 0); + + try { + $t->send(new RawMessage('')); + } catch (\Exception $e) { + $this->assertInstanceOf(TransportException::class, $e); + $this->assertStringContainsString('All transports failed.', $e->getMessage()); + $this->assertTransports($t, 0, [$t1, $t2]); + + return; + } + + $this->fail('The expected exception was not thrown.'); } public function testSendOneDead() @@ -127,6 +139,34 @@ public function testSendOneDeadAndRecoveryWithinRetryPeriod() $this->assertTransports($t, 1, []); } + public function testFailureDebugInformation() + { + $t1 = $this->createMock(TransportInterface::class); + $e1 = new TransportException(); + $e1->appendDebug('Debug message 1'); + $t1->expects($this->once())->method('send')->will($this->throwException($e1)); + $t1->expects($this->once())->method('__toString')->willReturn('t1'); + + $t2 = $this->createMock(TransportInterface::class); + $e2 = new TransportException(); + $e2->appendDebug('Debug message 2'); + $t2->expects($this->once())->method('send')->will($this->throwException($e2)); + $t2->expects($this->once())->method('__toString')->willReturn('t2'); + + $t = new RoundRobinTransport([$t1, $t2]); + + try { + $t->send(new RawMessage('')); + } catch (TransportExceptionInterface $e) { + $this->assertStringContainsString('Transport "t1": Debug message 1', $e->getDebug()); + $this->assertStringContainsString('Transport "t2": Debug message 2', $e->getDebug()); + + return; + } + + $this->fail('Expected exception was not thrown!'); + } + private function assertTransports(RoundRobinTransport $transport, int $cursor, array $deadTransports) { $p = new \ReflectionProperty($transport, 'cursor'); diff --git a/src/Symfony/Component/Mailer/Transport/RoundRobinTransport.php b/src/Symfony/Component/Mailer/Transport/RoundRobinTransport.php index 2f351389fa1f2..761b57f188b75 100644 --- a/src/Symfony/Component/Mailer/Transport/RoundRobinTransport.php +++ b/src/Symfony/Component/Mailer/Transport/RoundRobinTransport.php @@ -48,15 +48,19 @@ public function __construct(array $transports, int $retryPeriod = 60) public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage { + $exception = null; + while ($transport = $this->getNextTransport()) { try { return $transport->send($message, $envelope); } catch (TransportExceptionInterface $e) { + $exception = $exception ?? new TransportException('All transports failed.'); + $exception->appendDebug(sprintf("Transport \"%s\": %s\n", $transport, $e->getDebug())); $this->deadTransports[$transport] = microtime(true); } } - throw new TransportException('All transports failed.'); + throw $exception ?? new TransportException('No transports found.'); } public function __toString(): string From a34f99da945d12143632cb1bc6b8cff920321978 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 14 Dec 2022 17:19:02 +0100 Subject: [PATCH 47/58] Fix merge --- src/Symfony/Component/Mime/Part/MessagePart.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Symfony/Component/Mime/Part/MessagePart.php b/src/Symfony/Component/Mime/Part/MessagePart.php index 00129b4757b40..270d57aa343ac 100644 --- a/src/Symfony/Component/Mime/Part/MessagePart.php +++ b/src/Symfony/Component/Mime/Part/MessagePart.php @@ -60,10 +60,7 @@ public function bodyToIterable(): iterable return $this->message->toIterable(); } - /** - * @return array - */ - public function __sleep() + public function __sleep(): array { return ['message']; } From 949c30416f06eb9cd3057046f7e8fbd65c554a7a Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Wed, 14 Dec 2022 18:59:30 +0100 Subject: [PATCH 48/58] [DI] Fix undefined class in test --- .../Tests/Compiler/ServiceLocatorTagPassTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php index ead1df25a2123..96215c6d14883 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php @@ -184,19 +184,19 @@ public function testIndexedByServiceIdWithDecoration() $container->setDefinition(Service::class, $service); - $decorated = new Definition(Decorated::class); + $decorated = new Definition(DecoratedService::class); $decorated->setPublic(true); $decorated->setDecoratedService(Service::class); - $container->setDefinition(Decorated::class, $decorated); + $container->setDefinition(DecoratedService::class, $decorated); $container->compile(); /** @var ServiceLocator $locator */ $locator = $container->get(Locator::class)->locator; static::assertTrue($locator->has(Service::class)); - static::assertFalse($locator->has(Decorated::class)); - static::assertInstanceOf(Decorated::class, $locator->get(Service::class)); + static::assertFalse($locator->has(DecoratedService::class)); + static::assertInstanceOf(DecoratedService::class, $locator->get(Service::class)); } public function testDefinitionOrderIsTheSame() From 84374944057c993da2bcb456ef38e22b5192a08f Mon Sep 17 00:00:00 2001 From: Alex Pott Date: Sat, 10 Dec 2022 09:49:05 +0000 Subject: [PATCH 49/58] [DependencyInjection] Shared private services becomes public after a public service is accessed --- .../DependencyInjection/ContainerBuilder.php | 18 +++++++++++------- .../DependencyInjection/ReverseContainer.php | 7 +------ .../Tests/ContainerBuilderTest.php | 9 +++++++++ 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 485630a748764..db1ea84c2cf73 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -546,8 +546,8 @@ public function has(string $id) */ public function get(string $id, int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { - if ($this->isCompiled() && isset($this->removedIds[$id]) && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE >= $invalidBehavior) { - return parent::get($id); + if ($this->isCompiled() && isset($this->removedIds[$id])) { + return ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE >= $invalidBehavior ? parent::get($id) : null; } return $this->doGet($id, $invalidBehavior); @@ -564,9 +564,9 @@ private function doGet(string $id, int $invalidBehavior = ContainerInterface::EX } try { if (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior) { - return parent::get($id, $invalidBehavior); + return $this->privates[$id] ?? parent::get($id, $invalidBehavior); } - if ($service = parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) { + if (null !== $service = $this->privates[$id] ?? parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) { return $service; } } catch (ServiceCircularReferenceException $e) { @@ -1085,8 +1085,8 @@ private function createService(Definition $definition, array &$inlineServices, b } } - if (null !== $id && $definition->isShared() && isset($this->services[$id]) && ($tryProxy || !$definition->isLazy())) { - return $this->services[$id]; + if (null !== $id && $definition->isShared() && (isset($this->services[$id]) || isset($this->privates[$id])) && ($tryProxy || !$definition->isLazy())) { + return $this->services[$id] ?? $this->privates[$id]; } if (null !== $factory) { @@ -1665,7 +1665,11 @@ private function shareService(Definition $definition, $service, ?string $id, arr $inlineServices[$id ?? spl_object_hash($definition)] = $service; if (null !== $id && $definition->isShared()) { - $this->services[$id] = $service; + if ($definition->isPrivate() && $this->isCompiled()) { + $this->privates[$id] = $service; + } else { + $this->services[$id] = $service; + } unset($this->loading[$id]); } } diff --git a/src/Symfony/Component/DependencyInjection/ReverseContainer.php b/src/Symfony/Component/DependencyInjection/ReverseContainer.php index 280e9e2dd5a63..e5207e56dcf71 100644 --- a/src/Symfony/Component/DependencyInjection/ReverseContainer.php +++ b/src/Symfony/Component/DependencyInjection/ReverseContainer.php @@ -63,10 +63,6 @@ public function getId(object $service): ?string */ public function getService(string $id): object { - if ($this->serviceContainer->has($id)) { - return $this->serviceContainer->get($id); - } - if ($this->reversibleLocator->has($id)) { return $this->reversibleLocator->get($id); } @@ -75,7 +71,6 @@ public function getService(string $id): object throw new ServiceNotFoundException($id, null, null, [], sprintf('The "%s" service is private and cannot be accessed by reference. You should either make it public, or tag it as "%s".', $id, $this->tagName)); } - // will throw a ServiceNotFoundException - $this->serviceContainer->get($id); + return $this->serviceContainer->get($id); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index e491e2c790171..1dfd8f86eaf45 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -1105,10 +1105,19 @@ public function testPrivateServiceUser() $container->addDefinitions([ 'bar' => $fooDefinition, 'bar_user' => $fooUserDefinition->setPublic(true), + 'bar_user2' => $fooUserDefinition->setPublic(true), ]); $container->compile(); + $this->assertNull($container->get('bar', $container::NULL_ON_INVALID_REFERENCE)); $this->assertInstanceOf(\BarClass::class, $container->get('bar_user')->bar); + + // Ensure that accessing a public service with a shared private service + // does not make the private service available. + $this->assertNull($container->get('bar', $container::NULL_ON_INVALID_REFERENCE)); + + // Ensure the private service is still shared. + $this->assertSame($container->get('bar_user')->bar, $container->get('bar_user2')->bar); } public function testThrowsExceptionWhenSetServiceOnACompiledContainer() From 36ad0bed3666283b184c97489fe8fee1f44d4a4c Mon Sep 17 00:00:00 2001 From: rodmen Date: Tue, 13 Dec 2022 16:36:51 -0300 Subject: [PATCH 50/58] [HttpKernel] AbstractSessionListener should not override the cache lifetime for private responses --- .../EventListener/AbstractSessionListener.php | 5 +- .../EventListener/SessionListenerTest.php | 59 +++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php b/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php index 4603052e933df..27749b24b2504 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php @@ -200,10 +200,11 @@ public function onKernelResponse(ResponseEvent $event) } if ($autoCacheControl) { + $maxAge = $response->headers->hasCacheControlDirective('public') ? 0 : (int) $response->getMaxAge(); $response - ->setExpires(new \DateTime()) + ->setExpires(new \DateTimeImmutable('+'.$maxAge.' seconds')) ->setPrivate() - ->setMaxAge(0) + ->setMaxAge($maxAge) ->headers->addCacheControlDirective('must-revalidate'); } diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php index 5e11875bb1db8..96f66354ba7e4 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php @@ -39,6 +39,7 @@ class SessionListenerTest extends TestCase { /** * @dataProvider provideSessionOptions + * * @runInSeparateProcess */ public function testSessionCookieOptions(array $phpSessionOptions, array $sessionOptions, array $expectedSessionOptions) @@ -531,6 +532,64 @@ public function testUninitializedSessionWithoutInitializedSession() $this->assertSame('60', $response->headers->getCacheControlDirective('s-maxage')); } + public function testResponseHeadersMaxAgeAndExpiresNotBeOverridenIfSessionStarted() + { + $session = $this->createMock(Session::class); + $session->expects($this->exactly(2))->method('getUsageIndex')->will($this->onConsecutiveCalls(0, 1)); + + $container = new Container(); + $container->set('initialized_session', $session); + + $listener = new SessionListener($container); + $kernel = $this->createMock(HttpKernelInterface::class); + + $request = new Request(); + $listener->onKernelRequest(new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST)); + + $response = new Response(); + $response->setPrivate(); + $expiresHeader = gmdate('D, d M Y H:i:s', time() + 600).' GMT'; + $response->setMaxAge(600); + $listener->onKernelResponse(new ResponseEvent($kernel, new Request(), HttpKernelInterface::MAIN_REQUEST, $response)); + + $this->assertTrue($response->headers->has('expires')); + $this->assertSame($expiresHeader, $response->headers->get('expires')); + $this->assertFalse($response->headers->has('max-age')); + $this->assertSame('600', $response->headers->getCacheControlDirective('max-age')); + $this->assertFalse($response->headers->hasCacheControlDirective('public')); + $this->assertTrue($response->headers->hasCacheControlDirective('private')); + $this->assertTrue($response->headers->hasCacheControlDirective('must-revalidate')); + $this->assertFalse($response->headers->has(AbstractSessionListener::NO_AUTO_CACHE_CONTROL_HEADER)); + } + + public function testResponseHeadersMaxAgeAndExpiresDefaultValuesIfSessionStarted() + { + $session = $this->createMock(Session::class); + $session->expects($this->exactly(2))->method('getUsageIndex')->will($this->onConsecutiveCalls(0, 1)); + + $container = new Container(); + $container->set('initialized_session', $session); + + $listener = new SessionListener($container); + $kernel = $this->createMock(HttpKernelInterface::class); + + $request = new Request(); + $listener->onKernelRequest(new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST)); + + $response = new Response(); + $expiresHeader = gmdate('D, d M Y H:i:s', time()).' GMT'; + $listener->onKernelResponse(new ResponseEvent($kernel, new Request(), HttpKernelInterface::MAIN_REQUEST, $response)); + + $this->assertTrue($response->headers->has('expires')); + $this->assertSame($expiresHeader, $response->headers->get('expires')); + $this->assertFalse($response->headers->has('max-age')); + $this->assertSame('0', $response->headers->getCacheControlDirective('max-age')); + $this->assertFalse($response->headers->hasCacheControlDirective('public')); + $this->assertTrue($response->headers->hasCacheControlDirective('private')); + $this->assertTrue($response->headers->hasCacheControlDirective('must-revalidate')); + $this->assertFalse($response->headers->has(AbstractSessionListener::NO_AUTO_CACHE_CONTROL_HEADER)); + } + public function testSurrogateMainRequestIsPublic() { $session = $this->createMock(Session::class); From c87ac90c42c10895bdebe195f8b2963fddde5284 Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Thu, 15 Dec 2022 13:11:53 +0100 Subject: [PATCH 51/58] [Serializer] fix context attribute with serializedName --- .../Normalizer/AbstractNormalizer.php | 2 +- .../AbstractObjectNormalizerTest.php | 17 +++++++++++ .../ObjectDummyWithContextAttribute.php | 29 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 src/Symfony/Component/Serializer/Tests/Normalizer/Features/ObjectDummyWithContextAttribute.php diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index 12c778cb803af..829e178407bd2 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -331,8 +331,8 @@ protected function instantiateObject(array &$data, string $class, array &$contex $params = []; foreach ($constructorParameters as $constructorParameter) { $paramName = $constructorParameter->name; + $attributeContext = $this->getAttributeDenormalizationContext($class, $paramName, $context); $key = $this->nameConverter ? $this->nameConverter->normalize($paramName, $class, $format, $context) : $paramName; - $attributeContext = $this->getAttributeDenormalizationContext($class, $key, $context); $allowed = false === $allowedAttributes || \in_array($paramName, $allowedAttributes); $ignored = !$this->isAllowedAttribute($class, $paramName, $format, $context); diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php index 9c8ba3ffcf4e2..4cc6686586e89 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php @@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor; use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; +use Symfony\Component\PropertyInfo\PropertyInfoExtractor; use Symfony\Component\PropertyInfo\Type; use Symfony\Component\Serializer\Annotation\SerializedName; use Symfony\Component\Serializer\Annotation\SerializedPath; @@ -32,6 +33,7 @@ use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader; use Symfony\Component\Serializer\NameConverter\MetadataAwareNameConverter; use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; +use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Component\Serializer\Serializer; @@ -41,6 +43,7 @@ use Symfony\Component\Serializer\Tests\Fixtures\Annotations\AbstractDummyFirstChild; use Symfony\Component\Serializer\Tests\Fixtures\Annotations\AbstractDummySecondChild; use Symfony\Component\Serializer\Tests\Fixtures\DummySecondChildQuux; +use Symfony\Component\Serializer\Tests\Normalizer\Features\ObjectDummyWithContextAttribute; class AbstractObjectNormalizerTest extends TestCase { @@ -525,6 +528,20 @@ public function testDenormalizeRecursiveWithObjectAttributeWithStringValue() $this->assertInstanceOf(ObjectInner::class, $obj->getInner()); } + + public function testDenormalizeUsesContextAttributeForPropertiesInConstructorWithSeralizedName() + { + $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); + + $extractor = new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]); + $normalizer = new ObjectNormalizer($classMetadataFactory, new MetadataAwareNameConverter($classMetadataFactory), null, $extractor); + $serializer = new Serializer([new DateTimeNormalizer([DateTimeNormalizer::FORMAT_KEY => 'd-m-Y']), $normalizer]); + + /** @var ObjectDummyWithContextAttribute $obj */ + $obj = $serializer->denormalize(['property_with_serialized_name' => '01-02-2022', 'propertyWithoutSerializedName' => '01-02-2022'], ObjectDummyWithContextAttribute::class); + + $this->assertSame($obj->propertyWithSerializedName->format('Y-m-d'), $obj->propertyWithoutSerializedName->format('Y-m-d')); + } } class AbstractObjectNormalizerDummy extends AbstractObjectNormalizer diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/Features/ObjectDummyWithContextAttribute.php b/src/Symfony/Component/Serializer/Tests/Normalizer/Features/ObjectDummyWithContextAttribute.php new file mode 100644 index 0000000000000..b846f042fe620 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/Features/ObjectDummyWithContextAttribute.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Normalizer\Features; + +use Symfony\Component\Serializer\Annotation\Context; +use Symfony\Component\Serializer\Annotation\SerializedName; +use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer; + +final class ObjectDummyWithContextAttribute +{ + public function __construct( + #[Context([DateTimeNormalizer::FORMAT_KEY => 'm-d-Y'])] + #[SerializedName('property_with_serialized_name')] + public \DateTimeImmutable $propertyWithSerializedName, + + #[Context([DateTimeNormalizer::FORMAT_KEY => 'm-d-Y'])] + public \DateTimeImmutable $propertyWithoutSerializedName, + ) { + } +} From 50694897974d47c5e928843baeedca024b860d47 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 15 Dec 2022 15:19:50 +0100 Subject: [PATCH 52/58] [Notifier] composer.json cleanup --- .../Notifier/Bridge/Clickatell/composer.json | 3 -- .../Notifier/Bridge/Discord/composer.json | 3 -- .../Notifier/Bridge/FakeChat/composer.json | 1 - .../Notifier/Bridge/FakeSms/composer.json | 1 - .../Bridge/MessageMedia/composer.json | 54 +++++++++---------- .../Notifier/Bridge/OneSignal/composer.json | 3 -- .../Notifier/Bridge/Slack/composer.json | 3 -- .../Notifier/Bridge/Telegram/composer.json | 3 -- 8 files changed, 27 insertions(+), 44 deletions(-) diff --git a/src/Symfony/Component/Notifier/Bridge/Clickatell/composer.json b/src/Symfony/Component/Notifier/Bridge/Clickatell/composer.json index a176c0c3611ea..2d7d60a68e1e5 100644 --- a/src/Symfony/Component/Notifier/Bridge/Clickatell/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Clickatell/composer.json @@ -24,9 +24,6 @@ "symfony/http-client": "^4.3|^5.0|^6.0", "symfony/notifier": "^5.3|^6.0" }, - "require-dev": { - "symfony/event-dispatcher": "^4.3|^5.0|^6.0" - }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Clickatell\\": "" }, "exclude-from-classmap": [ diff --git a/src/Symfony/Component/Notifier/Bridge/Discord/composer.json b/src/Symfony/Component/Notifier/Bridge/Discord/composer.json index a67dbd48299ec..63bc7997575f3 100644 --- a/src/Symfony/Component/Notifier/Bridge/Discord/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Discord/composer.json @@ -21,9 +21,6 @@ "symfony/notifier": "^5.3|^6.0", "symfony/polyfill-mbstring": "^1.0" }, - "require-dev": { - "symfony/event-dispatcher": "^4.3|^5.0|^6.0" - }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Discord\\": "" }, "exclude-from-classmap": [ diff --git a/src/Symfony/Component/Notifier/Bridge/FakeChat/composer.json b/src/Symfony/Component/Notifier/Bridge/FakeChat/composer.json index 0d2a4792a0010..905f54b2dd7a8 100644 --- a/src/Symfony/Component/Notifier/Bridge/FakeChat/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/FakeChat/composer.json @@ -24,7 +24,6 @@ "php": ">=7.2.5", "symfony/http-client": "^4.4|^5.2|^6.0", "symfony/notifier": "^5.3|^6.0", - "symfony/event-dispatcher-contracts": "^2|^3", "symfony/mailer": "^5.2|^6.0" }, "autoload": { diff --git a/src/Symfony/Component/Notifier/Bridge/FakeSms/composer.json b/src/Symfony/Component/Notifier/Bridge/FakeSms/composer.json index e7199872f3629..7008743675e13 100644 --- a/src/Symfony/Component/Notifier/Bridge/FakeSms/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/FakeSms/composer.json @@ -24,7 +24,6 @@ "php": ">=7.2.5", "symfony/http-client": "^4.4|^5.2|^6.0", "symfony/notifier": "^5.3|^6.0", - "symfony/event-dispatcher-contracts": "^2|^3", "symfony/mailer": "^5.2|^6.0" }, "autoload": { diff --git a/src/Symfony/Component/Notifier/Bridge/MessageMedia/composer.json b/src/Symfony/Component/Notifier/Bridge/MessageMedia/composer.json index 845ec9f6cd2f2..2410b712a7402 100644 --- a/src/Symfony/Component/Notifier/Bridge/MessageMedia/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/MessageMedia/composer.json @@ -1,30 +1,30 @@ { - "name": "symfony/message-media-notifier", - "type": "symfony-notifier-bridge", - "description": "Symfony MessageMedia Notifier Bridge", - "keywords": ["sms", "messagemedia", "notifier"], - "homepage": "https://symfony.com", - "license": "MIT", - "authors": [ - { - "name": "Adrian Nguyen", - "email": "vuphuong87@gmail.com" + "name": "symfony/message-media-notifier", + "type": "symfony-notifier-bridge", + "description": "Symfony MessageMedia Notifier Bridge", + "keywords": ["sms", "messagemedia", "notifier"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Adrian Nguyen", + "email": "vuphuong87@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.2.5", + "symfony/http-client": "^4.4|^5.2|^6.0", + "symfony/notifier": "^5.3|^6.0" }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "require": { - "php": ">=7.2.5", - "symfony/http-client": "^4.4|^5.2|^6.0", - "symfony/notifier": "^5.3|^6.0" - }, - "autoload": { - "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\MessageMedia\\": "" }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "minimum-stability": "dev" + "autoload": { + "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\MessageMedia\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev" } diff --git a/src/Symfony/Component/Notifier/Bridge/OneSignal/composer.json b/src/Symfony/Component/Notifier/Bridge/OneSignal/composer.json index 12a97c870b0c9..af19fde7917d4 100644 --- a/src/Symfony/Component/Notifier/Bridge/OneSignal/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/OneSignal/composer.json @@ -20,9 +20,6 @@ "symfony/http-client": "^4.4|^5.2|^6.0", "symfony/notifier": "^5.4|^6.0" }, - "require-dev": { - "symfony/event-dispatcher": "^4.3|^5.0|^6.0" - }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\OneSignal\\": "" }, "exclude-from-classmap": [ diff --git a/src/Symfony/Component/Notifier/Bridge/Slack/composer.json b/src/Symfony/Component/Notifier/Bridge/Slack/composer.json index f5c7b522b2e1d..70cd75bdc354b 100644 --- a/src/Symfony/Component/Notifier/Bridge/Slack/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Slack/composer.json @@ -21,9 +21,6 @@ "symfony/http-client": "^4.3|^5.0|^6.0", "symfony/notifier": "^5.3|^6.0" }, - "require-dev": { - "symfony/event-dispatcher": "^4.3|^5.0|^6.0" - }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Slack\\": "" }, "exclude-from-classmap": [ diff --git a/src/Symfony/Component/Notifier/Bridge/Telegram/composer.json b/src/Symfony/Component/Notifier/Bridge/Telegram/composer.json index 0ba70d6e549cf..610f06c97195f 100644 --- a/src/Symfony/Component/Notifier/Bridge/Telegram/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Telegram/composer.json @@ -20,9 +20,6 @@ "symfony/http-client": "^4.3|^5.0|^6.0", "symfony/notifier": "^5.3|^6.0" }, - "require-dev": { - "symfony/event-dispatcher": "^4.3|^5.0|^6.0" - }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Telegram\\": "" }, "exclude-from-classmap": [ From 9d510a7083be0590fd96c12acba53b0d41f57159 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 16 Dec 2022 13:37:34 +0100 Subject: [PATCH 53/58] [Cache] fix lazyness of redis when using RedisTagAwareAdapter --- src/Symfony/Component/Cache/Adapter/RedisTagAwareAdapter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Cache/Adapter/RedisTagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/RedisTagAwareAdapter.php index 01128eb150714..a004709ba504d 100644 --- a/src/Symfony/Component/Cache/Adapter/RedisTagAwareAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/RedisTagAwareAdapter.php @@ -65,7 +65,7 @@ public function __construct(\Redis|\RedisArray|\RedisCluster|\Predis\ClientInter throw new InvalidArgumentException(sprintf('Unsupported Predis cluster connection: only "%s" is, "%s" given.', PredisCluster::class, get_debug_type($redis->getConnection()))); } - if (\defined('Redis::OPT_COMPRESSION') && ($redis instanceof \Redis || $redis instanceof \RedisArray || $redis instanceof \RedisCluster)) { + if (\defined('Redis::OPT_COMPRESSION') && \in_array($redis::class, [\Redis::class, \RedisArray::class, \RedisCluster::class], true)) { $compression = $redis->getOption(\Redis::OPT_COMPRESSION); foreach (\is_array($compression) ? $compression : [$compression] as $c) { From 30caa8ded114fad9d1553e0a744f4ab6dabbc6d6 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Fri, 16 Dec 2022 15:44:15 +0100 Subject: [PATCH 54/58] Revert "bug #48089 [Console] Fix clear line with question in section (maxbeckers)" This reverts commit caffee8d62e7f998fcf6116ca128b8343017f3d2, reversing changes made to f14901e3a4343bb628ff0f7e5f213752381a069e. --- .../Console/Output/ConsoleSectionOutput.php | 8 ----- .../Component/Console/Style/SymfonyStyle.php | 6 ---- .../Console/Tests/Style/SymfonyStyleTest.php | 36 ------------------- 3 files changed, 50 deletions(-) diff --git a/src/Symfony/Component/Console/Output/ConsoleSectionOutput.php b/src/Symfony/Component/Console/Output/ConsoleSectionOutput.php index 527c1a224f8b2..d4c2f20c71741 100644 --- a/src/Symfony/Component/Console/Output/ConsoleSectionOutput.php +++ b/src/Symfony/Component/Console/Output/ConsoleSectionOutput.php @@ -87,14 +87,6 @@ public function addContent(string $input) } } - /** - * @internal - */ - public function incrementLines() - { - ++$this->lines; - } - /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Console/Style/SymfonyStyle.php b/src/Symfony/Component/Console/Style/SymfonyStyle.php index 1de3b552f333d..f13c313d3a5c2 100644 --- a/src/Symfony/Component/Console/Style/SymfonyStyle.php +++ b/src/Symfony/Component/Console/Style/SymfonyStyle.php @@ -22,7 +22,6 @@ use Symfony\Component\Console\Helper\TableSeparator; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\ConsoleOutputInterface; -use Symfony\Component\Console\Output\ConsoleSectionOutput; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\TrimmedBufferOutput; use Symfony\Component\Console\Question\ChoiceQuestion; @@ -351,11 +350,6 @@ public function askQuestion(Question $question): mixed if ($this->input->isInteractive()) { $this->newLine(); $this->bufferedOutput->write("\n"); - if ($this->output instanceof ConsoleSectionOutput) { - // add one line more to the ConsoleSectionOutput because of the `return` to submit the input - // this is relevant when a `ConsoleSectionOutput::clear` is called. - $this->output->incrementLines(); - } } return $answer; diff --git a/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php b/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php index 3441449da9c60..74c24034095b1 100644 --- a/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php +++ b/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php @@ -16,13 +16,11 @@ use Symfony\Component\Console\Exception\RuntimeException; use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Input\ArrayInput; -use Symfony\Component\Console\Input\Input; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\ConsoleSectionOutput; use Symfony\Component\Console\Output\NullOutput; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Output\StreamOutput; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Console\Tester\CommandTester; @@ -183,38 +181,4 @@ public function testMemoryConsumption() $this->assertSame(0, memory_get_usage() - $start); } - - public function testAskAndClearExpectFullSectionCleared() - { - $answer = 'Answer'; - $inputStream = fopen('php://memory', 'r+'); - fwrite($inputStream, $answer.\PHP_EOL); - rewind($inputStream); - $input = $this->createMock(Input::class); - $sections = []; - $output = new ConsoleSectionOutput(fopen('php://memory', 'r+', false), $sections, StreamOutput::VERBOSITY_NORMAL, true, new OutputFormatter()); - $input - ->method('isInteractive') - ->willReturn(true); - $input - ->method('getStream') - ->willReturn($inputStream); - - $style = new SymfonyStyle($input, $output); - - $style->write('foo'); - $givenAnswer = $style->ask('Dummy question?'); - $output->write('bar'); - $output->clear(); - - rewind($output->getStream()); - $this->assertEquals($answer, $givenAnswer); - $this->assertEquals( - 'foo'.\PHP_EOL. // write foo - \PHP_EOL.\PHP_EOL.\PHP_EOL." \033[32mDummy question?\033[39m:".\PHP_EOL.' > '.\PHP_EOL.\PHP_EOL.\PHP_EOL. // question - 'bar'.\PHP_EOL. // write bar - "\033[10A\033[0J", // clear 10 lines (9 output lines and one from the answer input return) - stream_get_contents($output->getStream()) - ); - } } From 042c372d8d5b06a4a9ce29aaf17ff5de945b30e0 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 16 Dec 2022 15:48:25 +0100 Subject: [PATCH 55/58] [HttpKernel] fix merge --- .../Tests/EventListener/SessionListenerTest.php | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php index be1eaee13fbf4..719c4bc107c29 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php @@ -560,10 +560,13 @@ public function testUninitializedSessionWithoutInitializedSession() public function testResponseHeadersMaxAgeAndExpiresNotBeOverridenIfSessionStarted() { $session = $this->createMock(Session::class); - $session->expects($this->exactly(2))->method('getUsageIndex')->will($this->onConsecutiveCalls(0, 1)); + $session->expects($this->once())->method('getUsageIndex')->willReturn(1); + $session->expects($this->once())->method('getName')->willReturn('foo'); + $sessionFactory = $this->createMock(SessionFactory::class); + $sessionFactory->expects($this->once())->method('createSession')->willReturn($session); $container = new Container(); - $container->set('initialized_session', $session); + $container->set('session_factory', $sessionFactory); $listener = new SessionListener($container); $kernel = $this->createMock(HttpKernelInterface::class); @@ -571,11 +574,13 @@ public function testResponseHeadersMaxAgeAndExpiresNotBeOverridenIfSessionStarte $request = new Request(); $listener->onKernelRequest(new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST)); + $request->getSession(); + $response = new Response(); $response->setPrivate(); $expiresHeader = gmdate('D, d M Y H:i:s', time() + 600).' GMT'; $response->setMaxAge(600); - $listener->onKernelResponse(new ResponseEvent($kernel, new Request(), HttpKernelInterface::MAIN_REQUEST, $response)); + $listener->onKernelResponse(new ResponseEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $response)); $this->assertTrue($response->headers->has('expires')); $this->assertSame($expiresHeader, $response->headers->get('expires')); @@ -590,20 +595,20 @@ public function testResponseHeadersMaxAgeAndExpiresNotBeOverridenIfSessionStarte public function testResponseHeadersMaxAgeAndExpiresDefaultValuesIfSessionStarted() { $session = $this->createMock(Session::class); - $session->expects($this->exactly(2))->method('getUsageIndex')->will($this->onConsecutiveCalls(0, 1)); + $session->expects($this->once())->method('getUsageIndex')->willReturn(1); $container = new Container(); - $container->set('initialized_session', $session); $listener = new SessionListener($container); $kernel = $this->createMock(HttpKernelInterface::class); $request = new Request(); + $request->setSession($session); $listener->onKernelRequest(new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST)); $response = new Response(); $expiresHeader = gmdate('D, d M Y H:i:s', time()).' GMT'; - $listener->onKernelResponse(new ResponseEvent($kernel, new Request(), HttpKernelInterface::MAIN_REQUEST, $response)); + $listener->onKernelResponse(new ResponseEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $response)); $this->assertTrue($response->headers->has('expires')); $this->assertSame($expiresHeader, $response->headers->get('expires')); From dc5be00628287a924d4c12cf2c11d3cbc4005398 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 16 Dec 2022 16:06:54 +0100 Subject: [PATCH 56/58] [HttpKernel] fix merge --- .../Tests/DependencyInjection/WebProfilerExtensionTest.php | 4 ++++ .../RegisterControllerArgumentLocatorsPassTest.php | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php index 0038736820388..fbfd2ed586f87 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php @@ -33,8 +33,12 @@ class WebProfilerExtensionTest extends TestCase public static function assertSaneContainer(Container $container) { + $removedIds = $container->getRemovedIds(); $errors = []; foreach ($container->getServiceIds() as $id) { + if (isset($removedIds[$id])) { + continue; + } try { $container->get($id); } catch (\Exception $e) { diff --git a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php index 302f9e35dd70d..d6be9d3bd023c 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php @@ -467,7 +467,7 @@ public function testAutowireAttribute() $container = new ContainerBuilder(); $resolver = $container->register('argument_resolver.service', 'stdClass')->addArgument([]); - $container->register('some.id', \stdClass::class); + $container->register('some.id', \stdClass::class)->setPublic(true); $container->setParameter('some.parameter', 'foo'); $container->register('foo', WithAutowireAttribute::class) From c93e7057e7bb6f1fbaa2f1a8684d87cfe208cac0 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 16 Dec 2022 20:25:24 +0100 Subject: [PATCH 57/58] Update CHANGELOG for 6.2.2 --- CHANGELOG-6.2.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/CHANGELOG-6.2.md b/CHANGELOG-6.2.md index a0cc3772c78ac..311021e53c64e 100644 --- a/CHANGELOG-6.2.md +++ b/CHANGELOG-6.2.md @@ -7,6 +7,51 @@ in 6.2 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v6.2.0...v6.2.1 +* 6.2.2 (2022-12-16) + + * bug #48661 [Serializer] fix context attribute with serializedName (nikophil) + * bug #48681 [Console] Revert "bug #48089 Fix clear line with question in section (maxbeckers) (chalasr) + * bug #48680 [Cache] fix lazyness of redis when using RedisTagAwareAdapter (nicolas-grekas) + * bug #48651 [HttpKernel] AbstractSessionListener should not override the cache lifetime for private responses (rodmen) + * bug #48591 [DependencyInjection] Shared private services becomes public after a public service is accessed (alexpott) + * bug #48126 [Mailer] Include all transports' debug messages in RoundRobin transport exception (mixdf) + * bug #48644 [Validator] Allow opt-out of EmailValidator deprecation when using Validation::createValidatorBuilder() (nicolas-grekas) + * bug #48606 [FrameworkBundle] container:debug CLI output improvements for excluded services (apfelbox) + * bug #48089 [Console] Fix clear line with question in section (maxbeckers) + * bug #48602 [HtmlSanitizer] Fix HtmlSanitizer default configuration behavior for allowed schemes (Titouan Galopin) + * bug #48635 [HttpFoundation] Use relative timestamps with MemcachedSessionHandler (tvlooy) + * bug #47979 [Cache] Fix dealing with ext-redis' multi/exec returning a bool (João Nogueira) + * bug #48612 [Messenger] [Amqp] Added missing rpc_timeout option (lyrixx) + * bug #48233 [Serializer] Prevent `GetSetMethodNormalizer` from creating invalid magic method call (klaussilveira) + * bug #48628 [HttpFoundation] Fix dumping array cookies (nicolas-grekas) + * bug #48559 [ExpressionLanguage] Fix BC of cached SerializedParsedExpression containing GetAttrNode (fancyweb) + * bug #48524 [HttpKernel] Fix `CacheAttributeListener` priority (HypeMC) + * bug #48451 [Translation] Fix extraction when dealing with VariadicPlaceholder parameters (Kocal) + * bug #48601 [SecurityBundle] Fix authenticator existence check in `Security::login()` (chalasr) + * bug #48587 [TwigBundle] Alias BodyRendererInterface (n3o77) + * bug #48580 [Console] Fix missing command not matching namespace error message (Titouan Galopin) + * bug #48449 [DependencyInjection] Fix bug when tag name is a text node (BrandonlinU) + * bug #48048 [WebProfilerBundle] Fix dump header not being displayed (HypeMC) + * bug #47836 [HttpClient] TraceableHttpClient: increase decorator's priority (adpeyre) + * bug #48259 [FrameworkBundle] Allow configuring `framework.exceptions` with a config builder (MatTheCat) + * bug #48314 [Mime] Fix MessagePart serialization (Amunak) + * bug #48331 [Yaml] fix dumping top-level tagged values (xabbuh) + * bug #48615 Fix getting the name of closures on PHP 8.1.11+ (nicolas-grekas) + * bug #48624 [ErrorHandler][HttpKernel] Fix reading the SYMFONY_IDE env var (nicolas-grekas) + * bug #48618 [ErrorHandler] [DebugClassLoader] Fix some new return types support (fancyweb) + * bug #48605 [VarExporter] Fix adding a key to an uninitialized array (nicolas-grekas) + * bug #48554 [Security] Fix invalid deprecation messages in Security constants (IonBazan) + * bug #48538 [Clock] Fix `usleep` deprecation warning (victor-prdh) + * bug #48421 [HttpFoundation] IPv4-mapped IPv6 addresses incorrectly rejected (bonroyage) + * bug #48501 [RateLimiter] Add `int` to `Reservation::wait()` (DaRealFreak) + * bug #48359 [VarDumper] Ignore \Error in __debugInfo() (fancyweb) + * bug #48553 [VarExporter] Fix calling `parent::__wakeup()` when unserializing with LazyProxyTrait (azjezz) + * bug #48489 [DoctrineBridge] Skip resolving entities when the corresponding request attribute is already an object (nicolas-grekas) + * bug #48534 [FrameworkBundle] add `kernel.locale_aware` tag to `LocaleSwitcher` (kbond) + * bug #48521 [FrameworkBundle] fix removing commands if console not available (kbond) + * bug #48522 [DependencyInjection] Generate different classes for ghost objects and virtual proxies (nicolas-grekas) + * bug #48482 [DependencyInjection] Revert "bug #48027 Don't autoconfigure tag when it's already set with attributes" (nicolas-grekas) + * 6.2.1 (2022-12-06) * bug #48502 [DependencyInjection] Fix `ContainerBuilder` stats env usage with enum (alamirault) From 0957e42fe1d05e68d492c77c06a55be1cbd4e75e Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 16 Dec 2022 20:38:34 +0100 Subject: [PATCH 58/58] Update VERSION for 6.2.2 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index a8f6fdade9589..358dab3aed06a 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -75,12 +75,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '6.2.2-DEV'; + public const VERSION = '6.2.2'; public const VERSION_ID = 60202; public const MAJOR_VERSION = 6; public const MINOR_VERSION = 2; public const RELEASE_VERSION = 2; - public const EXTRA_VERSION = 'DEV'; + public const EXTRA_VERSION = ''; public const END_OF_MAINTENANCE = '07/2023'; public const END_OF_LIFE = '07/2023';