From 10bfe930bfb61b8c318e5c23551e009e7639110a Mon Sep 17 00:00:00 2001 From: Norbert Orzechowicz Date: Wed, 5 Aug 2026 19:21:51 +0200 Subject: [PATCH 1/2] feat(flow-php/symfony-telemetry-bundle): add flow_telemetry.enabled toggl --- .../bridges/symfony-telemetry-bundle.md | 24 +++ .../Compiler/ChannelLoggerPass.php | 9 + .../Compiler/FrameworkLoggerPass.php | 7 + .../Compiler/OTLPAvailabilityPass.php | 7 + .../TelemetryBundle/FlowTelemetryBundle.php | 72 +++++++- .../Tests/Context/ChannelLoggerContext.php | 46 +++++ .../Tests/Context/ConfigurationContext.php | 8 + .../Tests/Context/SymfonyContext.php | 41 +++++ .../Fixtures/CompiledIdCollectorPass.php | 60 +++++++ .../Integration/ChannelLoggerPassTest.php | 152 +++++++++++------ .../FlowTelemetryExtensionTest.php | 160 ++++++++++++++++++ .../Tests/Mother/ChannelLoggerMother.php | 48 ++++++ .../Compiler/ChannelLoggerPassTest.php | 102 ++++++----- .../Compiler/FrameworkLoggerPassTest.php | 50 ++++++ .../Compiler/OTLPAvailabilityPassTest.php | 43 +++++ .../DependencyInjection/ConfigurationTest.php | 42 +++++ 16 files changed, 763 insertions(+), 108 deletions(-) create mode 100644 src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Context/ChannelLoggerContext.php create mode 100644 src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Fixtures/CompiledIdCollectorPass.php create mode 100644 src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Mother/ChannelLoggerMother.php create mode 100644 src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/DependencyInjection/Compiler/FrameworkLoggerPassTest.php create mode 100644 src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/DependencyInjection/Compiler/OTLPAvailabilityPassTest.php diff --git a/documentation/components/bridges/symfony-telemetry-bundle.md b/documentation/components/bridges/symfony-telemetry-bundle.md index 26970acf65..a83bc4effc 100644 --- a/documentation/components/bridges/symfony-telemetry-bundle.md +++ b/documentation/components/bridges/symfony-telemetry-bundle.md @@ -32,6 +32,30 @@ This bundle integrates Flow PHP's Telemetry library with Symfony applications. I - **Configurable exporters** - Console, memory, void, or OTLP with multiple transport options - **Full Symfony configuration** - Configure everything through Symfony's config system +## Enabling and Disabling + +The bundle is enabled by default. `enabled: false` keeps `Flow\Telemetry\Telemetry` autowirable but removes +everything behind it — exporters, transports, resource detectors, instrumentation, profiler and the shutdown hook. + +```yaml +# config/packages/prod/flow_telemetry.yaml +flow_telemetry: + enabled: false +``` + +The surviving `Telemetry` uses void processors with sampling off: spans are non-recording, logs and metrics go +nowhere, `flush()` is a no-op. Named tracers, meters, loggers and `#[WithTelemetryChannel]` channel loggers are +still registered, so no injection point breaks. No Symfony service is decorated, and `framework_logger` / +`capture_framework_channels` are ignored — aliasing `logger` to a void logger would discard application logs. + +`flow.telemetry.enabled` is always defined as a container parameter, `true` or `false`. + +`enabled` is read at compile time, so `%env()%` is rejected — a placeholder would leave telemetry wired while you +believed it was off. Use a per-environment config file, or a parameter holding a literal bool. Environment +variables work everywhere else in the tree, including sections disabled mode drops. + +For a runtime switch that keeps services and only stops egress, use `exporters..enabled`. + ## Configuration Reference > Any `error_handler:` field that appears under a provider, processor, or `otlp` exporter references a name from the diff --git a/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/DependencyInjection/Compiler/ChannelLoggerPass.php b/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/DependencyInjection/Compiler/ChannelLoggerPass.php index 30063912cd..6612ee757c 100644 --- a/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/DependencyInjection/Compiler/ChannelLoggerPass.php +++ b/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/DependencyInjection/Compiler/ChannelLoggerPass.php @@ -58,6 +58,8 @@ final class ChannelLoggerPass implements CompilerPassInterface private const string CAPTURE_PARAMETER = 'flow.telemetry.capture_framework_channels'; + private const string ENABLED_PARAMETER = 'flow.telemetry.enabled'; + private const string ATTRIBUTE_TARGET_PARAMETER = 'flow.telemetry.channel_attribute_target'; private const string FRAMEWORK_TAG = 'monolog.logger'; @@ -119,6 +121,13 @@ private function camelize(string $channel): string private function capturesFrameworkChannels(ContainerBuilder $container): bool { + if ( + $container->hasParameter(self::ENABLED_PARAMETER) + && $container->getParameter(self::ENABLED_PARAMETER) !== true + ) { + return false; + } + if (!$container->hasParameter(self::CAPTURE_PARAMETER)) { return false; } diff --git a/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/DependencyInjection/Compiler/FrameworkLoggerPass.php b/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/DependencyInjection/Compiler/FrameworkLoggerPass.php index 385d59e3eb..255b5f12c6 100644 --- a/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/DependencyInjection/Compiler/FrameworkLoggerPass.php +++ b/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/DependencyInjection/Compiler/FrameworkLoggerPass.php @@ -17,6 +17,13 @@ final class FrameworkLoggerPass implements CompilerPassInterface public function process(ContainerBuilder $container): void { + if ( + $container->hasParameter('flow.telemetry.enabled') + && $container->getParameter('flow.telemetry.enabled') !== true + ) { + return; + } + $frameworkLogger = $container->hasParameter('flow.telemetry.framework_logger') ? $container->getParameter('flow.telemetry.framework_logger') : null; diff --git a/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/DependencyInjection/Compiler/OTLPAvailabilityPass.php b/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/DependencyInjection/Compiler/OTLPAvailabilityPass.php index 67ff94b510..4f8ad7d336 100644 --- a/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/DependencyInjection/Compiler/OTLPAvailabilityPass.php +++ b/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/DependencyInjection/Compiler/OTLPAvailabilityPass.php @@ -16,6 +16,13 @@ final class OTLPAvailabilityPass implements CompilerPassInterface public function process(ContainerBuilder $container): void { + if ( + $container->hasParameter('flow.telemetry.enabled') + && $container->getParameter('flow.telemetry.enabled') !== true + ) { + return; + } + $otlpAvailable = class_exists(self::OTLP_BRIDGE_CLASS); $container->setParameter('flow.telemetry.otlp_available', $otlpAvailable); diff --git a/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/FlowTelemetryBundle.php b/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/FlowTelemetryBundle.php index 4dc3d73fc5..e72484d03d 100644 --- a/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/FlowTelemetryBundle.php +++ b/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/FlowTelemetryBundle.php @@ -231,6 +231,10 @@ public function configure(DefinitionConfigurator $definition): void $definition ->rootNode() ->children() + ->booleanNode('enabled') + ->info('Enable the bundle. When false no flow.telemetry.* service is registered (default: true)') + ->defaultTrue() + ->end() ->arrayNode('resource') ->info( 'OpenTelemetry Resource configuration with automatic detection (https://opentelemetry.io/docs/specs/semconv/resource/)', @@ -919,19 +923,41 @@ public function configure(DefinitionConfigurator $definition): void } /** - * @param array{resource: array{detectors?: array{enabled?: bool, static?: array{cache?: array{enabled?: bool, path?: null|string}, os?: array{enabled?: bool}, host?: array{enabled?: bool}, service?: array{enabled?: bool}, deployment?: array{enabled?: bool}, git?: array{enabled?: bool, binary?: string, working_directory?: null|string}, environment?: array{enabled?: bool}}, dynamic?: array{process?: array{enabled?: bool}}}, custom?: array}, clock_service_id?: null|string, framework_logger?: null|string, capture_framework_channels?: bool, channel_attribute_target?: 'scope'|'signal'|'both', context_storage?: array{type?: string, service_id?: null|string}, propagator?: array{type?: string, service_id?: null|string}, exporters?: array>, error_handlers?: array>, tracer_provider?: array, meter_provider?: array, logger_provider?: array, instrumentation?: FlowTelemetryInstrumentationConfig, profiler?: array{enabled?: bool|null, capture_logs?: bool}, tracers?: array, signal?: array}}>, meters?: array, signal?: array}}>, loggers?: array, signal?: array}}>} $config + * @param array{enabled: bool, resource: array{detectors?: array{enabled?: bool, static?: array{cache?: array{enabled?: bool, path?: null|string}, os?: array{enabled?: bool}, host?: array{enabled?: bool}, service?: array{enabled?: bool}, deployment?: array{enabled?: bool}, git?: array{enabled?: bool, binary?: string, working_directory?: null|string}, environment?: array{enabled?: bool}}, dynamic?: array{process?: array{enabled?: bool}}}, custom?: array}, clock_service_id?: null|string, framework_logger?: null|string, capture_framework_channels?: bool, channel_attribute_target?: 'scope'|'signal'|'both', context_storage?: array{type?: string, service_id?: null|string}, propagator?: array{type?: string, service_id?: null|string}, exporters?: array>, error_handlers?: array>, tracer_provider?: array, meter_provider?: array, logger_provider?: array, instrumentation?: FlowTelemetryInstrumentationConfig, profiler?: array{enabled?: bool|null, capture_logs?: bool}, tracers?: array, signal?: array}}>, meters?: array, signal?: array}}>, loggers?: array, signal?: array}}>} $config */ #[Override] public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void { - $builder->setParameter('flow.telemetry.framework_logger', $config['framework_logger'] ?? null); - $builder->setParameter('flow.telemetry.capture_framework_channels', $config['capture_framework_channels'] ?? false); - $builder->setParameter('flow.telemetry.channel_attribute_target', $config['channel_attribute_target'] ?? 'both'); + if (!is_bool($config['enabled'])) { + throw new InvalidConfigurationException( + 'flow_telemetry.enabled is evaluated when the container is compiled, so environment variables ' + . 'are not supported. Use a per-environment config file (config/packages/prod/flow_telemetry.yaml) ' + . 'or a container parameter instead.', + ); + } + + $builder->resolveEnvPlaceholders($config); + + $builder->setParameter('flow.telemetry.enabled', $config['enabled']); $tracers = ($config['tracers'] ?? []) + ['default' => []]; $meters = ($config['meters'] ?? []) + ['default' => []]; $loggers = ($config['loggers'] ?? []) + ['default' => []]; + if (!$config['enabled']) { + $this->registerGlobalServices($config, $builder); + $this->registerNoOpTelemetry($config, $builder); + $this->registerTracers($tracers, $builder); + $this->registerMeters($meters, $builder); + $this->registerLoggers($loggers, $builder); + + return; + } + + $builder->setParameter('flow.telemetry.framework_logger', $config['framework_logger'] ?? null); + $builder->setParameter('flow.telemetry.capture_framework_channels', $config['capture_framework_channels'] ?? false); + $builder->setParameter('flow.telemetry.channel_attribute_target', $config['channel_attribute_target'] ?? 'both'); + $this->registerGlobalServices($config, $builder); $errorHandlers = $config['error_handlers'] ?? []; $this->registerErrorHandlers($errorHandlers, $builder); @@ -3496,6 +3522,44 @@ private function wrapExporterWithRuntimeToggle(string $serviceId, mixed $enabled /** * @param FlowTelemetryInstrumentationConfig $config */ + /** + * @param array{resource?: array{custom?: array}} $config + */ + private function registerNoOpTelemetry(array $config, ContainerBuilder $builder): void + { + $resource = new Definition(Resource::class); + $resource->setFactory([Resource::class, 'create']); + $resource->setArgument(0, $config['resource']['custom'] ?? []); + + $tracerProvider = new Definition(TracerProvider::class); + $tracerProvider->setArguments([ + new Definition(VoidSpanProcessor::class), + new Reference('flow.telemetry.clock'), + new Reference('flow.telemetry.context_storage'), + new Definition(AlwaysOffSampler::class), + ]); + + $meterProvider = new Definition(MeterProvider::class); + $meterProvider->setArguments([ + new Definition(VoidMetricProcessor::class), + new Reference('flow.telemetry.clock'), + ]); + + $loggerProvider = new Definition(LoggerProvider::class); + $loggerProvider->setArguments([ + new Definition(VoidLogProcessor::class), + new Reference('flow.telemetry.clock'), + new Reference('flow.telemetry.context_storage'), + ]); + + $telemetry = new Definition(Telemetry::class); + $telemetry->setArguments([$resource, $tracerProvider, $meterProvider, $loggerProvider]); + $telemetry->setPublic(true); + + $builder->setDefinition('flow.telemetry', $telemetry); + $builder->setAlias(Telemetry::class, 'flow.telemetry')->setPublic(true); + } + private function registerParameterOnlyInstrumentation(array $config, ContainerBuilder $builder): void { $httpClientConfig = $config['http_client'] ?? []; diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Context/ChannelLoggerContext.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Context/ChannelLoggerContext.php new file mode 100644 index 0000000000..9b57d80bfd --- /dev/null +++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Context/ChannelLoggerContext.php @@ -0,0 +1,46 @@ +setArgument(0, new Reference('logger')); + $definition->addTag('monolog.logger', ['channel' => $channel]); + $definition->setPublic(true); + + return $definition; + } + + /** + * @param class-string $class + */ + public function publicDefinition(string $class): Definition + { + $definition = new Definition($class); + $definition->setPublic(true); + + return $definition; + } + + /** + * @param class-string $class + */ + public function autoconfiguredConsumer(string $class): Definition + { + $definition = new Definition($class); + $definition->setAutowired(true); + $definition->setAutoconfigured(true); + $definition->setPublic(true); + + return $definition; + } +} diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Context/ConfigurationContext.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Context/ConfigurationContext.php index 938c2ec791..300a7fec02 100644 --- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Context/ConfigurationContext.php +++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Context/ConfigurationContext.php @@ -18,6 +18,14 @@ * @return array */ public function processConfig(array $config): array + { + return $this->processRawConfig($config); + } + + /** + * @return array + */ + public function processRawConfig(mixed $config): array { $extension = (new FlowTelemetryBundle())->getContainerExtension(); diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Context/SymfonyContext.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Context/SymfonyContext.php index 3cd48aed0b..4e94535e29 100644 --- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Context/SymfonyContext.php +++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Context/SymfonyContext.php @@ -41,6 +41,47 @@ public function bootKernel(array $options = []): TestKernel return $this->kernel; } + /** + * @return array + */ + public function fullyPopulatedTelemetryConfig(bool $enabled): array + { + return [ + 'enabled' => $enabled, + 'resource' => ['custom' => ['deployment.tenant' => '%env(default::FLOW_TELEMETRY_TEST_TENANT)%']], + 'exporters' => [ + 'memory' => ['memory' => null], + 'void' => ['void' => null], + 'otlp_http' => [ + 'enabled' => '%env(bool:FLOW_TELEMETRY_TEST_EXPORTERS_ENABLED)%', + 'otlp' => [ + 'transport' => [ + 'endpoint' => '%env(default::FLOW_TELEMETRY_TEST_OTLP_ENDPOINT)%', + ], + ], + ], + ], + 'tracer_provider' => ['processor' => ['type' => 'memory', 'exporter' => 'memory']], + 'instrumentation' => [ + 'http_kernel' => [ + 'enabled' => true, + 'trace_controller_resolution' => true, + 'trace_controller_arguments' => true, + 'trace_controller_argument_resolvers' => true, + ], + 'console' => ['enabled' => true], + 'messenger' => ['enabled' => true], + 'http_client' => ['enabled' => true], + 'psr18_client' => ['enabled' => true], + 'dbal' => ['enabled' => true], + 'cache' => ['enabled' => true], + ], + 'tracers' => ['app' => []], + 'meters' => ['app' => []], + 'loggers' => ['app' => []], + ]; + } + public function getContainer(): ContainerInterface { if ($this->kernel === null) { diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Fixtures/CompiledIdCollectorPass.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Fixtures/CompiledIdCollectorPass.php new file mode 100644 index 0000000000..7e81f6d1df --- /dev/null +++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Fixtures/CompiledIdCollectorPass.php @@ -0,0 +1,60 @@ + */ + public array $ids = []; + + /** @var array */ + public array $parameters = []; + + public function process(ContainerBuilder $container): void + { + $this->ids = array_merge(array_keys($container->getDefinitions()), array_keys($container->getAliases())); + + /** @var array $parameterNames */ + $parameterNames = array_keys($container->getParameterBag()->all()); + $this->parameters = $parameterNames; + } + + /** + * @return array + */ + public function flowTelemetryIds(): array + { + return array_values(array_filter( + $this->ids, + static fn(string $id): bool => ( + str_starts_with($id, 'flow.telemetry') + || str_ends_with($id, '.flow_telemetry') + || $id === Telemetry::class + ), + )); + } + + /** + * @return array + */ + public function flowTelemetryParameters(): array + { + return array_values(array_filter($this->parameters, static fn(string $name): bool => str_starts_with( + $name, + 'flow.telemetry', + ))); + } +} diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/ChannelLoggerPassTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/ChannelLoggerPassTest.php index bbf84848c6..770644f7de 100644 --- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/ChannelLoggerPassTest.php +++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/ChannelLoggerPassTest.php @@ -7,6 +7,7 @@ use Flow\Bridge\Symfony\TelemetryBundle\Attribute\WithTelemetryChannel; use Flow\Bridge\Symfony\TelemetryBundle\DependencyInjection\Compiler\ChannelLoggerPass; use Flow\Bridge\Symfony\TelemetryBundle\FlowTelemetryBundle; +use Flow\Bridge\Symfony\TelemetryBundle\Tests\Context\ChannelLoggerContext; use Flow\Bridge\Symfony\TelemetryBundle\Tests\Fixtures\Channel\AppChannelConsumer; use Flow\Bridge\Symfony\TelemetryBundle\Tests\Fixtures\Channel\EventsChannelConsumer; use Flow\Bridge\Symfony\TelemetryBundle\Tests\Fixtures\Channel\FrameworkChannelConsumer; @@ -16,8 +17,6 @@ use Flow\Telemetry\Logger\Logger; use PHPUnit\Framework\Attributes\CoversClass; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpKernel\Log\Logger as SymfonyDefaultLogger; #[CoversClass(FlowTelemetryBundle::class)] @@ -27,13 +26,17 @@ final class ChannelLoggerPassTest extends KernelTestCase { public function test_app_channel_attribute_binds_its_own_channel_logger(): void { + $channelContext = new ChannelLoggerContext(); + $this->bootKernel([ - 'config' => static function (TestKernel $kernel): void { + 'config' => static function (TestKernel $kernel) use ($channelContext): void { $kernel->addTestExtensionConfig('flow_telemetry', ['resource' => []]); - $kernel->addTestContainerConfigurator(static function (ContainerBuilder $container): void { + $kernel->addTestContainerConfigurator(static function (ContainerBuilder $container) use ( + $channelContext, + ): void { $container->setDefinition( 'test.app_consumer', - self::autoconfiguredConsumer(AppChannelConsumer::class), + $channelContext->autoconfiguredConsumer(AppChannelConsumer::class), ); }); }, @@ -47,13 +50,17 @@ public function test_app_channel_attribute_binds_its_own_channel_logger(): void public function test_channel_attribute_binds_the_channel_logger_to_an_autowired_consumer(): void { + $channelContext = new ChannelLoggerContext(); + $this->bootKernel([ - 'config' => static function (TestKernel $kernel): void { + 'config' => static function (TestKernel $kernel) use ($channelContext): void { $kernel->addTestExtensionConfig('flow_telemetry', ['resource' => []]); - $kernel->addTestContainerConfigurator(static function (ContainerBuilder $container): void { + $kernel->addTestContainerConfigurator(static function (ContainerBuilder $container) use ( + $channelContext, + ): void { $container->setDefinition( 'test.events_consumer', - self::autoconfiguredConsumer(EventsChannelConsumer::class), + $channelContext->autoconfiguredConsumer(EventsChannelConsumer::class), ); }); }, @@ -67,13 +74,17 @@ public function test_channel_attribute_binds_the_channel_logger_to_an_autowired_ public function test_channel_attribute_binds_the_native_logger_to_a_concrete_typehint(): void { + $channelContext = new ChannelLoggerContext(); + $this->bootKernel([ - 'config' => static function (TestKernel $kernel): void { + 'config' => static function (TestKernel $kernel) use ($channelContext): void { $kernel->addTestExtensionConfig('flow_telemetry', ['resource' => []]); - $kernel->addTestContainerConfigurator(static function (ContainerBuilder $container): void { + $kernel->addTestContainerConfigurator(static function (ContainerBuilder $container) use ( + $channelContext, + ): void { $container->setDefinition( 'test.native_consumer', - self::autoconfiguredConsumer(NativeChannelConsumer::class), + $channelContext->autoconfiguredConsumer(NativeChannelConsumer::class), ); }); }, @@ -87,13 +98,17 @@ public function test_channel_attribute_binds_the_native_logger_to_a_concrete_typ public function test_channel_logger_carries_the_log_channel_scope_attribute(): void { + $channelContext = new ChannelLoggerContext(); + $this->bootKernel([ - 'config' => static function (TestKernel $kernel): void { + 'config' => static function (TestKernel $kernel) use ($channelContext): void { $kernel->addTestExtensionConfig('flow_telemetry', ['resource' => []]); - $kernel->addTestContainerConfigurator(static function (ContainerBuilder $container): void { + $kernel->addTestContainerConfigurator(static function (ContainerBuilder $container) use ( + $channelContext, + ): void { $container->setDefinition( 'test.events_consumer', - self::autoconfiguredConsumer(EventsChannelConsumer::class), + $channelContext->autoconfiguredConsumer(EventsChannelConsumer::class), ); }); }, @@ -107,17 +122,21 @@ public function test_channel_logger_carries_the_log_channel_scope_attribute(): v public function test_named_argument_alias_resolves_to_the_channel_logger(): void { + $channelContext = new ChannelLoggerContext(); + $this->bootKernel([ - 'config' => static function (TestKernel $kernel): void { + 'config' => static function (TestKernel $kernel) use ($channelContext): void { $kernel->addTestExtensionConfig('flow_telemetry', ['resource' => []]); - $kernel->addTestContainerConfigurator(static function (ContainerBuilder $container): void { + $kernel->addTestContainerConfigurator(static function (ContainerBuilder $container) use ( + $channelContext, + ): void { $container->setDefinition( 'test.events_consumer', - self::autoconfiguredConsumer(EventsChannelConsumer::class), + $channelContext->autoconfiguredConsumer(EventsChannelConsumer::class), ); $container->setDefinition( 'test.named_arg_consumer', - self::autoconfiguredConsumer(NamedArgumentConsumer::class), + $channelContext->autoconfiguredConsumer(NamedArgumentConsumer::class), ); }); }, @@ -131,15 +150,19 @@ public function test_named_argument_alias_resolves_to_the_channel_logger(): void public function test_captures_a_framework_tagged_service_into_a_channel_logger(): void { + $channelContext = new ChannelLoggerContext(); + $this->bootKernel([ - 'config' => static function (TestKernel $kernel): void { + 'config' => static function (TestKernel $kernel) use ($channelContext): void { $kernel->addTestExtensionConfig('flow_telemetry', [ 'resource' => [], 'capture_framework_channels' => true, ]); - $kernel->addTestContainerConfigurator(static function (ContainerBuilder $container): void { - $container->setDefinition('logger', self::publicDefinition(SymfonyDefaultLogger::class)); - $container->setDefinition('test.framework_consumer', self::frameworkConsumer('events')); + $kernel->addTestContainerConfigurator(static function (ContainerBuilder $container) use ( + $channelContext, + ): void { + $container->setDefinition('logger', $channelContext->publicDefinition(SymfonyDefaultLogger::class)); + $container->setDefinition('test.framework_consumer', $channelContext->frameworkConsumer('events')); }); }, ]); @@ -152,15 +175,19 @@ public function test_captures_a_framework_tagged_service_into_a_channel_logger() public function test_does_not_capture_framework_channels_when_disabled(): void { + $channelContext = new ChannelLoggerContext(); + $this->bootKernel([ - 'config' => static function (TestKernel $kernel): void { + 'config' => static function (TestKernel $kernel) use ($channelContext): void { $kernel->addTestExtensionConfig('flow_telemetry', [ 'resource' => [], 'capture_framework_channels' => false, ]); - $kernel->addTestContainerConfigurator(static function (ContainerBuilder $container): void { - $container->setDefinition('logger', self::publicDefinition(SymfonyDefaultLogger::class)); - $container->setDefinition('test.framework_consumer', self::frameworkConsumer('events')); + $kernel->addTestContainerConfigurator(static function (ContainerBuilder $container) use ( + $channelContext, + ): void { + $container->setDefinition('logger', $channelContext->publicDefinition(SymfonyDefaultLogger::class)); + $container->setDefinition('test.framework_consumer', $channelContext->frameworkConsumer('events')); }); }, ]); @@ -172,37 +199,62 @@ public function test_does_not_capture_framework_channels_when_disabled(): void static::assertFalse($this->getContainer()->has('flow.telemetry.events.logger.psr3')); } - private static function frameworkConsumer(string $channel): Definition + public function test_channel_attribute_still_autowires_when_telemetry_is_disabled(): void { - $definition = new Definition(FrameworkChannelConsumer::class); - $definition->setArgument(0, new Reference('logger')); - $definition->addTag('monolog.logger', ['channel' => $channel]); - $definition->setPublic(true); + $channelContext = new ChannelLoggerContext(); - return $definition; - } + $this->bootKernel([ + 'config' => static function (TestKernel $kernel) use ($channelContext): void { + $kernel->addTestExtensionConfig('flow_telemetry', ['enabled' => false, 'resource' => []]); + $kernel->addTestContainerConfigurator(static function (ContainerBuilder $container) use ( + $channelContext, + ): void { + $container->setDefinition( + 'test.app_consumer', + $channelContext->autoconfiguredConsumer(AppChannelConsumer::class), + ); + $container->setDefinition( + 'test.native_consumer', + $channelContext->autoconfiguredConsumer(NativeChannelConsumer::class), + ); + }); + }, + ]); - /** - * @param class-string $class - */ - private static function publicDefinition(string $class): Definition - { - $definition = new Definition($class); - $definition->setPublic(true); + $psrConsumer = $this->getContainer()->get('test.app_consumer'); + $nativeConsumer = $this->getContainer()->get('test.native_consumer'); - return $definition; + static::assertInstanceOf(AppChannelConsumer::class, $psrConsumer); + static::assertSame($this->getContainer()->get('flow.telemetry.app.logger.psr3'), $psrConsumer->logger); + + static::assertInstanceOf(NativeChannelConsumer::class, $nativeConsumer); + static::assertSame($this->getContainer()->get('flow.telemetry.events.logger'), $nativeConsumer->logger); } - /** - * @param class-string $class - */ - private static function autoconfiguredConsumer(string $class): Definition + public function test_framework_channels_are_never_captured_when_telemetry_is_disabled(): void { - $definition = new Definition($class); - $definition->setAutowired(true); - $definition->setAutoconfigured(true); - $definition->setPublic(true); + $channelContext = new ChannelLoggerContext(); + + $this->bootKernel([ + 'config' => static function (TestKernel $kernel) use ($channelContext): void { + $kernel->addTestExtensionConfig('flow_telemetry', [ + 'enabled' => false, + 'resource' => [], + 'capture_framework_channels' => true, + ]); + $kernel->addTestContainerConfigurator(static function (ContainerBuilder $container) use ( + $channelContext, + ): void { + $container->setDefinition('logger', $channelContext->publicDefinition(SymfonyDefaultLogger::class)); + $container->setDefinition('test.framework_consumer', $channelContext->frameworkConsumer('events')); + }); + }, + ]); - return $definition; + $consumer = $this->getContainer()->get('test.framework_consumer'); + + static::assertInstanceOf(FrameworkChannelConsumer::class, $consumer); + static::assertInstanceOf(SymfonyDefaultLogger::class, $consumer->logger); + static::assertFalse($this->getContainer()->has('flow.telemetry.events.logger.psr3')); } } diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/FlowTelemetryExtensionTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/FlowTelemetryExtensionTest.php index f1d247bbb3..e5f02f7201 100644 --- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/FlowTelemetryExtensionTest.php +++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/FlowTelemetryExtensionTest.php @@ -12,6 +12,7 @@ use Flow\Bridge\Symfony\TelemetryBundle\Instrumentation\Messenger\MessengerFlushSubscriber; use Flow\Bridge\Symfony\TelemetryBundle\Propagation\TraceContextProvider; use Flow\Bridge\Symfony\TelemetryBundle\Routing\TraceContextUrlGenerator; +use Flow\Bridge\Symfony\TelemetryBundle\Tests\Fixtures\CompiledIdCollectorPass; use Flow\Bridge\Symfony\TelemetryBundle\Tests\Fixtures\Telemetry\SpySpanProcessor; use Flow\Bridge\Symfony\TelemetryBundle\Tests\Fixtures\TestKernel; use Flow\Bridge\Telemetry\OTLP\Exporter\OTLPExporter; @@ -54,8 +55,12 @@ use Flow\Telemetry\Tracer\Sampler\AttributeMatchingSampler; use Flow\Telemetry\Tracer\Sampler\ParentBasedSampler; use Flow\Telemetry\Tracer\Sampler\TraceIdRatioBasedSampler; +use Override; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\TestWith; +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; @@ -67,6 +72,7 @@ use function interface_exists; use function is_file; use function random_bytes; +use function restore_exception_handler; use function sys_get_temp_dir; use function uniqid; use function unlink; @@ -76,6 +82,13 @@ #[CoversClass(FrameworkLoggerPass::class)] final class FlowTelemetryExtensionTest extends KernelTestCase { + #[Override] + protected function tearDown(): void + { + restore_exception_handler(); + parent::tearDown(); + } + public function test_auto_alias_when_logger_service_is_symfony_default(): void { $this->bootKernel([ @@ -1713,4 +1726,151 @@ public function test_unknown_error_handler_reference_throws(): void }, ]); } + + public function test_fully_populated_config_wires_telemetry_when_enabled(): void + { + $collector = new CompiledIdCollectorPass(); + + $this->bootKernel([ + 'config' => function (TestKernel $kernel) use ($collector): void { + $kernel->addTestBundle(FrameworkBundle::class); + $kernel->addTestExtensionConfig('framework', [ + 'router' => ['utf8' => true, 'resource' => __DIR__ . '/../Fixtures/config/routes.php'], + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'http_client' => ['default_options' => []], + 'messenger' => [ + 'default_bus' => 'messenger.bus.default', + 'buses' => ['messenger.bus.default' => null], + ], + ]); + $kernel->addTestExtensionConfig( + 'flow_telemetry', + $this->symfonyContext()->fullyPopulatedTelemetryConfig(true), + ); + $kernel->addTestContainerConfigurator(static function (ContainerBuilder $container) use ( + $collector, + ): void { + $container->addCompilerPass($collector, PassConfig::TYPE_AFTER_REMOVING, -1024); + }); + }, + ]); + + static::assertTrue($this->getContainer()->getParameter('flow.telemetry.enabled')); + static::assertTrue($this->getContainer()->has(Telemetry::class)); + + static::assertNotSame([], $collector->flowTelemetryIds()); + static::assertContains('argument_resolver.flow_telemetry', $collector->ids); + static::assertContains('controller_resolver.flow_telemetry', $collector->ids); + static::assertContains('flow.telemetry.messenger.middleware', $collector->ids); + } + + public function test_fully_populated_config_keeps_only_the_contract_when_disabled(): void + { + $collector = new CompiledIdCollectorPass(); + + $this->bootKernel([ + 'config' => function (TestKernel $kernel) use ($collector): void { + $kernel->addTestBundle(FrameworkBundle::class); + $kernel->addTestExtensionConfig('framework', [ + 'router' => ['utf8' => true, 'resource' => __DIR__ . '/../Fixtures/config/routes.php'], + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'http_client' => ['default_options' => []], + 'messenger' => [ + 'default_bus' => 'messenger.bus.default', + 'buses' => ['messenger.bus.default' => null], + ], + ]); + $kernel->addTestExtensionConfig( + 'flow_telemetry', + $this->symfonyContext()->fullyPopulatedTelemetryConfig(false), + ); + $kernel->addTestContainerConfigurator(static function (ContainerBuilder $container) use ( + $collector, + ): void { + $container->addCompilerPass($collector, PassConfig::TYPE_AFTER_REMOVING, -1024); + }); + }, + ]); + + static::assertFalse($this->getContainer()->getParameter('flow.telemetry.enabled')); + + static::assertEqualsCanonicalizing( + [ + 'flow.telemetry', + Telemetry::class, + 'flow.telemetry.clock', + 'flow.telemetry.context_storage', + 'flow.telemetry.psr3.log_record_converter', + 'flow.telemetry.default.tracer', + 'flow.telemetry.app.tracer', + 'flow.telemetry.default.meter', + 'flow.telemetry.app.meter', + 'flow.telemetry.default.logger', + 'flow.telemetry.default.logger.psr3', + 'flow.telemetry.app.logger', + 'flow.telemetry.app.logger.psr3', + ], + $collector->flowTelemetryIds(), + ); + static::assertSame(['flow.telemetry.enabled'], $collector->flowTelemetryParameters()); + + static::assertNotContains('argument_resolver.flow_telemetry', $collector->ids); + static::assertNotContains('controller_resolver.flow_telemetry', $collector->ids); + static::assertNotContains('flow.telemetry.messenger.middleware', $collector->ids); + } + + public function test_disabled_telemetry_is_autowirable_and_records_nothing(): void + { + $this->bootKernel([ + 'config' => function (TestKernel $kernel): void { + $kernel->addTestExtensionConfig( + 'flow_telemetry', + $this->symfonyContext()->fullyPopulatedTelemetryConfig(false), + ); + }, + ]); + + $telemetry = $this->symfonyContext()->getService(Telemetry::class, Telemetry::class); + + static::assertFalse($telemetry->tracer('app')->span('work')->isRecording()); + + $telemetry->logger('app')->info('this must go nowhere'); + $telemetry->meter('app')->createCounter('things')->add(1); + + static::assertTrue($telemetry->flush()); + } + + public function test_env_placeholders_in_skipped_sections_do_not_break_the_dumped_container(): void + { + $this->bootKernel([ + 'config' => static function (TestKernel $kernel): void { + $kernel->addTestExtensionConfig('flow_telemetry', [ + 'resource' => [], + 'profiler' => [ + 'enabled' => false, + 'capture_logs' => '%env(bool:FLOW_TELEMETRY_TEST_PROFILER_CAPTURE)%', + ], + ]); + }, + ]); + + static::assertTrue($this->getContainer()->getParameter('flow.telemetry.enabled')); + } + + public function test_env_placeholder_for_enabled_is_rejected(): void + { + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage('flow_telemetry.enabled is evaluated when the container is compiled'); + + $this->bootKernel([ + 'config' => static function (TestKernel $kernel): void { + $kernel->addTestExtensionConfig('flow_telemetry', [ + 'resource' => [], + 'enabled' => '%env(bool:FLOW_TELEMETRY_ENABLED)%', + ]); + }, + ]); + } } diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Mother/ChannelLoggerMother.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Mother/ChannelLoggerMother.php new file mode 100644 index 0000000000..6598d79b37 --- /dev/null +++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Mother/ChannelLoggerMother.php @@ -0,0 +1,48 @@ +setParameter('flow.telemetry.capture_framework_channels', true); + $consumer = new Definition(); + $consumer->addTag('monolog.logger', ['channel' => $channel]); + $container->setDefinition('framework.consumer', $consumer); + + return $container; + } + + public static function taggedConsumerContainer(string $channel): ContainerBuilder + { + $container = new ContainerBuilder(); + $consumer = new Definition(); + $consumer->addTag(ChannelLoggerPass::TAG, ['channel' => $channel]); + $container->setDefinition('consumer', $consumer); + + return $container; + } + + /** + * @param class-string $type + */ + public static function loggerBinding(Definition $definition, string $type = LoggerInterface::class): Reference + { + $binding = type_instance_of(BoundArgument::class)->assert($definition->getBindings()[$type] ?? null); + + return type_instance_of(Reference::class)->assert($binding->getValues()[0] ?? null); + } +} diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/DependencyInjection/Compiler/ChannelLoggerPassTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/DependencyInjection/Compiler/ChannelLoggerPassTest.php index 99d03ee67d..3f81f11cc9 100644 --- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/DependencyInjection/Compiler/ChannelLoggerPassTest.php +++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/DependencyInjection/Compiler/ChannelLoggerPassTest.php @@ -6,12 +6,12 @@ use Flow\Bridge\Symfony\TelemetryBundle\DependencyInjection\Compiler\ChannelLoggerPass; use Flow\Bridge\Symfony\TelemetryBundle\Exception\RuntimeException; +use Flow\Bridge\Symfony\TelemetryBundle\Tests\Mother\ChannelLoggerMother; use Flow\Telemetry\Attributes; use Flow\Telemetry\Logger\Logger; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; -use Symfony\Component\DependencyInjection\Argument\BoundArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; @@ -22,20 +22,20 @@ final class ChannelLoggerPassTest extends TestCase { public function test_app_channel_is_synthesized_like_any_other_channel(): void { - $container = $this->containerWithTaggedConsumer('app'); + $container = ChannelLoggerMother::taggedConsumerContainer('app'); (new ChannelLoggerPass())->process($container); static::assertTrue($container->hasDefinition('flow.telemetry.app.logger')); static::assertSame( 'flow.telemetry.app.logger.psr3', - (string) $this->loggerBinding($container->getDefinition('consumer')), + (string) ChannelLoggerMother::loggerBinding($container->getDefinition('consumer')), ); } public function test_default_channel_reuses_the_always_present_default_logger(): void { - $container = $this->containerWithTaggedConsumer('default'); + $container = ChannelLoggerMother::taggedConsumerContainer('default'); $existing = new Definition(Logger::class); $container->setDefinition('flow.telemetry.default.logger', $existing); $container->setDefinition('flow.telemetry.default.logger.psr3', new Definition(Logger::class)); @@ -45,49 +45,49 @@ public function test_default_channel_reuses_the_always_present_default_logger(): static::assertSame($existing, $container->getDefinition('flow.telemetry.default.logger')); static::assertSame( 'flow.telemetry.default.logger.psr3', - (string) $this->loggerBinding($container->getDefinition('consumer')), + (string) ChannelLoggerMother::loggerBinding($container->getDefinition('consumer')), ); } public function test_binds_channel_logger_to_tagged_service(): void { - $container = $this->containerWithTaggedConsumer('events'); + $container = ChannelLoggerMother::taggedConsumerContainer('events'); (new ChannelLoggerPass())->process($container); static::assertSame( 'flow.telemetry.events.logger.psr3', - (string) $this->loggerBinding($container->getDefinition('consumer')), + (string) ChannelLoggerMother::loggerBinding($container->getDefinition('consumer')), ); } public function test_binds_native_logger_to_tagged_service(): void { - $container = $this->containerWithTaggedConsumer('events'); + $container = ChannelLoggerMother::taggedConsumerContainer('events'); (new ChannelLoggerPass())->process($container); static::assertSame( 'flow.telemetry.events.logger', - (string) $this->loggerBinding($container->getDefinition('consumer'), Logger::class), + (string) ChannelLoggerMother::loggerBinding($container->getDefinition('consumer'), Logger::class), ); } public function test_app_channel_binds_its_own_native_logger(): void { - $container = $this->containerWithTaggedConsumer('app'); + $container = ChannelLoggerMother::taggedConsumerContainer('app'); (new ChannelLoggerPass())->process($container); static::assertSame( 'flow.telemetry.app.logger', - (string) $this->loggerBinding($container->getDefinition('consumer'), Logger::class), + (string) ChannelLoggerMother::loggerBinding($container->getDefinition('consumer'), Logger::class), ); } public function test_registers_native_logger_named_argument_alias(): void { - $container = $this->containerWithTaggedConsumer('events'); + $container = ChannelLoggerMother::taggedConsumerContainer('events'); (new ChannelLoggerPass())->process($container); @@ -98,7 +98,7 @@ public function test_registers_native_logger_named_argument_alias(): void public function test_does_not_override_a_declared_logger(): void { - $container = $this->containerWithTaggedConsumer('events'); + $container = ChannelLoggerMother::taggedConsumerContainer('events'); $declared = new Definition(Logger::class); $container->setDefinition('flow.telemetry.events.logger', $declared); $container->setDefinition('flow.telemetry.events.logger.psr3', new Definition(Logger::class)); @@ -110,7 +110,7 @@ public function test_does_not_override_a_declared_logger(): void public function test_empty_channel_attribute_throws(): void { - $container = $this->containerWithTaggedConsumer(''); + $container = ChannelLoggerMother::taggedConsumerContainer(''); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('missing the required non-empty "channel" attribute'); @@ -120,7 +120,7 @@ public function test_empty_channel_attribute_throws(): void public function test_channel_parameter_resolving_to_empty_throws(): void { - $container = $this->containerWithTaggedConsumer('%flow.test.channel%'); + $container = ChannelLoggerMother::taggedConsumerContainer('%flow.test.channel%'); $container->setParameter('flow.test.channel', ''); $this->expectException(RuntimeException::class); @@ -144,7 +144,7 @@ public function test_missing_channel_attribute_throws(): void public function test_registers_camel_cased_named_argument_alias(): void { - $container = $this->containerWithTaggedConsumer('http_client'); + $container = ChannelLoggerMother::taggedConsumerContainer('http_client'); (new ChannelLoggerPass())->process($container); @@ -155,7 +155,7 @@ public function test_registers_camel_cased_named_argument_alias(): void public function test_rewrites_explicit_logger_reference_in_arguments(): void { - $container = $this->containerWithTaggedConsumer('events'); + $container = ChannelLoggerMother::taggedConsumerContainer('events'); $container->getDefinition('consumer')->setArgument(0, new Reference('logger')); (new ChannelLoggerPass())->process($container); @@ -168,7 +168,7 @@ public function test_rewrites_explicit_logger_reference_in_arguments(): void public function test_synthesizes_channel_logger_with_log_channel_on_both_scope_and_signal_by_default(): void { - $container = $this->containerWithTaggedConsumer('events'); + $container = ChannelLoggerMother::taggedConsumerContainer('events'); (new ChannelLoggerPass())->process($container); @@ -189,7 +189,7 @@ public function test_synthesizes_channel_logger_with_log_channel_on_both_scope_a public function test_channel_attribute_target_scope_places_log_channel_on_scope_only(): void { - $container = $this->containerWithTaggedConsumer('events'); + $container = ChannelLoggerMother::taggedConsumerContainer('events'); $container->setParameter('flow.telemetry.channel_attribute_target', 'scope'); (new ChannelLoggerPass())->process($container); @@ -205,7 +205,7 @@ public function test_channel_attribute_target_scope_places_log_channel_on_scope_ public function test_channel_attribute_target_signal_places_log_channel_on_signal_only(): void { - $container = $this->containerWithTaggedConsumer('events'); + $container = ChannelLoggerMother::taggedConsumerContainer('events'); $container->setParameter('flow.telemetry.channel_attribute_target', 'signal'); (new ChannelLoggerPass())->process($container); @@ -221,19 +221,19 @@ public function test_channel_attribute_target_signal_places_log_channel_on_signa public function test_captures_framework_monolog_logger_channel(): void { - $container = $this->containerWithFrameworkConsumer('request'); + $container = ChannelLoggerMother::frameworkConsumerContainer('request'); (new ChannelLoggerPass())->process($container); static::assertSame( 'flow.telemetry.request.logger.psr3', - (string) $this->loggerBinding($container->getDefinition('framework.consumer')), + (string) ChannelLoggerMother::loggerBinding($container->getDefinition('framework.consumer')), ); } public function test_does_not_capture_framework_channels_when_disabled(): void { - $container = $this->containerWithFrameworkConsumer('request'); + $container = ChannelLoggerMother::frameworkConsumerContainer('request'); $container->setParameter('flow.telemetry.capture_framework_channels', false); $container->getDefinition('framework.consumer')->setArgument(0, new Reference('logger')); @@ -259,13 +259,13 @@ public function test_explicit_channel_takes_precedence_over_framework_channel(): static::assertSame( 'flow.telemetry.events.logger.psr3', - (string) $this->loggerBinding($container->getDefinition('consumer')), + (string) ChannelLoggerMother::loggerBinding($container->getDefinition('consumer')), ); } public function test_preserves_invalid_reference_behavior_when_rewriting(): void { - $container = $this->containerWithFrameworkConsumer('router'); + $container = ChannelLoggerMother::frameworkConsumerContainer('router'); $container->getDefinition('framework.consumer')->setArgument( 0, new Reference('logger', ContainerInterface::IGNORE_ON_INVALID_REFERENCE), @@ -282,7 +282,7 @@ public function test_preserves_invalid_reference_behavior_when_rewriting(): void public function test_registers_named_argument_alias_for_framework_channel(): void { - $container = $this->containerWithFrameworkConsumer('http_client'); + $container = ChannelLoggerMother::frameworkConsumerContainer('http_client'); (new ChannelLoggerPass())->process($container); @@ -293,7 +293,7 @@ public function test_registers_named_argument_alias_for_framework_channel(): voi public function test_rewrites_logger_reference_in_method_call(): void { - $container = $this->containerWithFrameworkConsumer('cache'); + $container = ChannelLoggerMother::frameworkConsumerContainer('cache'); $container->getDefinition('framework.consumer')->addMethodCall('setLogger', [new Reference('logger')]); (new ChannelLoggerPass())->process($container); @@ -312,7 +312,7 @@ public function test_rewrites_logger_reference_in_method_call(): void public function test_skips_framework_tag_whose_channel_resolves_to_empty(): void { - $container = $this->containerWithFrameworkConsumer('%flow.test.channel%'); + $container = ChannelLoggerMother::frameworkConsumerContainer('%flow.test.channel%'); $container->setParameter('flow.test.channel', ''); $container->getDefinition('framework.consumer')->setArgument(0, new Reference('logger')); @@ -335,38 +335,32 @@ public function test_skips_framework_tag_without_a_channel(): void static::assertSame('logger', (string) $container->getDefinition('framework.consumer')->getArgument(0)); } - private function containerWithFrameworkConsumer(string $channel): ContainerBuilder + public function test_still_routes_explicitly_tagged_services_when_telemetry_is_disabled(): void { - $container = new ContainerBuilder(); - $container->setParameter('flow.telemetry.capture_framework_channels', true); - $consumer = new Definition(); - $consumer->addTag('monolog.logger', ['channel' => $channel]); - $container->setDefinition('framework.consumer', $consumer); - - return $container; - } + $container = ChannelLoggerMother::taggedConsumerContainer('events'); + $container->setParameter('flow.telemetry.enabled', false); - private function containerWithTaggedConsumer(string $channel): ContainerBuilder - { - $container = new ContainerBuilder(); - $consumer = new Definition(); - $consumer->addTag(ChannelLoggerPass::TAG, ['channel' => $channel]); - $container->setDefinition('consumer', $consumer); + (new ChannelLoggerPass())->process($container); - return $container; + static::assertTrue($container->hasDefinition('flow.telemetry.events.logger')); + static::assertSame( + 'flow.telemetry.events.logger.psr3', + (string) ChannelLoggerMother::loggerBinding($container->getDefinition('consumer')), + ); } - /** - * @param class-string $type - */ - private function loggerBinding(Definition $definition, string $type = LoggerInterface::class): Reference + public function test_does_not_capture_framework_channels_when_telemetry_is_disabled(): void { - $binding = $definition->getBindings()[$type]; - self::assertInstanceOf(BoundArgument::class, $binding); - // @mago-expect analysis:mixed-assignment - $value = $binding->getValues()[0]; - self::assertInstanceOf(Reference::class, $value); + $container = ChannelLoggerMother::frameworkConsumerContainer('request'); + $container->setParameter('flow.telemetry.enabled', false); + $container->getDefinition('framework.consumer')->setArgument(0, new Reference('logger')); + + (new ChannelLoggerPass())->process($container); - return $value; + static::assertSame('logger', (string) $container->getDefinition('framework.consumer')->getArgument(0)); + static::assertArrayNotHasKey( + LoggerInterface::class, + $container->getDefinition('framework.consumer')->getBindings(), + ); } } diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/DependencyInjection/Compiler/FrameworkLoggerPassTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/DependencyInjection/Compiler/FrameworkLoggerPassTest.php new file mode 100644 index 0000000000..32d62632cd --- /dev/null +++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/DependencyInjection/Compiler/FrameworkLoggerPassTest.php @@ -0,0 +1,50 @@ +setParameter('flow.telemetry.enabled', false); + $container->setDefinition('logger', new Definition(SymfonyDefaultLogger::class)); + + (new FrameworkLoggerPass())->process($container); + + static::assertFalse($container->hasAlias('logger')); + static::assertSame(SymfonyDefaultLogger::class, $container->getDefinition('logger')->getClass()); + } + + public function test_aliases_symfony_logger_when_telemetry_is_enabled(): void + { + $container = new ContainerBuilder(); + $container->setParameter('flow.telemetry.enabled', true); + $container->setDefinition('logger', new Definition(SymfonyDefaultLogger::class)); + + (new FrameworkLoggerPass())->process($container); + + static::assertTrue($container->hasAlias('logger')); + static::assertSame('flow.telemetry.default.logger.psr3', (string) $container->getAlias('logger')); + } + + public function test_aliases_symfony_logger_when_enabled_parameter_is_absent(): void + { + $container = new ContainerBuilder(); + $container->setDefinition('logger', new Definition(SymfonyDefaultLogger::class)); + + (new FrameworkLoggerPass())->process($container); + + static::assertTrue($container->hasAlias('logger')); + } +} diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/DependencyInjection/Compiler/OTLPAvailabilityPassTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/DependencyInjection/Compiler/OTLPAvailabilityPassTest.php new file mode 100644 index 0000000000..58a417a5c1 --- /dev/null +++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/DependencyInjection/Compiler/OTLPAvailabilityPassTest.php @@ -0,0 +1,43 @@ +setParameter('flow.telemetry.enabled', false); + + (new OTLPAvailabilityPass())->process($container); + + static::assertFalse($container->hasParameter('flow.telemetry.otlp_available')); + } + + public function test_sets_otlp_available_when_telemetry_is_enabled(): void + { + $container = new ContainerBuilder(); + $container->setParameter('flow.telemetry.enabled', true); + + (new OTLPAvailabilityPass())->process($container); + + static::assertTrue($container->hasParameter('flow.telemetry.otlp_available')); + } + + public function test_sets_otlp_available_when_enabled_parameter_is_absent(): void + { + $container = new ContainerBuilder(); + + (new OTLPAvailabilityPass())->process($container); + + static::assertTrue($container->hasParameter('flow.telemetry.otlp_available')); + } +} diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/DependencyInjection/ConfigurationTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/DependencyInjection/ConfigurationTest.php index 87224b9a42..d95e429556 100644 --- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/DependencyInjection/ConfigurationTest.php +++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/DependencyInjection/ConfigurationTest.php @@ -8,6 +8,7 @@ use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; use function sprintf; @@ -941,4 +942,45 @@ public function test_messenger_metrics_duration_unit_option_was_removed(): void ], ]); } + + public function test_enabled_defaults_to_true_when_omitted(): void + { + static::assertTrue($this->context->processConfig(['resource' => []])['enabled']); + } + + public function test_enabled_can_be_disabled(): void + { + static::assertFalse($this->context->processConfig(['resource' => [], 'enabled' => false])['enabled']); + } + + public function test_root_disabled_does_not_conflict_with_nested_enabled(): void + { + $config = $this->context->processConfig([ + 'resource' => [], + 'enabled' => false, + 'instrumentation' => ['http_kernel' => ['enabled' => true]], + ]); + + static::assertFalse($config['enabled']); + static::assertTrue($config['instrumentation']['http_kernel']['enabled']); + } + + public function test_nested_config_is_still_validated_when_root_is_disabled(): void + { + $this->expectException(InvalidConfigurationException::class); + + $this->context->processConfig([ + 'resource' => [], + 'enabled' => false, + 'instrumentation' => ['http_kernel' => ['route_naming' => 'not_a_valid_strategy']], + ]); + } + + public function test_root_shorthand_false_is_rejected(): void + { + $this->expectException(InvalidTypeException::class); + $this->expectExceptionMessage('Invalid type for path "flow_telemetry". Expected "array", but got "bool"'); + + $this->context->processRawConfig(false); + } } From 6d6a71ee05de9cc1c243ea9319e1fee54afad672 Mon Sep 17 00:00:00 2001 From: Norbert Orzechowicz Date: Wed, 5 Aug 2026 20:36:09 +0200 Subject: [PATCH 2/2] fix(flow-php/symfony-telemetry-bundle): use string env processor in test fixtur --- composer.lock | 330 +++++++++--------- .../Tests/Context/SymfonyContext.php | 2 +- tools/box/composer.lock | 74 ++-- tools/infection/composer.lock | 117 ++++--- tools/phpbench/composer.lock | 48 +-- tools/phpunit/composer.lock | 12 +- 6 files changed, 292 insertions(+), 291 deletions(-) diff --git a/composer.lock b/composer.lock index 66150be97a..fbf2bba252 100644 --- a/composer.lock +++ b/composer.lock @@ -717,16 +717,16 @@ }, { "name": "google/apiclient-services", - "version": "v0.452.0", + "version": "v0.453.0", "source": { "type": "git", "url": "https://github.com/googleapis/google-api-php-client-services.git", - "reference": "2b86a7203ffd6520968ec568d58131ed380a59e1" + "reference": "9eeab572613c0b533f8268e5df0954a63100061d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/2b86a7203ffd6520968ec568d58131ed380a59e1", - "reference": "2b86a7203ffd6520968ec568d58131ed380a59e1", + "url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/9eeab572613c0b533f8268e5df0954a63100061d", + "reference": "9eeab572613c0b533f8268e5df0954a63100061d", "shasum": "" }, "require": { @@ -755,9 +755,9 @@ ], "support": { "issues": "https://github.com/googleapis/google-api-php-client-services/issues", - "source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.452.0" + "source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.453.0" }, - "time": "2026-07-27T01:28:26+00:00" + "time": "2026-08-03T01:20:25+00:00" }, { "name": "google/auth", @@ -2079,16 +2079,16 @@ }, { "name": "symfony/config", - "version": "v7.4.14", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "7b665e443381ea7c4db03eb03b4bf79ea2b020eb" + "reference": "b18e33881ef402ad940f36e85935420624009bf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/7b665e443381ea7c4db03eb03b4bf79ea2b020eb", - "reference": "7b665e443381ea7c4db03eb03b4bf79ea2b020eb", + "url": "https://api.github.com/repos/symfony/config/zipball/b18e33881ef402ad940f36e85935420624009bf4", + "reference": "b18e33881ef402ad940f36e85935420624009bf4", "shasum": "" }, "require": { @@ -2134,7 +2134,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v7.4.14" + "source": "https://github.com/symfony/config/tree/v7.4.15" }, "funding": [ { @@ -2154,20 +2154,20 @@ "type": "tidelift" } ], - "time": "2026-06-09T07:51:57+00:00" + "time": "2026-07-22T12:54:40+00:00" }, { "name": "symfony/console", - "version": "v7.4.14", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "92f58bc4bf97a92ed1b9f367f0cd44f20bde0e87" + "reference": "088ec6fe0ef6819cbc301174093b6bfa4ad26930" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/92f58bc4bf97a92ed1b9f367f0cd44f20bde0e87", - "reference": "92f58bc4bf97a92ed1b9f367f0cd44f20bde0e87", + "url": "https://api.github.com/repos/symfony/console/zipball/088ec6fe0ef6819cbc301174093b6bfa4ad26930", + "reference": "088ec6fe0ef6819cbc301174093b6bfa4ad26930", "shasum": "" }, "require": { @@ -2232,7 +2232,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.14" + "source": "https://github.com/symfony/console/tree/v7.4.15" }, "funding": [ { @@ -2252,20 +2252,20 @@ "type": "tidelift" } ], - "time": "2026-06-16T11:50:14+00:00" + "time": "2026-07-27T13:51:00+00:00" }, { "name": "symfony/dependency-injection", - "version": "v7.4.14", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "2c8c64a33e2e6911579e1ff79a8e06c27d48d402" + "reference": "b7825671c553af46a98c744e23f37f972aee6427" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/2c8c64a33e2e6911579e1ff79a8e06c27d48d402", - "reference": "2c8c64a33e2e6911579e1ff79a8e06c27d48d402", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/b7825671c553af46a98c744e23f37f972aee6427", + "reference": "b7825671c553af46a98c744e23f37f972aee6427", "shasum": "" }, "require": { @@ -2316,7 +2316,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v7.4.14" + "source": "https://github.com/symfony/dependency-injection/tree/v7.4.15" }, "funding": [ { @@ -2336,7 +2336,7 @@ "type": "tidelift" } ], - "time": "2026-06-24T07:41:05+00:00" + "time": "2026-07-22T08:40:50+00:00" }, { "name": "symfony/deprecation-contracts", @@ -2411,16 +2411,16 @@ }, { "name": "symfony/error-handler", - "version": "v7.4.14", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "4e1a093b481f323e6e326451f9760c3868430673" + "reference": "d49f6a19f326db41ae7103bdc38e3eb35a791261" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/4e1a093b481f323e6e326451f9760c3868430673", - "reference": "4e1a093b481f323e6e326451f9760c3868430673", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/d49f6a19f326db41ae7103bdc38e3eb35a791261", + "reference": "d49f6a19f326db41ae7103bdc38e3eb35a791261", "shasum": "" }, "require": { @@ -2469,7 +2469,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.4.14" + "source": "https://github.com/symfony/error-handler/tree/v7.4.15" }, "funding": [ { @@ -2489,20 +2489,20 @@ "type": "tidelift" } ], - "time": "2026-06-05T06:22:21+00:00" + "time": "2026-07-21T15:13:06+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v7.4.14", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "51fe3d170227be8d1772214b82ae506e15ed78ff" + "reference": "336e7f3b9e95aba04f93ea9143920c2186abfbb9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/51fe3d170227be8d1772214b82ae506e15ed78ff", - "reference": "51fe3d170227be8d1772214b82ae506e15ed78ff", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/336e7f3b9e95aba04f93ea9143920c2186abfbb9", + "reference": "336e7f3b9e95aba04f93ea9143920c2186abfbb9", "shasum": "" }, "require": { @@ -2554,7 +2554,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.14" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.15" }, "funding": [ { @@ -2574,7 +2574,7 @@ "type": "tidelift" } ], - "time": "2026-06-06T11:10:32+00:00" + "time": "2026-07-21T15:13:06+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -2658,16 +2658,16 @@ }, { "name": "symfony/filesystem", - "version": "v7.4.11", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "d721ea61b4a5fba8c5b6e7c1feda19efea144b50" + "reference": "ff16a16bf87fdf264638b8f6995b3515975e3c79" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/d721ea61b4a5fba8c5b6e7c1feda19efea144b50", - "reference": "d721ea61b4a5fba8c5b6e7c1feda19efea144b50", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/ff16a16bf87fdf264638b8f6995b3515975e3c79", + "reference": "ff16a16bf87fdf264638b8f6995b3515975e3c79", "shasum": "" }, "require": { @@ -2704,7 +2704,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.4.11" + "source": "https://github.com/symfony/filesystem/tree/v7.4.15" }, "funding": [ { @@ -2724,7 +2724,7 @@ "type": "tidelift" } ], - "time": "2026-05-11T16:38:44+00:00" + "time": "2026-07-22T07:36:05+00:00" }, { "name": "symfony/http-client", @@ -2911,16 +2911,16 @@ }, { "name": "symfony/http-foundation", - "version": "v7.4.14", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "06db5ae1552177bf8572f8908839f12e3c06aed3" + "reference": "1f898ee8188adda9417fb52cf8425a8342c254e7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/06db5ae1552177bf8572f8908839f12e3c06aed3", - "reference": "06db5ae1552177bf8572f8908839f12e3c06aed3", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/1f898ee8188adda9417fb52cf8425a8342c254e7", + "reference": "1f898ee8188adda9417fb52cf8425a8342c254e7", "shasum": "" }, "require": { @@ -2969,7 +2969,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.4.14" + "source": "https://github.com/symfony/http-foundation/tree/v7.4.15" }, "funding": [ { @@ -2989,20 +2989,20 @@ "type": "tidelift" } ], - "time": "2026-06-11T07:31:44+00:00" + "time": "2026-07-29T07:12:33+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.4.14", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "e99af79b1e776646eda0e1c23b7b45c184ff99be" + "reference": "403275d94f94d5626c3288c599b3b48093ba24f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/e99af79b1e776646eda0e1c23b7b45c184ff99be", - "reference": "e99af79b1e776646eda0e1c23b7b45c184ff99be", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/403275d94f94d5626c3288c599b3b48093ba24f7", + "reference": "403275d94f94d5626c3288c599b3b48093ba24f7", "shasum": "" }, "require": { @@ -3060,7 +3060,7 @@ "symfony/validator": "^6.4|^7.0|^8.0", "symfony/var-dumper": "^6.4|^7.0|^8.0", "symfony/var-exporter": "^6.4|^7.0|^8.0", - "twig/twig": "^3.12" + "twig/twig": "^3.12|^4.0" }, "type": "library", "autoload": { @@ -3088,7 +3088,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.4.14" + "source": "https://github.com/symfony/http-kernel/tree/v7.4.15" }, "funding": [ { @@ -3108,7 +3108,7 @@ "type": "tidelift" } ], - "time": "2026-06-27T09:14:35+00:00" + "time": "2026-07-29T11:40:42+00:00" }, { "name": "symfony/polyfill-ctype", @@ -3195,16 +3195,16 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.38.1", + "version": "v1.41.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "e9247d281d694a5120554d9afaf54e070e88a603" + "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/e9247d281d694a5120554d9afaf54e070e88a603", - "reference": "e9247d281d694a5120554d9afaf54e070e88a603", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", + "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", "shasum": "" }, "require": { @@ -3253,7 +3253,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.38.1" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.41.0" }, "funding": [ { @@ -3273,7 +3273,7 @@ "type": "tidelift" } ], - "time": "2026-05-26T05:58:03+00:00" + "time": "2026-07-28T08:25:59+00:00" }, { "name": "symfony/polyfill-intl-normalizer", @@ -3611,16 +3611,16 @@ }, { "name": "symfony/polyfill-php85", - "version": "v1.38.1", + "version": "v1.41.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php85.git", - "reference": "ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1" + "reference": "255fab485aaa1006ed411040c42aecd7b5302d7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1", - "reference": "ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/255fab485aaa1006ed411040c42aecd7b5302d7a", + "reference": "255fab485aaa1006ed411040c42aecd7b5302d7a", "shasum": "" }, "require": { @@ -3667,7 +3667,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php85/tree/v1.38.1" + "source": "https://github.com/symfony/polyfill-php85/tree/v1.41.0" }, "funding": [ { @@ -3687,7 +3687,7 @@ "type": "tidelift" } ], - "time": "2026-05-26T02:25:22+00:00" + "time": "2026-07-01T12:47:55+00:00" }, { "name": "symfony/polyfill-uuid", @@ -3861,16 +3861,16 @@ }, { "name": "symfony/string", - "version": "v7.4.13", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "961683010db3b27ec6ebcd7308e6e1ee8fa7ffde" + "reference": "e394af32256bf9e7bf80849d95e589167c10097b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/961683010db3b27ec6ebcd7308e6e1ee8fa7ffde", - "reference": "961683010db3b27ec6ebcd7308e6e1ee8fa7ffde", + "url": "https://api.github.com/repos/symfony/string/zipball/e394af32256bf9e7bf80849d95e589167c10097b", + "reference": "e394af32256bf9e7bf80849d95e589167c10097b", "shasum": "" }, "require": { @@ -3928,7 +3928,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.4.13" + "source": "https://github.com/symfony/string/tree/v7.4.15" }, "funding": [ { @@ -3948,7 +3948,7 @@ "type": "tidelift" } ], - "time": "2026-05-23T15:23:29+00:00" + "time": "2026-07-28T07:33:02+00:00" }, { "name": "symfony/translation", @@ -4212,16 +4212,16 @@ }, { "name": "symfony/var-dumper", - "version": "v7.4.14", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "9a3a56a4a1e65a5cb4f8d13801fe8ab0a170e358" + "reference": "04ba4add636a95ff437af3a5a9499bb1d6c6d4bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/9a3a56a4a1e65a5cb4f8d13801fe8ab0a170e358", - "reference": "9a3a56a4a1e65a5cb4f8d13801fe8ab0a170e358", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/04ba4add636a95ff437af3a5a9499bb1d6c6d4bd", + "reference": "04ba4add636a95ff437af3a5a9499bb1d6c6d4bd", "shasum": "" }, "require": { @@ -4237,7 +4237,7 @@ "symfony/http-kernel": "^6.4|^7.0|^8.0", "symfony/process": "^6.4|^7.0|^8.0", "symfony/uid": "^6.4|^7.0|^8.0", - "twig/twig": "^3.12" + "twig/twig": "^3.12|^4.0" }, "bin": [ "Resources/bin/var-dump-server" @@ -4275,7 +4275,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.4.14" + "source": "https://github.com/symfony/var-dumper/tree/v7.4.15" }, "funding": [ { @@ -4295,7 +4295,7 @@ "type": "tidelift" } ], - "time": "2026-06-08T20:24:16+00:00" + "time": "2026-07-21T15:13:06+00:00" }, { "name": "symfony/var-exporter", @@ -5629,11 +5629,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.2.7", + "version": "2.2.8", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/692db47b9dddb0487934e5236e77d48594aef921", - "reference": "692db47b9dddb0487934e5236e77d48594aef921", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e285254e60f33c21902efef4a926ca0987c06804", + "reference": "e285254e60f33c21902efef4a926ca0987c06804", "shasum": "" }, "require": { @@ -5689,7 +5689,7 @@ "type": "github" } ], - "time": "2026-07-29T17:39:32+00:00" + "time": "2026-08-04T22:21:45+00:00" }, { "name": "ramsey/collection", @@ -5847,16 +5847,16 @@ }, { "name": "symfony/cache", - "version": "v7.4.14", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "9adfcb2a7fc3924473b09f5a0b058dcc2cc7be9a" + "reference": "c1e7abe8e8c9b315d6d8b86446ffd9cf73679303" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/9adfcb2a7fc3924473b09f5a0b058dcc2cc7be9a", - "reference": "9adfcb2a7fc3924473b09f5a0b058dcc2cc7be9a", + "url": "https://api.github.com/repos/symfony/cache/zipball/c1e7abe8e8c9b315d6d8b86446ffd9cf73679303", + "reference": "c1e7abe8e8c9b315d6d8b86446ffd9cf73679303", "shasum": "" }, "require": { @@ -5927,7 +5927,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v7.4.14" + "source": "https://github.com/symfony/cache/tree/v7.4.15" }, "funding": [ { @@ -5947,7 +5947,7 @@ "type": "tidelift" } ], - "time": "2026-06-17T14:44:48+00:00" + "time": "2026-07-29T04:03:42+00:00" }, { "name": "symfony/cache-contracts", @@ -6109,16 +6109,16 @@ }, { "name": "symfony/dotenv", - "version": "v7.4.14", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/dotenv.git", - "reference": "9b9c7a00e565238857eea0040dc9745e3576402e" + "reference": "99b5b14953237c89ed24f1af5ba34225caf598a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dotenv/zipball/9b9c7a00e565238857eea0040dc9745e3576402e", - "reference": "9b9c7a00e565238857eea0040dc9745e3576402e", + "url": "https://api.github.com/repos/symfony/dotenv/zipball/99b5b14953237c89ed24f1af5ba34225caf598a4", + "reference": "99b5b14953237c89ed24f1af5ba34225caf598a4", "shasum": "" }, "require": { @@ -6163,7 +6163,7 @@ "environment" ], "support": { - "source": "https://github.com/symfony/dotenv/tree/v7.4.14" + "source": "https://github.com/symfony/dotenv/tree/v7.4.15" }, "funding": [ { @@ -6183,7 +6183,7 @@ "type": "tidelift" } ], - "time": "2026-06-05T06:22:21+00:00" + "time": "2026-07-26T10:22:28+00:00" }, { "name": "symfony/finder", @@ -6255,16 +6255,16 @@ }, { "name": "symfony/framework-bundle", - "version": "v7.4.14", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "4d11fd50d0a3d2c43b400154ad7ec35b84ea3e5b" + "reference": "a430728797dda13ec60add8afd18e3fea50f5e93" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/4d11fd50d0a3d2c43b400154ad7ec35b84ea3e5b", - "reference": "4d11fd50d0a3d2c43b400154ad7ec35b84ea3e5b", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/a430728797dda13ec60add8afd18e3fea50f5e93", + "reference": "a430728797dda13ec60add8afd18e3fea50f5e93", "shasum": "" }, "require": { @@ -6273,7 +6273,7 @@ "php": ">=8.2", "symfony/cache": "^6.4.12|^7.0|^8.0", "symfony/config": "^7.4.4|^8.0.4", - "symfony/dependency-injection": "^7.4.4|^8.0.4", + "symfony/dependency-injection": "^7.4.15|~8.0.15|^8.1.2", "symfony/deprecation-contracts": "^2.5|^3", "symfony/error-handler": "^7.3|^8.0", "symfony/event-dispatcher": "^6.4|^7.0|^8.0", @@ -6292,7 +6292,7 @@ "symfony/asset": "<6.4", "symfony/asset-mapper": "<6.4", "symfony/clock": "<6.4", - "symfony/console": "<6.4", + "symfony/console": "<6.4.43|>=7.0,<7.4.15|>=8.0,<8.0.15", "symfony/dom-crawler": "<6.4", "symfony/dotenv": "<6.4", "symfony/form": "<7.4", @@ -6326,7 +6326,7 @@ "symfony/asset-mapper": "^6.4|^7.0|^8.0", "symfony/browser-kit": "^6.4|^7.0|^8.0", "symfony/clock": "^6.4|^7.0|^8.0", - "symfony/console": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4.43|^7.4.15|^8.0.15", "symfony/css-selector": "^6.4|^7.0|^8.0", "symfony/dom-crawler": "^6.4|^7.0|^8.0", "symfony/dotenv": "^6.4|^7.0|^8.0", @@ -6361,7 +6361,7 @@ "symfony/webhook": "^7.4|^8.0", "symfony/workflow": "^7.4|^8.0", "symfony/yaml": "^7.3|^8.0", - "twig/twig": "^3.12" + "twig/twig": "^3.12|^4.0" }, "type": "symfony-bundle", "autoload": { @@ -6389,7 +6389,7 @@ "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v7.4.14" + "source": "https://github.com/symfony/framework-bundle/tree/v7.4.15" }, "funding": [ { @@ -6409,20 +6409,20 @@ "type": "tidelift" } ], - "time": "2026-06-27T08:31:38+00:00" + "time": "2026-07-26T12:33:49+00:00" }, { "name": "symfony/messenger", - "version": "v7.4.14", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/messenger.git", - "reference": "1ebe448527c77d9efaf2d3205b23707ebaaa28c4" + "reference": "6338eed8f65c593469fdbdbe043d2a7552264403" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/messenger/zipball/1ebe448527c77d9efaf2d3205b23707ebaaa28c4", - "reference": "1ebe448527c77d9efaf2d3205b23707ebaaa28c4", + "url": "https://api.github.com/repos/symfony/messenger/zipball/6338eed8f65c593469fdbdbe043d2a7552264403", + "reference": "6338eed8f65c593469fdbdbe043d2a7552264403", "shasum": "" }, "require": { @@ -6483,7 +6483,7 @@ "description": "Helps applications send and receive messages to/from other applications or via message queues", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/messenger/tree/v7.4.14" + "source": "https://github.com/symfony/messenger/tree/v7.4.15" }, "funding": [ { @@ -6503,7 +6503,7 @@ "type": "tidelift" } ], - "time": "2026-06-08T20:24:16+00:00" + "time": "2026-07-22T12:46:20+00:00" }, { "name": "symfony/options-resolver", @@ -6960,16 +6960,16 @@ }, { "name": "symfony/property-info", - "version": "v7.4.8", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/property-info.git", - "reference": "ac5e82528b986c4f7cfccbf7764b5d2e824d6175" + "reference": "fce3f4d9cfeb4ddc674c357b5a4c3a23ccf408ad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-info/zipball/ac5e82528b986c4f7cfccbf7764b5d2e824d6175", - "reference": "ac5e82528b986c4f7cfccbf7764b5d2e824d6175", + "url": "https://api.github.com/repos/symfony/property-info/zipball/fce3f4d9cfeb4ddc674c357b5a4c3a23ccf408ad", + "reference": "fce3f4d9cfeb4ddc674c357b5a4c3a23ccf408ad", "shasum": "" }, "require": { @@ -7026,7 +7026,7 @@ "validator" ], "support": { - "source": "https://github.com/symfony/property-info/tree/v7.4.8" + "source": "https://github.com/symfony/property-info/tree/v7.4.15" }, "funding": [ { @@ -7046,20 +7046,20 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-07-28T07:09:44+00:00" }, { "name": "symfony/routing", - "version": "v7.4.13", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "3a162171bb008e5e0f15dce6581373a4c0e8390d" + "reference": "80c0a93d3f8e7499f716204a1fb38ead942a7a2b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/3a162171bb008e5e0f15dce6581373a4c0e8390d", - "reference": "3a162171bb008e5e0f15dce6581373a4c0e8390d", + "url": "https://api.github.com/repos/symfony/routing/zipball/80c0a93d3f8e7499f716204a1fb38ead942a7a2b", + "reference": "80c0a93d3f8e7499f716204a1fb38ead942a7a2b", "shasum": "" }, "require": { @@ -7111,7 +7111,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v7.4.13" + "source": "https://github.com/symfony/routing/tree/v7.4.15" }, "funding": [ { @@ -7131,20 +7131,20 @@ "type": "tidelift" } ], - "time": "2026-05-24T11:20:33+00:00" + "time": "2026-07-21T15:13:06+00:00" }, { "name": "symfony/security-core", - "version": "v7.4.14", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/security-core.git", - "reference": "880bb18eff6188d55115e795f06e4185373c35fd" + "reference": "bf45a7f56ed79c7228dd39eb1c5c42c575b53fc7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-core/zipball/880bb18eff6188d55115e795f06e4185373c35fd", - "reference": "880bb18eff6188d55115e795f06e4185373c35fd", + "url": "https://api.github.com/repos/symfony/security-core/zipball/bf45a7f56ed79c7228dd39eb1c5c42c575b53fc7", + "reference": "bf45a7f56ed79c7228dd39eb1c5c42c575b53fc7", "shasum": "" }, "require": { @@ -7202,7 +7202,7 @@ "description": "Symfony Security Component - Core Library", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-core/tree/v7.4.14" + "source": "https://github.com/symfony/security-core/tree/v7.4.15" }, "funding": [ { @@ -7222,7 +7222,7 @@ "type": "tidelift" } ], - "time": "2026-06-09T07:51:57+00:00" + "time": "2026-07-19T19:34:23+00:00" }, { "name": "symfony/security-http", @@ -7318,16 +7318,16 @@ }, { "name": "symfony/serializer", - "version": "v7.4.14", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/serializer.git", - "reference": "55acb01b9c8a5211dfbaf68c314d90d0ed2cc3d1" + "reference": "917f1575bec2853f45e012d8718f518365a9d258" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer/zipball/55acb01b9c8a5211dfbaf68c314d90d0ed2cc3d1", - "reference": "55acb01b9c8a5211dfbaf68c314d90d0ed2cc3d1", + "url": "https://api.github.com/repos/symfony/serializer/zipball/917f1575bec2853f45e012d8718f518365a9d258", + "reference": "917f1575bec2853f45e012d8718f518365a9d258", "shasum": "" }, "require": { @@ -7341,7 +7341,7 @@ "phpdocumentor/type-resolver": "<1.5.1", "symfony/dependency-injection": "<6.4", "symfony/property-access": "<6.4.31|>=7.0,<7.4.2|>=8.0,<8.0.2", - "symfony/property-info": "<6.4", + "symfony/property-info": "<6.4.43", "symfony/type-info": "<7.2.5", "symfony/uid": "<6.4", "symfony/validator": "<6.4", @@ -7363,7 +7363,7 @@ "symfony/messenger": "^6.4|^7.0|^8.0", "symfony/mime": "^6.4|^7.0|^8.0", "symfony/property-access": "^6.4.31|^7.4.2|^8.0.2", - "symfony/property-info": "^6.4|^7.0|^8.0", + "symfony/property-info": "^6.4.43|^7.4.15|^8.0.15", "symfony/translation-contracts": "^2.5|^3", "symfony/type-info": "^7.2.5|^8.0", "symfony/uid": "^6.4|^7.0|^8.0", @@ -7398,7 +7398,7 @@ "description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/serializer/tree/v7.4.14" + "source": "https://github.com/symfony/serializer/tree/v7.4.15" }, "funding": [ { @@ -7418,27 +7418,27 @@ "type": "tidelift" } ], - "time": "2026-06-27T08:31:18+00:00" + "time": "2026-07-29T07:59:49+00:00" }, { "name": "symfony/twig-bridge", - "version": "v7.4.14", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/twig-bridge.git", - "reference": "e4574ab4d5411a7c495d4189b15a8ecfbc720332" + "reference": "c81843850c1c791b7f516db62cb81beaadd8ab22" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/e4574ab4d5411a7c495d4189b15a8ecfbc720332", - "reference": "e4574ab4d5411a7c495d4189b15a8ecfbc720332", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/c81843850c1c791b7f516db62cb81beaadd8ab22", + "reference": "c81843850c1c791b7f516db62cb81beaadd8ab22", "shasum": "" }, "require": { "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", "symfony/translation-contracts": "^2.5|^3", - "twig/twig": "^3.21" + "twig/twig": "^3.21|^4.0" }, "conflict": { "phpdocumentor/reflection-docblock": "<5.2|>=7", @@ -7513,7 +7513,7 @@ "description": "Provides integration for Twig with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bridge/tree/v7.4.14" + "source": "https://github.com/symfony/twig-bridge/tree/v7.4.15" }, "funding": [ { @@ -7533,20 +7533,20 @@ "type": "tidelift" } ], - "time": "2026-06-17T13:16:29+00:00" + "time": "2026-07-21T15:27:07+00:00" }, { "name": "symfony/twig-bundle", - "version": "v7.4.14", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/twig-bundle.git", - "reference": "11b69c64efdd0c3465403bb2747bf4585add1ec7" + "reference": "e3d2bea0b594aa50c3482a278a5cd09d83d94517" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/11b69c64efdd0c3465403bb2747bf4585add1ec7", - "reference": "11b69c64efdd0c3465403bb2747bf4585add1ec7", + "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/e3d2bea0b594aa50c3482a278a5cd09d83d94517", + "reference": "e3d2bea0b594aa50c3482a278a5cd09d83d94517", "shasum": "" }, "require": { @@ -7558,7 +7558,7 @@ "symfony/http-foundation": "^6.4|^7.0|^8.0", "symfony/http-kernel": "^6.4.13|^7.1.6|^8.0", "symfony/twig-bridge": "^7.3|^8.0", - "twig/twig": "^3.12" + "twig/twig": "^3.12|^4.0" }, "conflict": { "symfony/framework-bundle": "<6.4", @@ -7603,7 +7603,7 @@ "description": "Provides a tight integration of Twig into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bundle/tree/v7.4.14" + "source": "https://github.com/symfony/twig-bundle/tree/v7.4.15" }, "funding": [ { @@ -7623,7 +7623,7 @@ "type": "tidelift" } ], - "time": "2026-06-05T06:22:21+00:00" + "time": "2026-07-21T15:13:06+00:00" }, { "name": "symfony/type-info", @@ -7710,16 +7710,16 @@ }, { "name": "symfony/web-profiler-bundle", - "version": "v7.4.14", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/web-profiler-bundle.git", - "reference": "5dead36a9202a6008b54b95308bce7ab97a41fe0" + "reference": "b234de974601fc254139e024e25610b208d0a7dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/5dead36a9202a6008b54b95308bce7ab97a41fe0", - "reference": "5dead36a9202a6008b54b95308bce7ab97a41fe0", + "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/b234de974601fc254139e024e25610b208d0a7dd", + "reference": "b234de974601fc254139e024e25610b208d0a7dd", "shasum": "" }, "require": { @@ -7731,7 +7731,7 @@ "symfony/http-kernel": "^6.4.13|^7.1.6|^8.0", "symfony/routing": "^6.4|^7.0|^8.0", "symfony/twig-bundle": "^6.4|^7.0|^8.0", - "twig/twig": "^3.15" + "twig/twig": "^3.15|^4.0" }, "conflict": { "symfony/form": "<6.4", @@ -7776,7 +7776,7 @@ "dev" ], "support": { - "source": "https://github.com/symfony/web-profiler-bundle/tree/v7.4.14" + "source": "https://github.com/symfony/web-profiler-bundle/tree/v7.4.15" }, "funding": [ { @@ -7796,20 +7796,20 @@ "type": "tidelift" } ], - "time": "2026-06-05T06:22:21+00:00" + "time": "2026-07-21T15:13:06+00:00" }, { "name": "symfony/yaml", - "version": "v7.4.14", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "f8f328665ace2370d1e10645b807ba1646dc7dcc" + "reference": "e101850ded5d2c0d44bf32abb8996404afec2dec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/f8f328665ace2370d1e10645b807ba1646dc7dcc", - "reference": "f8f328665ace2370d1e10645b807ba1646dc7dcc", + "url": "https://api.github.com/repos/symfony/yaml/zipball/e101850ded5d2c0d44bf32abb8996404afec2dec", + "reference": "e101850ded5d2c0d44bf32abb8996404afec2dec", "shasum": "" }, "require": { @@ -7852,7 +7852,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.4.14" + "source": "https://github.com/symfony/yaml/tree/v7.4.15" }, "funding": [ { @@ -7872,7 +7872,7 @@ "type": "tidelift" } ], - "time": "2026-06-08T20:24:16+00:00" + "time": "2026-07-21T15:13:06+00:00" }, { "name": "twig/twig", diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Context/SymfonyContext.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Context/SymfonyContext.php index 4e94535e29..e1318fb2fc 100644 --- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Context/SymfonyContext.php +++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Context/SymfonyContext.php @@ -56,7 +56,7 @@ public function fullyPopulatedTelemetryConfig(bool $enabled): array 'enabled' => '%env(bool:FLOW_TELEMETRY_TEST_EXPORTERS_ENABLED)%', 'otlp' => [ 'transport' => [ - 'endpoint' => '%env(default::FLOW_TELEMETRY_TEST_OTLP_ENDPOINT)%', + 'endpoint' => '%env(string:FLOW_TELEMETRY_TEST_OTLP_ENDPOINT)%', ], ], ], diff --git a/tools/box/composer.lock b/tools/box/composer.lock index 947a5b49b4..a998a7de03 100644 --- a/tools/box/composer.lock +++ b/tools/box/composer.lock @@ -2717,16 +2717,16 @@ }, { "name": "seld/phar-utils", - "version": "1.2.1", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/Seldaek/phar-utils.git", - "reference": "ea2f4014f163c1be4c601b9b7bd6af81ba8d701c" + "reference": "990bbd0e92caa216d52eca0935f6e35e589bfaa5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/ea2f4014f163c1be4c601b9b7bd6af81ba8d701c", - "reference": "ea2f4014f163c1be4c601b9b7bd6af81ba8d701c", + "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/990bbd0e92caa216d52eca0935f6e35e589bfaa5", + "reference": "990bbd0e92caa216d52eca0935f6e35e589bfaa5", "shasum": "" }, "require": { @@ -2759,22 +2759,22 @@ ], "support": { "issues": "https://github.com/Seldaek/phar-utils/issues", - "source": "https://github.com/Seldaek/phar-utils/tree/1.2.1" + "source": "https://github.com/Seldaek/phar-utils/tree/1.2.2" }, - "time": "2022-08-31T10:31:18+00:00" + "time": "2026-08-01T12:48:55+00:00" }, { "name": "symfony/console", - "version": "v7.4.14", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "92f58bc4bf97a92ed1b9f367f0cd44f20bde0e87" + "reference": "088ec6fe0ef6819cbc301174093b6bfa4ad26930" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/92f58bc4bf97a92ed1b9f367f0cd44f20bde0e87", - "reference": "92f58bc4bf97a92ed1b9f367f0cd44f20bde0e87", + "url": "https://api.github.com/repos/symfony/console/zipball/088ec6fe0ef6819cbc301174093b6bfa4ad26930", + "reference": "088ec6fe0ef6819cbc301174093b6bfa4ad26930", "shasum": "" }, "require": { @@ -2839,7 +2839,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.14" + "source": "https://github.com/symfony/console/tree/v7.4.15" }, "funding": [ { @@ -2859,7 +2859,7 @@ "type": "tidelift" } ], - "time": "2026-06-16T11:50:14+00:00" + "time": "2026-07-27T13:51:00+00:00" }, { "name": "symfony/deprecation-contracts", @@ -3014,16 +3014,16 @@ }, { "name": "symfony/filesystem", - "version": "v7.4.11", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "d721ea61b4a5fba8c5b6e7c1feda19efea144b50" + "reference": "ff16a16bf87fdf264638b8f6995b3515975e3c79" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/d721ea61b4a5fba8c5b6e7c1feda19efea144b50", - "reference": "d721ea61b4a5fba8c5b6e7c1feda19efea144b50", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/ff16a16bf87fdf264638b8f6995b3515975e3c79", + "reference": "ff16a16bf87fdf264638b8f6995b3515975e3c79", "shasum": "" }, "require": { @@ -3060,7 +3060,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.4.11" + "source": "https://github.com/symfony/filesystem/tree/v7.4.15" }, "funding": [ { @@ -3080,7 +3080,7 @@ "type": "tidelift" } ], - "time": "2026-05-11T16:38:44+00:00" + "time": "2026-07-22T07:36:05+00:00" }, { "name": "symfony/finder", @@ -3319,16 +3319,16 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.38.1", + "version": "v1.41.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "e9247d281d694a5120554d9afaf54e070e88a603" + "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/e9247d281d694a5120554d9afaf54e070e88a603", - "reference": "e9247d281d694a5120554d9afaf54e070e88a603", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", + "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", "shasum": "" }, "require": { @@ -3377,7 +3377,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.38.1" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.41.0" }, "funding": [ { @@ -3397,7 +3397,7 @@ "type": "tidelift" } ], - "time": "2026-05-26T05:58:03+00:00" + "time": "2026-07-28T08:25:59+00:00" }, { "name": "symfony/polyfill-intl-normalizer", @@ -3803,16 +3803,16 @@ }, { "name": "symfony/string", - "version": "v7.4.13", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "961683010db3b27ec6ebcd7308e6e1ee8fa7ffde" + "reference": "e394af32256bf9e7bf80849d95e589167c10097b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/961683010db3b27ec6ebcd7308e6e1ee8fa7ffde", - "reference": "961683010db3b27ec6ebcd7308e6e1ee8fa7ffde", + "url": "https://api.github.com/repos/symfony/string/zipball/e394af32256bf9e7bf80849d95e589167c10097b", + "reference": "e394af32256bf9e7bf80849d95e589167c10097b", "shasum": "" }, "require": { @@ -3870,7 +3870,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.4.13" + "source": "https://github.com/symfony/string/tree/v7.4.15" }, "funding": [ { @@ -3890,20 +3890,20 @@ "type": "tidelift" } ], - "time": "2026-05-23T15:23:29+00:00" + "time": "2026-07-28T07:33:02+00:00" }, { "name": "symfony/var-dumper", - "version": "v7.4.14", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "9a3a56a4a1e65a5cb4f8d13801fe8ab0a170e358" + "reference": "04ba4add636a95ff437af3a5a9499bb1d6c6d4bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/9a3a56a4a1e65a5cb4f8d13801fe8ab0a170e358", - "reference": "9a3a56a4a1e65a5cb4f8d13801fe8ab0a170e358", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/04ba4add636a95ff437af3a5a9499bb1d6c6d4bd", + "reference": "04ba4add636a95ff437af3a5a9499bb1d6c6d4bd", "shasum": "" }, "require": { @@ -3919,7 +3919,7 @@ "symfony/http-kernel": "^6.4|^7.0|^8.0", "symfony/process": "^6.4|^7.0|^8.0", "symfony/uid": "^6.4|^7.0|^8.0", - "twig/twig": "^3.12" + "twig/twig": "^3.12|^4.0" }, "bin": [ "Resources/bin/var-dump-server" @@ -3957,7 +3957,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.4.14" + "source": "https://github.com/symfony/var-dumper/tree/v7.4.15" }, "funding": [ { @@ -3977,7 +3977,7 @@ "type": "tidelift" } ], - "time": "2026-06-08T20:24:16+00:00" + "time": "2026-07-21T15:13:06+00:00" }, { "name": "thecodingmachine/safe", diff --git a/tools/infection/composer.lock b/tools/infection/composer.lock index a9c912bb3c..16e8f9748d 100644 --- a/tools/infection/composer.lock +++ b/tools/infection/composer.lock @@ -423,21 +423,21 @@ }, { "name": "infection/include-interceptor", - "version": "0.2.5", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/infection/include-interceptor.git", - "reference": "0cc76d95a79d9832d74e74492b0a30139904bdf7" + "reference": "c083331bc0cd1cd319ac1b7a08e3ad4a34aa7992" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/infection/include-interceptor/zipball/0cc76d95a79d9832d74e74492b0a30139904bdf7", - "reference": "0cc76d95a79d9832d74e74492b0a30139904bdf7", + "url": "https://api.github.com/repos/infection/include-interceptor/zipball/c083331bc0cd1cd319ac1b7a08e3ad4a34aa7992", + "reference": "c083331bc0cd1cd319ac1b7a08e3ad4a34aa7992", "shasum": "" }, "require-dev": { "friendsofphp/php-cs-fixer": "^2.16", - "infection/infection": "^0.15.0", + "infection/infection": "^0.19.0", "phan/phan": "^2.4 || ^3", "php-coveralls/php-coveralls": "^2.2", "phpstan/phpstan": "^0.12.8", @@ -463,7 +463,7 @@ "description": "Stream Wrapper: Include Interceptor. Allows to replace included (autoloaded) file with another one.", "support": { "issues": "https://github.com/infection/include-interceptor/issues", - "source": "https://github.com/infection/include-interceptor/tree/0.2.5" + "source": "https://github.com/infection/include-interceptor/tree/1.0.0" }, "funding": [ { @@ -475,20 +475,20 @@ "type": "open_collective" } ], - "time": "2021-08-09T10:03:57+00:00" + "time": "2024-05-03T21:48:06+00:00" }, { "name": "infection/infection", - "version": "0.34.0", + "version": "0.34.1", "source": { "type": "git", "url": "https://github.com/infection/infection.git", - "reference": "373c2ab1689696ac6969013a04bdbccae74d7e08" + "reference": "263a6e303e343f1cdfa378d259a199c594d0731d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/infection/infection/zipball/373c2ab1689696ac6969013a04bdbccae74d7e08", - "reference": "373c2ab1689696ac6969013a04bdbccae74d7e08", + "url": "https://api.github.com/repos/infection/infection/zipball/263a6e303e343f1cdfa378d259a199c594d0731d", + "reference": "263a6e303e343f1cdfa378d259a199c594d0731d", "shasum": "" }, "require": { @@ -535,12 +535,13 @@ "phpbench/phpbench": "^1.4", "phpstan/extension-installer": "^1.4", "phpstan/phpstan": "^2.1", + "phpstan/phpstan-deprecation-rules": "^2.0", "phpstan/phpstan-phpunit": "^2.0", "phpstan/phpstan-strict-rules": "^2.0", "phpstan/phpstan-webmozart-assert": "^2.0", - "phpunit/phpunit": "^11.5.27", + "phpunit/phpunit": "^12.5.29", "rector/rector": "^2.2.4", - "shipmonk/dead-code-detector": "^0.15", + "shipmonk/dead-code-detector": "^1.3", "shipmonk/name-collision-detector": "^2.1", "sidz/phpstan-rules": "^0.5.1", "symfony/yaml": "^6.4 || ^7.4 || ^8.0", @@ -601,7 +602,7 @@ ], "support": { "issues": "https://github.com/infection/infection/issues", - "source": "https://github.com/infection/infection/tree/0.34.0" + "source": "https://github.com/infection/infection/tree/0.34.1" }, "funding": [ { @@ -613,7 +614,7 @@ "type": "open_collective" } ], - "time": "2026-06-28T12:05:47+00:00" + "time": "2026-07-27T18:58:14+00:00" }, { "name": "infection/mutator", @@ -1246,16 +1247,16 @@ }, { "name": "sanmai/later", - "version": "0.1.7", + "version": "0.1.8", "source": { "type": "git", "url": "https://github.com/sanmai/later.git", - "reference": "72a82d783864bca90412d8a26c1878f8981fee97" + "reference": "c56aeb8fa7fdf81eda2640a68b51884685963d13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sanmai/later/zipball/72a82d783864bca90412d8a26c1878f8981fee97", - "reference": "72a82d783864bca90412d8a26c1878f8981fee97", + "url": "https://api.github.com/repos/sanmai/later/zipball/c56aeb8fa7fdf81eda2640a68b51884685963d13", + "reference": "c56aeb8fa7fdf81eda2640a68b51884685963d13", "shasum": "" }, "require": { @@ -1298,7 +1299,7 @@ "description": "Later: deferred wrapper object", "support": { "issues": "https://github.com/sanmai/later/issues", - "source": "https://github.com/sanmai/later/tree/0.1.7" + "source": "https://github.com/sanmai/later/tree/0.1.8" }, "funding": [ { @@ -1306,20 +1307,20 @@ "type": "github" } ], - "time": "2025-05-11T01:48:00+00:00" + "time": "2026-06-29T07:24:33+00:00" }, { "name": "sanmai/pipeline", - "version": "7.9", + "version": "7.10", "source": { "type": "git", "url": "https://github.com/sanmai/pipeline.git", - "reference": "d7046ecce91ae57fca403be694888371a21250eb" + "reference": "a8e4e57a6031efc7a92defd13fed9c1c4c73fc71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sanmai/pipeline/zipball/d7046ecce91ae57fca403be694888371a21250eb", - "reference": "d7046ecce91ae57fca403be694888371a21250eb", + "url": "https://api.github.com/repos/sanmai/pipeline/zipball/a8e4e57a6031efc7a92defd13fed9c1c4c73fc71", + "reference": "a8e4e57a6031efc7a92defd13fed9c1c4c73fc71", "shasum": "" }, "require": { @@ -1334,7 +1335,7 @@ "php-coveralls/php-coveralls": "^2.4.1", "phpstan/extension-installer": "^1.4", "phpstan/phpstan": "^2", - "phpunit/phpunit": ">=9.4 <12", + "phpunit/phpunit": "^11 || ^12", "sanmai/phpstan-rules": "^0.3.11", "sanmai/phpunit-double-colon-syntax": "^0.1.1", "vimeo/psalm": ">=2" @@ -1366,7 +1367,7 @@ "description": "General-purpose collections pipeline", "support": { "issues": "https://github.com/sanmai/pipeline/issues", - "source": "https://github.com/sanmai/pipeline/tree/7.9" + "source": "https://github.com/sanmai/pipeline/tree/7.10" }, "funding": [ { @@ -1374,7 +1375,7 @@ "type": "github" } ], - "time": "2026-01-16T11:54:05+00:00" + "time": "2026-08-03T04:56:22+00:00" }, { "name": "sebastian/diff", @@ -1445,16 +1446,16 @@ }, { "name": "symfony/console", - "version": "v7.4.14", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "92f58bc4bf97a92ed1b9f367f0cd44f20bde0e87" + "reference": "088ec6fe0ef6819cbc301174093b6bfa4ad26930" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/92f58bc4bf97a92ed1b9f367f0cd44f20bde0e87", - "reference": "92f58bc4bf97a92ed1b9f367f0cd44f20bde0e87", + "url": "https://api.github.com/repos/symfony/console/zipball/088ec6fe0ef6819cbc301174093b6bfa4ad26930", + "reference": "088ec6fe0ef6819cbc301174093b6bfa4ad26930", "shasum": "" }, "require": { @@ -1519,7 +1520,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.14" + "source": "https://github.com/symfony/console/tree/v7.4.15" }, "funding": [ { @@ -1539,7 +1540,7 @@ "type": "tidelift" } ], - "time": "2026-06-16T11:50:14+00:00" + "time": "2026-07-27T13:51:00+00:00" }, { "name": "symfony/deprecation-contracts", @@ -1614,16 +1615,16 @@ }, { "name": "symfony/filesystem", - "version": "v7.4.11", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "d721ea61b4a5fba8c5b6e7c1feda19efea144b50" + "reference": "ff16a16bf87fdf264638b8f6995b3515975e3c79" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/d721ea61b4a5fba8c5b6e7c1feda19efea144b50", - "reference": "d721ea61b4a5fba8c5b6e7c1feda19efea144b50", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/ff16a16bf87fdf264638b8f6995b3515975e3c79", + "reference": "ff16a16bf87fdf264638b8f6995b3515975e3c79", "shasum": "" }, "require": { @@ -1660,7 +1661,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.4.11" + "source": "https://github.com/symfony/filesystem/tree/v7.4.15" }, "funding": [ { @@ -1680,7 +1681,7 @@ "type": "tidelift" } ], - "time": "2026-05-11T16:38:44+00:00" + "time": "2026-07-22T07:36:05+00:00" }, { "name": "symfony/finder", @@ -1835,16 +1836,16 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.38.1", + "version": "v1.41.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "e9247d281d694a5120554d9afaf54e070e88a603" + "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/e9247d281d694a5120554d9afaf54e070e88a603", - "reference": "e9247d281d694a5120554d9afaf54e070e88a603", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", + "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", "shasum": "" }, "require": { @@ -1893,7 +1894,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.38.1" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.41.0" }, "funding": [ { @@ -1913,7 +1914,7 @@ "type": "tidelift" } ], - "time": "2026-05-26T05:58:03+00:00" + "time": "2026-07-28T08:25:59+00:00" }, { "name": "symfony/polyfill-intl-normalizer", @@ -2087,16 +2088,16 @@ }, { "name": "symfony/polyfill-php85", - "version": "v1.38.1", + "version": "v1.41.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php85.git", - "reference": "ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1" + "reference": "255fab485aaa1006ed411040c42aecd7b5302d7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1", - "reference": "ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/255fab485aaa1006ed411040c42aecd7b5302d7a", + "reference": "255fab485aaa1006ed411040c42aecd7b5302d7a", "shasum": "" }, "require": { @@ -2143,7 +2144,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php85/tree/v1.38.1" + "source": "https://github.com/symfony/polyfill-php85/tree/v1.41.0" }, "funding": [ { @@ -2163,7 +2164,7 @@ "type": "tidelift" } ], - "time": "2026-05-26T02:25:22+00:00" + "time": "2026-07-01T12:47:55+00:00" }, { "name": "symfony/process", @@ -2319,16 +2320,16 @@ }, { "name": "symfony/string", - "version": "v7.4.13", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "961683010db3b27ec6ebcd7308e6e1ee8fa7ffde" + "reference": "e394af32256bf9e7bf80849d95e589167c10097b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/961683010db3b27ec6ebcd7308e6e1ee8fa7ffde", - "reference": "961683010db3b27ec6ebcd7308e6e1ee8fa7ffde", + "url": "https://api.github.com/repos/symfony/string/zipball/e394af32256bf9e7bf80849d95e589167c10097b", + "reference": "e394af32256bf9e7bf80849d95e589167c10097b", "shasum": "" }, "require": { @@ -2386,7 +2387,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.4.13" + "source": "https://github.com/symfony/string/tree/v7.4.15" }, "funding": [ { @@ -2406,7 +2407,7 @@ "type": "tidelift" } ], - "time": "2026-05-23T15:23:29+00:00" + "time": "2026-07-28T07:33:02+00:00" }, { "name": "thecodingmachine/safe", diff --git a/tools/phpbench/composer.lock b/tools/phpbench/composer.lock index d28abb2717..173134c6df 100644 --- a/tools/phpbench/composer.lock +++ b/tools/phpbench/composer.lock @@ -529,16 +529,16 @@ }, { "name": "symfony/console", - "version": "v7.4.14", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "92f58bc4bf97a92ed1b9f367f0cd44f20bde0e87" + "reference": "088ec6fe0ef6819cbc301174093b6bfa4ad26930" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/92f58bc4bf97a92ed1b9f367f0cd44f20bde0e87", - "reference": "92f58bc4bf97a92ed1b9f367f0cd44f20bde0e87", + "url": "https://api.github.com/repos/symfony/console/zipball/088ec6fe0ef6819cbc301174093b6bfa4ad26930", + "reference": "088ec6fe0ef6819cbc301174093b6bfa4ad26930", "shasum": "" }, "require": { @@ -603,7 +603,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.14" + "source": "https://github.com/symfony/console/tree/v7.4.15" }, "funding": [ { @@ -623,7 +623,7 @@ "type": "tidelift" } ], - "time": "2026-06-16T11:50:14+00:00" + "time": "2026-07-27T13:51:00+00:00" }, { "name": "symfony/deprecation-contracts", @@ -698,16 +698,16 @@ }, { "name": "symfony/filesystem", - "version": "v7.4.11", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "d721ea61b4a5fba8c5b6e7c1feda19efea144b50" + "reference": "ff16a16bf87fdf264638b8f6995b3515975e3c79" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/d721ea61b4a5fba8c5b6e7c1feda19efea144b50", - "reference": "d721ea61b4a5fba8c5b6e7c1feda19efea144b50", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/ff16a16bf87fdf264638b8f6995b3515975e3c79", + "reference": "ff16a16bf87fdf264638b8f6995b3515975e3c79", "shasum": "" }, "require": { @@ -744,7 +744,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.4.11" + "source": "https://github.com/symfony/filesystem/tree/v7.4.15" }, "funding": [ { @@ -764,7 +764,7 @@ "type": "tidelift" } ], - "time": "2026-05-11T16:38:44+00:00" + "time": "2026-07-22T07:36:05+00:00" }, { "name": "symfony/finder", @@ -990,16 +990,16 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.38.1", + "version": "v1.41.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "e9247d281d694a5120554d9afaf54e070e88a603" + "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/e9247d281d694a5120554d9afaf54e070e88a603", - "reference": "e9247d281d694a5120554d9afaf54e070e88a603", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", + "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", "shasum": "" }, "require": { @@ -1048,7 +1048,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.38.1" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.41.0" }, "funding": [ { @@ -1068,7 +1068,7 @@ "type": "tidelift" } ], - "time": "2026-05-26T05:58:03+00:00" + "time": "2026-07-28T08:25:59+00:00" }, { "name": "symfony/polyfill-intl-normalizer", @@ -1394,16 +1394,16 @@ }, { "name": "symfony/string", - "version": "v7.4.13", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "961683010db3b27ec6ebcd7308e6e1ee8fa7ffde" + "reference": "e394af32256bf9e7bf80849d95e589167c10097b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/961683010db3b27ec6ebcd7308e6e1ee8fa7ffde", - "reference": "961683010db3b27ec6ebcd7308e6e1ee8fa7ffde", + "url": "https://api.github.com/repos/symfony/string/zipball/e394af32256bf9e7bf80849d95e589167c10097b", + "reference": "e394af32256bf9e7bf80849d95e589167c10097b", "shasum": "" }, "require": { @@ -1461,7 +1461,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.4.13" + "source": "https://github.com/symfony/string/tree/v7.4.15" }, "funding": [ { @@ -1481,7 +1481,7 @@ "type": "tidelift" } ], - "time": "2026-05-23T15:23:29+00:00" + "time": "2026-07-28T07:33:02+00:00" }, { "name": "webmozart/glob", diff --git a/tools/phpunit/composer.lock b/tools/phpunit/composer.lock index e160fe911e..fabfcbb2d3 100644 --- a/tools/phpunit/composer.lock +++ b/tools/phpunit/composer.lock @@ -589,16 +589,16 @@ }, { "name": "phpunit/phpunit", - "version": "12.5.32", + "version": "12.5.33", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "c02591dd7840ed124a98d7770753329ad28e9f8f" + "reference": "b98e028a26c5c5ba7e4a54be96ccf35f2914d184" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c02591dd7840ed124a98d7770753329ad28e9f8f", - "reference": "c02591dd7840ed124a98d7770753329ad28e9f8f", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b98e028a26c5c5ba7e4a54be96ccf35f2914d184", + "reference": "b98e028a26c5c5ba7e4a54be96ccf35f2914d184", "shasum": "" }, "require": { @@ -667,7 +667,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.32" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.33" }, "funding": [ { @@ -675,7 +675,7 @@ "type": "other" } ], - "time": "2026-07-25T06:54:13+00:00" + "time": "2026-07-28T13:58:09+00:00" }, { "name": "sebastian/cli-parser",