diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index af4acdd..97f3985 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -22,6 +22,18 @@ jobs: extensions: 'curl, iconv, mbstring, mongodb, pdo, pdo_sqlite, sqlite, zip' can-fail: false has-mongodb: true + - php: '8.2' + symfony: '6.4.*@dev' + composer-flags: '--prefer-stable' + extensions: 'curl, iconv, mbstring, mongodb, pdo, pdo_sqlite, sqlite, zip' + can-fail: true + has-mongodb: true + - php: '8.2' + symfony: '7.0.*@dev' + composer-flags: '--prefer-stable' + extensions: 'curl, iconv, mbstring, mongodb, pdo, pdo_sqlite, sqlite, zip' + can-fail: true + has-mongodb: true exclude: - php: '7.4' symfony: '6.3.*' diff --git a/composer.json b/composer.json index 469e57f..6da97b5 100644 --- a/composer.json +++ b/composer.json @@ -7,10 +7,10 @@ "require": { "php": "^7.4 || ^8.0", "moneyphp/money": "^3.3 || ^4.0", - "symfony/config": "^5.4 || ^6.3", - "symfony/dependency-injection": "^5.4 || ^6.3", + "symfony/config": "^5.4 || ^6.3 || ^7.0", + "symfony/dependency-injection": "^5.4 || ^6.3 || ^7.0", "symfony/deprecation-contracts": "^2.1 || ^3.0", - "symfony/http-kernel": "^5.4 || ^6.3", + "symfony/http-kernel": "^5.4 || ^6.3 || ^7.0", "symfony/polyfill-php80": "^1.16" }, "require-dev": { @@ -20,19 +20,19 @@ "doctrine/orm": "^2.7.3", "jms/serializer": "^3.2", "jms/serializer-bundle": "^3.5 || ^4.0 || ^5.0", - "matthiasnoback/symfony-dependency-injection-test": "^4.3", + "matthiasnoback/symfony-dependency-injection-test": "^4.3.1", "phpstan/extension-installer": "^1.3", "phpstan/phpstan": "1.10.34", "phpstan/phpstan-phpunit": "1.3.14", "phpstan/phpstan-symfony": "1.3.2", "phpunit/phpunit": "9.6.12", - "symfony/form": "^5.4 || ^6.3", - "symfony/intl": "^5.4 || ^6.3", - "symfony/phpunit-bridge": "^5.4 || ^6.3", - "symfony/property-access": "^5.4 || ^6.3", - "symfony/serializer": "^5.4 || ^6.3", - "symfony/twig-bundle": "^5.4 || ^6.3", - "symfony/validator": "^5.4 || ^6.3", + "symfony/form": "^5.4 || ^6.3 || ^7.0", + "symfony/intl": "^5.4 || ^6.3 || ^7.0", + "symfony/phpunit-bridge": "^5.4 || ^6.3 || ^7.0", + "symfony/property-access": "^5.4 || ^6.3 || ^7.0", + "symfony/serializer": "^5.4 || ^6.3 || ^7.0", + "symfony/twig-bundle": "^5.4 || ^6.3 || ^7.0", + "symfony/validator": "^5.4 || ^6.3 || ^7.0", "twig/twig": "^2.13 || ^3.0" }, "conflict": { diff --git a/src/DependencyInjection/BabDevMoneyExtension.php b/src/DependencyInjection/BabDevMoneyExtension.php index 28aecfa..522988f 100644 --- a/src/DependencyInjection/BabDevMoneyExtension.php +++ b/src/DependencyInjection/BabDevMoneyExtension.php @@ -2,10 +2,13 @@ namespace BabDev\MoneyBundle\DependencyInjection; +use BabDev\MoneyBundle\Serializer\Normalizer\LegacyMoneyNormalizer; +use Composer\InstalledVersions; use JMS\Serializer\SerializerInterface; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; +use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; @@ -40,6 +43,16 @@ protected function loadInternal(array $mergedConfig, ContainerBuilder $container if (ContainerBuilder::willBeAvailable('symfony/serializer', NormalizerInterface::class, ['babdev/money-bundle'])) { $loader->load('serializer.php'); + + if (class_exists(InstalledVersions::class)) { + $version = InstalledVersions::getVersion('symfony/serializer'); + + if (null !== $version && version_compare($version, '6.3', '<')) { + $container->register('money.serializer.normalizer.legacy', LegacyMoneyNormalizer::class) + ->setDecoratedService('money.serializer.normalizer') + ->addArgument(new Reference('.inner')); + } + } } if (ContainerBuilder::willBeAvailable('symfony/validator', ValidatorInterface::class, ['babdev/money-bundle'])) { diff --git a/src/Serializer/Normalizer/LegacyMoneyNormalizer.php b/src/Serializer/Normalizer/LegacyMoneyNormalizer.php new file mode 100644 index 0000000..a1d8986 --- /dev/null +++ b/src/Serializer/Normalizer/LegacyMoneyNormalizer.php @@ -0,0 +1,48 @@ +normalizer = $normalizer; + } + + public function normalize($object, ?string $format = null, array $context = []): array + { + return $this->normalizer->normalize($object, $format, $context); + } + + public function supportsNormalization($data, ?string $format = null, array $context = []): bool + { + return $this->normalizer->supportsNormalization($data, $format, $context); + } + + public function denormalize($data, string $type, ?string $format = null, array $context = []): Money + { + return $this->normalizer->denormalize($data, $type, $format, $context); + } + + public function supportsDenormalization($data, string $type, ?string $format = null, array $context = []): bool + { + return $this->normalizer->supportsDenormalization($data, $type, $format, $context); + } + + public function hasCacheableSupportsMethod(): bool + { + return true; + } +} diff --git a/src/Serializer/Normalizer/MoneyNormalizer.php b/src/Serializer/Normalizer/MoneyNormalizer.php index f78698e..dc88dfc 100644 --- a/src/Serializer/Normalizer/MoneyNormalizer.php +++ b/src/Serializer/Normalizer/MoneyNormalizer.php @@ -7,11 +7,10 @@ use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Exception\NotNormalizableValueException; use Symfony\Component\Serializer\Exception\UnexpectedValueException; -use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; -final class MoneyNormalizer implements NormalizerInterface, DenormalizerInterface, CacheableSupportsMethodInterface +final class MoneyNormalizer implements NormalizerInterface, DenormalizerInterface { /** * @param mixed $object Object to normalize @@ -72,9 +71,4 @@ public function getSupportedTypes(?string $format): array Money::class => true, ]; } - - public function hasCacheableSupportsMethod(): bool - { - return true; - } } diff --git a/tests/DependencyInjection/BabDevMoneyExtensionTest.php b/tests/DependencyInjection/BabDevMoneyExtensionTest.php index 2eb9a55..4d60dd1 100644 --- a/tests/DependencyInjection/BabDevMoneyExtensionTest.php +++ b/tests/DependencyInjection/BabDevMoneyExtensionTest.php @@ -3,7 +3,9 @@ namespace BabDev\MoneyBundle\Tests\DependencyInjection; use BabDev\MoneyBundle\DependencyInjection\BabDevMoneyExtension; +use Composer\InstalledVersions; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractExtensionTestCase; +use Matthias\SymfonyDependencyInjectionTest\PhpUnit\DefinitionDecoratesConstraint; use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; final class BabDevMoneyExtensionTest extends AbstractExtensionTestCase @@ -17,6 +19,18 @@ public function testContainerIsLoadedWithDefaultConfiguration(): void $this->assertContainerBuilderHasService('money.form.type.money'); $this->assertContainerBuilderHasService('money.serializer.normalizer'); $this->assertContainerBuilderHasService('money.validator.greater_than'); + + if (class_exists(InstalledVersions::class)) { + $version = InstalledVersions::getVersion('symfony/serializer'); + + if (null !== $version && version_compare($version, '6.3', '<')) { + // TODO - Fix upstream + // $this->assertContainerBuilderServiceDecoration('money.serializer.normalizer.legacy', 'money.serializer.normalizer'); + self::assertThat($this->container, new DefinitionDecoratesConstraint('money.serializer.normalizer.legacy', 'money.serializer.normalizer')); + } else { + $this->assertContainerBuilderNotHasService('money.serializer.normalizer.legacy'); + } + } } public function testContainerIsLoadedWithCustomConfiguration(): void diff --git a/tests/Serializer/Normalizer/MoneyNormalizerTest.php b/tests/Serializer/Normalizer/MoneyNormalizerTest.php index 89c1a65..73b1125 100644 --- a/tests/Serializer/Normalizer/MoneyNormalizerTest.php +++ b/tests/Serializer/Normalizer/MoneyNormalizerTest.php @@ -2,6 +2,7 @@ namespace BabDev\MoneyBundle\Tests\Serializer\Normalizer; +use BabDev\MoneyBundle\Serializer\Normalizer\LegacyMoneyNormalizer; use BabDev\MoneyBundle\Serializer\Normalizer\MoneyNormalizer; use Money\Currency; use Money\Money; @@ -9,6 +10,7 @@ use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Exception\NotNormalizableValueException; use Symfony\Component\Serializer\Exception\UnexpectedValueException; +use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface; final class MoneyNormalizerTest extends TestCase { @@ -20,6 +22,21 @@ public function testNormalize(): void ); } + /** + * @group legacy + */ + public function testNormalizeWithLegacyDecorator(): void + { + if (!interface_exists(CacheableSupportsMethodInterface::class)) { + self::markTestSkipped('Test requires symfony/serializer:<6.4'); + } + + self::assertEquals( + ['amount' => '100', 'currency' => 'USD'], + (new LegacyMoneyNormalizer(new MoneyNormalizer()))->normalize(new Money(100, new Currency('USD'))) + ); + } + public function testNormalizeOnlyAcceptsMoneyInstances(): void { $this->expectException(InvalidArgumentException::class); @@ -51,6 +68,20 @@ public function testDenormalize(): void (new MoneyNormalizer())->denormalize(['amount' => '100', 'currency' => 'USD'], Money::class) ); } + /** + * @group legacy + */ + public function testDenormalizeWithLegacyDecorator(): void + { + if (!interface_exists(CacheableSupportsMethodInterface::class)) { + self::markTestSkipped('Test requires symfony/serializer:<6.4'); + } + + self::assertEquals( + new Money(100, new Currency('USD')), + (new LegacyMoneyNormalizer(new MoneyNormalizer()))->denormalize(['amount' => '100', 'currency' => 'USD'], Money::class) + ); + } public function testDenormalizeOnlyAcceptsArrays(): void { @@ -91,8 +122,15 @@ public function testSupportsDenormalization($data, string $type, bool $supported self::assertSame($supported, (new MoneyNormalizer())->supportsDenormalization($data, $type)); } + /** + * @group legacy + */ public function testHasCacheableSupportsMethod(): void { - self::assertTrue((new MoneyNormalizer())->hasCacheableSupportsMethod()); + if (!interface_exists(CacheableSupportsMethodInterface::class)) { + self::markTestSkipped('Test requires symfony/serializer:<6.4'); + } + + self::assertTrue((new LegacyMoneyNormalizer(new MoneyNormalizer()))->hasCacheableSupportsMethod()); } }