From 3aa47e0ada20a18e047bd435040db935ad3a8b63 Mon Sep 17 00:00:00 2001 From: valtzu Date: Mon, 3 Feb 2025 17:55:58 +0200 Subject: [PATCH] Normalize `TriggerInterface` as `string` --- .../Resources/config/scheduler.php | 3 + src/Symfony/Component/Scheduler/CHANGELOG.md | 5 ++ .../Normalizer/SchedulerTriggerNormalizer.php | 64 ++++++++++++++ .../SchedulerTriggerNormalizerTest.php | 86 +++++++++++++++++++ 4 files changed, 158 insertions(+) create mode 100644 src/Symfony/Component/Scheduler/Messenger/Serializer/Normalizer/SchedulerTriggerNormalizer.php create mode 100644 src/Symfony/Component/Scheduler/Tests/Messenger/SchedulerTriggerNormalizerTest.php diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/scheduler.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/scheduler.php index 7b2856d8272ee..4cbfb73b56226 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/scheduler.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/scheduler.php @@ -13,6 +13,7 @@ use Symfony\Component\Scheduler\EventListener\DispatchSchedulerEventListener; use Symfony\Component\Scheduler\Messenger\SchedulerTransportFactory; +use Symfony\Component\Scheduler\Messenger\Serializer\Normalizer\SchedulerTriggerNormalizer; use Symfony\Component\Scheduler\Messenger\ServiceCallMessageHandler; return static function (ContainerConfigurator $container) { @@ -34,5 +35,7 @@ service('event_dispatcher'), ]) ->tag('kernel.event_subscriber') + ->set('serializer.normalizer.scheduler_trigger', SchedulerTriggerNormalizer::class) + ->tag('serializer.normalizer', ['built_in' => true, 'priority' => -880]) ; }; diff --git a/src/Symfony/Component/Scheduler/CHANGELOG.md b/src/Symfony/Component/Scheduler/CHANGELOG.md index 2fb6b75be694d..78947ca8508cb 100644 --- a/src/Symfony/Component/Scheduler/CHANGELOG.md +++ b/src/Symfony/Component/Scheduler/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.3 +--- + + * Add `TriggerNormalizer` + 7.2 --- diff --git a/src/Symfony/Component/Scheduler/Messenger/Serializer/Normalizer/SchedulerTriggerNormalizer.php b/src/Symfony/Component/Scheduler/Messenger/Serializer/Normalizer/SchedulerTriggerNormalizer.php new file mode 100644 index 0000000000000..ce124a98d11be --- /dev/null +++ b/src/Symfony/Component/Scheduler/Messenger/Serializer/Normalizer/SchedulerTriggerNormalizer.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Scheduler\Messenger\Serializer\Normalizer; + +use Symfony\Component\Messenger\Transport\Serialization\Serializer; +use Symfony\Component\Scheduler\Trigger\TriggerInterface; +use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + +final class SchedulerTriggerNormalizer implements DenormalizerInterface, NormalizerInterface +{ + public function getSupportedTypes(?string $format): array + { + return [ + TriggerInterface::class => false, + ]; + } + + /** + * @param TriggerInterface $data + */ + public function normalize(mixed $data, ?string $format = null, array $context = []): string + { + return (string) $data; + } + + public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool + { + return $data instanceof TriggerInterface && ($context[Serializer::MESSENGER_SERIALIZATION_CONTEXT] ?? false); + } + + public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): TriggerInterface + { + return new class($data) implements TriggerInterface { + public function __construct(private readonly string $description) + { + } + + public function __toString(): string + { + return $this->description; + } + + public function getNextRunDate(\DateTimeImmutable $run): ?\DateTimeImmutable + { + throw new \LogicException('Not possible to get next run date from a deserialized trigger.'); + } + }; + } + + public function supportsDenormalization(mixed $data, string $type, ?string $format = null, array $context = []): bool + { + return TriggerInterface::class === $type && ($context[Serializer::MESSENGER_SERIALIZATION_CONTEXT] ?? false) && \is_string($data); + } +} diff --git a/src/Symfony/Component/Scheduler/Tests/Messenger/SchedulerTriggerNormalizerTest.php b/src/Symfony/Component/Scheduler/Tests/Messenger/SchedulerTriggerNormalizerTest.php new file mode 100644 index 0000000000000..2bdf11656cb89 --- /dev/null +++ b/src/Symfony/Component/Scheduler/Tests/Messenger/SchedulerTriggerNormalizerTest.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Scheduler\Tests\Messenger; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Scheduler\Messenger\Serializer\Normalizer\SchedulerTriggerNormalizer; +use Symfony\Component\Scheduler\Trigger\CallbackTrigger; +use Symfony\Component\Scheduler\Trigger\PeriodicalTrigger; +use Symfony\Component\Scheduler\Trigger\TriggerInterface; + +class SchedulerTriggerNormalizerTest extends TestCase +{ + private SchedulerTriggerNormalizer $normalizer; + + /** + * @before + */ + protected function setUpNormalizer(): void + { + $this->normalizer = new SchedulerTriggerNormalizer(); + } + + /** + * @dataProvider normalizeProvider + */ + public function testNormalize(mixed $data, mixed $expected) + { + self::assertSame($expected, $this->normalizer->normalize($data)); + } + + public static function normalizeProvider(): iterable + { + yield 'CallbackTrigger' => [new CallbackTrigger(fn () => null, 'test1'), 'test1']; + yield 'PeriodicalTrigger' => [new PeriodicalTrigger(5), 'every 5 seconds']; + } + + /** + * @dataProvider supportsNormalizationProvider + */ + public function testSupportsNormalization(mixed $data, array $context, bool $expected) + { + self::assertSame($expected, $this->normalizer->supportsNormalization($data, 'json', $context)); + } + + public static function supportsNormalizationProvider(): iterable + { + yield 'CallbackTrigger, messenger context' => [new CallbackTrigger(fn () => null, 'test1'), ['messenger_serialization' => true], true]; + yield 'CallbackTrigger, normal context' => [new CallbackTrigger(fn () => null, 'test1'), [], false]; + yield 'PeriodicalTrigger, messenger context' => [new PeriodicalTrigger(5), ['messenger_serialization' => true], true]; + yield 'PeriodicalTrigger, normal context' => [new PeriodicalTrigger(5), [], false]; + yield 'stdClass, messenger context' => [new \stdClass(), ['messenger_serialization' => true], false]; + yield 'stdClass, normal context' => [new \stdClass(), [], false]; + } + + /** + * @dataProvider supportsDenormalizationProvider + */ + public function testSupportsDenormalization(mixed $data, string $type, array $context, bool $expected) + { + self::assertSame($expected, $this->normalizer->supportsDenormalization($data, $type, 'json', $context)); + } + + public static function supportsDenormalizationProvider(): iterable + { + yield 'unknown type' => ['test', \stdClass::class, ['messenger_serialization' => true], false]; + yield 'string, messenger context' => ['test', TriggerInterface::class, ['messenger_serialization' => true], true]; + yield 'string, normal context' => ['test', TriggerInterface::class, [], false]; + yield 'array, messenger context' => [['a' => 'b'], TriggerInterface::class, ['messenger_serialization' => true], false]; + yield 'array, normal context' => [['a' => 'b'], TriggerInterface::class, [], false]; + } + + public function testDenormalize() + { + $trigger = $this->normalizer->denormalize('every 5 seconds', TriggerInterface::class); + self::assertSame('every 5 seconds', (string) $trigger); + } +}