diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 833574c6..fc25270f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -25,7 +25,7 @@ jobs:
coverage: none
- name: 'Require Doctrine MongoDB dependencies'
- run: composer require --no-update --ignore-platform-reqs --dev --no-interaction --ansi "doctrine/mongodb-odm:^2.4" "doctrine/mongodb-odm-bundle:^4.5.1"
+ run: composer require --no-update --ignore-platform-reqs --dev --no-interaction --ansi "doctrine/mongodb-odm:^2.6" "doctrine/mongodb-odm-bundle:^5.0.0"
- name: 'Install dependencies'
id: deps
@@ -72,12 +72,7 @@ jobs:
mongodb: true
# Previous Symfony versions
- # …
- # Previous PHP versions
- # …
-
- # Most recent versions
- name: 'Test Symfony 5.4 [Linux, PHP 8.1]'
os: 'ubuntu-latest'
php: '8.1'
@@ -86,43 +81,58 @@ jobs:
mongodb: true
mysql: true
- - name: 'Test Symfony 5.4 [Windows, PHP 8.1]'
- os: 'windows-latest'
- php: '8.1'
- symfony: '5.4.*@dev'
+ - name: 'Test Symfony 6.4 [Linux, PHP 8.3]'
+ os: 'ubuntu-latest'
+ php: '8.3'
+ symfony: '6.4.*@dev'
+ allow-unstable: true
mongodb: true
mysql: true
- allow-unstable: true
- - name: 'Test Symfony 6.3 [Linux, PHP 8.1]'
+ - name: 'Test Symfony 7.0 [Linux, PHP 8.3]'
os: 'ubuntu-latest'
- php: '8.1'
- symfony: '6.3.*@dev'
- mongodb: true
- mysql: true
+ php: '8.3'
+ symfony: '7.0.*@dev'
allow-unstable: true
+ mysql: true
+ mongodb: true
+ mongodbnew: true
+
+ # Previous PHP versions
- - name: 'Test Symfony 6.4 [Linux, PHP 8.1]'
+ - name: 'Test Symfony 7.1 [Linux, PHP 8.2]'
os: 'ubuntu-latest'
- php: '8.1'
- symfony: '6.4.*@dev'
+ php: '8.2'
+ symfony: '7.0.*@dev'
allow-unstable: true
mysql: true
mongodb: true
+ mongodbnew: true
- - name: 'Test Symfony 7.0 [Linux, PHP 8.2]'
+ # Most recent versions
+
+ - name: 'Test Symfony 7.1 [Linux, PHP 8.3]'
os: 'ubuntu-latest'
- php: '8.2'
+ php: '8.3'
symfony: '7.0.*@dev'
allow-unstable: true
mysql: true
mongodb: true
mongodbnew: true
+ - name: 'Test Symfony 7.1 [Windows, PHP 8.3]'
+ os: 'windows-latest'
+ php: '8.3'
+ symfony: '7.1.*@dev'
+ mongodb: true
+ mongodbnew: true
+ mysql: true
+ allow-unstable: true
+
# Bleeding edge (unreleased dev versions where failures are allowed)
- - name: 'Test next Symfony 7.1 [Linux, PHP 8.2] (allowed failure)'
+ - name: 'Test next Symfony 7.2 [Linux, PHP 8.3] (allowed failure)'
os: 'ubuntu-latest'
- php: '8.2'
+ php: '8.3'
symfony: '7.1.*@dev'
composer-flags: '--ignore-platform-req php'
allow-unstable: true
@@ -131,10 +141,10 @@ jobs:
mongodb: true
mongodbnew: true
- - name: 'Test next Symfony 7.1 [Linux, PHP 8.3] (allowed failure)'
+ - name: 'Test next Symfony 7.2 [Linux, PHP 8.4] (allowed failure)'
os: 'ubuntu-latest'
- php: '8.3'
- symfony: '7.1.*@dev'
+ php: '8.4'
+ symfony: '7.2.*@dev'
composer-flags: '--ignore-platform-req php'
allow-unstable: true
allow-failure: true
@@ -202,7 +212,7 @@ jobs:
if: ${{ matrix.mongodb && !matrix.mongodbnew }}
- name: 'Require Doctrine MongoDB dependencies for new symfony'
- run: composer require --no-update ${{ matrix.composer-flags }} --dev --no-interaction --ansi "doctrine/mongodb-odm:^2.6" "doctrine/mongodb-odm-bundle:5.0.x-dev"
+ run: composer require --no-update ${{ matrix.composer-flags }} --dev --no-interaction --ansi "doctrine/mongodb-odm:^2.6" "doctrine/mongodb-odm-bundle:^5.0.0"
if: ${{ matrix.mongodb && matrix.mongodbnew }}
- name: 'Install dependencies'
diff --git a/.make/try.mk b/.make/try.mk
new file mode 100644
index 00000000..32090fb9
--- /dev/null
+++ b/.make/try.mk
@@ -0,0 +1,23 @@
+#######
+# Try #
+#######
+
+# Execute first command (try), unconditionnaly run second command (finally), and
+# exit with first command return code.
+#
+# @param $1 First command
+# @param $2 Second command
+#
+# Examples:
+#
+# Example #1: Run tests and remove artefacts
+#
+# $(call try_finally, phpunit, rm -Rf artefacts)
+
+define try_finally
+( \
+ $(strip $(1)) \
+) ; RC=$${?} \
+; $(strip $(2)) \
+&& exit $${RC}
+endef
diff --git a/.php-version b/.php-version
index b8eb0263..2983cad0 100644
--- a/.php-version
+++ b/.php-version
@@ -1 +1 @@
-8.1
+8.2
diff --git a/Makefile b/Makefile
index ca19c578..f38b9b9c 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,6 @@
include .make/help.mk
include .make/text.mk
+include .make/try.mk
PHP_CS_FIXER_VERSION=v3.13.0
@@ -14,7 +15,7 @@ setup:
## Install - Install deps
install: setup
install:
- rm -f symfony composer.lock
+ rm -f composer.lock
symfony composer config minimum-stability --unset
symfony composer update --prefer-dist --ignore-platform-req=ext-mongodb
@@ -32,13 +33,6 @@ install.54:
symfony composer config minimum-stability dev
symfony composer update --ignore-platform-req=ext-mongodb
-## Install - Install Symfony 6.3 deps
-install.63: setup
-install.63: export SYMFONY_REQUIRE = 6.3.*@dev
-install.63:
- symfony composer config minimum-stability dev
- symfony composer update --ignore-platform-req=ext-mongodb
-
## Install - Install Symfony 6.4 deps
install.64: setup
install.64: export SYMFONY_REQUIRE = 6.4.*@dev
@@ -61,8 +55,16 @@ install.71:
symfony composer update --ignore-platform-req=ext-mongodb
## Install - Add Doctrine ODM deps
-deps.odm.add:
- symfony composer require --no-update --no-interaction --dev "doctrine/mongodb-odm:^2.3" "doctrine/mongodb-odm-bundle:^4.4.1"
+deps.odm.add: deps.odm.add+sf64
+
+## Install - Add Doctrine ODM deps for Symfony 6.4+
+deps.odm.add+sf64:
+ symfony composer require --no-update --no-interaction --dev "doctrine/mongodb-odm:^2.6" "doctrine/mongodb-odm-bundle:^5.0"
+ @$(call log_warning, Run again appropriate install target to update dependencies. Be careful not to commit compose.json changes.)
+
+## Install - Add Doctrine ODM deps for Symfony 5.4+
+deps.odm.add+sf54:
+ symfony composer require --no-update --no-interaction --dev "doctrine/mongodb-odm:^2.4" "doctrine/mongodb-odm-bundle:^4.5.1"
@$(call log_warning, Run again appropriate install target to update dependencies. Be careful not to commit compose.json changes.)
## Install - Remove back Doctrine ODM deps
@@ -108,10 +110,12 @@ lint.update:
make php-cs-fixer.phar
lint.php-cs-fixer.fix: php-cs-fixer.phar
+lint.php-cs-fixer.fix: export PHP_CS_FIXER_IGNORE_ENV = 1
lint.php-cs-fixer.fix:
symfony php ./php-cs-fixer.phar fix --no-interaction
lint.php-cs-fixer: php-cs-fixer.phar
+lint.php-cs-fixer: export PHP_CS_FIXER_IGNORE_ENV = 1
lint.php-cs-fixer:
symfony php ./php-cs-fixer.phar fix --no-interaction --dry-run --diff -vvv
@@ -121,5 +125,4 @@ php-cs-fixer.phar:
lint.phpstan:
@make deps.odm.add install >> /dev/null 2>&1
- ./vendor/bin/phpstan
- @make deps.odm.rm install >> /dev/null 2>&1
+ $(call try_finally, ./vendor/bin/phpstan, make deps.odm.rm install >> /dev/null 2>&1)
diff --git a/composer.json b/composer.json
index 4eec0c03..07d926db 100644
--- a/composer.json
+++ b/composer.json
@@ -41,22 +41,22 @@
"require-dev": {
"ext-pdo_sqlite": "*",
"composer/semver": "^3.2",
- "doctrine/dbal": "^3.2|^4.0",
- "doctrine/doctrine-bundle": "^2.5",
- "doctrine/orm": "^2.10|^3.0",
+ "doctrine/dbal": "^3.8|^4.0",
+ "doctrine/doctrine-bundle": "^2.11",
+ "doctrine/orm": "^2.20|^3.0",
"phpstan/phpstan": "^1.10",
"phpstan/phpstan-symfony": "^1.2",
- "symfony/browser-kit": "^5.4|^6.3|^7.0",
- "symfony/config": "^5.4|^6.3|^7.0",
- "symfony/dependency-injection": "^5.4.2|^6.3|^7.0",
- "symfony/filesystem": "^5.4|^6.3|^7.0",
- "symfony/form": "^5.4|^6.3|^7.0",
- "symfony/framework-bundle": "^5.4|^6.3|^7.0",
- "symfony/http-kernel": "^5.4.2|^6.3|^7.0",
- "symfony/phpunit-bridge": "^5.4|^6.3|^7.0",
- "symfony/translation": "^5.4|^6.3|^7.0",
- "symfony/var-dumper": "^5.4|^6.3|^7.0",
- "symfony/yaml": "^5.4|^6.3|^7.0"
+ "symfony/browser-kit": "^5.4.40|^6.4|^7.0",
+ "symfony/config": "^5.4.40|^6.4|^7.0",
+ "symfony/dependency-injection": "^5.4.40|^6.4|^7.0",
+ "symfony/filesystem": "^5.4.40|^6.4|^7.0",
+ "symfony/form": "^5.4.40|^6.4|^7.0",
+ "symfony/framework-bundle": "^5.4.40|^6.4|^7.0",
+ "symfony/http-kernel": "^5.4.40|^6.4|^7.0",
+ "symfony/phpunit-bridge": "^5.4.40|^6.4|^7.0",
+ "symfony/translation": "^5.4.40|^6.4|^7.0",
+ "symfony/var-dumper": "^5.4.40|^6.4|^7.0",
+ "symfony/yaml": "^5.4.40|^6.4|^7.0"
},
"extra": {
"branch-alias": {
@@ -68,6 +68,5 @@
"*": "dist"
},
"sort-packages": true
- },
- "minimum-stability": "dev"
+ }
}
diff --git a/docker-compose.yml b/docker-compose.yml
index 4e518920..0eedd530 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,5 +1,3 @@
-version: '3.3'
-
services:
db:
image: mysql:8.0
diff --git a/phpstan.neon.dist b/phpstan.dist.neon
similarity index 72%
rename from phpstan.neon.dist
rename to phpstan.dist.neon
index 1fe2db1f..7b8b1967 100644
--- a/phpstan.neon.dist
+++ b/phpstan.dist.neon
@@ -7,5 +7,7 @@ parameters:
paths:
- src
- checkMissingIterableValueType: false
treatPhpDocTypesAsCertain: false
+
+ ignoreErrors:
+ - identifier: missingType.iterableValue
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 05c51501..82b4d027 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -16,7 +16,7 @@
-
+
diff --git a/src/Bridge/Doctrine/DBAL/Types/AbstractEnumSQLDeclarationType.php b/src/Bridge/Doctrine/DBAL/Types/AbstractEnumSQLDeclarationType.php
index 89b80a2d..34f94a47 100644
--- a/src/Bridge/Doctrine/DBAL/Types/AbstractEnumSQLDeclarationType.php
+++ b/src/Bridge/Doctrine/DBAL/Types/AbstractEnumSQLDeclarationType.php
@@ -25,7 +25,7 @@ abstract class AbstractEnumSQLDeclarationType extends AbstractEnumType
/**
* {@inheritdoc}
*/
- public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string
+ public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
{
if (class_exists(AbstractMySQLPlatform::class)) {
if (!$platform instanceof AbstractMySQLPlatform) {
diff --git a/src/Bridge/Doctrine/DBAL/Types/AbstractEnumType.php b/src/Bridge/Doctrine/DBAL/Types/AbstractEnumType.php
index 62c36f97..d101dc5f 100644
--- a/src/Bridge/Doctrine/DBAL/Types/AbstractEnumType.php
+++ b/src/Bridge/Doctrine/DBAL/Types/AbstractEnumType.php
@@ -13,9 +13,17 @@
namespace Elao\Enum\Bridge\Doctrine\DBAL\Types;
use Doctrine\DBAL\ParameterType;
+use Doctrine\DBAL\Platforms\AbstractPlatform;
+use Doctrine\DBAL\Types\Type;
+use Elao\Enum\Exception\InvalidArgumentException;
if (enum_exists(ParameterType::class)) {
- abstract class AbstractEnumType extends AbstractEnumTypeCommon
+ /**
+ * For doctrine/dbal 4
+ *
+ * @internal
+ */
+ trait DbalVersionEnumTypeTrait
{
/**
* {@inheritdoc}
@@ -26,7 +34,12 @@ public function getBindingType(): ParameterType
}
}
} else {
- abstract class AbstractEnumType extends AbstractEnumTypeCommon
+ /**
+ * For doctrine/dbal 3
+ *
+ * @internal
+ */
+ trait DbalVersionEnumTypeTrait
{
/**
* {@inheritdoc}
@@ -37,3 +50,132 @@ public function getBindingType(): int
}
}
}
+
+abstract class AbstractEnumType extends Type
+{
+ use DbalVersionEnumTypeTrait;
+
+ private bool $isIntBackedEnum;
+
+ /**
+ * The enum FQCN for which we should make the DBAL conversion.
+ *
+ * @psalm-return class-string<\BackedEnum>
+ */
+ abstract protected function getEnumClass(): string;
+
+ /**
+ * What should be returned on null value from the database.
+ */
+ protected function onNullFromDatabase(): ?\BackedEnum
+ {
+ return null;
+ }
+
+ /**
+ * What should be returned on null value from PHP.
+ */
+ protected function onNullFromPhp(): int|string|null
+ {
+ return null;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @param \BackedEnum|int|string|null $value
+ */
+ public function convertToDatabaseValue($value, AbstractPlatform $platform): string|int|null
+ {
+ if ($value !== null && !is_a($value, $this->getEnumClass())) {
+ $throwException = true;
+ if ($this->checkIfValueMatchesBackedEnumType($value)) {
+ $value = $this->getEnumClass()::tryFrom($this->cast($value));
+ if ($value !== null) {
+ $throwException = false;
+ }
+ }
+
+ if ($throwException) {
+ throw new InvalidArgumentException(sprintf(
+ 'Expected an instance of a %s. %s given.',
+ $this->getEnumClass(),
+ get_debug_type($value),
+ ));
+ }
+ }
+
+ if (null === $value) {
+ return $this->onNullFromPhp();
+ }
+
+ return $value->value;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @param int|string|null $value The value to convert.
+ */
+ public function convertToPHPValue($value, AbstractPlatform $platform): ?\BackedEnum
+ {
+ if (null === $value) {
+ return $this->onNullFromDatabase();
+ }
+
+ return $this->getEnumClass()::from($this->cast($value));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
+ {
+ if ($this->isIntBackedEnum()) {
+ return $platform->getIntegerTypeDeclarationSQL($column);
+ }
+
+ if (empty($column['length'])) {
+ $column['length'] = 255;
+ }
+
+ return $platform->getStringTypeDeclarationSQL($column);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function requiresSQLCommentHint(AbstractPlatform $platform): bool
+ {
+ return true;
+ }
+
+ /**
+ * Cast the value from database to proper enumeration internal type.
+ */
+ private function cast(int|string $value): int|string
+ {
+ return $this->isIntBackedEnum() ? (int) $value : (string) $value;
+ }
+
+ private function checkIfValueMatchesBackedEnumType(mixed $value): bool
+ {
+ return ($this->isIntBackedEnum() && \is_int($value)) || (!$this->isIntBackedEnum() && \is_string($value));
+ }
+
+ private function isIntBackedEnum(): bool
+ {
+ if (!isset($this->isIntBackedEnum)) {
+ $r = new \ReflectionEnum($this->getEnumClass());
+
+ $this->isIntBackedEnum = 'int' === (string) $r->getBackingType();
+ }
+
+ return $this->isIntBackedEnum;
+ }
+
+ public function getName(): string
+ {
+ return $this->getEnumClass();
+ }
+}
diff --git a/src/Bridge/Doctrine/DBAL/Types/AbstractEnumTypeCommon.php b/src/Bridge/Doctrine/DBAL/Types/AbstractEnumTypeCommon.php
deleted file mode 100644
index 9e478910..00000000
--- a/src/Bridge/Doctrine/DBAL/Types/AbstractEnumTypeCommon.php
+++ /dev/null
@@ -1,146 +0,0 @@
-
- */
-
-namespace Elao\Enum\Bridge\Doctrine\DBAL\Types;
-
-use Doctrine\DBAL\Platforms\AbstractPlatform;
-use Doctrine\DBAL\Types\Type;
-use Elao\Enum\Exception\InvalidArgumentException;
-
-abstract class AbstractEnumTypeCommon extends Type
-{
- private bool $isIntBackedEnum;
-
- /**
- * The enum FQCN for which we should make the DBAL conversion.
- *
- * @psalm-return class-string<\BackedEnum>
- */
- abstract protected function getEnumClass(): string;
-
- /**
- * What should be returned on null value from the database.
- */
- protected function onNullFromDatabase(): ?\BackedEnum
- {
- return null;
- }
-
- /**
- * What should be returned on null value from PHP.
- */
- protected function onNullFromPhp(): int|string|null
- {
- return null;
- }
-
- /**
- * {@inheritdoc}
- *
- * @param \BackedEnum|int|string|null $value
- */
- public function convertToDatabaseValue($value, AbstractPlatform $platform): string|int|null
- {
- if ($value !== null && !is_a($value, $this->getEnumClass())) {
- $throwException = true;
- if ($this->checkIfValueMatchesBackedEnumType($value)) {
- $value = $this->getEnumClass()::tryFrom($this->cast($value));
- if ($value !== null) {
- $throwException = false;
- }
- }
-
- if ($throwException) {
- throw new InvalidArgumentException(sprintf(
- 'Expected an instance of a %s. %s given.',
- $this->getEnumClass(),
- get_debug_type($value),
- ));
- }
- }
-
- if (null === $value) {
- return $this->onNullFromPhp();
- }
-
- return $value->value;
- }
-
- /**
- * {@inheritdoc}
- *
- * @param int|string|null $value The value to convert.
- */
- public function convertToPHPValue($value, AbstractPlatform $platform): ?\BackedEnum
- {
- if (null === $value) {
- return $this->onNullFromDatabase();
- }
-
- return $this->getEnumClass()::from($this->cast($value));
- }
-
- /**
- * {@inheritdoc}
- */
- public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
- {
- if ($this->isIntBackedEnum()) {
- return $platform->getIntegerTypeDeclarationSQL($column);
- }
-
- if (empty($column['length'])) {
- $column['length'] = 255;
- }
-
- return method_exists($platform, 'getStringTypeDeclarationSQL') ?
- $platform->getStringTypeDeclarationSQL($column) :
- $platform->getVarcharTypeDeclarationSQL($column);
- }
-
- /**
- * {@inheritdoc}
- */
- public function requiresSQLCommentHint(AbstractPlatform $platform): bool
- {
- return true;
- }
-
- /**
- * Cast the value from database to proper enumeration internal type.
- */
- private function cast(int|string $value): int|string
- {
- return $this->isIntBackedEnum() ? (int) $value : (string) $value;
- }
-
- private function checkIfValueMatchesBackedEnumType(mixed $value): bool
- {
- return ($this->isIntBackedEnum() && \is_int($value)) || (!$this->isIntBackedEnum() && \is_string($value));
- }
-
- protected function isIntBackedEnum(): bool
- {
- if (!isset($this->isIntBackedEnum)) {
- $r = new \ReflectionEnum($this->getEnumClass());
-
- $this->isIntBackedEnum = 'int' === (string) $r->getBackingType();
- }
-
- return $this->isIntBackedEnum;
- }
-
- public function getName(): string
- {
- return $this->getEnumClass();
- }
-}
diff --git a/src/Bridge/Doctrine/DBAL/Types/AbstractFlagBagType.php b/src/Bridge/Doctrine/DBAL/Types/AbstractFlagBagType.php
index 6724963a..6b74f15c 100644
--- a/src/Bridge/Doctrine/DBAL/Types/AbstractFlagBagType.php
+++ b/src/Bridge/Doctrine/DBAL/Types/AbstractFlagBagType.php
@@ -13,21 +13,129 @@
namespace Elao\Enum\Bridge\Doctrine\DBAL\Types;
use Doctrine\DBAL\ParameterType;
+use Doctrine\DBAL\Platforms\AbstractPlatform;
+use Doctrine\DBAL\Types\Type;
+use Elao\Enum\Exception\InvalidArgumentException;
+use Elao\Enum\FlagBag;
if (enum_exists(ParameterType::class)) {
- abstract class AbstractFlagBagType extends AbstractFlagBagTypeCommon
+ /**
+ * For doctrine/dbal 4
+ *
+ * @internal
+ */
+ trait DbalVersionFlagBagTypeTrait
{
+ /**
+ * {@inheritdoc}
+ */
public function getBindingType(): ParameterType
{
return ParameterType::INTEGER;
}
}
} else {
- abstract class AbstractFlagBagType extends AbstractFlagBagTypeCommon
+ /**
+ * For doctrine/dbal 3
+ *
+ * @internal
+ */
+ trait DbalVersionFlagBagTypeTrait
{
+ /**
+ * {@inheritdoc}
+ */
public function getBindingType(): int
{
return ParameterType::INTEGER;
}
}
}
+
+abstract class AbstractFlagBagType extends Type
+{
+ use DbalVersionFlagBagTypeTrait;
+
+ /**
+ * The enum FQCN for which we should make the DBAL conversion.
+ *
+ * @psalm-return class-string<\BackedEnum>
+ */
+ abstract protected function getEnumClass(): string;
+
+ /**
+ * What should be returned on null value from the database.
+ *
+ * @return FlagBag<\BackedEnum>|null
+ */
+ protected function onNullFromDatabase(): ?FlagBag
+ {
+ return null;
+ }
+
+ /**
+ * What should be returned on null value from PHP.
+ */
+ protected function onNullFromPhp(): int|null
+ {
+ return null;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @param FlagBag<\BackedEnum>|null $value
+ */
+ public function convertToDatabaseValue($value, AbstractPlatform $platform): ?int
+ {
+ if ($value !== null && !$value instanceof FlagBag) {
+ throw new InvalidArgumentException(sprintf(
+ 'Expected an instance of a %s. %s given.',
+ FlagBag::class,
+ get_debug_type($value),
+ ));
+ }
+
+ if (null === $value) {
+ return $this->onNullFromPhp();
+ }
+
+ return $value->getValue();
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @param int|null $value The value to convert.
+ *
+ * @return FlagBag<\BackedEnum>|null
+ */
+ public function convertToPHPValue($value, AbstractPlatform $platform): ?FlagBag
+ {
+ $value = parent::convertToPHPValue($value, $platform);
+
+ if (null === $value) {
+ return $this->onNullFromDatabase();
+ }
+
+ return new FlagBag($this->getEnumClass(), $value);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function requiresSQLCommentHint(AbstractPlatform $platform): bool
+ {
+ return true;
+ }
+
+ public function getName(): string
+ {
+ return $this->getEnumClass();
+ }
+
+ public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
+ {
+ return $platform->getIntegerTypeDeclarationSQL($column);
+ }
+}
diff --git a/src/Bridge/Doctrine/DBAL/Types/AbstractFlagBagTypeCommon.php b/src/Bridge/Doctrine/DBAL/Types/AbstractFlagBagTypeCommon.php
deleted file mode 100644
index 6fb21e66..00000000
--- a/src/Bridge/Doctrine/DBAL/Types/AbstractFlagBagTypeCommon.php
+++ /dev/null
@@ -1,104 +0,0 @@
-
- */
-
-namespace Elao\Enum\Bridge\Doctrine\DBAL\Types;
-
-use Doctrine\DBAL\Platforms\AbstractPlatform;
-use Doctrine\DBAL\Types\Type;
-use Elao\Enum\Exception\InvalidArgumentException;
-use Elao\Enum\FlagBag;
-
-abstract class AbstractFlagBagTypeCommon extends Type
-{
- /**
- * The enum FQCN for which we should make the DBAL conversion.
- *
- * @psalm-return class-string<\BackedEnum>
- */
- abstract protected function getEnumClass(): string;
-
- /**
- * What should be returned on null value from the database.
- *
- * @return FlagBag<\BackedEnum>|null
- */
- protected function onNullFromDatabase(): ?FlagBag
- {
- return null;
- }
-
- /**
- * What should be returned on null value from PHP.
- */
- protected function onNullFromPhp(): int|null
- {
- return null;
- }
-
- /**
- * {@inheritdoc}
- *
- * @param FlagBag<\BackedEnum>|null $value
- */
- public function convertToDatabaseValue($value, AbstractPlatform $platform): ?int
- {
- if ($value !== null && !$value instanceof FlagBag) {
- throw new InvalidArgumentException(sprintf(
- 'Expected an instance of a %s. %s given.',
- FlagBag::class,
- get_debug_type($value),
- ));
- }
-
- if (null === $value) {
- return $this->onNullFromPhp();
- }
-
- return $value->getValue();
- }
-
- /**
- * {@inheritdoc}
- *
- * @param int|null $value The value to convert.
- *
- * @return FlagBag<\BackedEnum>|null
- */
- public function convertToPHPValue($value, AbstractPlatform $platform): ?FlagBag
- {
- $value = parent::convertToPHPValue($value, $platform);
-
- if (null === $value) {
- return $this->onNullFromDatabase();
- }
-
- return new FlagBag($this->getEnumClass(), $value);
- }
-
- /**
- * {@inheritdoc}
- */
- public function requiresSQLCommentHint(AbstractPlatform $platform): bool
- {
- return true;
- }
-
- public function getName(): string
- {
- return $this->getEnumClass();
- }
-
- public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
- {
- return $platform->getIntegerTypeDeclarationSQL($column);
- }
-}
diff --git a/src/Bridge/Symfony/Bundle/config/services.php b/src/Bridge/Symfony/Bundle/config/services.php
index f817fb59..bbd283d4 100644
--- a/src/Bridge/Symfony/Bundle/config/services.php
+++ b/src/Bridge/Symfony/Bundle/config/services.php
@@ -15,9 +15,11 @@
use Elao\Enum\Bridge\Symfony\HttpKernel\Controller\ArgumentResolver\BackedEnumValueResolver;
use Elao\Enum\Bridge\Symfony\HttpKernel\Controller\ArgumentResolver\QueryBodyBackedEnumValueResolver;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\BackedEnumValueResolver as SymfonyBackedEnumValueResolver;
+use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
return static function (ContainerConfigurator $container) {
- if (!class_exists(SymfonyBackedEnumValueResolver::class)) {
+ // To be dropped when Symfony 5.4 support is dropped
+ if (!class_exists(SymfonyBackedEnumValueResolver::class) && interface_exists(ArgumentValueResolverInterface::class)) {
$container->services()
->set(BackedEnumValueResolver::class)->tag('controller.argument_value_resolver', [
'priority' => 105, // Prior RequestAttributeValueResolver
diff --git a/src/Bridge/Symfony/HttpKernel/Controller/ArgumentResolver/BackedEnumValueResolver.php b/src/Bridge/Symfony/HttpKernel/Controller/ArgumentResolver/BackedEnumValueResolver.php
index 7b2feb13..7c8cf9a8 100644
--- a/src/Bridge/Symfony/HttpKernel/Controller/ArgumentResolver/BackedEnumValueResolver.php
+++ b/src/Bridge/Symfony/HttpKernel/Controller/ArgumentResolver/BackedEnumValueResolver.php
@@ -18,7 +18,13 @@
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+if (!interface_exists(ArgumentValueResolverInterface::class)) {
+ // Interface does not exist anymore as of Symfony 7+
+ return;
+}
+
// Legacy (<6.1) resolver
+// To be dropped when Symfony 5.4 is EOL.
/**
* Attempt to resolve backed enum cases from request attributes, for a route path parameter,
* leading to a 404 Not Found if the attribute value isn't a valid backing value for the enum type.
diff --git a/src/Bridge/Symfony/HttpKernel/Controller/ArgumentResolver/QueryBodyBackedEnumValueResolver.php b/src/Bridge/Symfony/HttpKernel/Controller/ArgumentResolver/QueryBodyBackedEnumValueResolver.php
index 2a0445c7..057683d8 100644
--- a/src/Bridge/Symfony/HttpKernel/Controller/ArgumentResolver/QueryBodyBackedEnumValueResolver.php
+++ b/src/Bridge/Symfony/HttpKernel/Controller/ArgumentResolver/QueryBodyBackedEnumValueResolver.php
@@ -62,7 +62,8 @@ function resolveValues(Request $request, ArgumentMetadata $argument): array
}
// Legacy (<6.2) resolver
-if (!interface_exists(ValueResolverInterface::class)) {
+// To be dropped when Symfony 5.4 is EOL.
+if (!interface_exists(ValueResolverInterface::class) && interface_exists(ArgumentValueResolverInterface::class)) {
/**
* @final
*/
diff --git a/tests/Fixtures/Integration/Symfony/config/config-64+.yaml b/tests/Fixtures/Integration/Symfony/config/config-64+.yaml
new file mode 100644
index 00000000..71d6bc47
--- /dev/null
+++ b/tests/Fixtures/Integration/Symfony/config/config-64+.yaml
@@ -0,0 +1,7 @@
+framework:
+ session:
+ cookie_secure: auto
+ cookie_samesite: lax
+ handle_all_throwables: true
+ php_errors:
+ log: true
diff --git a/tests/Fixtures/Integration/Symfony/config/config.yaml b/tests/Fixtures/Integration/Symfony/config/config.yaml
index b40a5732..89dc35da 100644
--- a/tests/Fixtures/Integration/Symfony/config/config.yaml
+++ b/tests/Fixtures/Integration/Symfony/config/config.yaml
@@ -1,56 +1,55 @@
framework:
- secret: 'elao'
- form: true
- router:
- strict_requirements: '%kernel.debug%'
- utf8: true
- session:
- handler_id: null
- storage_factory_id: 'session.storage.factory.mock_file'
- test: ~
- assets: false
- http_method_override: false
+ secret: 'elao'
+ form: true
+ router:
+ strict_requirements: '%kernel.debug%'
+ utf8: true
+ session:
+ handler_id: null
+ storage_factory_id: 'session.storage.factory.mock_file'
+ test: ~
+ assets: false
+ http_method_override: false
doctrine:
- dbal:
- url: '%env(resolve:DOCTRINE_DBAL_URL)%'
+ dbal:
+ url: '%env(resolve:DOCTRINE_DBAL_URL)%'
- orm:
- auto_generate_proxy_classes: '%kernel.debug%'
- mappings:
- App:
- is_bundle: false
- type: attribute
- dir: '%kernel.project_dir%/src/Entity'
- prefix: 'App\Entity'
- alias: App
+ orm:
+ auto_generate_proxy_classes: '%kernel.debug%'
+ report_fields_where_declared: true
+ enable_lazy_ghost_objects: true
+ controller_resolver:
+ auto_mapping: false
+
+ mappings:
+ App:
+ is_bundle: false
+ type: attribute
+ dir: '%kernel.project_dir%/src/Entity'
+ prefix: 'App\Entity'
+ alias: App
elao_enum:
- doctrine:
- types:
- App\Enum\Suit:
- default: !php/const App\Enum\Suit::Spades
- permissions_flagbag:
- class: App\Enum\Permissions
- default: !php/const App\Enum\Permissions::Read
- type: flagbag
+ doctrine:
+ types:
+ App\Enum\Suit:
+ default: !php/const App\Enum\Suit::Spades
+ permissions_flagbag:
+ class: App\Enum\Permissions
+ default: !php/const App\Enum\Permissions::Read
+ type: flagbag
services:
- # Registers these controllers as a service so that we have the
- # \Symfony\Component\HttpKernel\Controller\ArgumentResolver\ServiceValueResolver enabled on it:
- App\Controller\BackedEnumValueResolverController:
- autoconfigure: true
- autowire: true
- App\Controller\QueryBodyBackedEnumValueResolverController:
- autoconfigure: true
- autowire: true
- App\ControllerAnnotation\BackedEnumValueResolverController:
- autoconfigure: true
- autowire: true
- App\ControllerAnnotation\QueryBodyBackedEnumValueResolverController:
- autoconfigure: true
- autowire: true
+ # Registers these controllers as a service so that we have the
+ # \Symfony\Component\HttpKernel\Controller\ArgumentResolver\ServiceValueResolver enabled on it:
+ App\Controller\BackedEnumValueResolverController:
+ autoconfigure: true
+ autowire: true
+ App\Controller\QueryBodyBackedEnumValueResolverController:
+ autoconfigure: true
+ autowire: true
- logger:
- class: Psr\Log\NullLogger
- public: false
+ logger:
+ class: Psr\Log\NullLogger
+ public: false
diff --git a/tests/Fixtures/Integration/Symfony/config/doctrine-new.yaml b/tests/Fixtures/Integration/Symfony/config/doctrine-new.yaml
deleted file mode 100644
index e2affd90..00000000
--- a/tests/Fixtures/Integration/Symfony/config/doctrine-new.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-doctrine:
- orm:
- report_fields_where_declared: true
- enable_lazy_ghost_objects: true
- controller_resolver:
- auto_mapping: true
diff --git a/tests/Fixtures/Integration/Symfony/config/doctrine-old.yaml b/tests/Fixtures/Integration/Symfony/config/doctrine-old.yaml
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/Fixtures/Integration/Symfony/config/routing-annotation.yaml b/tests/Fixtures/Integration/Symfony/config/routing-annotation.yaml
index 14f4ce66..a9bbdc3a 100644
--- a/tests/Fixtures/Integration/Symfony/config/routing-annotation.yaml
+++ b/tests/Fixtures/Integration/Symfony/config/routing-annotation.yaml
@@ -1,3 +1,3 @@
controllers:
- resource: ../src/ControllerAnnotation/
+ resource: ../src/Controller/
type: annotation
diff --git a/tests/Fixtures/Integration/Symfony/src/Controller/BackedEnumValueResolverController.php b/tests/Fixtures/Integration/Symfony/src/Controller/BackedEnumValueResolverController.php
index 8168a50c..1f711d95 100644
--- a/tests/Fixtures/Integration/Symfony/src/Controller/BackedEnumValueResolverController.php
+++ b/tests/Fixtures/Integration/Symfony/src/Controller/BackedEnumValueResolverController.php
@@ -19,6 +19,10 @@
use Symfony\Component\VarDumper\Dumper\CliDumper;
use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
+if (!class_exists(Route::class)) {
+ class_alias(\Symfony\Component\Routing\Annotation\Route::class, Route::class);
+}
+
#[Route(path: '/resolver', name: 'from-attributes')]
class BackedEnumValueResolverController extends AbstractController
{
diff --git a/tests/Fixtures/Integration/Symfony/src/Controller/QueryBodyBackedEnumValueResolverController.php b/tests/Fixtures/Integration/Symfony/src/Controller/QueryBodyBackedEnumValueResolverController.php
index 465af7ca..575847a3 100644
--- a/tests/Fixtures/Integration/Symfony/src/Controller/QueryBodyBackedEnumValueResolverController.php
+++ b/tests/Fixtures/Integration/Symfony/src/Controller/QueryBodyBackedEnumValueResolverController.php
@@ -21,6 +21,10 @@
use Symfony\Component\VarDumper\Dumper\CliDumper;
use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
+if (!class_exists(Route::class)) {
+ class_alias(\Symfony\Component\Routing\Annotation\Route::class, Route::class);
+}
+
#[Route(path: '/resolver', name: 'from-query-body')]
class QueryBodyBackedEnumValueResolverController extends AbstractController
{
diff --git a/tests/Fixtures/Integration/Symfony/src/ControllerAnnotation/BackedEnumValueResolverController.php b/tests/Fixtures/Integration/Symfony/src/ControllerAnnotation/BackedEnumValueResolverController.php
deleted file mode 100644
index 07f1852c..00000000
--- a/tests/Fixtures/Integration/Symfony/src/ControllerAnnotation/BackedEnumValueResolverController.php
+++ /dev/null
@@ -1,49 +0,0 @@
-
- */
-
-namespace App\ControllerAnnotation;
-
-use App\Enum\Suit;
-use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
-use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\Routing\Annotation\Route;
-use Symfony\Component\VarDumper\Dumper\CliDumper;
-use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
-
-#[Route(path: '/resolver', name: 'from-attributes')]
-class BackedEnumValueResolverController extends AbstractController
-{
- use VarDumperTestTrait;
-
- public function __construct()
- {
- $this->setUpVarDumper([], CliDumper::DUMP_LIGHT_ARRAY);
- }
-
- #[Route(path: '/from-attributes/{suit}')]
- public function fromAttributes(Suit $suit): Response
- {
- return new Response($this->getDump($suit));
- }
-
- #[Route(path: '/from-attributes-nullable/{suit}')]
- public function fromAttributesNullable(?Suit $suit = null): Response
- {
- return new Response($this->getDump($suit));
- }
-
- #[Route(path: '/from-attributes-with-default')]
- public function fromAttributesWithDefault(Suit $suit = Suit::Spades): Response
- {
- return new Response($this->getDump($suit));
- }
-}
diff --git a/tests/Fixtures/Integration/Symfony/src/ControllerAnnotation/QueryBodyBackedEnumValueResolverController.php b/tests/Fixtures/Integration/Symfony/src/ControllerAnnotation/QueryBodyBackedEnumValueResolverController.php
deleted file mode 100644
index b3c6702e..00000000
--- a/tests/Fixtures/Integration/Symfony/src/ControllerAnnotation/QueryBodyBackedEnumValueResolverController.php
+++ /dev/null
@@ -1,89 +0,0 @@
-
- */
-
-namespace App\ControllerAnnotation;
-
-use App\Enum\Suit;
-use Elao\Enum\Bridge\Symfony\HttpKernel\Controller\ArgumentResolver\Attributes\BackedEnumFromBody;
-use Elao\Enum\Bridge\Symfony\HttpKernel\Controller\ArgumentResolver\Attributes\BackedEnumFromQuery;
-use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
-use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\Routing\Annotation\Route;
-use Symfony\Component\VarDumper\Dumper\CliDumper;
-use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
-
-#[Route(path: '/resolver', name: 'from-query-body')]
-class QueryBodyBackedEnumValueResolverController extends AbstractController
-{
- use VarDumperTestTrait;
-
- public function __construct()
- {
- $this->setUpVarDumper([], CliDumper::DUMP_LIGHT_ARRAY);
- }
-
- #[Route(path: '/from-query')]
- public function fromQuery(
- #[BackedEnumFromQuery]
- Suit $suit
- ): Response {
- return new Response($this->getDump($suit));
- }
-
- #[Route(path: '/from-query-nullable')]
- public function fromQueryNullable(
- #[BackedEnumFromQuery]
- ?Suit $suit
- ): Response {
- return new Response($this->getDump($suit));
- }
-
- #[Route(path: '/from-query-with-default')]
- public function fromQueryWithDefault(
- #[BackedEnumFromQuery]
- ?Suit $suit = Suit::Hearts
- ): Response {
- return new Response($this->getDump($suit));
- }
-
- #[Route(path: '/from-query-with-default-non-nullable')]
- public function fromQueryWithDefaultNonNullable(
- #[BackedEnumFromQuery]
- Suit $suit = Suit::Hearts
- ): Response {
- return new Response($this->getDump($suit));
- }
-
- #[Route(path: '/from-query-variadics')]
- public function fromQueryVariadics(
- #[BackedEnumFromQuery]
- Suit ...$suit
- ): Response {
- return new Response($this->getDump($suit));
- }
-
- #[Route(path: '/from-body', methods: 'POST')]
- public function fromBody(
- #[BackedEnumFromBody]
- Suit $suit
- ): Response {
- return new Response($this->getDump($suit));
- }
-
- #[Route(path: '/from-body-variadics', methods: 'POST')]
- public function fromBodyVariadics(
- #[BackedEnumFromBody]
- Suit ...$suit
- ): Response {
- return new Response($this->getDump($suit));
- }
-}
diff --git a/tests/Fixtures/Integration/Symfony/src/Kernel.php b/tests/Fixtures/Integration/Symfony/src/Kernel.php
index 5f89b7a1..5f671024 100644
--- a/tests/Fixtures/Integration/Symfony/src/Kernel.php
+++ b/tests/Fixtures/Integration/Symfony/src/Kernel.php
@@ -40,16 +40,10 @@ public function registerContainerConfiguration(LoaderInterface $loader): void
{
$loader->load($this->getProjectDir() . '/config/config.yaml');
- // TODO: we can remove when 5.4 is dropped
- if (InstalledVersions::satisfies(new VersionParser(), 'doctrine/doctrine-bundle', '>=2.10')) {
- $loader->load($this->getProjectDir() . '/config/doctrine-new.yaml');
- } else {
- $loader->load($this->getProjectDir() . '/config/doctrine-old.yaml');
- }
-
- // TODO: we can remove when 5.4 and 6.3 is dropped
+ // TODO: we can remove when Sf 5.4 is dropped
if (InstalledVersions::satisfies(new VersionParser(), 'symfony/http-kernel', '>=6.4')) {
$loader->load($this->getProjectDir() . '/config/config-routing-attribute.yaml');
+ $loader->load($this->getProjectDir() . '/config/config-64+.yaml');
} else {
$loader->load($this->getProjectDir() . '/config/config-routing-annotation.yaml');
}