From 24c30c905ed92088de8a1532e3a4b2dbfa37fe8f Mon Sep 17 00:00:00 2001 From: gnito-org <70450336+gnito-org@users.noreply.github.com> Date: Fri, 9 Dec 2022 15:16:13 -0400 Subject: [PATCH 01/43] [Notifier] Add SMS options to MessageMedia notifier --- .../Notifier/Bridge/MessageMedia/CHANGELOG.md | 5 + .../MessageMedia/MessageMediaOptions.php | 157 ++++++++++++++++++ .../MessageMedia/MessageMediaTransport.php | 16 +- .../Tests/MessageMediaOptionsTest.php | 35 ++++ .../Tests/MessageMediaTransportTest.php | 2 + 5 files changed, 209 insertions(+), 6 deletions(-) create mode 100644 src/Symfony/Component/Notifier/Bridge/MessageMedia/MessageMediaOptions.php create mode 100644 src/Symfony/Component/Notifier/Bridge/MessageMedia/Tests/MessageMediaOptionsTest.php diff --git a/src/Symfony/Component/Notifier/Bridge/MessageMedia/CHANGELOG.md b/src/Symfony/Component/Notifier/Bridge/MessageMedia/CHANGELOG.md index 7f6ce4e6893ba..4a9ac7171cfaa 100644 --- a/src/Symfony/Component/Notifier/Bridge/MessageMedia/CHANGELOG.md +++ b/src/Symfony/Component/Notifier/Bridge/MessageMedia/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +6.3 +--- + + * Add `MessageMediaOptions` class + 6.2 --- diff --git a/src/Symfony/Component/Notifier/Bridge/MessageMedia/MessageMediaOptions.php b/src/Symfony/Component/Notifier/Bridge/MessageMedia/MessageMediaOptions.php new file mode 100644 index 0000000000000..e0ff03ace5289 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/MessageMedia/MessageMediaOptions.php @@ -0,0 +1,157 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\MessageMedia; + +use Symfony\Component\Notifier\Message\MessageOptionsInterface; + +/** + * @author gnito-org + */ +final class MessageMediaOptions implements MessageOptionsInterface +{ + private array $options; + + public function __construct(array $options = []) + { + $this->options = $options; + } + + public function getCallbackUrl(): ?string + { + return $this->options['callback_url'] ?? null; + } + + public function getDeliveryReport(): ?bool + { + return $this->options['delivery_report'] ?? null; + } + + public function getFormat(): ?string + { + return $this->options['format'] ?? null; + } + + public function getFrom(): ?string + { + return $this->options['from'] ?? null; + } + + public function getMedia(): ?array + { + return $this->options['media'] ?? null; + } + + public function getMessageExpiryTimestamp(): ?int + { + return $this->options['message_expiry_timestamp'] ?? null; + } + + public function getMetadata(): ?array + { + return $this->options['metadata'] ?? null; + } + + public function getRecipientId(): ?string + { + return $this->options['recipient_id'] ?? null; + } + + public function getScheduled(): ?string + { + return $this->options['scheduled'] ?? null; + } + + public function getSubject(): ?string + { + return $this->options['subject'] ?? null; + } + + public function setCallbackUrl(string $callbackUrl): self + { + $this->options['callback_url'] = $callbackUrl; + + return $this; + } + + public function setDeliveryReport(bool $deliveryReport): self + { + $this->options['delivery_report'] = $deliveryReport; + + return $this; + } + + public function setFormat(string $format): self + { + $this->options['format'] = $format; + + return $this; + } + + public function setFrom(string $from): self + { + $this->options['from'] = $from; + + return $this; + } + + public function setMedia(array $media): self + { + $this->options['media'] = $media; + + return $this; + } + + public function setMessageExpiryTimestamp(int $messageExpiryTimestamp): self + { + $this->options['message_expiry_timestamp'] = $messageExpiryTimestamp; + + return $this; + } + + public function setMetadata(array $metadata): self + { + $this->options['metadata'] = $metadata; + + return $this; + } + + public function setRecipientId(string $id): self + { + $this->options['recipient_id'] = $id; + + return $this; + } + + public function setScheduled(string $scheduled): self + { + $this->options['scheduled'] = $scheduled; + + return $this; + } + + public function setSubject(string $subject): self + { + $this->options['subject'] = $subject; + + return $this; + } + + public function toArray(): array + { + $options = $this->options; + if (isset($options['recipient_id'])) { + unset($options['recipient_id']); + } + + return $options; + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/MessageMedia/MessageMediaTransport.php b/src/Symfony/Component/Notifier/Bridge/MessageMedia/MessageMediaTransport.php index 364c0dffb20ae..3359816eac7c0 100644 --- a/src/Symfony/Component/Notifier/Bridge/MessageMedia/MessageMediaTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/MessageMedia/MessageMediaTransport.php @@ -53,7 +53,7 @@ public function __toString(): string public function supports(MessageInterface $message): bool { - return $message instanceof SmsMessage; + return $message instanceof SmsMessage && (null === $message->getOptions() || $message->getOptions() instanceof MessageMediaOptions); } protected function doSend(MessageInterface $message): SentMessage @@ -64,6 +64,14 @@ protected function doSend(MessageInterface $message): SentMessage $from = $message->getFrom() ?: $this->from; + $opts = $message->getOptions(); + $options = $opts ? $opts->toArray() : []; + $options['source_number'] = $options['from'] ?? $from; + $options['destination_number'] = $message->getPhone(); + $options['content'] = $message->getSubject(); + + unset($options['from']); + $endpoint = sprintf('https://%s/v1/messages', $this->getEndpoint()); $response = $this->client->request( 'POST', @@ -72,11 +80,7 @@ protected function doSend(MessageInterface $message): SentMessage 'auth_basic' => $this->apiKey.':'.$this->apiSecret, 'json' => [ 'messages' => [ - [ - 'destination_number' => $message->getPhone(), - 'source_number' => $from, - 'content' => $message->getSubject(), - ], + array_filter($options), ], ], ] diff --git a/src/Symfony/Component/Notifier/Bridge/MessageMedia/Tests/MessageMediaOptionsTest.php b/src/Symfony/Component/Notifier/Bridge/MessageMedia/Tests/MessageMediaOptionsTest.php new file mode 100644 index 0000000000000..bd349a0390162 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/MessageMedia/Tests/MessageMediaOptionsTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\MessageMedia\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Notifier\Bridge\MessageMedia\MessageMediaOptions; + +class MessageMediaOptionsTest extends TestCase +{ + public function testMessageMediaOptions() + { + $messageMediaOptions = (new MessageMediaOptions())->setFrom('test_from')->setMedia(['test_media'])->setCallbackUrl('test_callback_url')->setFormat('test_format')->setRecipientId('test_recipient')->setDeliveryReport(true)->setMessageExpiryTimestamp(999)->setMetadata(['test_metadata'])->setScheduled('test_scheduled')->setSubject('test_subject'); + + self::assertSame([ + 'from' => 'test_from', + 'media' => ['test_media'], + 'callback_url' => 'test_callback_url', + 'format' => 'test_format', + 'delivery_report' => true, + 'message_expiry_timestamp' => 999, + 'metadata' => ['test_metadata'], + 'scheduled' => 'test_scheduled', + 'subject' => 'test_subject', + ], $messageMediaOptions->toArray()); + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/MessageMedia/Tests/MessageMediaTransportTest.php b/src/Symfony/Component/Notifier/Bridge/MessageMedia/Tests/MessageMediaTransportTest.php index ee1c84060c41e..fd7991e35f19a 100644 --- a/src/Symfony/Component/Notifier/Bridge/MessageMedia/Tests/MessageMediaTransportTest.php +++ b/src/Symfony/Component/Notifier/Bridge/MessageMedia/Tests/MessageMediaTransportTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Notifier\Bridge\MessageMedia\Tests; use Symfony\Component\HttpClient\MockHttpClient; +use Symfony\Component\Notifier\Bridge\MessageMedia\MessageMediaOptions; use Symfony\Component\Notifier\Bridge\MessageMedia\MessageMediaTransport; use Symfony\Component\Notifier\Exception\TransportException; use Symfony\Component\Notifier\Exception\TransportExceptionInterface; @@ -38,6 +39,7 @@ public function toStringProvider(): iterable public function supportedMessagesProvider(): iterable { yield [new SmsMessage('0491570156', 'Hello!')]; + yield [new SmsMessage('0491570156', 'Hello!', 'from', new MessageMediaOptions(['from' => 'foo']))]; } public function unsupportedMessagesProvider(): iterable From 033dace4c3f87d73ef936bb6d204699cd8c2e8fa Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 7 May 2023 14:04:08 +0200 Subject: [PATCH 02/43] Bump Symfony version to 6.3.0 --- 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 34db55f198f99..f9a969b659130 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -76,12 +76,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '6.3.0-BETA2'; + public const VERSION = '6.3.0-DEV'; public const VERSION_ID = 60300; public const MAJOR_VERSION = 6; public const MINOR_VERSION = 3; public const RELEASE_VERSION = 0; - public const EXTRA_VERSION = 'BETA2'; + public const EXTRA_VERSION = 'DEV'; public const END_OF_MAINTENANCE = '01/2024'; public const END_OF_LIFE = '01/2024'; From 5ff1db081f333e3bd61e28dbca32a79cc0e114be Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 7 May 2023 14:06:34 +0200 Subject: [PATCH 03/43] [HttpClient] Fix setting duplicate-name headers when redirecting with AmpHttpClient --- src/Symfony/Component/HttpClient/Response/AmpResponse.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpClient/Response/AmpResponse.php b/src/Symfony/Component/HttpClient/Response/AmpResponse.php index 6d0ce6e33e01f..3fac5860bb188 100644 --- a/src/Symfony/Component/HttpClient/Response/AmpResponse.php +++ b/src/Symfony/Component/HttpClient/Response/AmpResponse.php @@ -358,7 +358,7 @@ private static function followRedirects(Request $originRequest, AmpClientState $ } foreach ($originRequest->getRawHeaders() as [$name, $value]) { - $request->setHeader($name, $value); + $request->addHeader($name, $value); } if ($request->getUri()->getAuthority() !== $originRequest->getUri()->getAuthority()) { From 87ca700dce7f96d1646e471a85947cc5a0c753b8 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 7 May 2023 19:34:16 +0200 Subject: [PATCH 04/43] [HttpClient] Fix test suite --- .../Component/HttpClient/Tests/HttpClientTraitTest.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpClient/Tests/HttpClientTraitTest.php b/src/Symfony/Component/HttpClient/Tests/HttpClientTraitTest.php index fbef0230332ed..ecb5107693c64 100644 --- a/src/Symfony/Component/HttpClient/Tests/HttpClientTraitTest.php +++ b/src/Symfony/Component/HttpClient/Tests/HttpClientTraitTest.php @@ -155,12 +155,11 @@ public function testNormalizeBodyMultipartForwardStream($stream) public static function provideNormalizeBodyMultipartForwardStream() { - yield 'native' => [fopen('https://github.githubassets.com/images/icons/emoji/unicode/1f44d.png', 'r')]; - - if (!\defined('OPENSSL_DEFAULT_STREAM_CIPHERS')) { - return; + if (!\extension_loaded('openssl')) { + throw self::markTestSkipped('Extension openssl required.'); } + yield 'native' => [fopen('https://github.githubassets.com/images/icons/emoji/unicode/1f44d.png', 'r')]; yield 'symfony' => [HttpClient::create()->request('GET', 'https://github.githubassets.com/images/icons/emoji/unicode/1f44d.png')->toStream()]; } From c2ca09e3231cfaf052cd80da00b56fb773e4ebe4 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 7 May 2023 20:05:06 -0400 Subject: [PATCH 05/43] [AssetMapper] Adding autoconfiguration tag for asset compilers --- .../FrameworkBundle/DependencyInjection/FrameworkExtension.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 668f2be322411..5ebfe388c7bbe 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -32,6 +32,7 @@ use Symfony\Bundle\MercureBundle\MercureBundle; use Symfony\Component\Asset\PackageInterface; use Symfony\Component\AssetMapper\AssetMapper; +use Symfony\Component\AssetMapper\Compiler\AssetCompilerInterface; use Symfony\Component\AssetMapper\ImportMap\ImportMapManager; use Symfony\Component\BrowserKit\AbstractBrowser; use Symfony\Component\Cache\Adapter\AdapterInterface; @@ -559,6 +560,8 @@ public function load(array $configs, ContainerBuilder $container) $container->registerForAutoconfiguration(PackageInterface::class) ->addTag('assets.package'); + $container->registerForAutoconfiguration(AssetCompilerInterface::class) + ->addTag('asset_mapper.compiler'); $container->registerForAutoconfiguration(Command::class) ->addTag('console.command'); $container->registerForAutoconfiguration(ResourceCheckerInterface::class) From 3a69c6cd02c6c8ea7358a0b197575aa7dd61605f Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 7 May 2023 18:37:45 +0200 Subject: [PATCH 06/43] [DependencyInjection] Fix dumping/loading errored definitions in XML/Yaml --- .../Command/ContainerLintCommand.php | 11 +---------- .../DependencyInjection/FrameworkExtension.php | 9 ++++----- src/Symfony/Bundle/FrameworkBundle/composer.json | 2 +- .../Compiler/DefinitionErrorExceptionPass.php | 2 +- .../DependencyInjection/Dumper/XmlDumper.php | 4 +++- .../DependencyInjection/Dumper/YamlDumper.php | 4 +++- .../DependencyInjection/Loader/FileLoader.php | 6 ++++++ .../Compiler/DefinitionErrorExceptionPassTest.php | 14 ++++++++++++++ .../Tests/Fixtures/xml/services9.xml | 4 +++- .../Tests/Fixtures/yaml/services9.yml | 2 ++ 10 files changed, 38 insertions(+), 20 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php index af08235512e33..0d71e6985e206 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php @@ -88,8 +88,6 @@ private function getContainerBuilder(): ContainerBuilder return $this->buildContainer(); }, $kernel, $kernel::class); $container = $buildContainer(); - - $skippedIds = []; } else { if (!$kernelContainer instanceof Container) { throw new RuntimeException(sprintf('This command does not support the application container: "%s" does not extend "%s".', get_debug_type($kernelContainer), Container::class)); @@ -100,13 +98,6 @@ private function getContainerBuilder(): ContainerBuilder $refl = new \ReflectionProperty($parameterBag, 'resolved'); $refl->setValue($parameterBag, true); - $skippedIds = []; - foreach ($container->getServiceIds() as $serviceId) { - if (str_starts_with($serviceId, '.errored.')) { - $skippedIds[$serviceId] = true; - } - } - $container->getCompilerPassConfig()->setBeforeOptimizationPasses([]); $container->getCompilerPassConfig()->setOptimizationPasses([]); $container->getCompilerPassConfig()->setBeforeRemovingPasses([]); @@ -115,7 +106,7 @@ private function getContainerBuilder(): ContainerBuilder $container->setParameter('container.build_hash', 'lint_container'); $container->setParameter('container.build_id', 'lint_container'); - $container->addCompilerPass(new CheckTypeDeclarationsPass(true, $skippedIds), PassConfig::TYPE_AFTER_REMOVING, -100); + $container->addCompilerPass(new CheckTypeDeclarationsPass(true), PassConfig::TYPE_AFTER_REMOVING, -100); return $this->containerBuilder = $container; } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 668f2be322411..96fe487b5426e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -375,13 +375,12 @@ public function load(array $configs, ContainerBuilder $container) $this->registerSerializerConfiguration($config['serializer'], $container, $loader); } else { - $container->register('.argument_resolver.request_payload.no_serializer', Serializer::class) + $container->getDefinition('argument_resolver.request_payload') + ->setArguments([]) ->addError('You can neither use "#[MapRequestPayload]" nor "#[MapQueryString]" since the Serializer component is not ' .(class_exists(Serializer::class) ? 'enabled. Try setting "framework.serializer" to true.' : 'installed. Try running "composer require symfony/serializer-pack".') - ); - - $container->getDefinition('argument_resolver.request_payload') - ->replaceArgument(0, new Reference('.argument_resolver.request_payload.no_serializer', ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE)) + ) + ->addTag('container.error') ->clearTag('kernel.event_subscriber'); $container->removeDefinition('console.command.serializer_debug'); diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index eb3d340a71b96..572353b5d7efd 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -21,7 +21,7 @@ "ext-xml": "*", "symfony/cache": "^5.4|^6.0", "symfony/config": "^6.1", - "symfony/dependency-injection": "^6.2.8", + "symfony/dependency-injection": "^6.3", "symfony/deprecation-contracts": "^2.5|^3", "symfony/error-handler": "^6.1", "symfony/event-dispatcher": "^5.4|^6.0", diff --git a/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php b/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php index 5f290f91575b0..759b1d22d15ba 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php @@ -25,7 +25,7 @@ class DefinitionErrorExceptionPass extends AbstractRecursivePass { protected function processValue(mixed $value, bool $isRoot = false): mixed { - if (!$value instanceof Definition || !$value->hasErrors()) { + if (!$value instanceof Definition || !$value->hasErrors() || $value->hasTag('container.error')) { return parent::processValue($value, $isRoot); } diff --git a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php index 74633a4fbbbc5..ae9c790d7274c 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php @@ -129,7 +129,9 @@ private function addService(Definition $definition, ?string $id, \DOMElement $pa } } - foreach ($definition->getTags() as $name => $tags) { + $tags = $definition->getTags(); + $tags['container.error'] = array_map(fn ($e) => ['message' => $e], $definition->getErrors()); + foreach ($tags as $name => $tags) { foreach ($tags as $attributes) { $tag = $this->document->createElement('tag'); if (!\array_key_exists('name', $attributes)) { diff --git a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php index 802b49a466bab..f12cf9a009814 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php @@ -69,7 +69,9 @@ private function addService(string $id, Definition $definition): string } $tagsCode = ''; - foreach ($definition->getTags() as $name => $tags) { + $tags = $definition->getTags(); + $tags['container.error'] = array_map(fn ($e) => ['message' => $e], $definition->getErrors()); + foreach ($tags as $name => $tags) { foreach ($tags as $attributes) { $att = []; foreach ($attributes as $key => $value) { diff --git a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php index c817f16422f80..c265fb6e8d9f8 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php @@ -214,6 +214,12 @@ protected function setDefinition(string $id, Definition $definition) { $this->container->removeBindings($id); + foreach ($definition->getTag('container.error') as $error) { + if (isset($error['message'])) { + $definition->addError($error['message']); + } + } + if ($this->isLoadingInstanceof) { if (!$definition instanceof ChildDefinition) { throw new InvalidArgumentException(sprintf('Invalid type definition "%s": ChildDefinition expected, "%s" given.', $id, get_debug_type($definition))); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/DefinitionErrorExceptionPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/DefinitionErrorExceptionPassTest.php index b22b934dd80cd..a62fe0cc79153 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/DefinitionErrorExceptionPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/DefinitionErrorExceptionPassTest.php @@ -49,4 +49,18 @@ public function testNoExceptionThrown() $pass->process($container); $this->assertSame($def, $container->getDefinition('foo_service_id')->getArgument(0)); } + + public function testSkipErrorFromTag() + { + $container = new ContainerBuilder(); + $def = new Definition(); + $def->addError('Things went wrong!'); + $def->addTag('container.error'); + $container->register('foo_service_id') + ->setArguments([$def]); + + $pass = new DefinitionErrorExceptionPass(); + $pass->process($container); + $this->assertSame($def, $container->getDefinition('foo_service_id')->getArgument(0)); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml index 9b2462d076068..a873301af0651 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml @@ -145,7 +145,9 @@ - + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml index 22a6d5549557e..878a18c795b31 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml @@ -173,6 +173,8 @@ services: public: true errored_definition: class: stdClass + tags: + - container.error: { message: 'Service "errored_definition" is broken.' } preload_sidekick: class: stdClass tags: From b0ec0c75d9bba1e151e8003c39b852b4db9711cf Mon Sep 17 00:00:00 2001 From: Laurent VOULLEMIER Date: Mon, 8 May 2023 21:46:50 +0200 Subject: [PATCH 07/43] Fix param type annotation --- src/Symfony/Bridge/Doctrine/Middleware/Debug/Query.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/Middleware/Debug/Query.php b/src/Symfony/Bridge/Doctrine/Middleware/Debug/Query.php index 71da329a996cd..eb835caa41b25 100644 --- a/src/Symfony/Bridge/Doctrine/Middleware/Debug/Query.php +++ b/src/Symfony/Bridge/Doctrine/Middleware/Debug/Query.php @@ -46,8 +46,8 @@ public function stop(): void } /** - * @param string|int $param - * @param string|int|float|bool|null $variable + * @param string|int $param + * @param mixed $variable */ public function setParam($param, &$variable, int $type): void { From 318e0e481b643b7316eca4a804b5d4440bc339ca Mon Sep 17 00:00:00 2001 From: Laurent VOULLEMIER Date: Mon, 8 May 2023 19:32:07 +0200 Subject: [PATCH 08/43] Allow resources in Query::setParam --- .../Doctrine/Middleware/Debug/Query.php | 2 +- .../Tests/Middleware/Debug/MiddlewareTest.php | 22 +++++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/Middleware/Debug/Query.php b/src/Symfony/Bridge/Doctrine/Middleware/Debug/Query.php index 9813d712132f4..695a0a1535aa2 100644 --- a/src/Symfony/Bridge/Doctrine/Middleware/Debug/Query.php +++ b/src/Symfony/Bridge/Doctrine/Middleware/Debug/Query.php @@ -43,7 +43,7 @@ public function stop(): void } } - public function setParam(string|int $param, null|string|int|float|bool &$variable, int $type): void + public function setParam(string|int $param, mixed &$variable, int $type): void { // Numeric indexes start at 0 in profiler $idx = \is_int($param) ? $param - 1 : $param; diff --git a/src/Symfony/Bridge/Doctrine/Tests/Middleware/Debug/MiddlewareTest.php b/src/Symfony/Bridge/Doctrine/Tests/Middleware/Debug/MiddlewareTest.php index e605afec5a630..2bb628442459c 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Middleware/Debug/MiddlewareTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Middleware/Debug/MiddlewareTest.php @@ -145,23 +145,31 @@ public function testWithParamBound(callable $executeMethod) { $this->init(); - $product = 'product1'; - $price = 12.5; - $stock = 5; + $sql = <<getResourceFromString('mydata'); - $stmt = $this->conn->prepare('INSERT INTO products(name, price, stock) VALUES (?, ?, ?)'); + $stmt = $this->conn->prepare($sql); $stmt->bindParam(1, $product); $stmt->bindParam(2, $price); $stmt->bindParam(3, $stock, ParameterType::INTEGER); + $stmt->bindParam(4, $res, ParameterType::BINARY); + + $product = 'product1'; + $price = 12.5; + $stock = 5; $executeMethod($stmt); // Debug data should not be affected by these changes $debug = $this->debugDataHolder->getData()['default'] ?? []; $this->assertCount(2, $debug); - $this->assertSame('INSERT INTO products(name, price, stock) VALUES (?, ?, ?)', $debug[1]['sql']); - $this->assertSame(['product1', '12.5', 5], $debug[1]['params']); - $this->assertSame([ParameterType::STRING, ParameterType::STRING, ParameterType::INTEGER], $debug[1]['types']); + $this->assertSame($sql, $debug[1]['sql']); + $this->assertSame(['product1', 12.5, 5, $expectedRes], $debug[1]['params']); + $this->assertSame([ParameterType::STRING, ParameterType::STRING, ParameterType::INTEGER, ParameterType::BINARY], $debug[1]['types']); $this->assertGreaterThan(0, $debug[1]['executionMS']); } From 86609b6f37077e068bf4829a3a00b940aebe7200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20G=C3=BCnter?= Date: Tue, 9 May 2023 11:05:09 -0400 Subject: [PATCH 09/43] [Messenger] Add `IS_REPEATABLE` flag to `AsMessageHandler` attribute --- src/Symfony/Component/Messenger/Attribute/AsMessageHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Messenger/Attribute/AsMessageHandler.php b/src/Symfony/Component/Messenger/Attribute/AsMessageHandler.php index c25c8b2c974dc..c0acf6f6c3d3b 100644 --- a/src/Symfony/Component/Messenger/Attribute/AsMessageHandler.php +++ b/src/Symfony/Component/Messenger/Attribute/AsMessageHandler.php @@ -16,7 +16,7 @@ * * @author Alireza Mirsepassi */ -#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)] +#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class AsMessageHandler { public function __construct( From fd3185e782f873a0f1f4ca43cd90b3a60dbc425e Mon Sep 17 00:00:00 2001 From: "hubert.lenoir" Date: Wed, 3 May 2023 15:41:44 +0200 Subject: [PATCH 10/43] [FrameworkBundle][Webhook] Throw when required services are missing when using the Webhook component --- .../FrameworkExtension.php | 24 +++++++++++-- .../Fixtures/php/webhook.php | 8 +++++ .../php/webhook_without_serializer.php | 8 +++++ .../Fixtures/xml/webhook.xml | 14 ++++++++ .../xml/webhook_without_serializer.xml | 14 ++++++++ .../Fixtures/yml/webhook.yml | 8 +++++ .../yml/webhook_without_serializer.yml | 8 +++++ .../FrameworkExtensionTestCase.php | 34 +++++++++++++++++++ 8 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/webhook.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/webhook_without_serializer.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/webhook.xml create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/webhook_without_serializer.xml create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/webhook.yml create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/webhook_without_serializer.yml diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 6852cb0108e8d..9cbd4d2285ebc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -281,11 +281,11 @@ public function load(array $configs, ContainerBuilder $container) // If the slugger is used but the String component is not available, we should throw an error if (!ContainerBuilder::willBeAvailable('symfony/string', SluggerInterface::class, ['symfony/framework-bundle'])) { - $container->register('slugger', 'stdClass') + $container->register('slugger', SluggerInterface::class) ->addError('You cannot use the "slugger" service since the String component is not installed. Try running "composer require symfony/string".'); } else { if (!ContainerBuilder::willBeAvailable('symfony/translation', LocaleAwareInterface::class, ['symfony/framework-bundle'])) { - $container->register('slugger', 'stdClass') + $container->register('slugger', SluggerInterface::class) ->addError('You cannot use the "slugger" service since the Translation contracts are not installed. Try running "composer require symfony/translation".'); } @@ -379,7 +379,7 @@ public function load(array $configs, ContainerBuilder $container) $container->getDefinition('argument_resolver.request_payload') ->setArguments([]) ->addError('You can neither use "#[MapRequestPayload]" nor "#[MapQueryString]" since the Serializer component is not ' - .(class_exists(Serializer::class) ? 'enabled. Try setting "framework.serializer" to true.' : 'installed. Try running "composer require symfony/serializer-pack".') + .(class_exists(Serializer::class) ? 'enabled. Try setting "framework.serializer.enabled" to true.' : 'installed. Try running "composer require symfony/serializer-pack".') ) ->addTag('container.error') ->clearTag('kernel.event_subscriber'); @@ -531,6 +531,24 @@ public function load(array $configs, ContainerBuilder $container) if ($this->readConfigEnabled('webhook', $container, $config['webhook'])) { $this->registerWebhookConfiguration($config['webhook'], $container, $loader); + + // If Webhook is installed but the HttpClient or Serializer components are not available, we should throw an error + if (!$this->readConfigEnabled('http_client', $container, $config['http_client'])) { + $container->getDefinition('webhook.transport') + ->setArguments([]) + ->addError('You cannot use the "webhook transport" service since the HttpClient component is not ' + .(class_exists(ScopingHttpClient::class) ? 'enabled. Try setting "framework.http_client.enabled" to true.' : 'installed. Try running "composer require symfony/http-client".') + ) + ->addTag('container.error'); + } + if (!$this->readConfigEnabled('serializer', $container, $config['serializer'])) { + $container->getDefinition('webhook.body_configurator.json') + ->setArguments([]) + ->addError('You cannot use the "webhook transport" service since the Serializer component is not ' + .(class_exists(Serializer::class) ? 'enabled. Try setting "framework.serializer.enabled" to true.' : 'installed. Try running "composer require symfony/serializer-pack".') + ) + ->addTag('container.error'); + } } if ($this->readConfigEnabled('remote-event', $container, $config['remote-event'])) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/webhook.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/webhook.php new file mode 100644 index 0000000000000..5a50a738b4747 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/webhook.php @@ -0,0 +1,8 @@ +loadFromExtension('framework', [ + 'http_method_override' => false, + 'webhook' => ['enabled' => true], + 'http_client' => ['enabled' => true], + 'serializer' => ['enabled' => true], +]); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/webhook_without_serializer.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/webhook_without_serializer.php new file mode 100644 index 0000000000000..cd6f3ec903a24 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/webhook_without_serializer.php @@ -0,0 +1,8 @@ +loadFromExtension('framework', [ + 'http_method_override' => false, + 'webhook' => ['enabled' => true], + 'http_client' => ['enabled' => true], + 'serializer' => ['enabled' => false], +]); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/webhook.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/webhook.xml new file mode 100644 index 0000000000000..aaf6952708672 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/webhook.xml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/webhook_without_serializer.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/webhook_without_serializer.xml new file mode 100644 index 0000000000000..76e72b4144bb0 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/webhook_without_serializer.xml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/webhook.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/webhook.yml new file mode 100644 index 0000000000000..7c13a0fc2aa4f --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/webhook.yml @@ -0,0 +1,8 @@ +framework: + http_method_override: false + webhook: + enabled: true + http_client: + enabled: true + serializer: + enabled: true diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/webhook_without_serializer.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/webhook_without_serializer.yml new file mode 100644 index 0000000000000..e61c199420451 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/webhook_without_serializer.yml @@ -0,0 +1,8 @@ +framework: + http_method_override: false + webhook: + enabled: true + http_client: + enabled: true + serializer: + enabled: false diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php index fe3b773186218..97831b04e7773 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -82,6 +82,8 @@ use Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass; use Symfony\Component\Validator\Validation; use Symfony\Component\Validator\Validator\ValidatorInterface; +use Symfony\Component\Webhook\Client\RequestParser; +use Symfony\Component\Webhook\Controller\WebhookController; use Symfony\Component\Workflow; use Symfony\Component\Workflow\Exception\InvalidDefinitionException; use Symfony\Component\Workflow\Metadata\InMemoryMetadataStore; @@ -2276,6 +2278,38 @@ public function testNotifierWithSpecificMessageBus() $this->assertEquals(new Reference('app.another_bus'), $container->getDefinition('notifier.channel.sms')->getArgument(1)); } + public function testWebhook() + { + if (!class_exists(WebhookController::class)) { + $this->markTestSkipped('Webhook not available.'); + } + + $container = $this->createContainerFromFile('webhook'); + + $this->assertTrue($container->hasAlias(RequestParser::class)); + $this->assertSame('webhook.request_parser', (string) $container->getAlias(RequestParser::class)); + $this->assertSame(RequestParser::class, $container->getDefinition('webhook.request_parser')->getClass()); + + $this->assertFalse($container->getDefinition('webhook.transport')->hasErrors()); + $this->assertFalse($container->getDefinition('webhook.body_configurator.json')->hasErrors()); + } + + public function testWebhookWithoutSerializer() + { + if (!class_exists(WebhookController::class)) { + $this->markTestSkipped('Webhook not available.'); + } + + $container = $this->createContainerFromFile('webhook_without_serializer'); + + $this->assertFalse($container->getDefinition('webhook.transport')->hasErrors()); + $this->assertTrue($container->getDefinition('webhook.body_configurator.json')->hasErrors()); + $this->assertSame( + ['You cannot use the "webhook transport" service since the Serializer component is not enabled. Try setting "framework.serializer.enabled" to true.'], + $container->getDefinition('webhook.body_configurator.json')->getErrors() + ); + } + protected function createContainer(array $data = []) { return new ContainerBuilder(new EnvPlaceholderParameterBag(array_merge([ From b0d984b35c394a573d615baa7543a87db49149ef Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Mon, 8 May 2023 18:01:55 -0400 Subject: [PATCH 11/43] [Scheduler] add `JitterTrigger` Co-authored-by: Uladzimir Tsykun --- .../Component/Scheduler/RecurringMessage.php | 6 +++ .../Tests/Trigger/JitterTriggerTest.php | 42 +++++++++++++++++++ .../Scheduler/Trigger/JitterTrigger.php | 39 +++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 src/Symfony/Component/Scheduler/Tests/Trigger/JitterTriggerTest.php create mode 100644 src/Symfony/Component/Scheduler/Trigger/JitterTrigger.php diff --git a/src/Symfony/Component/Scheduler/RecurringMessage.php b/src/Symfony/Component/Scheduler/RecurringMessage.php index 4e89adc8c783a..3553e96119bf8 100644 --- a/src/Symfony/Component/Scheduler/RecurringMessage.php +++ b/src/Symfony/Component/Scheduler/RecurringMessage.php @@ -14,6 +14,7 @@ use Symfony\Component\Scheduler\Exception\InvalidArgumentException; use Symfony\Component\Scheduler\Trigger\CronExpressionTrigger; use Symfony\Component\Scheduler\Trigger\DateIntervalTrigger; +use Symfony\Component\Scheduler\Trigger\JitterTrigger; use Symfony\Component\Scheduler\Trigger\TriggerInterface; /** @@ -59,6 +60,11 @@ public static function trigger(TriggerInterface $trigger, object $message): self return new self($trigger, $message); } + public function withJitter(int $maxSeconds = 60): self + { + return new self(new JitterTrigger($this->trigger, $maxSeconds), $this->message); + } + public function getMessage(): object { return $this->message; diff --git a/src/Symfony/Component/Scheduler/Tests/Trigger/JitterTriggerTest.php b/src/Symfony/Component/Scheduler/Tests/Trigger/JitterTriggerTest.php new file mode 100644 index 0000000000000..52dd7d593959a --- /dev/null +++ b/src/Symfony/Component/Scheduler/Tests/Trigger/JitterTriggerTest.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Scheduler\Tests\Trigger; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Scheduler\Trigger\JitterTrigger; +use Symfony\Component\Scheduler\Trigger\TriggerInterface; + +class JitterTriggerTest extends TestCase +{ + public function testCanAddJitter() + { + $time = new \DateTimeImmutable(); + $inner = $this->createMock(TriggerInterface::class); + $inner->method('getNextRunDate')->willReturn($time); + + $trigger = new JitterTrigger($inner); + + $values = array_map( + fn () => (int) $trigger->getNextRunDate($time)?->getTimestamp(), + array_fill(0, 100, null) + ); + + foreach ($values as $value) { + $this->assertGreaterThanOrEqual($time->getTimestamp(), $value); + $this->assertLessThanOrEqual($time->getTimestamp() + 60, $value); + } + + $values = array_unique($values); + + $this->assertGreaterThan(1, \count($values)); + } +} diff --git a/src/Symfony/Component/Scheduler/Trigger/JitterTrigger.php b/src/Symfony/Component/Scheduler/Trigger/JitterTrigger.php new file mode 100644 index 0000000000000..639c490c12070 --- /dev/null +++ b/src/Symfony/Component/Scheduler/Trigger/JitterTrigger.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Scheduler\Trigger; + +/** + * @author Kevin Bond + */ +final class JitterTrigger implements TriggerInterface +{ + /** + * @param positive-int $maxSeconds + */ + public function __construct(private readonly TriggerInterface $trigger, private readonly int $maxSeconds = 60) + { + } + + public function __toString(): string + { + return sprintf('%s with 0-%d second jitter', $this->trigger, $this->maxSeconds); + } + + public function getNextRunDate(\DateTimeImmutable $run): ?\DateTimeImmutable + { + if (!$nextRun = $this->trigger->getNextRunDate($run)) { + return null; + } + + return $nextRun->add(new \DateInterval(sprintf('PT%sS', random_int(0, $this->maxSeconds)))); + } +} From f139be1eeda2e098de89a9551725b0b160cb4d00 Mon Sep 17 00:00:00 2001 From: Kevin Mian Kraiker Date: Tue, 9 May 2023 19:17:09 -0300 Subject: [PATCH 12/43] Update HeaderBag::all PhpDoc For better compliance with code quality tools. --- src/Symfony/Component/HttpFoundation/HeaderBag.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpFoundation/HeaderBag.php b/src/Symfony/Component/HttpFoundation/HeaderBag.php index 0883024b3b50b..dc5a9a60a2e48 100644 --- a/src/Symfony/Component/HttpFoundation/HeaderBag.php +++ b/src/Symfony/Component/HttpFoundation/HeaderBag.php @@ -63,7 +63,7 @@ public function __toString(): string * * @param string|null $key The name of the headers to return or null to get them all * - * @return array>|array + * @return array>|list */ public function all(string $key = null): array { From 3f2c19679b4146ed07ce696751914df22d01406c Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 10 May 2023 10:52:57 +0200 Subject: [PATCH 13/43] Consistently use var $container to reference the container builder+configurator --- .../Command/BuildDebugContainerTrait.php | 8 +-- .../Command/ContainerDebugCommand.php | 22 +++---- .../Command/ContainerLintCommand.php | 8 +-- .../Command/DebugAutowiringCommand.php | 18 +++--- .../Console/Descriptor/Descriptor.php | 36 +++++------ .../Console/Descriptor/JsonDescriptor.php | 50 +++++++-------- .../Console/Descriptor/MarkdownDescriptor.php | 36 +++++------ .../Console/Descriptor/TextDescriptor.php | 34 +++++----- .../Console/Descriptor/XmlDescriptor.php | 64 +++++++++---------- .../Functional/WebProfilerBundleKernel.php | 6 +- .../ResolveParameterPlaceHoldersPassTest.php | 50 +++++++-------- .../config/services_with_enumeration.php | 6 +- .../Tests/Loader/XmlFileLoaderTest.php | 6 +- 13 files changed, 172 insertions(+), 172 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/BuildDebugContainerTrait.php b/src/Symfony/Bundle/FrameworkBundle/Command/BuildDebugContainerTrait.php index 0b1038e1cb424..04e3c3c456194 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/BuildDebugContainerTrait.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/BuildDebugContainerTrait.php @@ -26,7 +26,7 @@ */ trait BuildDebugContainerTrait { - protected $containerBuilder; + protected ContainerBuilder $container; /** * Loads the ContainerBuilder from the cache. @@ -35,8 +35,8 @@ trait BuildDebugContainerTrait */ protected function getContainerBuilder(KernelInterface $kernel): ContainerBuilder { - if ($this->containerBuilder) { - return $this->containerBuilder; + if (isset($this->container)) { + return $this->container; } if (!$kernel->isDebug() || !$kernel->getContainer()->getParameter('debug.container.dump') || !(new ConfigCache($kernel->getContainer()->getParameter('debug.container.dump'), true))->isFresh()) { @@ -59,6 +59,6 @@ protected function getContainerBuilder(KernelInterface $kernel): ContainerBuilde $container->getCompilerPassConfig()->setBeforeRemovingPasses([]); } - return $this->containerBuilder = $container; + return $this->container = $container; } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php index 300fae1b8aa79..cd1af0d5d43c0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php @@ -261,15 +261,15 @@ protected function validateInput(InputInterface $input): void } } - private function findProperServiceName(InputInterface $input, SymfonyStyle $io, ContainerBuilder $builder, string $name, bool $showHidden): string + private function findProperServiceName(InputInterface $input, SymfonyStyle $io, ContainerBuilder $container, string $name, bool $showHidden): string { $name = ltrim($name, '\\'); - if ($builder->has($name) || !$input->isInteractive()) { + if ($container->has($name) || !$input->isInteractive()) { return $name; } - $matchingServices = $this->findServiceIdsContaining($builder, $name, $showHidden); + $matchingServices = $this->findServiceIdsContaining($container, $name, $showHidden); if (!$matchingServices) { throw new InvalidArgumentException(sprintf('No services found that match "%s".', $name)); } @@ -281,13 +281,13 @@ private function findProperServiceName(InputInterface $input, SymfonyStyle $io, return $io->choice('Select one of the following services to display its information', $matchingServices); } - private function findProperTagName(InputInterface $input, SymfonyStyle $io, ContainerBuilder $builder, string $tagName): string + private function findProperTagName(InputInterface $input, SymfonyStyle $io, ContainerBuilder $container, string $tagName): string { - if (\in_array($tagName, $builder->findTags(), true) || !$input->isInteractive()) { + if (\in_array($tagName, $container->findTags(), true) || !$input->isInteractive()) { return $tagName; } - $matchingTags = $this->findTagsContaining($builder, $tagName); + $matchingTags = $this->findTagsContaining($container, $tagName); if (!$matchingTags) { throw new InvalidArgumentException(sprintf('No tags found that match "%s".', $tagName)); } @@ -299,15 +299,15 @@ private function findProperTagName(InputInterface $input, SymfonyStyle $io, Cont return $io->choice('Select one of the following tags to display its information', $matchingTags); } - private function findServiceIdsContaining(ContainerBuilder $builder, string $name, bool $showHidden): array + private function findServiceIdsContaining(ContainerBuilder $container, string $name, bool $showHidden): array { - $serviceIds = $builder->getServiceIds(); + $serviceIds = $container->getServiceIds(); $foundServiceIds = $foundServiceIdsIgnoringBackslashes = []; foreach ($serviceIds as $serviceId) { if (!$showHidden && str_starts_with($serviceId, '.')) { continue; } - if (!$showHidden && $builder->hasDefinition($serviceId) && $builder->getDefinition($serviceId)->hasTag('container.excluded')) { + if (!$showHidden && $container->hasDefinition($serviceId) && $container->getDefinition($serviceId)->hasTag('container.excluded')) { continue; } if (false !== stripos(str_replace('\\', '', $serviceId), $name)) { @@ -321,9 +321,9 @@ private function findServiceIdsContaining(ContainerBuilder $builder, string $nam return $foundServiceIds ?: $foundServiceIdsIgnoringBackslashes; } - private function findTagsContaining(ContainerBuilder $builder, string $tagName): array + private function findTagsContaining(ContainerBuilder $container, string $tagName): array { - $tags = $builder->findTags(); + $tags = $container->findTags(); $foundTags = []; foreach ($tags as $tag) { if (str_contains($tag, $tagName)) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php index 0d71e6985e206..188c56585f97d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php @@ -31,7 +31,7 @@ #[AsCommand(name: 'lint:container', description: 'Ensure that arguments injected into services match type declarations')] final class ContainerLintCommand extends Command { - private ContainerBuilder $containerBuilder; + private ContainerBuilder $container; protected function configure(): void { @@ -70,8 +70,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int private function getContainerBuilder(): ContainerBuilder { - if (isset($this->containerBuilder)) { - return $this->containerBuilder; + if (isset($this->container)) { + return $this->container; } $kernel = $this->getApplication()->getKernel(); @@ -108,6 +108,6 @@ private function getContainerBuilder(): ContainerBuilder $container->addCompilerPass(new CheckTypeDeclarationsPass(true), PassConfig::TYPE_AFTER_REMOVING, -100); - return $this->containerBuilder = $container; + return $this->container = $container; } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/DebugAutowiringCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/DebugAutowiringCommand.php index 185278a662e1c..8c47cb12c586b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/DebugAutowiringCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/DebugAutowiringCommand.php @@ -70,8 +70,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io = new SymfonyStyle($input, $output); $errorIo = $io->getErrorStyle(); - $builder = $this->getContainerBuilder($this->getApplication()->getKernel()); - $serviceIds = $builder->getServiceIds(); + $container = $this->getContainerBuilder($this->getApplication()->getKernel()); + $serviceIds = $container->getServiceIds(); $serviceIds = array_filter($serviceIds, $this->filterToServiceTypes(...)); if ($search = $input->getArgument('search')) { @@ -98,7 +98,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $previousId = '-'; $serviceIdsNb = 0; foreach ($serviceIds as $serviceId) { - if ($builder->hasDefinition($serviceId) && $builder->getDefinition($serviceId)->hasTag('container.excluded')) { + if ($container->hasDefinition($serviceId) && $container->getDefinition($serviceId)->hasTag('container.excluded')) { continue; } $text = []; @@ -119,11 +119,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int $serviceLine = sprintf('%s', $fileLink, $serviceId); } - if ($builder->hasAlias($serviceId)) { + if ($container->hasAlias($serviceId)) { $hasAlias[$serviceId] = true; - $serviceAlias = $builder->getAlias($serviceId); + $serviceAlias = $container->getAlias($serviceId); - if ($builder->hasDefinition($serviceAlias) && $decorated = $builder->getDefinition($serviceAlias)->getTag('container.decorator')) { + if ($container->hasDefinition($serviceAlias) && $decorated = $container->getDefinition($serviceAlias)->getTag('container.decorator')) { $serviceLine .= ' ('.$decorated[0]['id'].')'; } else { $serviceLine .= ' ('.$serviceAlias.')'; @@ -135,7 +135,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } elseif (!$all) { ++$serviceIdsNb; continue; - } elseif ($builder->getDefinition($serviceId)->isDeprecated()) { + } elseif ($container->getDefinition($serviceId)->isDeprecated()) { $serviceLine .= ' - deprecated'; } $text[] = $serviceLine; @@ -169,9 +169,9 @@ private function getFileLink(string $class): string public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { if ($input->mustSuggestArgumentValuesFor('search')) { - $builder = $this->getContainerBuilder($this->getApplication()->getKernel()); + $container = $this->getContainerBuilder($this->getApplication()->getKernel()); - $suggestions->suggestValues(array_filter($builder->getServiceIds(), $this->filterToServiceTypes(...))); + $suggestions->suggestValues(array_filter($container->getServiceIds(), $this->filterToServiceTypes(...))); } } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php index 24b1545f104bf..f4de2f09192da 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php @@ -84,7 +84,7 @@ abstract protected function describeRoute(Route $route, array $options = []): vo abstract protected function describeContainerParameters(ParameterBag $parameters, array $options = []): void; - abstract protected function describeContainerTags(ContainerBuilder $builder, array $options = []): void; + abstract protected function describeContainerTags(ContainerBuilder $container, array $options = []): void; /** * Describes a container service by its name. @@ -94,7 +94,7 @@ abstract protected function describeContainerTags(ContainerBuilder $builder, arr * * @param Definition|Alias|object $service */ - abstract protected function describeContainerService(object $service, array $options = [], ContainerBuilder $builder = null): void; + abstract protected function describeContainerService(object $service, array $options = [], ContainerBuilder $container = null): void; /** * Describes container services. @@ -102,13 +102,13 @@ abstract protected function describeContainerService(object $service, array $opt * Common options are: * * tag: filters described services by given tag */ - abstract protected function describeContainerServices(ContainerBuilder $builder, array $options = []): void; + abstract protected function describeContainerServices(ContainerBuilder $container, array $options = []): void; - abstract protected function describeContainerDeprecations(ContainerBuilder $builder, array $options = []): void; + abstract protected function describeContainerDeprecations(ContainerBuilder $container, array $options = []): void; - abstract protected function describeContainerDefinition(Definition $definition, array $options = [], ContainerBuilder $builder = null): void; + abstract protected function describeContainerDefinition(Definition $definition, array $options = [], ContainerBuilder $container = null): void; - abstract protected function describeContainerAlias(Alias $alias, array $options = [], ContainerBuilder $builder = null): void; + abstract protected function describeContainerAlias(Alias $alias, array $options = [], ContainerBuilder $container = null): void; abstract protected function describeContainerParameter(mixed $parameter, array $options = []): void; @@ -170,15 +170,15 @@ protected function formatParameter(mixed $value): string return (string) $value; } - protected function resolveServiceDefinition(ContainerBuilder $builder, string $serviceId): mixed + protected function resolveServiceDefinition(ContainerBuilder $container, string $serviceId): mixed { - if ($builder->hasDefinition($serviceId)) { - return $builder->getDefinition($serviceId); + if ($container->hasDefinition($serviceId)) { + return $container->getDefinition($serviceId); } // Some service IDs don't have a Definition, they're aliases - if ($builder->hasAlias($serviceId)) { - return $builder->getAlias($serviceId); + if ($container->hasAlias($serviceId)) { + return $container->getAlias($serviceId); } if ('service_container' === $serviceId) { @@ -186,18 +186,18 @@ protected function resolveServiceDefinition(ContainerBuilder $builder, string $s } // the service has been injected in some special way, just return the service - return $builder->get($serviceId); + return $container->get($serviceId); } - protected function findDefinitionsByTag(ContainerBuilder $builder, bool $showHidden): array + protected function findDefinitionsByTag(ContainerBuilder $container, bool $showHidden): array { $definitions = []; - $tags = $builder->findTags(); + $tags = $container->findTags(); asort($tags); foreach ($tags as $tag) { - foreach ($builder->findTaggedServiceIds($tag) as $serviceId => $attributes) { - $definition = $this->resolveServiceDefinition($builder, $serviceId); + foreach ($container->findTaggedServiceIds($tag) as $serviceId => $attributes) { + $definition = $this->resolveServiceDefinition($container, $serviceId); if ($showHidden xor '.' === ($serviceId[0] ?? null)) { continue; @@ -334,12 +334,12 @@ private function getContainerEnvVars(ContainerBuilder $container): array return array_values($envs); } - protected function getServiceEdges(ContainerBuilder $builder, string $serviceId): array + protected function getServiceEdges(ContainerBuilder $container, string $serviceId): array { try { return array_values(array_unique(array_map( fn (ServiceReferenceGraphEdge $edge) => $edge->getSourceNode()->getId(), - $builder->getCompiler()->getServiceReferenceGraph()->getNode($serviceId)->getInEdges() + $container->getCompiler()->getServiceReferenceGraph()->getNode($serviceId)->getInEdges() ))); } catch (InvalidArgumentException $exception) { return []; diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php index 5806fd32f8ad8..09e975ad4a3d7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php @@ -52,41 +52,41 @@ protected function describeContainerParameters(ParameterBag $parameters, array $ $this->writeData($this->sortParameters($parameters), $options); } - protected function describeContainerTags(ContainerBuilder $builder, array $options = []): void + protected function describeContainerTags(ContainerBuilder $container, array $options = []): void { $showHidden = isset($options['show_hidden']) && $options['show_hidden']; $data = []; - foreach ($this->findDefinitionsByTag($builder, $showHidden) as $tag => $definitions) { + foreach ($this->findDefinitionsByTag($container, $showHidden) as $tag => $definitions) { $data[$tag] = []; foreach ($definitions as $definition) { - $data[$tag][] = $this->getContainerDefinitionData($definition, true, false, $builder, $options['id'] ?? null); + $data[$tag][] = $this->getContainerDefinitionData($definition, true, false, $container, $options['id'] ?? null); } } $this->writeData($data, $options); } - protected function describeContainerService(object $service, array $options = [], ContainerBuilder $builder = null): void + protected function describeContainerService(object $service, array $options = [], ContainerBuilder $container = null): void { if (!isset($options['id'])) { throw new \InvalidArgumentException('An "id" option must be provided.'); } if ($service instanceof Alias) { - $this->describeContainerAlias($service, $options, $builder); + $this->describeContainerAlias($service, $options, $container); } elseif ($service instanceof Definition) { - $this->writeData($this->getContainerDefinitionData($service, isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments'], $builder, $options['id']), $options); + $this->writeData($this->getContainerDefinitionData($service, isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments'], $container, $options['id']), $options); } else { $this->writeData($service::class, $options); } } - protected function describeContainerServices(ContainerBuilder $builder, array $options = []): void + protected function describeContainerServices(ContainerBuilder $container, array $options = []): void { $serviceIds = isset($options['tag']) && $options['tag'] - ? $this->sortTaggedServicesByPriority($builder->findTaggedServiceIds($options['tag'])) - : $this->sortServiceIds($builder->getServiceIds()); + ? $this->sortTaggedServicesByPriority($container->findTaggedServiceIds($options['tag'])) + : $this->sortServiceIds($container->getServiceIds()); $showHidden = isset($options['show_hidden']) && $options['show_hidden']; $omitTags = isset($options['omit_tags']) && $options['omit_tags']; $showArguments = isset($options['show_arguments']) && $options['show_arguments']; @@ -97,7 +97,7 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o } foreach ($serviceIds as $serviceId) { - $service = $this->resolveServiceDefinition($builder, $serviceId); + $service = $this->resolveServiceDefinition($container, $serviceId); if ($showHidden xor '.' === ($serviceId[0] ?? null)) { continue; @@ -106,7 +106,7 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o if ($service instanceof Alias) { $data['aliases'][$serviceId] = $this->getContainerAliasData($service); } elseif ($service instanceof Definition) { - $data['definitions'][$serviceId] = $this->getContainerDefinitionData($service, $omitTags, $showArguments, $builder, $serviceId); + $data['definitions'][$serviceId] = $this->getContainerDefinitionData($service, $omitTags, $showArguments, $container, $serviceId); } else { $data['services'][$serviceId] = $service::class; } @@ -115,21 +115,21 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o $this->writeData($data, $options); } - protected function describeContainerDefinition(Definition $definition, array $options = [], ContainerBuilder $builder = null): void + protected function describeContainerDefinition(Definition $definition, array $options = [], ContainerBuilder $container = null): void { - $this->writeData($this->getContainerDefinitionData($definition, isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments'], $builder, $options['id'] ?? null), $options); + $this->writeData($this->getContainerDefinitionData($definition, isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments'], $container, $options['id'] ?? null), $options); } - protected function describeContainerAlias(Alias $alias, array $options = [], ContainerBuilder $builder = null): void + protected function describeContainerAlias(Alias $alias, array $options = [], ContainerBuilder $container = null): void { - if (!$builder) { + if (!$container) { $this->writeData($this->getContainerAliasData($alias), $options); return; } $this->writeData( - [$this->getContainerAliasData($alias), $this->getContainerDefinitionData($builder->getDefinition((string) $alias), isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments'], $builder, (string) $alias)], + [$this->getContainerAliasData($alias), $this->getContainerDefinitionData($container->getDefinition((string) $alias), isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments'], $container, (string) $alias)], array_merge($options, ['id' => (string) $alias]) ); } @@ -156,9 +156,9 @@ protected function describeContainerEnvVars(array $envs, array $options = []): v throw new LogicException('Using the JSON format to debug environment variables is not supported.'); } - protected function describeContainerDeprecations(ContainerBuilder $builder, array $options = []): void + protected function describeContainerDeprecations(ContainerBuilder $container, array $options = []): void { - $containerDeprecationFilePath = sprintf('%s/%sDeprecations.log', $builder->getParameter('kernel.build_dir'), $builder->getParameter('kernel.container_class')); + $containerDeprecationFilePath = sprintf('%s/%sDeprecations.log', $container->getParameter('kernel.build_dir'), $container->getParameter('kernel.container_class')); if (!file_exists($containerDeprecationFilePath)) { throw new RuntimeException('The deprecation file does not exist, please try warming the cache first.'); } @@ -217,7 +217,7 @@ protected function getRouteData(Route $route): array return $data; } - private function getContainerDefinitionData(Definition $definition, bool $omitTags = false, bool $showArguments = false, ContainerBuilder $builder = null, string $id = null): array + private function getContainerDefinitionData(Definition $definition, bool $omitTags = false, bool $showArguments = false, ContainerBuilder $container = null, string $id = null): array { $data = [ 'class' => (string) $definition->getClass(), @@ -242,7 +242,7 @@ private function getContainerDefinitionData(Definition $definition, bool $omitTa } if ($showArguments) { - $data['arguments'] = $this->describeValue($definition->getArguments(), $omitTags, $showArguments, $builder, $id); + $data['arguments'] = $this->describeValue($definition->getArguments(), $omitTags, $showArguments, $container, $id); } $data['file'] = $definition->getFile(); @@ -279,7 +279,7 @@ private function getContainerDefinitionData(Definition $definition, bool $omitTa } } - $data['usages'] = null !== $builder && null !== $id ? $this->getServiceEdges($builder, $id) : []; + $data['usages'] = null !== $container && null !== $id ? $this->getServiceEdges($container, $id) : []; return $data; } @@ -390,12 +390,12 @@ private function getCallableData(mixed $callable): array throw new \InvalidArgumentException('Callable is not describable.'); } - private function describeValue($value, bool $omitTags, bool $showArguments, ContainerBuilder $builder = null, string $id = null): mixed + private function describeValue($value, bool $omitTags, bool $showArguments, ContainerBuilder $container = null, string $id = null): mixed { if (\is_array($value)) { $data = []; foreach ($value as $k => $v) { - $data[$k] = $this->describeValue($v, $omitTags, $showArguments, $builder, $id); + $data[$k] = $this->describeValue($v, $omitTags, $showArguments, $container, $id); } return $data; @@ -417,11 +417,11 @@ private function describeValue($value, bool $omitTags, bool $showArguments, Cont } if ($value instanceof ArgumentInterface) { - return $this->describeValue($value->getValues(), $omitTags, $showArguments, $builder, $id); + return $this->describeValue($value->getValues(), $omitTags, $showArguments, $container, $id); } if ($value instanceof Definition) { - return $this->getContainerDefinitionData($value, $omitTags, $showArguments, $builder, $id); + return $this->getContainerDefinitionData($value, $omitTags, $showArguments, $container, $id); } return $value; diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php index 4581e0e198b99..1289c8ded9303 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php @@ -74,21 +74,21 @@ protected function describeContainerParameters(ParameterBag $parameters, array $ } } - protected function describeContainerTags(ContainerBuilder $builder, array $options = []): void + protected function describeContainerTags(ContainerBuilder $container, array $options = []): void { $showHidden = isset($options['show_hidden']) && $options['show_hidden']; $this->write("Container tags\n=============="); - foreach ($this->findDefinitionsByTag($builder, $showHidden) as $tag => $definitions) { + foreach ($this->findDefinitionsByTag($container, $showHidden) as $tag => $definitions) { $this->write("\n\n".$tag."\n".str_repeat('-', \strlen($tag))); foreach ($definitions as $serviceId => $definition) { $this->write("\n\n"); - $this->describeContainerDefinition($definition, ['omit_tags' => true, 'id' => $serviceId], $builder); + $this->describeContainerDefinition($definition, ['omit_tags' => true, 'id' => $serviceId], $container); } } } - protected function describeContainerService(object $service, array $options = [], ContainerBuilder $builder = null): void + protected function describeContainerService(object $service, array $options = [], ContainerBuilder $container = null): void { if (!isset($options['id'])) { throw new \InvalidArgumentException('An "id" option must be provided.'); @@ -97,17 +97,17 @@ protected function describeContainerService(object $service, array $options = [] $childOptions = array_merge($options, ['id' => $options['id'], 'as_array' => true]); if ($service instanceof Alias) { - $this->describeContainerAlias($service, $childOptions, $builder); + $this->describeContainerAlias($service, $childOptions, $container); } elseif ($service instanceof Definition) { - $this->describeContainerDefinition($service, $childOptions, $builder); + $this->describeContainerDefinition($service, $childOptions, $container); } else { $this->write(sprintf('**`%s`:** `%s`', $options['id'], $service::class)); } } - protected function describeContainerDeprecations(ContainerBuilder $builder, array $options = []): void + protected function describeContainerDeprecations(ContainerBuilder $container, array $options = []): void { - $containerDeprecationFilePath = sprintf('%s/%sDeprecations.log', $builder->getParameter('kernel.build_dir'), $builder->getParameter('kernel.container_class')); + $containerDeprecationFilePath = sprintf('%s/%sDeprecations.log', $container->getParameter('kernel.build_dir'), $container->getParameter('kernel.container_class')); if (!file_exists($containerDeprecationFilePath)) { throw new RuntimeException('The deprecation file does not exist, please try warming the cache first.'); } @@ -132,7 +132,7 @@ protected function describeContainerDeprecations(ContainerBuilder $builder, arra } } - protected function describeContainerServices(ContainerBuilder $builder, array $options = []): void + protected function describeContainerServices(ContainerBuilder $container, array $options = []): void { $showHidden = isset($options['show_hidden']) && $options['show_hidden']; @@ -143,8 +143,8 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o $this->write($title."\n".str_repeat('=', \strlen($title))); $serviceIds = isset($options['tag']) && $options['tag'] - ? $this->sortTaggedServicesByPriority($builder->findTaggedServiceIds($options['tag'])) - : $this->sortServiceIds($builder->getServiceIds()); + ? $this->sortTaggedServicesByPriority($container->findTaggedServiceIds($options['tag'])) + : $this->sortServiceIds($container->getServiceIds()); $showArguments = isset($options['show_arguments']) && $options['show_arguments']; $services = ['definitions' => [], 'aliases' => [], 'services' => []]; @@ -153,7 +153,7 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o } foreach ($serviceIds as $serviceId) { - $service = $this->resolveServiceDefinition($builder, $serviceId); + $service = $this->resolveServiceDefinition($container, $serviceId); if ($showHidden xor '.' === ($serviceId[0] ?? null)) { continue; @@ -172,7 +172,7 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o $this->write("\n\nDefinitions\n-----------\n"); foreach ($services['definitions'] as $id => $service) { $this->write("\n"); - $this->describeContainerDefinition($service, ['id' => $id, 'show_arguments' => $showArguments], $builder); + $this->describeContainerDefinition($service, ['id' => $id, 'show_arguments' => $showArguments], $container); } } @@ -193,7 +193,7 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o } } - protected function describeContainerDefinition(Definition $definition, array $options = [], ContainerBuilder $builder = null): void + protected function describeContainerDefinition(Definition $definition, array $options = [], ContainerBuilder $container = null): void { $output = ''; @@ -257,13 +257,13 @@ protected function describeContainerDefinition(Definition $definition, array $op } } - $inEdges = null !== $builder && isset($options['id']) ? $this->getServiceEdges($builder, $options['id']) : []; + $inEdges = null !== $container && isset($options['id']) ? $this->getServiceEdges($container, $options['id']) : []; $output .= "\n".'- Usages: '.($inEdges ? implode(', ', $inEdges) : 'none'); $this->write(isset($options['id']) ? sprintf("### %s\n\n%s\n", $options['id'], $output) : $output); } - protected function describeContainerAlias(Alias $alias, array $options = [], ContainerBuilder $builder = null): void + protected function describeContainerAlias(Alias $alias, array $options = [], ContainerBuilder $container = null): void { $output = '- Service: `'.$alias.'`' ."\n".'- Public: '.($alias->isPublic() && !$alias->isPrivate() ? 'yes' : 'no'); @@ -276,12 +276,12 @@ protected function describeContainerAlias(Alias $alias, array $options = [], Con $this->write(sprintf("### %s\n\n%s\n", $options['id'], $output)); - if (!$builder) { + if (!$container) { return; } $this->write("\n"); - $this->describeContainerDefinition($builder->getDefinition((string) $alias), array_merge($options, ['id' => (string) $alias]), $builder); + $this->describeContainerDefinition($container->getDefinition((string) $alias), array_merge($options, ['id' => (string) $alias]), $container); } protected function describeContainerParameter(mixed $parameter, array $options = []): void diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php index f555e7220901c..519d99f3a97cd 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php @@ -125,7 +125,7 @@ protected function describeContainerParameters(ParameterBag $parameters, array $ $options['output']->table($tableHeaders, $tableRows); } - protected function describeContainerTags(ContainerBuilder $builder, array $options = []): void + protected function describeContainerTags(ContainerBuilder $container, array $options = []): void { $showHidden = isset($options['show_hidden']) && $options['show_hidden']; @@ -135,22 +135,22 @@ protected function describeContainerTags(ContainerBuilder $builder, array $optio $options['output']->title('Symfony Container Tags'); } - foreach ($this->findDefinitionsByTag($builder, $showHidden) as $tag => $definitions) { + foreach ($this->findDefinitionsByTag($container, $showHidden) as $tag => $definitions) { $options['output']->section(sprintf('"%s" tag', $tag)); $options['output']->listing(array_keys($definitions)); } } - protected function describeContainerService(object $service, array $options = [], ContainerBuilder $builder = null): void + protected function describeContainerService(object $service, array $options = [], ContainerBuilder $container = null): void { if (!isset($options['id'])) { throw new \InvalidArgumentException('An "id" option must be provided.'); } if ($service instanceof Alias) { - $this->describeContainerAlias($service, $options, $builder); + $this->describeContainerAlias($service, $options, $container); } elseif ($service instanceof Definition) { - $this->describeContainerDefinition($service, $options, $builder); + $this->describeContainerDefinition($service, $options, $container); } else { $options['output']->title(sprintf('Information for Service "%s"', $options['id'])); $options['output']->table( @@ -162,7 +162,7 @@ protected function describeContainerService(object $service, array $options = [] } } - protected function describeContainerServices(ContainerBuilder $builder, array $options = []): void + protected function describeContainerServices(ContainerBuilder $container, array $options = []): void { $showHidden = isset($options['show_hidden']) && $options['show_hidden']; $showTag = $options['tag'] ?? null; @@ -180,8 +180,8 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o $options['output']->title($title); $serviceIds = isset($options['tag']) && $options['tag'] - ? $this->sortTaggedServicesByPriority($builder->findTaggedServiceIds($options['tag'])) - : $this->sortServiceIds($builder->getServiceIds()); + ? $this->sortTaggedServicesByPriority($container->findTaggedServiceIds($options['tag'])) + : $this->sortServiceIds($container->getServiceIds()); $maxTags = []; if (isset($options['filter'])) { @@ -189,7 +189,7 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o } foreach ($serviceIds as $key => $serviceId) { - $definition = $this->resolveServiceDefinition($builder, $serviceId); + $definition = $this->resolveServiceDefinition($container, $serviceId); // filter out hidden services unless shown explicitly if ($showHidden xor '.' === ($serviceId[0] ?? null)) { @@ -221,7 +221,7 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o $tableRows = []; $rawOutput = isset($options['raw_text']) && $options['raw_text']; foreach ($serviceIds as $serviceId) { - $definition = $this->resolveServiceDefinition($builder, $serviceId); + $definition = $this->resolveServiceDefinition($container, $serviceId); $styledServiceId = $rawOutput ? $serviceId : sprintf('%s', OutputFormatter::escape($serviceId)); if ($definition instanceof Definition) { @@ -251,7 +251,7 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o $options['output']->table($tableHeaders, $tableRows); } - protected function describeContainerDefinition(Definition $definition, array $options = [], ContainerBuilder $builder = null): void + protected function describeContainerDefinition(Definition $definition, array $options = [], ContainerBuilder $container = null): void { if (isset($options['id'])) { $options['output']->title(sprintf('Information for Service "%s"', $options['id'])); @@ -358,15 +358,15 @@ protected function describeContainerDefinition(Definition $definition, array $op $tableRows[] = ['Arguments', implode("\n", $argumentsInformation)]; } - $inEdges = null !== $builder && isset($options['id']) ? $this->getServiceEdges($builder, $options['id']) : []; + $inEdges = null !== $container && isset($options['id']) ? $this->getServiceEdges($container, $options['id']) : []; $tableRows[] = ['Usages', $inEdges ? implode(\PHP_EOL, $inEdges) : 'none']; $options['output']->table($tableHeaders, $tableRows); } - protected function describeContainerDeprecations(ContainerBuilder $builder, array $options = []): void + protected function describeContainerDeprecations(ContainerBuilder $container, array $options = []): void { - $containerDeprecationFilePath = sprintf('%s/%sDeprecations.log', $builder->getParameter('kernel.build_dir'), $builder->getParameter('kernel.container_class')); + $containerDeprecationFilePath = sprintf('%s/%sDeprecations.log', $container->getParameter('kernel.build_dir'), $container->getParameter('kernel.container_class')); if (!file_exists($containerDeprecationFilePath)) { $options['output']->warning('The deprecation file does not exist, please try warming the cache first.'); @@ -390,7 +390,7 @@ protected function describeContainerDeprecations(ContainerBuilder $builder, arra $options['output']->listing($formattedLogs); } - protected function describeContainerAlias(Alias $alias, array $options = [], ContainerBuilder $builder = null): void + protected function describeContainerAlias(Alias $alias, array $options = [], ContainerBuilder $container = null): void { if ($alias->isPublic() && !$alias->isPrivate()) { $options['output']->comment(sprintf('This service is a public alias for the service %s', (string) $alias)); @@ -398,11 +398,11 @@ protected function describeContainerAlias(Alias $alias, array $options = [], Con $options['output']->comment(sprintf('This service is a private alias for the service %s', (string) $alias)); } - if (!$builder) { + if (!$container) { return; } - $this->describeContainerDefinition($builder->getDefinition((string) $alias), array_merge($options, ['id' => (string) $alias]), $builder); + $this->describeContainerDefinition($container->getDefinition((string) $alias), array_merge($options, ['id' => (string) $alias]), $container); } protected function describeContainerParameter(mixed $parameter, array $options = []): void diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php index 17870bb96a69b..79253d53f1b5f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php @@ -48,42 +48,42 @@ protected function describeContainerParameters(ParameterBag $parameters, array $ $this->writeDocument($this->getContainerParametersDocument($parameters)); } - protected function describeContainerTags(ContainerBuilder $builder, array $options = []): void + protected function describeContainerTags(ContainerBuilder $container, array $options = []): void { - $this->writeDocument($this->getContainerTagsDocument($builder, isset($options['show_hidden']) && $options['show_hidden'])); + $this->writeDocument($this->getContainerTagsDocument($container, isset($options['show_hidden']) && $options['show_hidden'])); } - protected function describeContainerService(object $service, array $options = [], ContainerBuilder $builder = null): void + protected function describeContainerService(object $service, array $options = [], ContainerBuilder $container = null): void { if (!isset($options['id'])) { throw new \InvalidArgumentException('An "id" option must be provided.'); } - $this->writeDocument($this->getContainerServiceDocument($service, $options['id'], $builder, isset($options['show_arguments']) && $options['show_arguments'])); + $this->writeDocument($this->getContainerServiceDocument($service, $options['id'], $container, isset($options['show_arguments']) && $options['show_arguments'])); } - protected function describeContainerServices(ContainerBuilder $builder, array $options = []): void + protected function describeContainerServices(ContainerBuilder $container, array $options = []): void { - $this->writeDocument($this->getContainerServicesDocument($builder, $options['tag'] ?? null, isset($options['show_hidden']) && $options['show_hidden'], isset($options['show_arguments']) && $options['show_arguments'], $options['filter'] ?? null, $options['id'] ?? null)); + $this->writeDocument($this->getContainerServicesDocument($container, $options['tag'] ?? null, isset($options['show_hidden']) && $options['show_hidden'], isset($options['show_arguments']) && $options['show_arguments'], $options['filter'] ?? null, $options['id'] ?? null)); } - protected function describeContainerDefinition(Definition $definition, array $options = [], ContainerBuilder $builder = null): void + protected function describeContainerDefinition(Definition $definition, array $options = [], ContainerBuilder $container = null): void { - $this->writeDocument($this->getContainerDefinitionDocument($definition, $options['id'] ?? null, isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments'], $builder)); + $this->writeDocument($this->getContainerDefinitionDocument($definition, $options['id'] ?? null, isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments'], $container)); } - protected function describeContainerAlias(Alias $alias, array $options = [], ContainerBuilder $builder = null): void + protected function describeContainerAlias(Alias $alias, array $options = [], ContainerBuilder $container = null): void { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($dom->importNode($this->getContainerAliasDocument($alias, $options['id'] ?? null)->childNodes->item(0), true)); - if (!$builder) { + if (!$container) { $this->writeDocument($dom); return; } - $dom->appendChild($dom->importNode($this->getContainerDefinitionDocument($builder->getDefinition((string) $alias), (string) $alias, false, false, $builder)->childNodes->item(0), true)); + $dom->appendChild($dom->importNode($this->getContainerDefinitionDocument($container->getDefinition((string) $alias), (string) $alias, false, false, $container)->childNodes->item(0), true)); $this->writeDocument($dom); } @@ -108,9 +108,9 @@ protected function describeContainerEnvVars(array $envs, array $options = []): v throw new LogicException('Using the XML format to debug environment variables is not supported.'); } - protected function describeContainerDeprecations(ContainerBuilder $builder, array $options = []): void + protected function describeContainerDeprecations(ContainerBuilder $container, array $options = []): void { - $containerDeprecationFilePath = sprintf('%s/%sDeprecations.log', $builder->getParameter('kernel.build_dir'), $builder->getParameter('kernel.container_class')); + $containerDeprecationFilePath = sprintf('%s/%sDeprecations.log', $container->getParameter('kernel.build_dir'), $container->getParameter('kernel.container_class')); if (!file_exists($containerDeprecationFilePath)) { throw new RuntimeException('The deprecation file does not exist, please try warming the cache first.'); } @@ -236,17 +236,17 @@ private function getContainerParametersDocument(ParameterBag $parameters): \DOMD return $dom; } - private function getContainerTagsDocument(ContainerBuilder $builder, bool $showHidden = false): \DOMDocument + private function getContainerTagsDocument(ContainerBuilder $container, bool $showHidden = false): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($containerXML = $dom->createElement('container')); - foreach ($this->findDefinitionsByTag($builder, $showHidden) as $tag => $definitions) { + foreach ($this->findDefinitionsByTag($container, $showHidden) as $tag => $definitions) { $containerXML->appendChild($tagXML = $dom->createElement('tag')); $tagXML->setAttribute('name', $tag); foreach ($definitions as $serviceId => $definition) { - $definitionXML = $this->getContainerDefinitionDocument($definition, $serviceId, true, false, $builder); + $definitionXML = $this->getContainerDefinitionDocument($definition, $serviceId, true, false, $container); $tagXML->appendChild($dom->importNode($definitionXML->childNodes->item(0), true)); } } @@ -254,17 +254,17 @@ private function getContainerTagsDocument(ContainerBuilder $builder, bool $showH return $dom; } - private function getContainerServiceDocument(object $service, string $id, ContainerBuilder $builder = null, bool $showArguments = false): \DOMDocument + private function getContainerServiceDocument(object $service, string $id, ContainerBuilder $container = null, bool $showArguments = false): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); if ($service instanceof Alias) { $dom->appendChild($dom->importNode($this->getContainerAliasDocument($service, $id)->childNodes->item(0), true)); - if ($builder) { - $dom->appendChild($dom->importNode($this->getContainerDefinitionDocument($builder->getDefinition((string) $service), (string) $service, false, $showArguments, $builder)->childNodes->item(0), true)); + if ($container) { + $dom->appendChild($dom->importNode($this->getContainerDefinitionDocument($container->getDefinition((string) $service), (string) $service, false, $showArguments, $container)->childNodes->item(0), true)); } } elseif ($service instanceof Definition) { - $dom->appendChild($dom->importNode($this->getContainerDefinitionDocument($service, $id, false, $showArguments, $builder)->childNodes->item(0), true)); + $dom->appendChild($dom->importNode($this->getContainerDefinitionDocument($service, $id, false, $showArguments, $container)->childNodes->item(0), true)); } else { $dom->appendChild($serviceXML = $dom->createElement('service')); $serviceXML->setAttribute('id', $id); @@ -274,20 +274,20 @@ private function getContainerServiceDocument(object $service, string $id, Contai return $dom; } - private function getContainerServicesDocument(ContainerBuilder $builder, string $tag = null, bool $showHidden = false, bool $showArguments = false, callable $filter = null, string $id = null): \DOMDocument + private function getContainerServicesDocument(ContainerBuilder $container, string $tag = null, bool $showHidden = false, bool $showArguments = false, callable $filter = null, string $id = null): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($containerXML = $dom->createElement('container')); $serviceIds = $tag - ? $this->sortTaggedServicesByPriority($builder->findTaggedServiceIds($tag)) - : $this->sortServiceIds($builder->getServiceIds()); + ? $this->sortTaggedServicesByPriority($container->findTaggedServiceIds($tag)) + : $this->sortServiceIds($container->getServiceIds()); if ($filter) { $serviceIds = array_filter($serviceIds, $filter); } foreach ($serviceIds as $serviceId) { - $service = $this->resolveServiceDefinition($builder, $serviceId); + $service = $this->resolveServiceDefinition($container, $serviceId); if ($showHidden xor '.' === ($serviceId[0] ?? null)) { continue; @@ -300,7 +300,7 @@ private function getContainerServicesDocument(ContainerBuilder $builder, string return $dom; } - private function getContainerDefinitionDocument(Definition $definition, string $id = null, bool $omitTags = false, bool $showArguments = false, ContainerBuilder $builder = null): \DOMDocument + private function getContainerDefinitionDocument(Definition $definition, string $id = null, bool $omitTags = false, bool $showArguments = false, ContainerBuilder $container = null): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($serviceXML = $dom->createElement('definition')); @@ -361,7 +361,7 @@ private function getContainerDefinitionDocument(Definition $definition, string $ } if ($showArguments) { - foreach ($this->getArgumentNodes($definition->getArguments(), $dom, $builder) as $node) { + foreach ($this->getArgumentNodes($definition->getArguments(), $dom, $container) as $node) { $serviceXML->appendChild($node); } } @@ -383,8 +383,8 @@ private function getContainerDefinitionDocument(Definition $definition, string $ } } - if (null !== $builder && null !== $id) { - $edges = $this->getServiceEdges($builder, $id); + if (null !== $container && null !== $id) { + $edges = $this->getServiceEdges($container, $id); if ($edges) { $serviceXML->appendChild($usagesXML = $dom->createElement('usages')); foreach ($edges as $edge) { @@ -400,7 +400,7 @@ private function getContainerDefinitionDocument(Definition $definition, string $ /** * @return \DOMNode[] */ - private function getArgumentNodes(array $arguments, \DOMDocument $dom, ContainerBuilder $builder = null): array + private function getArgumentNodes(array $arguments, \DOMDocument $dom, ContainerBuilder $container = null): array { $nodes = []; @@ -421,18 +421,18 @@ private function getArgumentNodes(array $arguments, \DOMDocument $dom, Container } elseif ($argument instanceof IteratorArgument || $argument instanceof ServiceLocatorArgument) { $argumentXML->setAttribute('type', $argument instanceof IteratorArgument ? 'iterator' : 'service_locator'); - foreach ($this->getArgumentNodes($argument->getValues(), $dom, $builder) as $childArgumentXML) { + foreach ($this->getArgumentNodes($argument->getValues(), $dom, $container) as $childArgumentXML) { $argumentXML->appendChild($childArgumentXML); } } elseif ($argument instanceof Definition) { - $argumentXML->appendChild($dom->importNode($this->getContainerDefinitionDocument($argument, null, false, true, $builder)->childNodes->item(0), true)); + $argumentXML->appendChild($dom->importNode($this->getContainerDefinitionDocument($argument, null, false, true, $container)->childNodes->item(0), true)); } elseif ($argument instanceof AbstractArgument) { $argumentXML->setAttribute('type', 'abstract'); $argumentXML->appendChild(new \DOMText($argument->getText())); } elseif (\is_array($argument)) { $argumentXML->setAttribute('type', 'collection'); - foreach ($this->getArgumentNodes($argument, $dom, $builder) as $childArgumentXML) { + foreach ($this->getArgumentNodes($argument, $dom, $container) as $childArgumentXML) { $argumentXML->appendChild($childArgumentXML); } } elseif ($argument instanceof \UnitEnum) { diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/Functional/WebProfilerBundleKernel.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/Functional/WebProfilerBundleKernel.php index 78395c4b75a2d..0c98636536771 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Tests/Functional/WebProfilerBundleKernel.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/Functional/WebProfilerBundleKernel.php @@ -48,7 +48,7 @@ protected function configureRoutes(RoutingConfigurator $routes): void $routes->add('_', '/')->controller('kernel::homepageController'); } - protected function configureContainer(ContainerBuilder $containerBuilder, LoaderInterface $loader): void + protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void { $config = [ 'http_method_override' => false, @@ -58,9 +58,9 @@ protected function configureContainer(ContainerBuilder $containerBuilder, Loader 'router' => ['utf8' => true], ]; - $containerBuilder->loadFromExtension('framework', $config); + $container->loadFromExtension('framework', $config); - $containerBuilder->loadFromExtension('web_profiler', [ + $container->loadFromExtension('web_profiler', [ 'toolbar' => true, 'intercept_redirects' => false, ]); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveParameterPlaceHoldersPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveParameterPlaceHoldersPassTest.php index 96c45205459df..2f4a8e1d94141 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveParameterPlaceHoldersPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveParameterPlaceHoldersPassTest.php @@ -77,55 +77,55 @@ public function testParameterNotFoundExceptionsIsThrown() $this->expectException(ParameterNotFoundException::class); $this->expectExceptionMessage('The service "baz_service_id" has a dependency on a non-existent parameter "non_existent_param".'); - $containerBuilder = new ContainerBuilder(); - $definition = $containerBuilder->register('baz_service_id'); + $container = new ContainerBuilder(); + $definition = $container->register('baz_service_id'); $definition->setArgument(0, '%non_existent_param%'); $pass = new ResolveParameterPlaceHoldersPass(); - $pass->process($containerBuilder); + $pass->process($container); } public function testParameterNotFoundExceptionsIsNotThrown() { - $containerBuilder = new ContainerBuilder(); - $definition = $containerBuilder->register('baz_service_id'); + $container = new ContainerBuilder(); + $definition = $container->register('baz_service_id'); $definition->setArgument(0, '%non_existent_param%'); $pass = new ResolveParameterPlaceHoldersPass(true, false); - $pass->process($containerBuilder); + $pass->process($container); $this->assertCount(1, $definition->getErrors()); } public function testOnlyProxyTagIsResolved() { - $containerBuilder = new ContainerBuilder(); - $containerBuilder->setParameter('a_param', 'here_you_go'); - $definition = $containerBuilder->register('def'); + $container = new ContainerBuilder(); + $container->setParameter('a_param', 'here_you_go'); + $definition = $container->register('def'); $definition->addTag('foo', ['bar' => '%a_param%']); $definition->addTag('proxy', ['interface' => '%a_param%']); $pass = new ResolveParameterPlaceHoldersPass(true, false); - $pass->process($containerBuilder); + $pass->process($container); $this->assertSame(['foo' => [['bar' => '%a_param%']], 'proxy' => [['interface' => 'here_you_go']]], $definition->getTags()); } private function createContainerBuilder(): ContainerBuilder { - $containerBuilder = new ContainerBuilder(); - - $containerBuilder->setParameter('foo.class', 'Foo'); - $containerBuilder->setParameter('foo.factory.class', 'FooFactory'); - $containerBuilder->setParameter('foo.arg1', 'bar'); - $containerBuilder->setParameter('foo.arg2', ['%foo.arg1%' => 'baz']); - $containerBuilder->setParameter('foo.method', 'foobar'); - $containerBuilder->setParameter('foo.property.name', 'bar'); - $containerBuilder->setParameter('foo.property.value', 'baz'); - $containerBuilder->setParameter('foo.file', 'foo.php'); - $containerBuilder->setParameter('alias.id', 'bar'); - - $fooDefinition = $containerBuilder->register('foo', '%foo.class%'); + $container = new ContainerBuilder(); + + $container->setParameter('foo.class', 'Foo'); + $container->setParameter('foo.factory.class', 'FooFactory'); + $container->setParameter('foo.arg1', 'bar'); + $container->setParameter('foo.arg2', ['%foo.arg1%' => 'baz']); + $container->setParameter('foo.method', 'foobar'); + $container->setParameter('foo.property.name', 'bar'); + $container->setParameter('foo.property.value', 'baz'); + $container->setParameter('foo.file', 'foo.php'); + $container->setParameter('alias.id', 'bar'); + + $fooDefinition = $container->register('foo', '%foo.class%'); $fooDefinition->setFactory(['%foo.factory.class%', 'getFoo']); $fooDefinition->setArguments(['%foo.arg1%', ['%foo.arg1%' => 'baz']]); $fooDefinition->addMethodCall('%foo.method%', ['%foo.arg1%', '%foo.arg2%']); @@ -133,8 +133,8 @@ private function createContainerBuilder(): ContainerBuilder $fooDefinition->setFile('%foo.file%'); $fooDefinition->setBindings(['$baz' => '%env(BAZ)%']); - $containerBuilder->setAlias('%alias.id%', 'foo'); + $container->setAlias('%alias.id%', 'foo'); - return $containerBuilder; + return $container; } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services_with_enumeration.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services_with_enumeration.php index 6499081f248d5..2261f39732130 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services_with_enumeration.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services_with_enumeration.php @@ -6,12 +6,12 @@ use Symfony\Component\DependencyInjection\Tests\Fixtures\FooClassWithEnumAttribute; use Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum; -return function (ContainerConfigurator $containerConfigurator) { - $containerConfigurator->parameters() +return function (ContainerConfigurator $container) { + $container->parameters() ->set('unit_enum', FooUnitEnum::BAR) ->set('enum_array', [FooUnitEnum::BAR, FooUnitEnum::FOO]); - $services = $containerConfigurator->services(); + $services = $container->services(); $services->defaults()->public(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index 8d5954d3846f4..44ac44a25b791 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -112,11 +112,11 @@ public function testParseFile() public function testLoadWithExternalEntitiesDisabled() { - $containerBuilder = new ContainerBuilder(); - $loader = new XmlFileLoader($containerBuilder, new FileLocator(self::$fixturesPath.'/xml')); + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); $loader->load('services2.xml'); - $this->assertGreaterThan(0, $containerBuilder->getParameterBag()->all(), 'Parameters can be read from the config file.'); + $this->assertGreaterThan(0, $container->getParameterBag()->all(), 'Parameters can be read from the config file.'); } public function testLoadParameters() From 1f5c97786c2bfa22c9bd40a07395f2c58ef95774 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 10 May 2023 11:20:58 +0200 Subject: [PATCH 14/43] [Cache] Sync RelayProxy --- .../Component/Cache/Traits/RelayProxy.php | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/Symfony/Component/Cache/Traits/RelayProxy.php b/src/Symfony/Component/Cache/Traits/RelayProxy.php index 88574331d26bf..eedfd8f59e87f 100644 --- a/src/Symfony/Component/Cache/Traits/RelayProxy.php +++ b/src/Symfony/Component/Cache/Traits/RelayProxy.php @@ -537,6 +537,11 @@ public function publish($channel, $message): \Relay\Relay|false|int return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->publish(...\func_get_args()); } + public function spublish($channel, $message): \Relay\Relay|false|int + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->spublish(...\func_get_args()); + } + public function setnx($key, $value): \Relay\Relay|bool { return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->setnx(...\func_get_args()); @@ -887,6 +892,36 @@ public function sunionstore($key, ...$other_keys): \Relay\Relay|false|int return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->sunionstore(...\func_get_args()); } + public function subscribe($channels, $callback): bool + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->subscribe(...\func_get_args()); + } + + public function unsubscribe($channels = []): bool + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->unsubscribe(...\func_get_args()); + } + + public function psubscribe($patterns, $callback): bool + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->psubscribe(...\func_get_args()); + } + + public function punsubscribe($patterns = []): bool + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->punsubscribe(...\func_get_args()); + } + + public function ssubscribe($channels, $callback): bool + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->ssubscribe(...\func_get_args()); + } + + public function sunsubscribe($channels = []): bool + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->sunsubscribe(...\func_get_args()); + } + public function touch($key_or_array, ...$more_keys): \Relay\Relay|false|int { return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->touch(...\func_get_args()); From a9f25a15ea9fa63bd50b4fb1646377673df3718c Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 9 May 2023 15:41:26 +0200 Subject: [PATCH 15/43] [HttpClient] Add option `crypto_method` to set the minimum TLS version and make it default to v1.2 --- UPGRADE-6.3.md | 2 ++ .../DependencyInjection/Configuration.php | 11 +++++++---- src/Symfony/Component/HttpClient/AmpHttpClient.php | 4 ++++ src/Symfony/Component/HttpClient/CHANGELOG.md | 1 + src/Symfony/Component/HttpClient/CurlHttpClient.php | 10 ++++++++++ src/Symfony/Component/HttpClient/HttpClientTrait.php | 10 ++++++++++ .../Component/HttpClient/Internal/AmpClientState.php | 2 ++ src/Symfony/Component/HttpClient/NativeHttpClient.php | 11 +++++++++++ src/Symfony/Contracts/CHANGELOG.md | 5 +++++ .../Contracts/HttpClient/HttpClientInterface.php | 1 + 10 files changed, 53 insertions(+), 4 deletions(-) diff --git a/UPGRADE-6.3.md b/UPGRADE-6.3.md index dcc98bf2cfc3f..883fef0ba544b 100644 --- a/UPGRADE-6.3.md +++ b/UPGRADE-6.3.md @@ -68,6 +68,8 @@ FrameworkBundle HttpClient ---------- + * The minimum TLS version now defaults to v1.2; use the `crypto_method` + option if you need to connect to servers that don't support it * The default user agents have been renamed from `Symfony HttpClient/Amp`, `Symfony HttpClient/Curl` and `Symfony HttpClient/Native` to `Symfony HttpClient (Amp)`, `Symfony HttpClient (Curl)` and `Symfony HttpClient (Native)` respectively to comply with the RFC 9110 specification diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index d668d435a42e2..0dd13d245dce4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -1817,7 +1817,7 @@ private function addHttpClientSection(ArrayNodeDefinition $rootNode, callable $e ->info('A network interface name, IP address, a host name or a UNIX socket to bind to.') ->end() ->booleanNode('verify_peer') - ->info('Indicates if the peer should be verified in an SSL/TLS context.') + ->info('Indicates if the peer should be verified in a TLS context.') ->end() ->booleanNode('verify_host') ->info('Indicates if the host should exist as a certificate common name.') @@ -1838,7 +1838,7 @@ private function addHttpClientSection(ArrayNodeDefinition $rootNode, callable $e ->info('The passphrase used to encrypt the "local_pk" file.') ->end() ->scalarNode('ciphers') - ->info('A list of SSL/TLS ciphers separated by colons, commas or spaces (e.g. "RC3-SHA:TLS13-AES-128-GCM-SHA256"...)') + ->info('A list of TLS ciphers separated by colons, commas or spaces (e.g. "RC3-SHA:TLS13-AES-128-GCM-SHA256"...)') ->end() ->arrayNode('peer_fingerprint') ->info('Associative array: hashing algorithm => hash(es).') @@ -1849,6 +1849,9 @@ private function addHttpClientSection(ArrayNodeDefinition $rootNode, callable $e ->variableNode('md5')->end() ->end() ->end() + ->scalarNode('crypto_method') + ->info('The minimum version of TLS to accept; must be one of STREAM_CRYPTO_METHOD_TLSv*_CLIENT constants.') + ->end() ->arrayNode('extra') ->info('Extra options for specific HTTP client') ->normalizeKeys(false) @@ -1965,7 +1968,7 @@ private function addHttpClientSection(ArrayNodeDefinition $rootNode, callable $e ->info('A network interface name, IP address, a host name or a UNIX socket to bind to.') ->end() ->booleanNode('verify_peer') - ->info('Indicates if the peer should be verified in an SSL/TLS context.') + ->info('Indicates if the peer should be verified in a TLS context.') ->end() ->booleanNode('verify_host') ->info('Indicates if the host should exist as a certificate common name.') @@ -1986,7 +1989,7 @@ private function addHttpClientSection(ArrayNodeDefinition $rootNode, callable $e ->info('The passphrase used to encrypt the "local_pk" file.') ->end() ->scalarNode('ciphers') - ->info('A list of SSL/TLS ciphers separated by colons, commas or spaces (e.g. "RC3-SHA:TLS13-AES-128-GCM-SHA256"...)') + ->info('A list of TLS ciphers separated by colons, commas or spaces (e.g. "RC3-SHA:TLS13-AES-128-GCM-SHA256"...)') ->end() ->arrayNode('peer_fingerprint') ->info('Associative array: hashing algorithm => hash(es).') diff --git a/src/Symfony/Component/HttpClient/AmpHttpClient.php b/src/Symfony/Component/HttpClient/AmpHttpClient.php index 94d8fa414ee45..26b1977314deb 100644 --- a/src/Symfony/Component/HttpClient/AmpHttpClient.php +++ b/src/Symfony/Component/HttpClient/AmpHttpClient.php @@ -47,6 +47,10 @@ final class AmpHttpClient implements HttpClientInterface, LoggerAwareInterface, use HttpClientTrait; use LoggerAwareTrait; + public const OPTIONS_DEFAULTS = HttpClientInterface::OPTIONS_DEFAULTS + [ + 'crypto_method' => \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, + ]; + private array $defaultOptions = self::OPTIONS_DEFAULTS; private static array $emptyDefaults = self::OPTIONS_DEFAULTS; private AmpClientState $multi; diff --git a/src/Symfony/Component/HttpClient/CHANGELOG.md b/src/Symfony/Component/HttpClient/CHANGELOG.md index 7cd69191c5411..ae2111ac71f7e 100644 --- a/src/Symfony/Component/HttpClient/CHANGELOG.md +++ b/src/Symfony/Component/HttpClient/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 6.3 --- + * Add option `crypto_method` to set the minimum TLS version and make it default to v1.2 * Add `UriTemplateHttpClient` to use URI templates as specified in the RFC 6570 * Add `ServerSentEvent::getArrayData()` to get the Server-Sent Event's data decoded as an array when it's a JSON payload * Allow array of urls as `base_uri` option value in `RetryableHttpClient` to retry on a new url each time diff --git a/src/Symfony/Component/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php index 5c19ed0bf1e69..ccb2a5707e87e 100644 --- a/src/Symfony/Component/HttpClient/CurlHttpClient.php +++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php @@ -36,6 +36,10 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface, { use HttpClientTrait; + public const OPTIONS_DEFAULTS = HttpClientInterface::OPTIONS_DEFAULTS + [ + 'crypto_method' => \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, + ]; + private array $defaultOptions = self::OPTIONS_DEFAULTS + [ 'auth_ntlm' => null, // array|string - an array containing the username as first value, and optionally the // password as the second one; or string like username:password - enabling NTLM auth @@ -116,6 +120,12 @@ public function request(string $method, string $url, array $options = []): Respo \CURLOPT_SSLKEY => $options['local_pk'], \CURLOPT_KEYPASSWD => $options['passphrase'], \CURLOPT_CERTINFO => $options['capture_peer_cert_chain'], + \CURLOPT_SSLVERSION => match ($options['crypto_method']) { + \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT => \CURL_SSLVERSION_TLSv1_3, + \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT => \CURL_SSLVERSION_TLSv1_2, + \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT => \CURL_SSLVERSION_TLSv1_1, + \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT => \CURL_SSLVERSION_TLSv1_0, + }, ]; if (1.0 === (float) $options['http_version']) { diff --git a/src/Symfony/Component/HttpClient/HttpClientTrait.php b/src/Symfony/Component/HttpClient/HttpClientTrait.php index c767ca81aac01..48782153b4076 100644 --- a/src/Symfony/Component/HttpClient/HttpClientTrait.php +++ b/src/Symfony/Component/HttpClient/HttpClientTrait.php @@ -16,6 +16,7 @@ use Symfony\Component\HttpClient\Response\StreamableInterface; use Symfony\Component\HttpClient\Response\StreamWrapper; use Symfony\Component\Mime\MimeTypes; +use Symfony\Contracts\HttpClient\HttpClientInterface; /** * Provides the common logic from writing HttpClientInterface implementations. @@ -116,6 +117,15 @@ private static function prepareRequest(?string $method, ?string $url, array $opt $options['peer_fingerprint'] = self::normalizePeerFingerprint($options['peer_fingerprint']); } + if (isset($options['crypto_method']) && !\in_array($options['crypto_method'], [ + \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT, + \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT, + \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, + \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT, + ], true)) { + throw new InvalidArgumentException('Option "crypto_method" must be one of "STREAM_CRYPTO_METHOD_TLSv1_*_CLIENT".'); + } + // Validate on_progress if (isset($options['on_progress']) && !\is_callable($onProgress = $options['on_progress'])) { throw new InvalidArgumentException(sprintf('Option "on_progress" must be callable, "%s" given.', get_debug_type($onProgress))); diff --git a/src/Symfony/Component/HttpClient/Internal/AmpClientState.php b/src/Symfony/Component/HttpClient/Internal/AmpClientState.php index a83176629b9d3..539bde252a427 100644 --- a/src/Symfony/Component/HttpClient/Internal/AmpClientState.php +++ b/src/Symfony/Component/HttpClient/Internal/AmpClientState.php @@ -126,6 +126,7 @@ private function getClient(array $options): array 'ciphers' => $options['ciphers'], 'capture_peer_cert_chain' => $options['capture_peer_cert_chain'] || $options['peer_fingerprint'], 'proxy' => $options['proxy'], + 'crypto_method' => $options['crypto_method'], ]; $key = hash('xxh128', serialize($options)); @@ -141,6 +142,7 @@ private function getClient(array $options): array $options['local_cert'] && $context = $context->withCertificate(new Certificate($options['local_cert'], $options['local_pk'])); $options['ciphers'] && $context = $context->withCiphers($options['ciphers']); $options['capture_peer_cert_chain'] && $context = $context->withPeerCapturing(); + $options['crypto_method'] && $context = $context->withMinimumVersion($options['crypto_method']); $connector = $handleConnector = new class() implements Connector { public $connector; diff --git a/src/Symfony/Component/HttpClient/NativeHttpClient.php b/src/Symfony/Component/HttpClient/NativeHttpClient.php index e4a2b0c28c67e..c8f382efd7b6d 100644 --- a/src/Symfony/Component/HttpClient/NativeHttpClient.php +++ b/src/Symfony/Component/HttpClient/NativeHttpClient.php @@ -36,6 +36,10 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac use HttpClientTrait; use LoggerAwareTrait; + public const OPTIONS_DEFAULTS = HttpClientInterface::OPTIONS_DEFAULTS + [ + 'crypto_method' => \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, + ]; + private array $defaultOptions = self::OPTIONS_DEFAULTS; private static array $emptyDefaults = self::OPTIONS_DEFAULTS; @@ -198,6 +202,12 @@ public function request(string $method, string $url, array $options = []): Respo $options['timeout'] = min($options['max_duration'], $options['timeout']); } + switch ($cryptoMethod = $options['crypto_method']) { + case \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT: $cryptoMethod |= \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; + case \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT: $cryptoMethod |= \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; + case \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT: $cryptoMethod |= \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT; + } + $context = [ 'http' => [ 'protocol_version' => min($options['http_version'] ?: '1.1', '1.1'), @@ -224,6 +234,7 @@ public function request(string $method, string $url, array $options = []): Respo 'allow_self_signed' => (bool) $options['peer_fingerprint'], 'SNI_enabled' => true, 'disable_compression' => true, + 'crypto_method' => $cryptoMethod, ], static fn ($v) => null !== $v), 'socket' => [ 'bindto' => $options['bindto'], diff --git a/src/Symfony/Contracts/CHANGELOG.md b/src/Symfony/Contracts/CHANGELOG.md index d8dd36863bafa..d944ba18707b7 100644 --- a/src/Symfony/Contracts/CHANGELOG.md +++ b/src/Symfony/Contracts/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +3.3 +--- + + * Add option `crypto_method` to `HttpClientInterface` to define the minimum TLS version to accept + 3.2 --- diff --git a/src/Symfony/Contracts/HttpClient/HttpClientInterface.php b/src/Symfony/Contracts/HttpClient/HttpClientInterface.php index b148b19ac8199..59636258ff6e3 100644 --- a/src/Symfony/Contracts/HttpClient/HttpClientInterface.php +++ b/src/Symfony/Contracts/HttpClient/HttpClientInterface.php @@ -66,6 +66,7 @@ interface HttpClientInterface 'ciphers' => null, 'peer_fingerprint' => null, 'capture_peer_cert_chain' => false, + 'crypto_method' => \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, // STREAM_CRYPTO_METHOD_TLSv*_CLIENT - minimum TLS version 'extra' => [], // array - additional options that can be ignored if unsupported, unlike regular options ]; From 18df31b351da08a684509b5a41ef3166f84fbe5d Mon Sep 17 00:00:00 2001 From: MatTheCat Date: Wed, 10 May 2023 14:03:13 +0200 Subject: [PATCH 16/43] Sync `createTabs` from WebProfilerBundle --- .../Resources/assets/js/exception.js | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/src/Symfony/Component/ErrorHandler/Resources/assets/js/exception.js b/src/Symfony/Component/ErrorHandler/Resources/assets/js/exception.js index 95b8ea17197c9..22ce675dfb7d2 100644 --- a/src/Symfony/Component/ErrorHandler/Resources/assets/js/exception.js +++ b/src/Symfony/Component/ErrorHandler/Resources/assets/js/exception.js @@ -39,23 +39,31 @@ } (function createTabs() { + /* the accessibility options of this component have been defined according to: */ + /* www.w3.org/WAI/ARIA/apg/example-index/tabs/tabs-manual.html */ var tabGroups = document.querySelectorAll('.sf-tabs:not([data-processed=true])'); /* create the tab navigation for each group of tabs */ for (var i = 0; i < tabGroups.length; i++) { var tabs = tabGroups[i].querySelectorAll(':scope > .tab'); - var tabNavigation = document.createElement('ul'); + var tabNavigation = document.createElement('div'); tabNavigation.className = 'tab-navigation'; + tabNavigation.setAttribute('role', 'tablist'); var selectedTabId = 'tab-' + i + '-0'; /* select the first tab by default */ for (var j = 0; j < tabs.length; j++) { var tabId = 'tab-' + i + '-' + j; var tabTitle = tabs[j].querySelector('.tab-title').innerHTML; - var tabNavigationItem = document.createElement('li'); + var tabNavigationItem = document.createElement('button'); + addClass(tabNavigationItem, 'tab-control'); tabNavigationItem.setAttribute('data-tab-id', tabId); + tabNavigationItem.setAttribute('role', 'tab'); + tabNavigationItem.setAttribute('aria-controls', tabId); if (hasClass(tabs[j], 'active')) { selectedTabId = tabId; } - if (hasClass(tabs[j], 'disabled')) { addClass(tabNavigationItem, 'disabled'); } + if (hasClass(tabs[j], 'disabled')) { + addClass(tabNavigationItem, 'disabled'); + } tabNavigationItem.innerHTML = tabTitle; tabNavigation.appendChild(tabNavigationItem); @@ -69,24 +77,31 @@ /* display the active tab and add the 'click' event listeners */ for (i = 0; i < tabGroups.length; i++) { - tabNavigation = tabGroups[i].querySelectorAll(':scope >.tab-navigation li'); + tabNavigation = tabGroups[i].querySelectorAll(':scope > .tab-navigation .tab-control'); for (j = 0; j < tabNavigation.length; j++) { tabId = tabNavigation[j].getAttribute('data-tab-id'); - document.getElementById(tabId).querySelector('.tab-title').className = 'hidden'; + var tabPanel = document.getElementById(tabId); + tabPanel.setAttribute('role', 'tabpanel'); + tabPanel.setAttribute('aria-labelledby', tabId); + tabPanel.querySelector('.tab-title').className = 'hidden'; if (hasClass(tabNavigation[j], 'active')) { - document.getElementById(tabId).className = 'block'; + tabPanel.className = 'block'; + tabNavigation[j].setAttribute('aria-selected', 'true'); + tabNavigation[j].removeAttribute('tabindex'); } else { - document.getElementById(tabId).className = 'hidden'; + tabPanel.className = 'hidden'; + tabNavigation[j].removeAttribute('aria-selected'); + tabNavigation[j].setAttribute('tabindex', '-1'); } tabNavigation[j].addEventListener('click', function(e) { var activeTab = e.target || e.srcElement; /* needed because when the tab contains HTML contents, user can click */ - /* on any of those elements instead of their parent '
  • ' element */ - while (activeTab.tagName.toLowerCase() !== 'li') { + /* on any of those elements instead of their parent '