8000 Add basic schedule component · symfony/symfony@b65a683 · GitHub
[go: up one dir, main page]

Skip to content

Commit b65a683

Browse files
committed
Add basic schedule component
1 parent f9d5e24 commit b65a683

33 files changed

+1019
-7
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ class UnusedTagsPass implements CompilerPassInterface
6565
'messenger.bus',
6666
'messenger.message_handler',
6767
'messenger.receiver',
68+
'messenger.schedule',
6869
'messenger.transport_factory',
6970
'mime.mime_type_guesser',
7071
'monolog.logger',

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
use Symfony\Component\Messenger\Bridge\Amqp\Transport\AmqpTransportFactory;
110110
use Symfony\Component\Messenger\Bridge\Beanstalkd\Transport\BeanstalkdTransportFactory;
111111
use Symfony\Component\Messenger\Bridge\Redis\Transport\RedisTransportFactory;
112+
use Symfony\Component\Messenger\Bridge\Schedule\Attribute\AsSchedule;
112113
use Symfony\Component\Messenger\Command\StatsCommand;
113114
use Symfony\Component\Messenger\Handler\BatchHandlerInterface;
114115
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
@@ -675,6 +676,9 @@ public function load(array $configs, ContainerBuilder $container)
675676
}
676677
$definition->addTag('messenger.message_handler', $tagAttributes);
677678
});
679+
$container->registerAttributeForAutoconfiguration(AsSchedule::class, static function (ChildDefinition $definition, AsSchedule $attribute): void {
680+
$definition->addTag('messenger.schedule', get_object_vars($attribute));
681+
});
678682

679683
if (!$container->getParameter('kernel.debug')) {
680684
// remove tagged iterator argument for resource checkers
@@ -2066,6 +2070,7 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder
20662070
$container->removeDefinition('messenger.transport.redis.factory');
20672071
$container->removeDefinition('messenger.transport.sqs.factory');
20682072
$container->removeDefinition('messenger.transport.beanstalkd.factory');
2073+
$container->removeDefinition('messenger.transport.schedule.factory');
20692074
$container->removeAlias(SerializerInterface::class);
20702075
} else {
20712076
$container->getDefinition('messenger.transport.symfony_serializer')

src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Component\Messenger\Bridge\Amqp\Transport\AmqpTransportFactory;
1717
use Symfony\Component\Messenger\Bridge\Beanstalkd\Transport\BeanstalkdTransportFactory;
1818
use Symfony\Component\Messenger\Bridge\Redis\Transport\RedisTransportFactory;
19+
use Symfony\Component\Messenger\Bridge\Schedule\Transport\ScheduleTransportFactory;
1920
use Symfony\Component\Messenger\EventListener\AddErrorDetailsStampListener;
2021
use Symfony\Component\Messenger\EventListener\DispatchPcntlSignalListener;
2122
use Symfony\Component\Messenger\EventListener\ResetServicesListener;
@@ -143,6 +144,12 @@
143144

144145
->set('messenger.transport.beanstalkd.factory', BeanstalkdTransportFactory::class)
145146

147+
->set('messenger.transport.schedule.factory', ScheduleTransportFactory::class)
148+
->args([
149+
tagged_locator('messenger.schedule', 'name'),
150+
])
151+
->tag('messenger.transport_factory')
152+
146153
// retry
147154
->set('messenger.retry_strategy_locator', ServiceLocator::class)
148155
->args([
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
$container->loadFromExtension('framework', [
4+
'http_method_override' => false,
5+
'messenger' => [
6+
'transports' => [
7+
'schedule' => 'schedule://default',
8+
],
9+
],
10+
]);

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transports.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
'failed' => 'in-memory:///',
2525
'redis' => 'redis://127.0.0.1:6379/messages',
2626
'beanstalkd' => 'beanstalkd://127.0.0.1:11300',
27+
'schedule' => 'schedule://default',
2728
],
2829
],
2930
]);
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<container xmlns="http://symfony.com/schema/dic/services"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xmlns:framework="http://symfony.com/schema/dic/symfony"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
6+
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
7+
8+
<framework:config http-method-override="false">
9+
<framework:messenger>
10+
<framework:transport name="schedule" dsn="schedule://default" />
11+
</framework:messenger>
12+
</framework:config>
13+
</container>

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transports.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
<framework:transport name="failed" dsn="in-memory:///" />
2222
<framework:transport name="redis" dsn="redis://127.0.0.1:6379/messages" />
2323
<framework:transport name="beanstalkd" dsn="beanstalkd://127.0.0.1:11300" />
24+
<framework:transport name="schedule" dsn="schedule://default" />
2425
</framework:messenger>
2526
</framework:config>
2627
</container>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
framework:
2+
http_method_override: false
3+
messenger:
4+
transports:
5+
schedule: 'schedule://default'

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transports.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ framework:
2121
failed: 'in-memory:///'
2222
redis: 'redis://127.0.0.1:6379/messages'
2323
beanstalkd: 'beanstalkd://127.0.0.1:11300'
24+
schedule: 'schedule://default'

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php

Lines changed: 92 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
1818
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddAnnotationsCachedReaderPass;
1919
use Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension;
20+
use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\BarMessage;
21+
use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\DefaultSchedule;
2022
use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\DummyMessage;
23+
use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\FooMessage;
2124
use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
2225
use Symfony\Bundle\FullStack;
2326
use Symfony\Component\Cache\Adapter\AdapterInterface;
@@ -30,13 +33,19 @@
3033
use Symfony\Component\Cache\Adapter\RedisTagAwareAdapter;
3134
use Symfony\Component\Cache\Adapter\TagAwareAdapter;
3235
use Symfony\Component\Cache\DependencyInjection\CachePoolPass;
36+
use Symfony\Component\Clock\MockClock;
3337
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
3438
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
3539
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
3640
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
3741
use Symfony\Component\DependencyInjection\ChildDefinition;
42+
use Symfony\Component\DependencyInjection\Compiler\AttributeAutoconfigurationPass;
3843
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
44+
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
45+
use Symfony\Component\DependencyInjection\Compiler\ResolveChildDefinitionsPass;
3946
use Symfony\Component\DependencyInjection\Compiler\ResolveInstanceofConditionalsPass;
47+
use Symfony\Component\DependencyInjection\Compiler\ResolveTaggedIteratorArgumentPass;
48+
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
4049
use Symfony\Component\DependencyInjection\ContainerBuilder;
4150
use Symfony\Component\DependencyInjection\ContainerInterface;
4251
use Symfony\Component\DependencyInjection\Definition;
@@ -55,6 +64,10 @@
5564
use Symfony\Component\HttpClient\ScopingHttpClient;
5665
use Symfony\Component\HttpKernel\DependencyInjection\LoggerPass;
5766
use Symfony\Component\HttpKernel\Fragment\FragmentUriGeneratorInterface;
67+
use Symfony\Component\Messenger\Bridge\Schedule\Transport\PeriodicalJob;
68+
use Symfony\Component\Messenger\Bridge\Schedule\Transport\Schedule;
69+
use Symfony\Component\Messenger\Bridge\Schedule\Transport\ScheduleTransport;
70+
use Symfony\Component\Messenger\DependencyInjection\MessengerPass;
5871
use Symfony\Component\Messenger\Transport\TransportFactory;
5972
use Symfony\Component\Notifier\ChatterInterface;
6073
use Symfony\Component\Notifier\TexterInterface;
@@ -740,6 +753,13 @@ public function testWebLink()
740753
public function testMessengerServicesRemovedWhenDisabled()
741754
{
742755
$container = $this->createContainerFromFile('messenger_disabled');
756+
$messengerDefinitions = array_filter(
757+
$container->getDefinitions(),
758+
static fn ($name) => str_starts_with($name, 'messenger.'),
759+
\ARRAY_FILTER_USE_KEY
760+
);
761+
762+
$this->assertEmpty($messengerDefinitions);
743763
$this->assertFalse($container->hasDefinition('console.command.messenger_consume_messages'));
744764
$this->assertFalse($container->hasDefinition('console.command.messenger_debug'));
745765
$this->assertFalse($container->hasDefinition('console.command.messenger_stop_workers'));
@@ -772,14 +792,28 @@ public function testMessengerWithExplictResetOnMessageLegacy()
772792

773793
public function testMessenger()
774794
{
775-
$container = $this->createContainerFromFile('messenger');
795+
$container = $this->createContainerFromFile('messenger', [], true, false);
796+
$container->addCompilerPass(new ResolveTaggedIteratorArgumentPass());
797+
$container->compile();
798+
799+
$expectedFactories = [
800+
new Reference('messenger.transport.amqp.factory'),
801+
new Reference('messenger.transport.redis.factory'),
802+
new Reference('messenger.transport.sync.factory'),
803+
new Reference('messenger.transport.in_memory.factory'),
804+
new Reference('messenger.transport.sqs.factory'),
805+
new Reference('messenger.transport.beanstalkd.factory'),
806+
new Reference('messenger.transport.schedule.factory'),
807+
];
808+
809+
$this->assertTrue($container->hasDefinition('messenger.receiver_locator'));
776810
$this->assertTrue($container->hasDefinition('console.command.messenger_consume_messages'));
777811
$this->assertTrue($container->hasAlias('messenger.default_bus'));
778812
$this->assertTrue($container->getAlias('messenger.default_bus')->isPublic());
779-
$this->assertTrue($container->hasDefinition('messenger.transport.amqp.factory'));
780-
$this->assertTrue($container->hasDefinition('messenger.transport.redis.factory'));
781813
$this->assertTrue($container->hasDefinition('messenger.transport_factory'));
782814
$this->assertSame(TransportFactory::class, $container->getDefinition('messenger.transport_factory')->getClass());
815+
$this->assertInstanceOf(TaggedIteratorArgument::class, $container->getDefinition('messenger.transport_factory')->getArgument(0));
816+
$this->assertEquals($expectedFactories, $container->getDefinition('messenger.transport_factory')->getArgument(0)->getValues());
783817
$this->assertTrue($container->hasDefinition('messenger.listener.reset_services'));
784818
$this->assertSame('messenger.listener.reset_services', (string) $container->getDefinition('console.command.messenger_consume_messages')->getArgument(5));
785819
}
@@ -796,10 +830,7 @@ public function testMessengerWithoutConsole()
796830
$this->assertFalse($container->hasDefinition('console.command.messenger_consume_messages'));
797831
$this->assertTrue($container->hasAlias('messenger.default_bus'));
798832
$this->assertTrue($container->getAlias('messenger.default_bus')->isPublic());
799-
$this->assertTrue($container->hasDefinition('messenger.transport.amqp.factory'));
800-
$this->assertTrue($container->hasDefinition('messenger.transport.redis.factory'));
801833
$this->assertTrue($container->hasDefinition('messenger.transport_factory'));
802-
$this->assertSame(TransportFactory::class, $container->getDefinition('messenger.transport_factory')->getClass());
803834
$this->assertFalse($container->hasDefinition('messenger.listener.reset_services'));
804835
}
805836

@@ -924,6 +955,16 @@ public function testMessengerTransports()
924955

925956
$this->assertTrue($container->hasDefinition('messenger.transport.beanstalkd.factory'));
926957

958+
$this->assertTrue($container->hasDefinition('messenger.transport.schedule'));
959+
$transportFactory = $container->getDefinition('messenger.transport.schedule')->getFactory();
960+
$transportArguments = $container->getDefinition('messenger.transport.schedule')->getArguments();
961+
962+
$this->assertEquals([new Reference('messenger.transport_factory'), 'createTransport'], $transportFactory);
963+
$this->assertCount(3, $transportArguments);
964+
$this->assertSame('schedule://default', $transportArguments[0]);
965+
966+
$this->assertTrue($container->hasDefinition('messenger.transport.schedule.factory'));
967+
927968
$this->assertSame(10, $container->getDefinition('messenger.retry.multiplier_retry_strategy.customised')->getArgument(0));
928969
$this->assertSame(7, $container->getDefinition('messenger.retry.multiplier_retry_strategy.customised')->getArgument(1));
929970
$this->assertSame(3, $container->getDefinition('messenger.retry.multiplier_retry_strategy.customised')->getArgument(2));
@@ -937,6 +978,7 @@ public function testMessengerTransports()
937978
'default' => new Reference('messenger.transport.failed'),
938979
'failed' => new Reference('messenger.transport.failed'),
939980
'redis' => new Reference('messenger.transport.failed'),
981+
'schedule' => new Reference('messenger.transport.failed'),
940982
];
941983

942984
$failureTransportsReferences = array_map(function (ServiceClosureArgument $serviceClosureArgument) {
@@ -980,6 +1022,50 @@ public function testMessengerTransportConfiguration()
9801022
$this->assertSame(['enable_max_depth' => true], $serializerTransportDefinition->getArgument(2));
9811023
}
9821024

1025+
// fixme where to place it?
1026+
public function testMessengerScheduler()
1027+
{
1028+
DefaultSchedule::$schedule = new Schedule(
1029+
$clock = new MockClock('2020-01-01T00:09:59Z'),
1030+
PeriodicalJob::create($foo = new FooMessage(), 600, '2020-01-01T00:00:00Z'),
1031+
PeriodicalJob::create($bar = new BarMessage(), 600, '2020-01-01T00:01:00Z'),
1032+
);
1033+
1034+
$container = $this->createContainerFromFile('messenger_schedule', [], true, false);
1035+
$container->addCompilerPass(new AttributeAutoconfigurationPass());
1036+
$container->addCompilerPass(new ResolveInstanceofConditionalsPass());
1037+
$container->addCompilerPass(new ResolveChildDefinitionsPass(), PassConfig::TYPE_OPTIMIZE);
1038+
$container->addCompilerPass(new ResolveTaggedIteratorArgumentPass(), PassConfig::TYPE_OPTIMIZE);
1039+
$container->addCompilerPass(new ServiceLocatorTagPass(), PassConfig::TYPE_OPTIMIZE);
1040+
$container->addCompilerPass(new MessengerPass());
1041+
$container->register(DefaultSchedule::class, DefaultSchedule::class)
1042+
->setAutoconfigured(true);
1043+
$container->setAlias('receivers', 'messenger.receiver_locator')
1044+
->setPublic(true);
1045+
$container->compile();
1046+
1047+
$this->assertTrue($container->get('receivers')->has('schedule'));
1048+
$this->assertInstanceOf(ScheduleTransport::class, $cron = $container->get('receivers')->get('schedule'));
1049+
1050+
$fetchMessages = static function (float $sleep) use ($clock, $cron) {
1051+
if (0 < $sleep) {
1052+
$clock->sleep($sleep);
1053+
}
1054+
$messages = [];
1055+
foreach ($cron->get() as $key => $envelope) {
1056+
$messages[$key] = $envelope->getMessage();
1057+
}
1058+
1059+
return $messages;
1060+
};
1061+
1062+
$this->assertSame([], $fetchMessages(0.0));
1063+
$this->assertSame([$foo], $fetchMessages(1.0));
1064+
$this->assertSame([], $fetchMessages(1.0));
1065+
$this->assertSame([$bar], $fetchMessages(60.0));
1066+
$this->assertSame([$foo, $bar], $fetchMessages(600.0));
1067+
}
1068+
9831069
public function testMessengerWithMultipleBuses()
9841070
{
9851071
$container = $this->createContainerFromFile('messenger_multiple_buses');
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger;
4+
5+
use Symfony\Component\Messenger\Bridge\Schedule\Attribute\AsSchedule;
6+
7+
#[AsSchedule('custom')]
8+
class DefaultSchedule implements \IteratorAggregate
9+
{
10+
public static array|\Traversable $schedule = [];
11+
12+
public function getIterator(): \Generator
13+
{
14+
yield from static::$schedule;
15+
}
16+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/Tests export-ignore
2+
/phpunit.xml.dist export-ignore
3+
/.gitattributes export-ignore
4+
/.gitignore export-ignore
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
vendor/
2+
composer.lock
3+
phpunit.xml
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Messenger\Bridge\Schedule\Attribute;
13+
14+
/**
15+
* Service tag to autoconfigure schedules.
16+
*/
17+
#[\Attribute(\Attribute::TARGET_CLASS)]
18+
class AsSchedule
19+
{
20+
public function __construct(public $name = 'default')
21+
{
22+
}
23+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CHANGELOG
2+
=========
3+
4+
6.2
5+
---
6+
7+
* Add the component
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2022 Fabien Potencier
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is furnished
8+
to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
Schedule Messenger
2+
====================
3+
4+
Provides basic schedules through Symfony Messenger.
5+
6+
Full DSN with schedule name: `schedule://<name>`
7+
8+
Example
9+
-------
10+
11+
```php
12+
<?php // src/DefaultSchedule.php
13+
14+
use Symfony\Component\Clock\ClockInterface;
15+
use Symfony\Component\Messenger\Bridge\Schedule\Attribute\AsSchedule;
16+
use Symfony\Component\Messenger\Bridge\Schedule\Transport\PeriodicalJob;
17+
use Symfony\Component\Messenger\Bridge\Schedule\Transport\Schedule;
18+
19+
#[AsSchedule]
20+
class DefaultSchedule extends Schedule
21+
{
22+
public function __construct(ClockInterface $clock)
23+
{
24+
// do the MaintenanceJob every night at 3 a.m. UTC
25+
$jobs[] = PeriodicalJob::create(new MaintenanceJob, 'P1D', '03:00:00+00');
26+
27+
parent::__construct($clock, ...$jobs);
28+
}
29+
}
30+
```
31+
32+
Resources
33+
---------
34+
35+
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
36+
* [Report issues](https://github.com/symfony/symfony/issues) and
37+
[send Pull Requests](https://github.com/symfony/symfony/pulls)
38+
in the [main Symfony repository](https://github.com/symfony/symfony)

0 commit comments

Comments
 (0)
0