diff --git a/src/Symfony/Component/Scheduler/Generator/MessageContext.php b/src/Symfony/Component/Scheduler/Generator/MessageContext.php new file mode 100644 index 0000000000000..51caf94e9e042 --- /dev/null +++ b/src/Symfony/Component/Scheduler/Generator/MessageContext.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Scheduler\Generator; + +use Symfony\Component\Scheduler\Trigger\TriggerInterface; + +/** + * @author Tugdual Saunier + * + * @experimental + */ +final class MessageContext +{ + public function __construct( + public readonly TriggerInterface $trigger, + public readonly \DateTimeImmutable $triggeredAt, + public readonly \DateTimeImmutable|null $nextTriggerAt = null, + ) { + } +} diff --git a/src/Symfony/Component/Scheduler/Generator/MessageGenerator.php b/src/Symfony/Component/Scheduler/Generator/MessageGenerator.php index 2f9743c59bfc2..ffa5618332624 100644 --- a/src/Symfony/Component/Scheduler/Generator/MessageGenerator.php +++ b/src/Symfony/Component/Scheduler/Generator/MessageGenerator.php @@ -70,7 +70,7 @@ public function getMessages(): \Generator } if ($yield) { - yield $message; + yield (new MessageContext($trigger, $time, $nextTime)) => $message; $this->checkpoint->save($time, $index); } } diff --git a/src/Symfony/Component/Scheduler/Generator/MessageGeneratorInterface.php b/src/Symfony/Component/Scheduler/Generator/MessageGeneratorInterface.php index 8b2f7eeacc4ce..64835e19d6138 100644 --- a/src/Symfony/Component/Scheduler/Generator/MessageGeneratorInterface.php +++ b/src/Symfony/Component/Scheduler/Generator/MessageGeneratorInterface.php @@ -17,7 +17,7 @@ interface MessageGeneratorInterface { /** - * @return iterable + * @return iterable */ public function getMessages(): iterable; } diff --git a/src/Symfony/Component/Scheduler/Messenger/ScheduledStamp.php b/src/Symfony/Component/Scheduler/Messenger/ScheduledStamp.php index 4b1b5cf1b577d..79efc1121e581 100644 --- a/src/Symfony/Component/Scheduler/Messenger/ScheduledStamp.php +++ b/src/Symfony/Component/Scheduler/Messenger/ScheduledStamp.php @@ -12,10 +12,14 @@ namespace Symfony\Component\Scheduler\Messenger; use Symfony\Component\Messenger\Stamp\NonSendableStampInterface; +use Symfony\Component\Scheduler\Generator\MessageContext; /** * @experimental */ final class ScheduledStamp implements NonSendableStampInterface { + public function __construct(public readonly MessageContext $messageContext) + { + } } diff --git a/src/Symfony/Component/Scheduler/Messenger/SchedulerTransport.php b/src/Symfony/Component/Scheduler/Messenger/SchedulerTransport.php index 9625e8c41bba9..414945e617c83 100644 --- a/src/Symfony/Component/Scheduler/Messenger/SchedulerTransport.php +++ b/src/Symfony/Component/Scheduler/Messenger/SchedulerTransport.php @@ -28,8 +28,8 @@ public function __construct( public function get(): iterable { - foreach ($this->messageGenerator->getMessages() as $message) { - yield Envelope::wrap($message, [new ScheduledStamp()]); + foreach ($this->messageGenerator->getMessages() as $context => $message) { + yield Envelope::wrap($message, [new ScheduledStamp($context)]); } } diff --git a/src/Symfony/Component/Scheduler/Tests/Generator/MessageGeneratorTest.php b/src/Symfony/Component/Scheduler/Tests/Generator/MessageGeneratorTest.php index 8b6900b8bb037..1926bbb70fb7f 100644 --- a/src/Symfony/Component/Scheduler/Tests/Generator/MessageGeneratorTest.php +++ b/src/Symfony/Component/Scheduler/Tests/Generator/MessageGeneratorTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Clock\ClockInterface; +use Symfony\Component\Scheduler\Generator\MessageContext; use Symfony\Component\Scheduler\Generator\MessageGenerator; use Symfony\Component\Scheduler\RecurringMessage; use Symfony\Component\Scheduler\Schedule; @@ -43,14 +44,47 @@ public function testGetMessages(string $startTime, array $runs, array $schedule) $scheduler = new MessageGenerator($schedule, 'dummy', $clock); // Warmup. The first run is always returns nothing. - $this->assertSame([], iterator_to_array($scheduler->getMessages())); + $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())); + $this->assertSame($expected, iterator_to_array($scheduler->getMessages(), false)); } } + public function testYieldedContext() + { + // for referencing + $now = self::makeDateTime('22:12:00'); + + $clock = $this->createMock(ClockInterface::class); + $clock->method('now')->willReturnReference($now); + + $message = $this->createMessage((object) ['id' => 'message'], '22:13:00', '22:14:00', '22:16:00'); + $schedule = (new Schedule())->add($message); + $schedule->stateful(new ArrayAdapter()); + + $scheduler = new MessageGenerator($schedule, 'dummy', $clock); + + // Warmup. The first run is alw ays returns nothing. + $this->assertSame([], iterator_to_array($scheduler->getMessages(), false)); + + $now = self::makeDateTime('22:14:10'); + + $iterator = $scheduler->getMessages(); + + $this->assertInstanceOf(MessageContext::class, $context = $iterator->key()); + $this->assertSame($message->getTrigger(), $context->trigger); + $this->assertEquals(self::makeDateTime('22:13:00'), $context->triggeredAt); + $this->assertEquals(self::makeDateTime('22:14:00'), $context->nextTriggerAt); + + $iterator->next(); + $this->assertInstanceOf(MessageContext::class, $context = $iterator->key()); + $this->assertSame($message->getTrigger(), $context->trigger); + $this->assertEquals(self::makeDateTime('22:14:00'), $context->triggeredAt); + $this->assertEquals(self::makeDateTime('22:16:00'), $context->nextTriggerAt); + } + public static function messagesProvider(): \Generator { $first = (object) ['id' => 'first']; diff --git a/src/Symfony/Component/Scheduler/Tests/Messenger/SchedulerTransportTest.php b/src/Symfony/Component/Scheduler/Tests/Messenger/SchedulerTransportTest.php index 09f980c07ba2f..315048e9fbe0e 100644 --- a/src/Symfony/Component/Scheduler/Tests/Messenger/SchedulerTransportTest.php +++ b/src/Symfony/Component/Scheduler/Tests/Messenger/SchedulerTransportTest.php @@ -14,9 +14,11 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Messenger\Envelope; use Symfony\Component\Scheduler\Exception\LogicException; +use Symfony\Component\Scheduler\Generator\MessageContext; use Symfony\Component\Scheduler\Generator\MessageGeneratorInterface; use Symfony\Component\Scheduler\Messenger\ScheduledStamp; use Symfony\Component\Scheduler\Messenger\SchedulerTransport; +use Symfony\Component\Scheduler\Trigger\TriggerInterface; class SchedulerTransportTest extends TestCase { @@ -26,9 +28,15 @@ public function testGetFromIterator() (object) ['id' => 'first'], (object) ['id' => 'second'], ]; - $generator = $this->createConfiguredMock(MessageGeneratorInterface::class, [ + $generator = $this->createMock(MessageGeneratorInterface::class, [ 'getMessages' => $messages, ]); + $generator->method('getMessages')->willReturnCallback(function () use ($messages): \Generator { + $trigger = $this->createMock(TriggerInterface::class); + $triggerAt = new \DateTimeImmutable('2020-02-20T02:00:00', new \DateTimeZone('UTC')); + yield (new MessageContext($trigger, $triggerAt)) => $messages[0]; + yield (new MessageContext($trigger, $triggerAt)) => $messages[1]; + }); $transport = new SchedulerTransport($generator); foreach ($transport->get() as $envelope) {