diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 7d386b543..dc5290c09 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -107,7 +107,7 @@ public function getConfigTreeBuilder(): TreeBuilder ->info('Set true to enable support for xsendfile in binary file responses.') ->defaultFalse() ->end() - ->scalarNode('ide')->defaultValue('%env(default::SYMFONY_IDE)%')->end() + ->scalarNode('ide')->defaultValue($this->debug ? '%env(default::SYMFONY_IDE)%' : null)->end() ->booleanNode('test')->end() ->scalarNode('default_locale')->defaultValue('en')->end() ->booleanNode('set_locale_from_accept_language') @@ -1026,27 +1026,30 @@ private function addValidationSection(ArrayNodeDefinition $rootNode, callable $e trigger_deprecation('symfony/framework-bundle', '6.4', 'Not setting the "framework.validation.email_validation_mode" config option is deprecated. It will default to "html5" in 7.0.'); } - if (isset($v['enable_annotations'])) { - trigger_deprecation('symfony/framework-bundle', '6.4', 'Option "enable_annotations" at "framework.validation" is deprecated. Use the "enable_attributes" option instead.'); - - if (!isset($v['enable_attributes'])) { - $v['enable_attributes'] = $v['enable_annotations']; - } else { - throw new LogicException('The "enable_annotations" and "enable_attributes" options at path "framework.validation" must not be both set. Only the "enable_attributes" option must be used.'); - } - } - return $v; }) ->end() ->children() ->arrayNode('validation') + ->beforeNormalization() + ->ifTrue(fn ($v) => isset($v['enable_annotations'])) + ->then(function ($v) { + trigger_deprecation('symfony/framework-bundle', '6.4', 'Option "enable_annotations" at "framework.validation" is deprecated. Use the "enable_attributes" option instead.'); + + if (isset($v['enable_attributes'])) { + throw new LogicException('The "enable_annotations" and "enable_attributes" options at path "framework.validation" must not be both set. Only the "enable_attributes" option must be used.'); + } + $v['enable_attributes'] = $v['enable_annotations']; + + return $v; + }) + ->end() ->info('validation configuration') ->{$enableIfStandalone('symfony/validator', Validation::class)}() ->children() ->scalarNode('cache')->end() ->booleanNode('enable_annotations')->end() - ->booleanNode('enable_attributes')->{!class_exists(FullStack::class) ? 'defaultTrue' : 'defaultFalse'}()->end() + ->booleanNode('enable_attributes')->{class_exists(FullStack::class) ? 'defaultFalse' : 'defaultTrue'}()->end() ->arrayNode('static_method') ->defaultValue(['loadValidatorMetadata']) ->prototype('scalar')->end() @@ -1152,25 +1155,24 @@ private function addSerializerSection(ArrayNodeDefinition $rootNode, callable $e $rootNode ->children() ->arrayNode('serializer') - ->validate() - ->always(function ($v) { - if (isset($v['enable_annotations'])) { + ->beforeNormalization() + ->ifTrue(fn ($v) => isset($v['enable_annotations'])) + ->then(function ($v) { trigger_deprecation('symfony/framework-bundle', '6.4', 'Option "enable_annotations" at "framework.serializer" is deprecated. Use the "enable_attributes" option instead.'); - if (!isset($v['enable_attributes'])) { - $v['enable_attributes'] = $v['enable_annotations']; - } else { + if (isset($v['enable_attributes'])) { throw new LogicException('The "enable_annotations" and "enable_attributes" options at path "framework.serializer" must not be both set. Only the "enable_attributes" option must be used.'); } - } + $v['enable_attributes'] = $v['enable_annotations']; - return $v; - })->end() + return $v; + }) + ->end() ->info('serializer configuration') ->{$enableIfStandalone('symfony/serializer', Serializer::class)}() ->children() ->booleanNode('enable_annotations')->end() - ->booleanNode('enable_attributes')->{!class_exists(FullStack::class) ? 'defaultTrue' : 'defaultFalse'}()->end() + ->booleanNode('enable_attributes')->{class_exists(FullStack::class) ? 'defaultFalse' : 'defaultTrue'}()->end() ->scalarNode('name_converter')->end() ->scalarNode('circular_reference_handler')->end() ->scalarNode('max_depth_handler')->end() diff --git a/EventListener/ConsoleProfilerListener.php b/EventListener/ConsoleProfilerListener.php index f9a55a62e..d3fc38106 100644 --- a/EventListener/ConsoleProfilerListener.php +++ b/EventListener/ConsoleProfilerListener.php @@ -59,7 +59,8 @@ public static function getSubscribedEvents(): array public function initialize(ConsoleCommandEvent $event): void { - if (!$event->getInput()->getOption('profile')) { + $input = $event->getInput(); + if (!$input->hasOption('profile') || !$input->getOption('profile')) { $this->profiler->disable(); return; diff --git a/Resources/config/web.php b/Resources/config/web.php index a3a6ef773..817ed07c1 100644 --- a/Resources/config/web.php +++ b/Resources/config/web.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver; use Symfony\Component\HttpKernel\Controller\ArgumentResolver; use Symfony\Component\HttpKernel\Controller\ArgumentResolver\BackedEnumValueResolver; @@ -40,6 +41,7 @@ service('service_container'), service('logger')->ignoreOnInvalid(), ]) + ->call('allowControllers', [[AbstractController::class]]) ->tag('monolog.logger', ['channel' => 'request']) ->set('argument_metadata_factory', ArgumentMetadataFactory::class) diff --git a/Tests/CacheWarmer/SerializerCacheWarmerTest.php b/Tests/CacheWarmer/SerializerCacheWarmerTest.php index 85dbd8810..5feb0c8ec 100644 --- a/Tests/CacheWarmer/SerializerCacheWarmerTest.php +++ b/Tests/CacheWarmer/SerializerCacheWarmerTest.php @@ -40,7 +40,7 @@ public function testWarmUp(array $loaders) $this->assertTrue($arrayPool->getItem('Symfony_Bundle_FrameworkBundle_Tests_Fixtures_Serialization_Author')->isHit()); } - public static function loaderProvider() + public static function loaderProvider(): array { return [ [ diff --git a/Tests/Command/CachePoolClearCommandTest.php b/Tests/Command/CachePoolClearCommandTest.php index e1997777d..fb7358831 100644 --- a/Tests/Command/CachePoolClearCommandTest.php +++ b/Tests/Command/CachePoolClearCommandTest.php @@ -44,7 +44,7 @@ public function testComplete(array $input, array $expectedSuggestions) $this->assertSame($expectedSuggestions, $suggestions); } - public static function provideCompletionSuggestions() + public static function provideCompletionSuggestions(): iterable { yield 'pool_name' => [ ['f'], diff --git a/Tests/Command/CachePoolDeleteCommandTest.php b/Tests/Command/CachePoolDeleteCommandTest.php index c581d3095..caa7eb550 100644 --- a/Tests/Command/CachePoolDeleteCommandTest.php +++ b/Tests/Command/CachePoolDeleteCommandTest.php @@ -98,7 +98,7 @@ public function testComplete(array $input, array $expectedSuggestions) $this->assertSame($expectedSuggestions, $suggestions); } - public static function provideCompletionSuggestions() + public static function provideCompletionSuggestions(): iterable { yield 'pool_name' => [ ['f'], diff --git a/Tests/Command/EventDispatcherDebugCommandTest.php b/Tests/Command/EventDispatcherDebugCommandTest.php index 331d8d44a..359196e11 100644 --- a/Tests/Command/EventDispatcherDebugCommandTest.php +++ b/Tests/Command/EventDispatcherDebugCommandTest.php @@ -32,7 +32,7 @@ public function testComplete(array $input, array $expectedSuggestions) $this->assertSame($expectedSuggestions, $suggestions); } - public static function provideCompletionSuggestions() + public static function provideCompletionSuggestions(): iterable { yield 'event' => [[''], [MessageEvent::class, 'console.command']]; yield 'event for other dispatcher' => [['--dispatcher', 'other_event_dispatcher', ''], ['other_event', 'App\OtherEvent']]; diff --git a/Tests/Command/SecretsRemoveCommandTest.php b/Tests/Command/SecretsRemoveCommandTest.php index 88c247ec6..2c12b6128 100644 --- a/Tests/Command/SecretsRemoveCommandTest.php +++ b/Tests/Command/SecretsRemoveCommandTest.php @@ -37,7 +37,7 @@ public function testComplete(bool $withLocalVault, array $input, array $expected $this->assertSame($expectedSuggestions, $suggestions); } - public static function provideCompletionSuggestions() + public static function provideCompletionSuggestions(): iterable { yield 'name' => [true, [''], ['SECRET', 'OTHER_SECRET']]; yield '--local name (with local vault)' => [true, ['--local', ''], ['SECRET']]; diff --git a/Tests/Command/SecretsSetCommandTest.php b/Tests/Command/SecretsSetCommandTest.php index 8e8e968c8..678fb417c 100644 --- a/Tests/Command/SecretsSetCommandTest.php +++ b/Tests/Command/SecretsSetCommandTest.php @@ -32,7 +32,7 @@ public function testComplete(array $input, array $expectedSuggestions) $this->assertSame($expectedSuggestions, $suggestions); } - public static function provideCompletionSuggestions() + public static function provideCompletionSuggestions(): iterable { yield 'name' => [[''], ['SECRET', 'OTHER_SECRET']]; yield '--local name (with local vault)' => [['--local', ''], ['SECRET', 'OTHER_SECRET']]; diff --git a/Tests/Command/TranslationDebugCommandTest.php b/Tests/Command/TranslationDebugCommandTest.php index 916967478..251d2fa6a 100644 --- a/Tests/Command/TranslationDebugCommandTest.php +++ b/Tests/Command/TranslationDebugCommandTest.php @@ -118,7 +118,6 @@ public function testDebugCustomDirectory() public function testDebugInvalidDirectory() { - $this->expectException(\InvalidArgumentException::class); $kernel = $this->createMock(KernelInterface::class); $kernel->expects($this->once()) ->method('getBundle') @@ -126,6 +125,9 @@ public function testDebugInvalidDirectory() ->willThrowException(new \InvalidArgumentException()); $tester = $this->createCommandTester([], [], $kernel); + + $this->expectException(\InvalidArgumentException::class); + $tester->execute(['locale' => 'en', 'bundle' => 'dir']); } @@ -269,7 +271,7 @@ function ($path, $catalogue) use ($extractedMessagesWithDomains) { $this->assertSame($expectedSuggestions, $suggestions); } - public static function provideCompletionSuggestions() + public static function provideCompletionSuggestions(): iterable { yield 'locale' => [ [''], diff --git a/Tests/Command/TranslationUpdateCommandCompletionTest.php b/Tests/Command/TranslationUpdateCommandCompletionTest.php index 90f88341b..ee80e1932 100644 --- a/Tests/Command/TranslationUpdateCommandCompletionTest.php +++ b/Tests/Command/TranslationUpdateCommandCompletionTest.php @@ -42,7 +42,7 @@ public function testComplete(array $input, array $expectedSuggestions) $this->assertSame($expectedSuggestions, $suggestions); } - public static function provideCompletionSuggestions() + public static function provideCompletionSuggestions(): iterable { $bundle = new ExtensionPresentBundle(); diff --git a/Tests/Command/YamlLintCommandTest.php b/Tests/Command/YamlLintCommandTest.php index 667c76a4a..08f4a7526 100644 --- a/Tests/Command/YamlLintCommandTest.php +++ b/Tests/Command/YamlLintCommandTest.php @@ -60,11 +60,12 @@ public function testLintIncorrectFile() public function testLintFileNotReadable() { - $this->expectException(\RuntimeException::class); $tester = $this->createCommandTester(); $filename = $this->createFile(''); unlink($filename); + $this->expectException(\RuntimeException::class); + $tester->execute(['filename' => $filename], ['decorated' => false]); } diff --git a/Tests/Controller/AbstractControllerTest.php b/Tests/Controller/AbstractControllerTest.php index efa9c7bec..f806c540b 100644 --- a/Tests/Controller/AbstractControllerTest.php +++ b/Tests/Controller/AbstractControllerTest.php @@ -92,13 +92,14 @@ public function testGetParameter() public function testMissingParameterBag() { - $this->expectException(ServiceNotFoundException::class); - $this->expectExceptionMessage('TestAbstractController::getParameter()" method is missing a parameter bag'); $container = new Container(); $controller = $this->createController(); $controller->setContainer($container); + $this->expectException(ServiceNotFoundException::class); + $this->expectExceptionMessage('TestAbstractController::getParameter()" method is missing a parameter bag'); + $controller->getParameter('foo'); } @@ -146,12 +147,12 @@ public function testGetUserWithEmptyTokenStorage() public function testGetUserWithEmptyContainer() { - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('The SecurityBundle is not registered in your application.'); - $controller = $this->createController(); $controller->setContainer(new Container()); + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('The SecurityBundle is not registered in your application.'); + $controller->getUser(); } @@ -327,10 +328,10 @@ public function testFileFromPathWithCustomizedFileName() public function testFileWhichDoesNotExist() { - $this->expectException(FileNotFoundException::class); - $controller = $this->createController(); + $this->expectException(FileNotFoundException::class); + $controller->file('some-file.txt', 'test.php'); } @@ -350,8 +351,6 @@ public function testIsGranted() public function testdenyAccessUnlessGranted() { - $this->expectException(AccessDeniedException::class); - $authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class); $authorizationChecker->expects($this->once())->method('isGranted')->willReturn(false); @@ -361,6 +360,8 @@ public function testdenyAccessUnlessGranted() $controller = $this->createController(); $controller->setContainer($container); + $this->expectException(AccessDeniedException::class); + $controller->denyAccessUnlessGranted('foo'); } @@ -386,7 +387,7 @@ public function testdenyAccessUnlessGrantedSetsAttributesAsArray($attribute, $ex } } - public static function provideDenyAccessUnlessGrantedSetsAttributesAsArray() + public static function provideDenyAccessUnlessGrantedSetsAttributesAsArray(): array { $obj = new \stdClass(); $obj->foo = 'bar'; diff --git a/Tests/Controller/RedirectControllerTest.php b/Tests/Controller/RedirectControllerTest.php index de72396df..b2da9ef58 100644 --- a/Tests/Controller/RedirectControllerTest.php +++ b/Tests/Controller/RedirectControllerTest.php @@ -103,7 +103,7 @@ public function testRoute($permanent, $keepRequestMethod, $keepQueryParams, $ign $this->assertEquals($expectedCode, $returnResponse->getStatusCode()); } - public static function provider() + public static function provider(): array { return [ [true, false, false, false, 301, ['additional-parameter' => 'value']], @@ -210,7 +210,7 @@ public function testUrlRedirectDefaultPorts() $this->assertRedirectUrl($returnValue, $expectedUrl); } - public static function urlRedirectProvider() + public static function urlRedirectProvider(): array { return [ // Standard ports @@ -262,7 +262,7 @@ public function testUrlRedirect($scheme, $httpPort, $httpsPort, $requestScheme, $this->assertRedirectUrl($returnValue, $expectedUrl); } - public static function pathQueryParamsProvider() + public static function pathQueryParamsProvider(): array { return [ ['http://www.example.com/base/redirect-path', '/redirect-path', ''], diff --git a/Tests/Controller/TemplateControllerTest.php b/Tests/Controller/TemplateControllerTest.php index f558333f3..c972151d2 100644 --- a/Tests/Controller/TemplateControllerTest.php +++ b/Tests/Controller/TemplateControllerTest.php @@ -32,13 +32,23 @@ public function testTwig() $this->assertEquals('bar', $controller('mytemplate')->getContent()); } - public function testNoTwig() + public function testNoTwigTemplateActionMethod() { + $controller = new TemplateController(); + $this->expectException(\LogicException::class); $this->expectExceptionMessage('You cannot use the TemplateController if the Twig Bundle is not available. Try running "composer require symfony/twig-bundle".'); - $controller = new TemplateController(); $controller->templateAction('mytemplate')->getContent(); + } + + public function testNoTwigInvokeMethod() + { + $controller = new TemplateController(); + + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('You cannot use the TemplateController if the Twig Bundle is not available. Try running "composer require symfony/twig-bundle".'); + $controller('mytemplate')->getContent(); } diff --git a/Tests/DependencyInjection/Compiler/ProfilerPassTest.php b/Tests/DependencyInjection/Compiler/ProfilerPassTest.php index eef047dfa..469825782 100644 --- a/Tests/DependencyInjection/Compiler/ProfilerPassTest.php +++ b/Tests/DependencyInjection/Compiler/ProfilerPassTest.php @@ -34,13 +34,15 @@ class ProfilerPassTest extends TestCase */ public function testTemplateNoIdThrowsException() { - $this->expectException(\InvalidArgumentException::class); $builder = new ContainerBuilder(); $builder->register('profiler', 'ProfilerClass'); $builder->register('my_collector_service') ->addTag('data_collector', ['template' => 'foo']); $profilerPass = new ProfilerPass(); + + $this->expectException(\InvalidArgumentException::class); + $profilerPass->process($builder); } diff --git a/Tests/DependencyInjection/Compiler/WorkflowGuardListenerPassTest.php b/Tests/DependencyInjection/Compiler/WorkflowGuardListenerPassTest.php index 4c3327847..15016df36 100644 --- a/Tests/DependencyInjection/Compiler/WorkflowGuardListenerPassTest.php +++ b/Tests/DependencyInjection/Compiler/WorkflowGuardListenerPassTest.php @@ -58,49 +58,53 @@ public function testNoExeptionIfAllDependenciesArePresent() public function testExceptionIfTheTokenStorageServiceIsNotPresent() { - $this->expectException(LogicException::class); - $this->expectExceptionMessage('The "security.token_storage" service is needed to be able to use the workflow guard listener.'); $this->container->setParameter('workflow.has_guard_listeners', true); $this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class); $this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class); $this->container->register('security.role_hierarchy', RoleHierarchy::class); + $this->expectException(LogicException::class); + $this->expectExceptionMessage('The "security.token_storage" service is needed to be able to use the workflow guard listener.'); + $this->compilerPass->process($this->container); } public function testExceptionIfTheAuthorizationCheckerServiceIsNotPresent() { - $this->expectException(LogicException::class); - $this->expectExceptionMessage('The "security.authorization_checker" service is needed to be able to use the workflow guard listener.'); $this->container->setParameter('workflow.has_guard_listeners', true); $this->container->register('security.token_storage', TokenStorageInterface::class); $this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class); $this->container->register('security.role_hierarchy', RoleHierarchy::class); + $this->expectException(LogicException::class); + $this->expectExceptionMessage('The "security.authorization_checker" service is needed to be able to use the workflow guard listener.'); + $this->compilerPass->process($this->container); } public function testExceptionIfTheAuthenticationTrustResolverServiceIsNotPresent() { - $this->expectException(LogicException::class); - $this->expectExceptionMessage('The "security.authentication.trust_resolver" service is needed to be able to use the workflow guard listener.'); $this->container->setParameter('workflow.has_guard_listeners', true); $this->container->register('security.token_storage', TokenStorageInterface::class); $this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class); $this->container->register('security.role_hierarchy', RoleHierarchy::class); + $this->expectException(LogicException::class); + $this->expectExceptionMessage('The "security.authentication.trust_resolver" service is needed to be able to use the workflow guard listener.'); + $this->compilerPass->process($this->container); } public function testExceptionIfTheRoleHierarchyServiceIsNotPresent() { - $this->expectException(LogicException::class); - $this->expectExceptionMessage('The "security.role_hierarchy" service is needed to be able to use the workflow guard listener.'); $this->container->setParameter('workflow.has_guard_listeners', true); $this->container->register('security.token_storage', TokenStorageInterface::class); $this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class); $this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class); + $this->expectException(LogicException::class); + $this->expectExceptionMessage('The "security.role_hierarchy" service is needed to be able to use the workflow guard listener.'); + $this->compilerPass->process($this->container); } } diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index 6ed9c2478..ab08f4765 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -61,8 +61,10 @@ public function getTestValidSessionName() */ public function testInvalidSessionName($sessionName) { - $this->expectException(InvalidConfigurationException::class); $processor = new Processor(); + + $this->expectException(InvalidConfigurationException::class); + $processor->processConfiguration( new Configuration(true), [[ @@ -163,7 +165,7 @@ public function testValidAssetsPackageNameConfiguration($packageName) $this->assertArrayHasKey($packageName, $config['assets']['packages']); } - public static function provideValidAssetsPackageNameConfigurationTests() + public static function provideValidAssetsPackageNameConfigurationTests(): array { return [ ['foobar'], @@ -177,11 +179,12 @@ public static function provideValidAssetsPackageNameConfigurationTests() */ public function testInvalidAssetsConfiguration(array $assetConfig, $expectedMessage) { + $processor = new Processor(); + $configuration = new Configuration(true); + $this->expectException(InvalidConfigurationException::class); $this->expectExceptionMessage($expectedMessage); - $processor = new Processor(); - $configuration = new Configuration(true); $processor->processConfiguration($configuration, [ [ 'http_method_override' => false, @@ -192,7 +195,7 @@ public function testInvalidAssetsConfiguration(array $assetConfig, $expectedMess ]); } - public static function provideInvalidAssetConfigurationTests() + public static function provideInvalidAssetConfigurationTests(): iterable { // helper to turn config into embedded package config $createPackageConfig = fn (array $packageConfig) => [ @@ -246,7 +249,7 @@ public function testValidLockConfiguration($lockConfig, $processedConfig) $this->assertEquals($processedConfig, $config['lock']); } - public static function provideValidLockConfigurationTests() + public static function provideValidLockConfigurationTests(): iterable { yield [null, ['enabled' => true, 'resources' => ['default' => [class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphore' : 'flock']]]]; diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 6e3db3a0f..70e013292 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -634,9 +634,11 @@ public function testRouter() public function testRouterRequiresResourceOption() { - $this->expectException(InvalidConfigurationException::class); $container = $this->createContainer(); $loader = new FrameworkExtension(); + + $this->expectException(InvalidConfigurationException::class); + $loader->load([['http_method_override' => false, 'handle_all_throwables' => true, 'php_errors' => ['log' => true], 'router' => true]], $container); } diff --git a/Tests/Fixtures/Messenger/DummyTask.php b/Tests/Fixtures/Messenger/DummyTask.php index ef8e986fa..94773b4e1 100644 --- a/Tests/Fixtures/Messenger/DummyTask.php +++ b/Tests/Fixtures/Messenger/DummyTask.php @@ -8,13 +8,13 @@ #[AsCronTask(expression: '* * * * *', arguments: [1], schedule: 'dummy_task')] #[AsCronTask(expression: '0 * * * *', timezone: 'Europe/Berlin', arguments: ['2'], schedule: 'dummy_task', method: 'method2')] #[AsPeriodicTask(frequency: 5, arguments: [3], schedule: 'dummy_task')] -#[AsPeriodicTask(frequency: '1 day', from: '00:00:00', jitter: 60, arguments: ['4'], schedule: 'dummy_task', method: 'method4')] +#[AsPeriodicTask(frequency: '1 day', from: '2023-10-25 09:59:00Z', jitter: 60, arguments: ['4'], schedule: 'dummy_task', method: 'method4')] class DummyTask { public static array $calls = []; - #[AsPeriodicTask(frequency: '1 hour', from: '09:00:00', until: '17:00:00', arguments: ['b' => 6, 'a' => '5'], schedule: 'dummy_task')] - #[AsCronTask(expression: '0 0 * * *', arguments: ['7', 8], schedule: 'dummy_task')] + #[AsPeriodicTask(frequency: '1 hour', from: '2023-10-26 09:00:00Z', until: '2023-10-26 17:00:00Z', arguments: ['b' => 6, 'a' => '5'], schedule: 'dummy_task')] + #[AsCronTask(expression: '0 10 * * *', arguments: ['7', 8], schedule: 'dummy_task')] public function attributesOnMethod(string $a, int $b): void { self::$calls[__FUNCTION__][] = [$a, $b]; diff --git a/Tests/Functional/Bundle/RoutingConditionServiceBundle/Controller/DefaultController.php b/Tests/Functional/Bundle/RoutingConditionServiceBundle/Controller/DefaultController.php index feaa957e7..573c81cb4 100644 --- a/Tests/Functional/Bundle/RoutingConditionServiceBundle/Controller/DefaultController.php +++ b/Tests/Functional/Bundle/RoutingConditionServiceBundle/Controller/DefaultController.php @@ -12,7 +12,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\RoutingConditionServiceBundle\Controller; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; class DefaultController { diff --git a/Tests/Functional/Bundle/TestBundle/Controller/AnnotatedController.php b/Tests/Functional/Bundle/TestBundle/Controller/AnnotatedController.php index 2bee3d0b8..1fdb31dcc 100644 --- a/Tests/Functional/Bundle/TestBundle/Controller/AnnotatedController.php +++ b/Tests/Functional/Bundle/TestBundle/Controller/AnnotatedController.php @@ -13,7 +13,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; class AnnotatedController { diff --git a/Tests/Functional/Bundle/TestBundle/Controller/UidController.php b/Tests/Functional/Bundle/TestBundle/Controller/UidController.php index 2653f9fbc..91f681414 100644 --- a/Tests/Functional/Bundle/TestBundle/Controller/UidController.php +++ b/Tests/Functional/Bundle/TestBundle/Controller/UidController.php @@ -12,7 +12,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Uid\Ulid; use Symfony\Component\Uid\UuidV1; diff --git a/Tests/Functional/Bundle/TestBundle/TestBundle.php b/Tests/Functional/Bundle/TestBundle/TestBundle.php index 7dbbb096b..d0c6588b0 100644 --- a/Tests/Functional/Bundle/TestBundle/TestBundle.php +++ b/Tests/Functional/Bundle/TestBundle/TestBundle.php @@ -11,7 +11,6 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle; -use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\DependencyInjection\AnnotationReaderPass; use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\DependencyInjection\Config\CustomConfig; use Symfony\Component\DependencyInjection\Compiler\CheckTypeDeclarationsPass; use Symfony\Component\DependencyInjection\Compiler\PassConfig; diff --git a/Tests/Functional/ConfigDebugCommandTest.php b/Tests/Functional/ConfigDebugCommandTest.php index f81c32f66..c9bfba234 100644 --- a/Tests/Functional/ConfigDebugCommandTest.php +++ b/Tests/Functional/ConfigDebugCommandTest.php @@ -141,7 +141,7 @@ public function testParametersValuesAreFullyResolved(bool $debug) $this->assertStringContainsString('locale: en', $tester->getDisplay()); $this->assertStringContainsString('secret: test', $tester->getDisplay()); $this->assertStringContainsString('cookie_httponly: true', $tester->getDisplay()); - $this->assertStringContainsString('ide: '.($_ENV['SYMFONY_IDE'] ?? $_SERVER['SYMFONY_IDE'] ?? 'null'), $tester->getDisplay()); + $this->assertStringContainsString('ide: '.$debug ? ($_ENV['SYMFONY_IDE'] ?? $_SERVER['SYMFONY_IDE'] ?? 'null') : 'null', $tester->getDisplay()); } /** diff --git a/Tests/Functional/ContainerDebugCommandTest.php b/Tests/Functional/ContainerDebugCommandTest.php index 8e73cebc7..efbc1f54a 100644 --- a/Tests/Functional/ContainerDebugCommandTest.php +++ b/Tests/Functional/ContainerDebugCommandTest.php @@ -269,7 +269,7 @@ public function testGetDeprecationNoFile() $this->assertStringContainsString('[WARNING] The deprecation file does not exist', $tester->getDisplay()); } - public static function provideIgnoreBackslashWhenFindingService() + public static function provideIgnoreBackslashWhenFindingService(): array { return [ [BackslashClass::class], @@ -297,7 +297,7 @@ public function testComplete(array $input, array $expectedSuggestions, array $no } } - public static function provideCompletionSuggestions() + public static function provideCompletionSuggestions(): iterable { $serviceId = 'console.command.container_debug'; $hiddenServiceId = '.console.command.container_debug.lazy'; diff --git a/Tests/Functional/RouterDebugCommandTest.php b/Tests/Functional/RouterDebugCommandTest.php index 314915d8a..614078804 100644 --- a/Tests/Functional/RouterDebugCommandTest.php +++ b/Tests/Functional/RouterDebugCommandTest.php @@ -81,9 +81,11 @@ public function testSearchMultipleRoutesWithoutInteraction() public function testSearchWithThrow() { + $tester = $this->createCommandTester(); + $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('The route "gerard" does not exist.'); - $tester = $this->createCommandTester(); + $tester->execute(['name' => 'gerard'], ['interactive' => true]); } @@ -110,7 +112,7 @@ public function testShowAliases(string $format) $this->assertStringContainsString('my_custom_alias', $tester->getDisplay()); } - public static function provideCompletionSuggestions() + public static function provideCompletionSuggestions(): iterable { yield 'option --format' => [ ['--format', ''], diff --git a/Tests/Functional/SchedulerTest.php b/Tests/Functional/SchedulerTest.php index 5aef74f47..7b3cd197d 100644 --- a/Tests/Functional/SchedulerTest.php +++ b/Tests/Functional/SchedulerTest.php @@ -13,9 +13,12 @@ use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\BarMessage; use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\DummySchedule; +use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\DummyTask; use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\FooMessage; use Symfony\Component\Clock\MockClock; use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\Messenger\MessageBusInterface; +use Symfony\Component\Messenger\Stamp\ReceivedStamp; use Symfony\Component\Scheduler\Messenger\SchedulerTransport; use Symfony\Component\Scheduler\RecurringMessage; @@ -54,6 +57,36 @@ public function testScheduler() $this->assertSame([$foo, $bar, $foo, $bar], $fetchMessages(600.0)); } + public function testAutoconfiguredScheduler() + { + $container = self::getContainer(); + $container->set('clock', $clock = new MockClock('2023-10-26T08:59:59Z')); + + $this->assertTrue($container->get('receivers')->has('scheduler_dummy_task')); + $this->assertInstanceOf(SchedulerTransport::class, $cron = $container->get('receivers')->get('scheduler_dummy_task')); + $bus = $container->get(MessageBusInterface::class); + + $getCalls = static function (float $sleep) use ($clock, $cron, $bus) { + DummyTask::$calls = []; + $clock->sleep($sleep); + foreach ($cron->get() as $message) { + $bus->dispatch($message->with(new ReceivedStamp('scheduler_dummy_task'))); + } + + return DummyTask::$calls; + }; + + $this->assertSame([], $getCalls(0)); + $this->assertSame(['__invoke' => [[1]], 'method2' => [['2']], 'attributesOnMethod' => [['5', 6]]], $getCalls(1)); + $this->assertSame(['__invoke' => [[3]]], $getCalls(5)); + $this->assertSame(['__invoke' => [[3]]], $getCalls(5)); + $calls = $getCalls(3595); + $this->assertCount(779, $calls['__invoke']); + $this->assertSame([['2']], $calls['method2']); + $this->assertSame([['4']], $calls['method4']); + $this->assertSame([['5', 6], ['7', 8]], $calls['attributesOnMethod']); + } + protected static function createKernel(array $options = []): KernelInterface { return parent::createKernel(['test_case' => 'Scheduler'] + $options); diff --git a/Tests/Kernel/flex-style/src/FlexStyleMicroKernel.php b/Tests/Kernel/flex-style/src/FlexStyleMicroKernel.php index 11f756ee0..6f7c84d8b 100644 --- a/Tests/Kernel/flex-style/src/FlexStyleMicroKernel.php +++ b/Tests/Kernel/flex-style/src/FlexStyleMicroKernel.php @@ -18,7 +18,7 @@ use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Kernel; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; class FlexStyleMicroKernel extends Kernel diff --git a/Tests/Routing/RouterTest.php b/Tests/Routing/RouterTest.php index d89a09489..3e185b54c 100644 --- a/Tests/Routing/RouterTest.php +++ b/Tests/Routing/RouterTest.php @@ -299,25 +299,29 @@ public function testPatternPlaceholdersWithSfContainer() public function testEnvPlaceholders() { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Using "%env(FOO)%" is not allowed in routing configuration.'); $routes = new RouteCollection(); $routes->add('foo', new Route('/%env(FOO)%')); $router = new Router($this->getPsr11ServiceContainer($routes), 'foo', [], null, $this->getParameterBag()); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Using "%env(FOO)%" is not allowed in routing configuration.'); + $router->getRouteCollection(); } public function testEnvPlaceholdersWithSfContainer() { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Using "%env(FOO)%" is not allowed in routing configuration.'); $routes = new RouteCollection(); $routes->add('foo', new Route('/%env(FOO)%')); $router = new Router($this->getServiceContainer($routes), 'foo'); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Using "%env(FOO)%" is not allowed in routing configuration.'); + $router->getRouteCollection(); } @@ -381,8 +385,6 @@ public function testHostPlaceholdersWithSfContainer() public function testExceptionOnNonExistentParameterWithSfContainer() { - $this->expectException(ParameterNotFoundException::class); - $this->expectExceptionMessage('You have requested a non-existent parameter "nope".'); $routes = new RouteCollection(); $routes->add('foo', new Route('/%nope%')); @@ -390,13 +392,15 @@ public function testExceptionOnNonExistentParameterWithSfContainer() $sc = $this->getServiceContainer($routes); $router = new Router($sc, 'foo'); + + $this->expectException(ParameterNotFoundException::class); + $this->expectExceptionMessage('You have requested a non-existent parameter "nope".'); + $router->getRouteCollection()->get('foo'); } public function testExceptionOnNonStringParameter() { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The container parameter "object", used in the route configuration value "/%object%", must be a string or numeric, but it is of type "stdClass".'); $routes = new RouteCollection(); $routes->add('foo', new Route('/%object%')); @@ -405,6 +409,10 @@ public function testExceptionOnNonStringParameter() $parameters = $this->getParameterBag(['object' => new \stdClass()]); $router = new Router($sc, 'foo', [], null, $parameters); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The container parameter "object", used in the route configuration value "/%object%", must be a string or numeric, but it is of type "stdClass".'); + $router->getRouteCollection()->get('foo'); } diff --git a/composer.json b/composer.json index 7d9bf9cdb..e63a672e8 100644 --- a/composer.json +++ b/composer.json @@ -35,6 +35,7 @@ "require-dev": { "doctrine/annotations": "^1.13.1|^2", "doctrine/persistence": "^1.3|^2|^3", + "dragonmantank/cron-expression": "^3.1", "seld/jsonlint": "^1.10", "symfony/asset": "^5.4|^6.0|^7.0", "symfony/asset-mapper": "^6.4|^7.0",