diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml
index af4acdd..ffcef22 100644
--- a/.github/workflows/run-tests.yml
+++ b/.github/workflows/run-tests.yml
@@ -9,24 +9,28 @@ jobs:
strategy:
fail-fast: true
matrix:
- php: ['7.4', '8.0', '8.1', '8.2']
- symfony: ['5.4.*', '6.3.*']
+ php: ['8.1', '8.2', '8.3']
+ symfony: ['5.4.*', '6.3.*', '6.4.*']
composer-flags: ['--prefer-stable']
can-fail: [false]
has-mongodb: [true]
extensions: ['curl, iconv, mbstring, mongodb, pdo, pdo_sqlite, sqlite, zip']
include:
- - php: '7.4'
+ - php: '8.1'
symfony: '5.4.*'
composer-flags: '--prefer-stable --prefer-lowest'
extensions: 'curl, iconv, mbstring, mongodb, pdo, pdo_sqlite, sqlite, zip'
can-fail: false
has-mongodb: true
+ - php: '8.3'
+ symfony: '7.0.*'
+ composer-flags: '--prefer-stable --prefer-lowest'
+ extensions: 'curl, iconv, mbstring, pdo, pdo_sqlite, sqlite, zip'
+ can-fail: false
+ has-mongodb: false
exclude:
- - php: '7.4'
- symfony: '6.3.*'
- - php: '8.0'
- symfony: '6.3.*'
+ - php: '8.1'
+ symfony: '7.0.*'
name: "PHP ${{ matrix.php }} - Symfony ${{ matrix.symfony }}${{ matrix.composer-flags != '' && format(' - Composer {0}', matrix.composer-flags) || '' }}"
diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml
index 9f26dc6..4350ee5 100644
--- a/.github/workflows/static-analysis.yml
+++ b/.github/workflows/static-analysis.yml
@@ -15,7 +15,7 @@ jobs:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
- php-version: '8.2'
+ php-version: '8.3'
tools: composer:v2,flex
extensions: curl, iconv, mbstring, mongodb, pdo, pdo_sqlite, sqlite, zip
coverage: none
@@ -23,7 +23,7 @@ jobs:
- name: Install dependencies
run: composer update --prefer-stable --prefer-dist
env:
- SYMFONY_REQUIRE: '6.3.*'
+ SYMFONY_REQUIRE: '6.4.*'
- name: Run PHPStan
run: vendor/bin/phpstan analyze --error-format=github
diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php
index 45d3415..85b0ac1 100644
--- a/.php-cs-fixer.php
+++ b/.php-cs-fixer.php
@@ -4,13 +4,12 @@
->setRules([
'@Symfony' => true,
'@Symfony:risky' => true,
- '@PHP74Migration' => true,
- '@PHP74Migration:risky' => true,
+ '@PHP81Migration' => true,
+ '@PHP80Migration:risky' => true,
'@PHPUnit84Migration:risky' => true,
'array_syntax' => ['syntax' => 'short'],
'blank_line_after_opening_tag' => false,
'fopen_flags' => false,
- 'get_class_to_class_keyword' => false, // Re-enable when dropping PHP 7.4 support
'linebreak_after_opening_tag' => false,
'no_superfluous_phpdoc_tags' => ['remove_inheritdoc' => true],
'nullable_type_declaration_for_default_null_value' => ['use_nullable_type_declaration' => true],
diff --git a/composer.json b/composer.json
index 469e57f..5f01fd2 100644
--- a/composer.json
+++ b/composer.json
@@ -5,42 +5,41 @@
"keywords": ["money", "moneyphp", "currency", "symfony"],
"license": "MIT",
"require": {
- "php": "^7.4 || ^8.0",
+ "php": "^8.1",
"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/polyfill-php80": "^1.16"
+ "symfony/http-kernel": "^5.4 || ^6.3 || ^7.0"
},
"require-dev": {
- "doctrine/doctrine-bundle": "^2.0",
- "doctrine/mongodb-odm": "^2.1.2",
- "doctrine/mongodb-odm-bundle": "^4.0.1",
- "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",
+ "doctrine/doctrine-bundle": "^2.1.1",
+ "doctrine/mongodb-odm": "^2.2",
+ "doctrine/mongodb-odm-bundle": "^4.3",
+ "doctrine/orm": "^2.8",
+ "jms/serializer": "^3.14",
+ "jms/serializer-bundle": "^3.8 || ^4.0 || ^5.0",
+ "matthiasnoback/symfony-dependency-injection-test": "^4.3.1 || ^5.0",
"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": {
- "doctrine/doctrine-bundle": "<2.0",
- "doctrine/mongodb-odm": "<2.1.2",
- "doctrine/mongodb-odm-bundle": "<4.0.1",
- "doctrine/orm": "<2.7.3",
- "jms/serializer": "<3.5",
+ "doctrine/doctrine-bundle": "<2.1.1",
+ "doctrine/mongodb-odm": "<2.2",
+ "doctrine/mongodb-odm-bundle": "<4.3",
+ "doctrine/orm": "<2.8",
+ "jms/serializer": "<3.14",
"symfony/form": "<5.4 || >=6.0 <6.3",
"symfony/serializer": "<5.4 || >=6.0 <6.3",
"symfony/validator": "<5.4 || >=6.0 <6.3",
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index 90b4073..fe29687 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -56,7 +56,7 @@ parameters:
path: tests/Form/DataTransformer/MoneyToLocalizedStringTransformerTest.php
-
- message: "#^Parameter \\#2 \\$locale of function setlocale expects string\\|null, string\\|false given\\.$#"
+ message: "#^Parameter \\#2 \\$locale of function setlocale expects string\\|null, bool\\|string given\\.$#"
count: 1
path: tests/Form/DataTransformer/MoneyToLocalizedStringTransformerTest.php
diff --git a/phpstan.neon b/phpstan.neon
index 97332e8..5281cee 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -6,5 +6,7 @@ parameters:
paths:
- %currentWorkingDirectory%/src
- %currentWorkingDirectory%/tests
+ excludePaths:
+ - %currentWorkingDirectory%/src/Serializer/Normalizer/LegacyMoneyNormalizer.php
checkMissingIterableValueType: false
treatPhpDocTypesAsCertain: false
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 1aefa9a..557bf3f 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -22,6 +22,7 @@
-
+
+
diff --git a/src/BabDevMoneyBundle.php b/src/BabDevMoneyBundle.php
index c53fdf0..8e594df 100644
--- a/src/BabDevMoneyBundle.php
+++ b/src/BabDevMoneyBundle.php
@@ -32,7 +32,7 @@ public function build(ContainerBuilder $container): void
public function getContainerExtension(): ?ExtensionInterface
{
- if (null === $this->extension) {
+ if (!isset($this->extension)) {
$this->extension = new BabDevMoneyExtension();
}
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/Factory/Exception/UnsupportedFormatException.php b/src/Factory/Exception/UnsupportedFormatException.php
index bb0d690..1d2535b 100644
--- a/src/Factory/Exception/UnsupportedFormatException.php
+++ b/src/Factory/Exception/UnsupportedFormatException.php
@@ -7,28 +7,23 @@
final class UnsupportedFormatException extends \InvalidArgumentException
{
/**
- * @var string[]
+ * @param list $formats
*
- * @phpstan-var array
+ * @phpstan-param list $formats
*/
- private array $formats;
-
- /**
- * @param string[] $formats
- *
- * @phpstan-param array $formats
- */
- public function __construct(array $formats, string $message = '', int $code = 0, ?\Throwable $previous = null)
- {
+ public function __construct(
+ private readonly array $formats,
+ string $message = '',
+ int $code = 0,
+ ?\Throwable $previous = null,
+ ) {
parent::__construct($message, $code, $previous);
-
- $this->formats = $formats;
}
/**
- * @return string[]
+ * @return list
*
- * @phpstan-return array
+ * @phpstan-return list
*/
public function getFormats(): array
{
diff --git a/src/Factory/FormatterFactory.php b/src/Factory/FormatterFactory.php
index 68b6f21..71975a0 100644
--- a/src/Factory/FormatterFactory.php
+++ b/src/Factory/FormatterFactory.php
@@ -28,12 +28,7 @@ final class FormatterFactory implements FormatterFactoryInterface
Format::INTL_MONEY => IntlMoneyFormatter::class,
];
- private string $defaultLocale;
-
- public function __construct(string $defaultLocale)
- {
- $this->defaultLocale = $defaultLocale;
- }
+ public function __construct(private readonly string $defaultLocale) {}
/**
* @phpstan-param Format::* $format
diff --git a/src/Factory/ParserFactory.php b/src/Factory/ParserFactory.php
index c621ccf..279a449 100644
--- a/src/Factory/ParserFactory.php
+++ b/src/Factory/ParserFactory.php
@@ -27,12 +27,7 @@ final class ParserFactory implements ParserFactoryInterface
Format::INTL_MONEY => IntlMoneyParser::class,
];
- private string $defaultLocale;
-
- public function __construct(string $defaultLocale)
- {
- $this->defaultLocale = $defaultLocale;
- }
+ public function __construct(private readonly string $defaultLocale) {}
/**
* @phpstan-param Format::* $format
diff --git a/src/Form/DataTransformer/MoneyToLocalizedStringTransformer.php b/src/Form/DataTransformer/MoneyToLocalizedStringTransformer.php
index f876062..bb918c8 100644
--- a/src/Form/DataTransformer/MoneyToLocalizedStringTransformer.php
+++ b/src/Form/DataTransformer/MoneyToLocalizedStringTransformer.php
@@ -16,7 +16,7 @@
/**
* Transforms between a normalized format and a localized money string.
*
- * Class is based on \Symfony\Component\Form\Extension\Core\DataTransformer\MoneyToLocalizedStringTransformer
+ * Class is based on {@see \Symfony\Component\Form\Extension\Core\DataTransformer\MoneyToLocalizedStringTransformer}
*
* @template T of Money
* @template R of string
@@ -25,10 +25,6 @@
*/
final class MoneyToLocalizedStringTransformer implements DataTransformerInterface
{
- private FormatterFactoryInterface $formatterFactory;
- private ParserFactoryInterface $parserFactory;
- private Currency $currency;
- private ?string $locale;
private NumberToLocalizedStringTransformer $numberTransformer;
/**
@@ -36,8 +32,15 @@ final class MoneyToLocalizedStringTransformer implements DataTransformerInterfac
*
* @throws InvalidArgumentException if an invalid constructor parameter is provided
*/
- public function __construct(FormatterFactoryInterface $formatterFactory, ParserFactoryInterface $parserFactory, Currency $currency, $scaleOrTransformer = 2, ?bool $grouping = true, ?int $roundingMode = \NumberFormatter::ROUND_HALFUP, ?string $locale = null)
- {
+ public function __construct(
+ private readonly FormatterFactoryInterface $formatterFactory,
+ private readonly ParserFactoryInterface $parserFactory,
+ private readonly Currency $currency,
+ $scaleOrTransformer = 2,
+ ?bool $grouping = true,
+ ?int $roundingMode = \NumberFormatter::ROUND_HALFUP,
+ private readonly ?string $locale = null,
+ ) {
if ($scaleOrTransformer instanceof NumberToLocalizedStringTransformer) {
$this->numberTransformer = $scaleOrTransformer;
} elseif (\is_int($scaleOrTransformer) || null === $scaleOrTransformer) {
@@ -47,11 +50,6 @@ public function __construct(FormatterFactoryInterface $formatterFactory, ParserF
} else {
throw new InvalidArgumentException(sprintf('The fourth argument to the %s constructor must be an instance of %s, an integer, or null; %s given', self::class, NumberToLocalizedStringTransformer::class, get_debug_type($scaleOrTransformer)));
}
-
- $this->formatterFactory = $formatterFactory;
- $this->parserFactory = $parserFactory;
- $this->currency = $currency;
- $this->locale = $locale;
}
/**
diff --git a/src/Form/Type/MoneyType.php b/src/Form/Type/MoneyType.php
index 34e16ec..cd6f23d 100644
--- a/src/Form/Type/MoneyType.php
+++ b/src/Form/Type/MoneyType.php
@@ -6,6 +6,7 @@
use BabDev\MoneyBundle\Factory\ParserFactoryInterface;
use BabDev\MoneyBundle\Form\DataTransformer\MoneyToLocalizedStringTransformer;
use Money\Currency;
+use Money\Money;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Exception\LogicException;
use Symfony\Component\Form\Extension\Core\DataTransformer\NumberToLocalizedStringTransformer;
@@ -16,15 +17,13 @@
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
- * Alternative money form type supporting a `Money\Money` object as a data input.
+ * Alternative money form type supporting a {@see Money} object as a data input.
*
- * Class is based on \Symfony\Component\Form\Extension\Core\Type\MoneyType
+ * Class is based on {@see \Symfony\Component\Form\Extension\Core\Type\MoneyType}
*/
final class MoneyType extends AbstractType
{
- private FormatterFactoryInterface $formatterFactory;
- private ParserFactoryInterface $parserFactory;
- private Currency $defaultCurrency;
+ private readonly Currency $defaultCurrency;
/**
* @var array>
@@ -34,10 +33,11 @@ final class MoneyType extends AbstractType
/**
* @phpstan-param non-empty-string $defaultCurrency
*/
- public function __construct(FormatterFactoryInterface $formatterFactory, ParserFactoryInterface $parserFactory, string $defaultCurrency)
- {
- $this->formatterFactory = $formatterFactory;
- $this->parserFactory = $parserFactory;
+ public function __construct(
+ private readonly FormatterFactoryInterface $formatterFactory,
+ private readonly ParserFactoryInterface $parserFactory,
+ string $defaultCurrency,
+ ) {
$this->defaultCurrency = new Currency($defaultCurrency);
}
diff --git a/src/Serializer/Normalizer/LegacyMoneyNormalizer.php b/src/Serializer/Normalizer/LegacyMoneyNormalizer.php
new file mode 100644
index 0000000..ac4e81b
--- /dev/null
+++ b/src/Serializer/Normalizer/LegacyMoneyNormalizer.php
@@ -0,0 +1,43 @@
+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/src/Twig/MoneyExtension.php b/src/Twig/MoneyExtension.php
index 5146466..6c57455 100644
--- a/src/Twig/MoneyExtension.php
+++ b/src/Twig/MoneyExtension.php
@@ -15,51 +15,41 @@
final class MoneyExtension extends AbstractExtension
{
- private FormatterFactoryInterface $formatterFactory;
-
- /**
- * @phpstan-var non-empty-string
- */
- private string $defaultCurrency;
-
/**
* @phpstan-param non-empty-string $defaultCurrency
*/
- public function __construct(FormatterFactoryInterface $formatterFactory, string $defaultCurrency)
- {
- $this->formatterFactory = $formatterFactory;
- $this->defaultCurrency = $defaultCurrency;
- }
+ public function __construct(
+ private readonly FormatterFactoryInterface $formatterFactory,
+ private readonly string $defaultCurrency
+ ) {}
/**
- * @return TwigFilter[]
+ * @return list
*/
public function getFilters(): array
{
return [
- new TwigFilter('money', [$this, 'formatMoney']),
+ new TwigFilter('money', $this->formatMoney(...)),
];
}
/**
- * @return TwigFunction[]
+ * @return list
*/
public function getFunctions(): array
{
return [
- new TwigFunction('money', [$this, 'createMoney']),
+ new TwigFunction('money', $this->createMoney(...)),
];
}
/**
- * @param string|int $amount
- *
* @phpstan-param numeric-string|int $amount
* @phpstan-param non-empty-string|null $currency
*
* @throws \InvalidArgumentException if the amount cannot be converted to a {@see Money} instance
*/
- public function createMoney($amount, ?string $currency = null): Money
+ public function createMoney(string|int $amount, ?string $currency = null): Money
{
return new Money($amount, new Currency($currency ?: $this->defaultCurrency));
}
diff --git a/src/Validator/Constraints/AbstractMoneyComparison.php b/src/Validator/Constraints/AbstractMoneyComparison.php
index ff749fa..47a7243 100644
--- a/src/Validator/Constraints/AbstractMoneyComparison.php
+++ b/src/Validator/Constraints/AbstractMoneyComparison.php
@@ -13,7 +13,7 @@
/**
* Used for the comparison of Money objects.
*
- * Class is based on \Symfony\Component\Validator\Constraints\AbstractComparison
+ * Class is based on {@see \Symfony\Component\Validator\Constraints\AbstractComparison}
*/
abstract class AbstractMoneyComparison extends Constraint
{
diff --git a/src/Validator/Constraints/AbstractMoneyComparisonValidator.php b/src/Validator/Constraints/AbstractMoneyComparisonValidator.php
index eb62dc0..ee596f4 100644
--- a/src/Validator/Constraints/AbstractMoneyComparisonValidator.php
+++ b/src/Validator/Constraints/AbstractMoneyComparisonValidator.php
@@ -19,30 +19,19 @@
/**
* Provides a base class for the validation of property comparisons.
*
- * Class is based on \Symfony\Component\Validator\Constraints\AbstractComparisonValidator
+ * Class is based on {@see \Symfony\Component\Validator\Constraints\AbstractComparisonValidator}
*/
abstract class AbstractMoneyComparisonValidator extends ConstraintValidator
{
- private FormatterFactoryInterface $formatterFactory;
- private ParserFactoryInterface $parserFactory;
-
- /**
- * @phpstan-var non-empty-string
- */
- private string $defaultCurrency;
-
- private ?PropertyAccessorInterface $propertyAccessor;
-
/**
* @phpstan-param non-empty-string $defaultCurrency
*/
- public function __construct(FormatterFactoryInterface $formatterFactory, ParserFactoryInterface $parserFactory, string $defaultCurrency, ?PropertyAccessorInterface $propertyAccessor = null)
- {
- $this->formatterFactory = $formatterFactory;
- $this->parserFactory = $parserFactory;
- $this->defaultCurrency = $defaultCurrency;
- $this->propertyAccessor = $propertyAccessor;
- }
+ public function __construct(
+ private readonly FormatterFactoryInterface $formatterFactory,
+ private readonly ParserFactoryInterface $parserFactory,
+ private readonly string $defaultCurrency,
+ private ?PropertyAccessorInterface $propertyAccessor = null
+ ) {}
/**
* @param mixed $value
@@ -100,53 +89,39 @@ private function createFactoryOptions(AbstractMoneyComparison $constraint): arra
}
/**
- * @param Money|float|int|string|null $value
- *
* @phpstan-param Money|float|int|numeric-string|null $value
*/
- private function ensureMoneyObject(AbstractMoneyComparison $constraint, $value): ?Money
+ private function ensureMoneyObject(AbstractMoneyComparison $constraint, Money|float|int|string|null $value): ?Money
{
if ($value instanceof Money || null === $value) {
return $value;
}
- if (\is_object($value) || \is_array($value)) {
- throw new InvalidArgumentException(sprintf('Could not convert value of type "%s" to a "%s" instance for comparison.', get_debug_type($value), Money::class));
- }
-
// First try to parse (assuming formatted input) then fall back to treating as a number
if (\is_string($value) && str_contains($value, '.')) {
try {
return $this->parserFactory->createParser($constraint->parserFormat, $constraint->locale, $this->createFactoryOptions($constraint))->parse($value, new Currency($constraint->currency ?: $this->defaultCurrency));
} catch (ParserException $exception) {
- throw new InvalidArgumentException(sprintf('Could not convert value "%s" to a "%s" instance for comparison.', $value, Money::class));
+ throw new InvalidArgumentException(sprintf('Could not convert value "%s" to a "%s" instance for comparison.', $value, Money::class), 0, $exception);
}
}
try {
- if (\is_float($value)) {
- $number = Number::fromFloat($value);
- } else {
- $number = Number::fromNumber($value);
- }
+ $number = \is_float($value) ? Number::fromFloat($value) : Number::fromNumber($value);
} catch (\InvalidArgumentException $exception) {
- throw new InvalidArgumentException(sprintf('Could not convert value "%s" to a "%s" instance for comparison.', $value, Number::class));
+ throw new InvalidArgumentException(sprintf('Could not convert value "%s" to a "%s" instance for comparison.', $value, Number::class), 0, $exception);
}
try {
return new Money((string) $number, new Currency($constraint->currency ?: $this->defaultCurrency));
} catch (\InvalidArgumentException $exception) {
- throw new InvalidArgumentException(sprintf('Could not convert value "%s" to a "%s" instance for comparison.', $value, Money::class));
+ throw new InvalidArgumentException(sprintf('Could not convert value "%s" to a "%s" instance for comparison.', $value, Money::class), 0, $exception);
}
}
private function getPropertyAccessor(): PropertyAccessorInterface
{
- if (null === $this->propertyAccessor) {
- $this->propertyAccessor = PropertyAccess::createPropertyAccessor();
- }
-
- return $this->propertyAccessor;
+ return $this->propertyAccessor ??= PropertyAccess::createPropertyAccessor();
}
abstract protected function compareValues(Money $value1, ?Money $value2): bool;
diff --git a/src/Validator/Constraints/MoneyEqualTo.php b/src/Validator/Constraints/MoneyEqualTo.php
index 80ff56b..cba663d 100644
--- a/src/Validator/Constraints/MoneyEqualTo.php
+++ b/src/Validator/Constraints/MoneyEqualTo.php
@@ -14,14 +14,14 @@ class MoneyEqualTo extends AbstractMoneyComparison
{
public const NOT_EQUAL_ERROR = '0057eef9-7cbd-43fc-b0ca-bb3b7a82567f';
- /**
- * Maps error codes to the names of their constants.
- *
- * @var array
- */
- protected static $errorNames = [
+ protected const ERROR_NAMES = [
self::NOT_EQUAL_ERROR => 'NOT_EQUAL_ERROR',
];
+ /**
+ * @deprecated to be removed when dropping support for Symfony 6.1 and older
+ */
+ protected static $errorNames = self::ERROR_NAMES;
+
public ?string $message = 'This value should be equal to {{ compared_value }}.';
}
diff --git a/src/Validator/Constraints/MoneyGreaterThan.php b/src/Validator/Constraints/MoneyGreaterThan.php
index 6919567..5998272 100644
--- a/src/Validator/Constraints/MoneyGreaterThan.php
+++ b/src/Validator/Constraints/MoneyGreaterThan.php
@@ -14,14 +14,14 @@ class MoneyGreaterThan extends AbstractMoneyComparison
{
public const TOO_LOW_ERROR = '11c8f681-95b7-47ee-ad9a-d28dfdbb8443';
- /**
- * Maps error codes to the names of their constants.
- *
- * @var array
- */
- protected static $errorNames = [
+ protected const ERROR_NAMES = [
self::TOO_LOW_ERROR => 'TOO_LOW_ERROR',
];
+ /**
+ * @deprecated to be removed when dropping support for Symfony 6.1 and older
+ */
+ protected static $errorNames = self::ERROR_NAMES;
+
public ?string $message = 'This value should be greater than {{ compared_value }}.';
}
diff --git a/src/Validator/Constraints/MoneyGreaterThanOrEqual.php b/src/Validator/Constraints/MoneyGreaterThanOrEqual.php
index 786a78c..cf15ecf 100644
--- a/src/Validator/Constraints/MoneyGreaterThanOrEqual.php
+++ b/src/Validator/Constraints/MoneyGreaterThanOrEqual.php
@@ -14,14 +14,14 @@ class MoneyGreaterThanOrEqual extends AbstractMoneyComparison
{
public const TOO_LOW_ERROR = '61fa5754-e197-4db4-abfa-d51326e4d737';
- /**
- * Maps error codes to the names of their constants.
- *
- * @var array
- */
- protected static $errorNames = [
+ protected const ERROR_NAMES = [
self::TOO_LOW_ERROR => 'TOO_LOW_ERROR',
];
+ /**
+ * @deprecated to be removed when dropping support for Symfony 6.1 and older
+ */
+ protected static $errorNames = self::ERROR_NAMES;
+
public ?string $message = 'This value should be greater than or equal to {{ compared_value }}.';
}
diff --git a/src/Validator/Constraints/MoneyLessThan.php b/src/Validator/Constraints/MoneyLessThan.php
index d7ca7b7..81b766d 100644
--- a/src/Validator/Constraints/MoneyLessThan.php
+++ b/src/Validator/Constraints/MoneyLessThan.php
@@ -14,14 +14,14 @@ class MoneyLessThan extends AbstractMoneyComparison
{
public const TOO_HIGH_ERROR = 'dbeda9a5-ab67-4c21-a8b9-db816ec0c912';
- /**
- * Maps error codes to the names of their constants.
- *
- * @var array
- */
- protected static $errorNames = [
+ protected const ERROR_NAMES = [
self::TOO_HIGH_ERROR => 'TOO_HIGH_ERROR',
];
+ /**
+ * @deprecated to be removed when dropping support for Symfony 6.1 and older
+ */
+ protected static $errorNames = self::ERROR_NAMES;
+
public ?string $message = 'This value should be less than {{ compared_value }}.';
}
diff --git a/src/Validator/Constraints/MoneyLessThanOrEqual.php b/src/Validator/Constraints/MoneyLessThanOrEqual.php
index 449d993..9f6fb2d 100644
--- a/src/Validator/Constraints/MoneyLessThanOrEqual.php
+++ b/src/Validator/Constraints/MoneyLessThanOrEqual.php
@@ -14,14 +14,14 @@ class MoneyLessThanOrEqual extends AbstractMoneyComparison
{
public const TOO_HIGH_ERROR = 'eca16a86-47e0-4a2f-bc44-f9b3d58561e5';
- /**
- * Maps error codes to the names of their constants.
- *
- * @var array
- */
- protected static $errorNames = [
+ protected const ERROR_NAMES = [
self::TOO_HIGH_ERROR => 'TOO_HIGH_ERROR',
];
+ /**
+ * @deprecated to be removed when dropping support for Symfony 6.1 and older
+ */
+ protected static $errorNames = self::ERROR_NAMES;
+
public ?string $message = 'This value should be less than or equal to {{ compared_value }}.';
}
diff --git a/src/Validator/Constraints/MoneyNotEqualTo.php b/src/Validator/Constraints/MoneyNotEqualTo.php
index 1dea94d..1edffed 100644
--- a/src/Validator/Constraints/MoneyNotEqualTo.php
+++ b/src/Validator/Constraints/MoneyNotEqualTo.php
@@ -14,14 +14,14 @@ class MoneyNotEqualTo extends AbstractMoneyComparison
{
public const IS_EQUAL_ERROR = '6dcecf9b-093b-4342-8cf7-060a3ef55faa';
- /**
- * Maps error codes to the names of their constants.
- *
- * @var array
- */
- protected static $errorNames = [
+ protected const ERROR_NAMES = [
self::IS_EQUAL_ERROR => 'IS_EQUAL_ERROR',
];
+ /**
+ * @deprecated to be removed when dropping support for Symfony 6.1 and older
+ */
+ protected static $errorNames = self::ERROR_NAMES;
+
public ?string $message = 'This value should not be equal to {{ compared_value }}.';
}
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/DependencyInjection/ConfigurationTest.php b/tests/DependencyInjection/ConfigurationTest.php
index 39bef83..169c2d9 100644
--- a/tests/DependencyInjection/ConfigurationTest.php
+++ b/tests/DependencyInjection/ConfigurationTest.php
@@ -23,10 +23,7 @@ public function testConfigWithCustomDefaultCurrency(): void
$config = (new Processor())->processConfiguration(new Configuration(), [$extraConfig]);
- self::assertEquals(
- array_merge(self::getBundleDefaultConfig(), $extraConfig),
- $config
- );
+ self::assertEquals(array_merge(self::getBundleDefaultConfig(), $extraConfig), $config);
}
protected static function getBundleDefaultConfig(): array
diff --git a/tests/Factory/FormatterFactoryTest.php b/tests/Factory/FormatterFactoryTest.php
index 752a8b7..3649b4b 100644
--- a/tests/Factory/FormatterFactoryTest.php
+++ b/tests/Factory/FormatterFactoryTest.php
@@ -14,10 +14,7 @@
final class FormatterFactoryTest extends TestCase
{
- /**
- * @var FormatterFactory
- */
- private $factory;
+ private FormatterFactory $factory;
protected function setUp(): void
{
diff --git a/tests/Factory/ParserFactoryTest.php b/tests/Factory/ParserFactoryTest.php
index 0475b43..7247fe7 100644
--- a/tests/Factory/ParserFactoryTest.php
+++ b/tests/Factory/ParserFactoryTest.php
@@ -14,10 +14,7 @@
final class ParserFactoryTest extends TestCase
{
- /**
- * @var ParserFactory
- */
- private $factory;
+ private ParserFactory $factory;
protected function setUp(): void
{
diff --git a/tests/Form/DataTransformer/MoneyToLocalizedStringTransformerTest.php b/tests/Form/DataTransformer/MoneyToLocalizedStringTransformerTest.php
index cc7da1b..966f4a7 100644
--- a/tests/Form/DataTransformer/MoneyToLocalizedStringTransformerTest.php
+++ b/tests/Form/DataTransformer/MoneyToLocalizedStringTransformerTest.php
@@ -15,10 +15,7 @@
final class MoneyToLocalizedStringTransformerTest extends TestCase
{
- /**
- * @var false|string
- */
- private $previousLocale;
+ private string|bool $previousLocale;
protected function setUp(): void
{
diff --git a/tests/Serializer/Handler/MoneyHandlerTest.php b/tests/Serializer/Handler/MoneyHandlerTest.php
index 2933fc2..ee580be 100644
--- a/tests/Serializer/Handler/MoneyHandlerTest.php
+++ b/tests/Serializer/Handler/MoneyHandlerTest.php
@@ -16,7 +16,7 @@ public function testSerializeMoneyToJson(): void
{
self::assertJsonStringEqualsJsonString(
'{"amount":"1000","currency":"USD"}',
- $this->createSerializer()->serialize(Money::USD(1000), 'json')
+ $this->createSerializer()->serialize(Money::USD(1000), 'json'),
);
}
@@ -33,7 +33,7 @@ public function testSerializeMoneyToXml(): void
self::assertXmlStringEqualsXmlString(
$expectedXml,
- $this->createSerializer()->serialize(Money::USD(1000), 'xml')
+ $this->createSerializer()->serialize(Money::USD(1000), 'xml'),
);
}
@@ -41,7 +41,7 @@ public function testDeserializeMoneyFromJson(): void
{
self::assertEquals(
Money::USD(1000),
- $this->createSerializer()->deserialize('{"amount":"1000","currency":"USD"}', Money::class, 'json')
+ $this->createSerializer()->deserialize('{"amount":"1000","currency":"USD"}', Money::class, 'json'),
);
}
@@ -55,9 +55,10 @@ public function testDeserializeMoneyFromXml(): void
XML;
+
self::assertEquals(
Money::USD(1000),
- $this->createSerializer()->deserialize($generatedXml, Money::class, 'xml')
+ $this->createSerializer()->deserialize($generatedXml, Money::class, 'xml'),
);
}
diff --git a/tests/Serializer/Normalizer/MoneyNormalizerTest.php b/tests/Serializer/Normalizer/MoneyNormalizerTest.php
index 89c1a65..283e550 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
{
@@ -16,7 +18,22 @@ public function testNormalize(): void
{
self::assertEquals(
['amount' => '100', 'currency' => 'USD'],
- (new MoneyNormalizer())->normalize(new Money(100, new Currency('USD')))
+ (new MoneyNormalizer())->normalize(new Money(100, new Currency('USD'))),
+ );
+ }
+
+ /**
+ * @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'))),
);
}
@@ -35,11 +52,9 @@ public function dataSupportsNormalization(): \Generator
}
/**
- * @param mixed $data
- *
* @dataProvider dataSupportsNormalization
*/
- public function testSupportsNormalization($data, bool $supported): void
+ public function testSupportsNormalization(mixed $data, bool $supported): void
{
self::assertSame($supported, (new MoneyNormalizer())->supportsNormalization($data));
}
@@ -48,7 +63,22 @@ public function testDenormalize(): void
{
self::assertEquals(
new Money(100, new Currency('USD')),
- (new MoneyNormalizer())->denormalize(['amount' => '100', 'currency' => 'USD'], Money::class)
+ (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),
);
}
@@ -82,17 +112,22 @@ public function dataSupportsDenormalization(): \Generator
}
/**
- * @param mixed $data
- *
* @dataProvider dataSupportsDenormalization
*/
- public function testSupportsDenormalization($data, string $type, bool $supported): void
+ public function testSupportsDenormalization(mixed $data, string $type, bool $supported): void
{
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());
}
}
diff --git a/tests/Twig/MoneyExtensionTest.php b/tests/Twig/MoneyExtensionTest.php
index cac2171..b6f132e 100644
--- a/tests/Twig/MoneyExtensionTest.php
+++ b/tests/Twig/MoneyExtensionTest.php
@@ -16,12 +16,9 @@ final class MoneyExtensionTest extends TestCase
/**
* @var MockObject&FormatterFactoryInterface
*/
- private $formatterFactory;
+ private MockObject $formatterFactory;
- /**
- * @var MoneyExtension
- */
- private $extension;
+ private MoneyExtension $extension;
protected function setUp(): void
{
@@ -32,34 +29,22 @@ protected function setUp(): void
public function testExtensionRegistersFilters(): void
{
- self::assertContainsOnlyInstancesOf(
- TwigFilter::class,
- $this->extension->getFilters()
- );
+ self::assertContainsOnlyInstancesOf(TwigFilter::class, $this->extension->getFilters());
}
public function testExtensionRegistersFunctions(): void
{
- self::assertContainsOnlyInstancesOf(
- TwigFunction::class,
- $this->extension->getFunctions()
- );
+ self::assertContainsOnlyInstancesOf(TwigFunction::class, $this->extension->getFunctions());
}
public function testMoneyIsCreatedWithDefaultCurrency(): void
{
- self::assertEquals(
- Money::USD(100),
- $this->extension->createMoney(100)
- );
+ self::assertEquals(Money::USD(100), $this->extension->createMoney(100));
}
public function testMoneyIsCreatedWithCustomCurrency(): void
{
- self::assertEquals(
- Money::EUR(100),
- $this->extension->createMoney('100', 'EUR')
- );
+ self::assertEquals(Money::EUR(100), $this->extension->createMoney('100', 'EUR'));
}
public function testMoneyIsFormatted(): void
diff --git a/tests/Validator/Constraints/AbstractMoneyComparisonValidatorTestCase.php b/tests/Validator/Constraints/AbstractMoneyComparisonValidatorTestCase.php
index f1bce55..2c35726 100644
--- a/tests/Validator/Constraints/AbstractMoneyComparisonValidatorTestCase.php
+++ b/tests/Validator/Constraints/AbstractMoneyComparisonValidatorTestCase.php
@@ -22,17 +22,12 @@ abstract class AbstractMoneyComparisonValidatorTestCase extends ConstraintValida
/**
* @param mixed $options The value to compare or a set of options
*/
- abstract protected function createConstraint($options = null): AbstractMoneyComparison;
+ abstract protected function createConstraint(mixed $options = null): AbstractMoneyComparison;
protected function createValueObject(?Money $value): object
{
return new class($value) {
- private ?Money $value;
-
- public function __construct(?Money $value)
- {
- $this->value = $value;
- }
+ public function __construct(private readonly ?Money $value) {}
public function getValue(): ?Money
{
@@ -123,29 +118,13 @@ public function testInvalidValuePath(): void
$constraint = $this->createConstraint(['propertyPath' => 'foo']);
$this->expectException(InvalidArgumentException::class);
- $this->expectExceptionMessage(sprintf('Invalid property path "foo" provided to "%s" constraint', \get_class($constraint)));
+ $this->expectExceptionMessage(sprintf('Invalid property path "foo" provided to "%s" constraint', $constraint::class));
$this->setObject($this->createValueObject(Money::USD(500)));
$this->validator->validate(500, $constraint);
}
- public function testInvalidValueAsArray(): void
- {
- $this->expectException(InvalidArgumentException::class);
- $this->expectExceptionMessage(sprintf('Could not convert value of type "array" to a "%s" instance for comparison.', Money::class));
-
- $this->validator->validate(500, $this->createConstraint(['value' => ['amount' => '500', 'currency' => 'USD']]));
- }
-
- public function testInvalidValueAsNonMoneyObject(): void
- {
- $this->expectException(InvalidArgumentException::class);
- $this->expectExceptionMessage(sprintf('Could not convert value of type "%s" to a "%s" instance for comparison.', \stdClass::class, Money::class));
-
- $this->validator->validate(500, $this->createConstraint(new \stdClass()));
- }
-
public function testInvalidValueAsBadlyFormattedString(): void
{
$this->expectException(InvalidArgumentException::class);