diff --git a/composer.json b/composer.json index cb477380a304a..de1a6521e1484 100644 --- a/composer.json +++ b/composer.json @@ -133,7 +133,7 @@ "doctrine/data-fixtures": "^1.1", "doctrine/dbal": "^2.13.1|^3.0", "doctrine/orm": "^2.12", - "dragonmantank/cron-expression": "^3", + "dragonmantank/cron-expression": "^3.1", "egulias/email-validator": "^2.1.10|^3.1|^4", "guzzlehttp/promises": "^1.4", "league/html-to-markdown": "^5.0", diff --git a/src/Symfony/Component/Scheduler/Generator/MessageGenerator.php b/src/Symfony/Component/Scheduler/Generator/MessageGenerator.php index d7a30a2347002..a5ac35dcdbd79 100644 --- a/src/Symfony/Component/Scheduler/Generator/MessageGenerator.php +++ b/src/Symfony/Component/Scheduler/Generator/MessageGenerator.php @@ -15,37 +15,39 @@ use Symfony\Component\Clock\Clock; use Symfony\Component\Scheduler\RecurringMessage; use Symfony\Component\Scheduler\Schedule; +use Symfony\Component\Scheduler\ScheduleProviderInterface; /** * @experimental */ final class MessageGenerator implements MessageGeneratorInterface { + private Schedule $schedule; private TriggerHeap $triggerHeap; private ?\DateTimeImmutable $waitUntil; - private CheckpointInterface $checkpoint; public function __construct( - private readonly Schedule $schedule, + private readonly ScheduleProviderInterface $scheduleProvider, private readonly string $name, private readonly ClockInterface $clock = new Clock(), - CheckpointInterface $checkpoint = null, + private ?CheckpointInterface $checkpoint = null, ) { $this->waitUntil = new \DateTimeImmutable('@0'); - $this->checkpoint = $checkpoint ?? new Checkpoint('scheduler_checkpoint_'.$this->name, $this->schedule->getLock(), $this->schedule->getState()); } public function getMessages(): \Generator { + $checkpoint = $this->checkpoint(); + if (!$this->waitUntil || $this->waitUntil > ($now = $this->clock->now()) - || !$this->checkpoint->acquire($now) + || !$checkpoint->acquire($now) ) { return; } - $lastTime = $this->checkpoint->time(); - $lastIndex = $this->checkpoint->index(); + $lastTime = $checkpoint->time(); + $lastIndex = $checkpoint->index(); $heap = $this->heap($lastTime); while (!$heap->isEmpty() && $heap->top()[0] <= $now) { @@ -71,13 +73,13 @@ public function getMessages(): \Generator if ($yield) { yield (new MessageContext($this->name, $id, $trigger, $time, $nextTime)) => $message; - $this->checkpoint->save($time, $index); + $checkpoint->save($time, $index); } } $this->waitUntil = $heap->isEmpty() ? null : $heap->top()[0]; - $this->checkpoint->release($now, $this->waitUntil); + $checkpoint->release($now, $this->waitUntil); } private function heap(\DateTimeImmutable $time): TriggerHeap @@ -88,7 +90,7 @@ private function heap(\DateTimeImmutable $time): TriggerHeap $heap = new TriggerHeap($time); - foreach ($this->schedule->getRecurringMessages() as $index => $recurringMessage) { + foreach ($this->schedule()->getRecurringMessages() as $index => $recurringMessage) { if (!$nextTime = $recurringMessage->getTrigger()->getNextRunDate($time)) { continue; } @@ -98,4 +100,14 @@ private function heap(\DateTimeImmutable $time): TriggerHeap return $this->triggerHeap = $heap; } + + private function schedule(): Schedule + { + return $this->schedule ??= $this->scheduleProvider->getSchedule(); + } + + private function checkpoint(): Checkpoint + { + return $this->checkpoint ??= new Checkpoint('scheduler_checkpoint_'.$this->name, $this->schedule()->getLock(), $this->schedule()->getState()); + } } diff --git a/src/Symfony/Component/Scheduler/Messenger/SchedulerTransportFactory.php b/src/Symfony/Component/Scheduler/Messenger/SchedulerTransportFactory.php index 3b7fc9b94503f..ab79ded2e6145 100644 --- a/src/Symfony/Component/Scheduler/Messenger/SchedulerTransportFactory.php +++ b/src/Symfony/Component/Scheduler/Messenger/SchedulerTransportFactory.php @@ -18,7 +18,7 @@ use Symfony\Component\Messenger\Transport\TransportFactoryInterface; use Symfony\Component\Scheduler\Exception\InvalidArgumentException; use Symfony\Component\Scheduler\Generator\MessageGenerator; -use Symfony\Component\Scheduler\Schedule; +use Symfony\Component\Scheduler\ScheduleProviderInterface; /** * @experimental @@ -43,10 +43,10 @@ public function createTransport(string $dsn, array $options, SerializerInterface throw new InvalidArgumentException(sprintf('The schedule "%s" is not found.', $scheduleName)); } - /** @var Schedule $schedule */ - $schedule = $this->scheduleProviders->get($scheduleName)->getSchedule(); + /** @var ScheduleProviderInterface $scheduleProvider */ + $scheduleProvider = $this->scheduleProviders->get($scheduleName); - return new SchedulerTransport(new MessageGenerator($schedule, $scheduleName, $this->clock)); + return new SchedulerTransport(new MessageGenerator($scheduleProvider, $scheduleName, $this->clock)); } public function supports(string $dsn, array $options): bool diff --git a/src/Symfony/Component/Scheduler/Tests/Generator/MessageGeneratorTest.php b/src/Symfony/Component/Scheduler/Tests/Generator/MessageGeneratorTest.php index 5f4f0bf21f1de..eb35766cf3961 100644 --- a/src/Symfony/Component/Scheduler/Tests/Generator/MessageGeneratorTest.php +++ b/src/Symfony/Component/Scheduler/Tests/Generator/MessageGeneratorTest.php @@ -18,6 +18,7 @@ use Symfony\Component\Scheduler\Generator\MessageGenerator; use Symfony\Component\Scheduler\RecurringMessage; use Symfony\Component\Scheduler\Schedule; +use Symfony\Component\Scheduler\ScheduleProviderInterface; use Symfony\Component\Scheduler\Trigger\TriggerInterface; class MessageGeneratorTest extends TestCase @@ -25,7 +26,7 @@ class MessageGeneratorTest extends TestCase /** * @dataProvider messagesProvider */ - public function testGetMessages(string $startTime, array $runs, array $schedule) + public function testGetMessagesFromSchedule(string $startTime, array $runs, array $schedule) { // for referencing $now = self::makeDateTime($startTime); @@ -43,7 +44,49 @@ public function testGetMessages(string $startTime, array $runs, array $schedule) $scheduler = new MessageGenerator($schedule, 'dummy', $clock); - // Warmup. The first run is always returns nothing. + // Warmup. The first run always returns nothing. + $this->assertSame([], iterator_to_array($scheduler->getMessages(), false)); + + foreach ($runs as $time => $expected) { + $now = self::makeDateTime($time); + $this->assertSame($expected, iterator_to_array($scheduler->getMessages(), false)); + } + } + + /** + * @dataProvider messagesProvider + */ + public function testGetMessagesFromScheduleProvider(string $startTime, array $runs, array $schedule) + { + // for referencing + $now = self::makeDateTime($startTime); + + $clock = $this->createMock(ClockInterface::class); + $clock->method('now')->willReturnReference($now); + + foreach ($schedule as $i => $s) { + if (\is_array($s)) { + $schedule[$i] = $this->createMessage(...$s); + } + } + + $scheduleProvider = new class($schedule) implements ScheduleProviderInterface { + public function __construct(private readonly array $schedule) + { + } + + public function getSchedule(): Schedule + { + $schedule = (new Schedule())->add(...$this->schedule); + $schedule->stateful(new ArrayAdapter()); + + return $schedule; + } + }; + + $scheduler = new MessageGenerator($scheduleProvider, 'dummy', $clock); + + // Warmup. The first run always returns nothing. $this->assertSame([], iterator_to_array($scheduler->getMessages(), false)); foreach ($runs as $time => $expected) { diff --git a/src/Symfony/Component/Scheduler/Tests/Messenger/SchedulerTransportFactoryTest.php b/src/Symfony/Component/Scheduler/Tests/Messenger/SchedulerTransportFactoryTest.php index 55767d6b4740c..64166e314ba75 100644 --- a/src/Symfony/Component/Scheduler/Tests/Messenger/SchedulerTransportFactoryTest.php +++ b/src/Symfony/Component/Scheduler/Tests/Messenger/SchedulerTransportFactoryTest.php @@ -36,13 +36,13 @@ public function testCreateTransport() $defaultRecurringMessage = RecurringMessage::trigger($trigger, (object) ['id' => 'default']); $customRecurringMessage = RecurringMessage::trigger($trigger, (object) ['id' => 'custom']); - $default = new SchedulerTransport(new MessageGenerator((new SomeScheduleProvider([$defaultRecurringMessage]))->getSchedule(), 'default', $clock)); - $custom = new SchedulerTransport(new MessageGenerator((new SomeScheduleProvider([$customRecurringMessage]))->getSchedule(), 'custom', $clock)); + $default = new SchedulerTransport(new MessageGenerator(new SomeScheduleProvider([$defaultRecurringMessage]), 'default', $clock)); + $custom = new SchedulerTransport(new MessageGenerator(new SomeScheduleProvider([$customRecurringMessage]), 'custom', $clock)); $factory = new SchedulerTransportFactory( new Container([ - 'default' => fn () => (new SomeScheduleProvider([$defaultRecurringMessage]))->getSchedule(), - 'custom' => fn () => (new SomeScheduleProvider([$customRecurringMessage]))->getSchedule(), + 'default' => fn () => new SomeScheduleProvider([$defaultRecurringMessage]), + 'custom' => fn () => new SomeScheduleProvider([$customRecurringMessage]), ]), $clock, ); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy