diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 98ab313e7a..c45551bbbd 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -38,7 +38,6 @@ jobs: matrix: php: - '7.4' - - '8.0' - '8.1' steps: @@ -88,8 +87,9 @@ jobs: matrix: php: - '7.4' - - '8.0' - - '8.1' + doctrine-persistence: + - 'doctrine/persistence:^2.0' + - 'doctrine/persistence:^3.0' image: - 'postgres:14' - 'postgres:18' @@ -103,6 +103,7 @@ jobs: gh-client-secret: ${{ secrets.AUTOMATION_CLIENT_SECRET }} satis-network-key: ${{ secrets.SATIS_NETWORK_KEY }} satis-network-token: ${{ secrets.SATIS_NETWORK_TOKEN }} + composer-options: "--with ${{ matrix.doctrine-persistence }}" - name: Setup problem matchers for PHPUnit run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" @@ -141,8 +142,9 @@ jobs: matrix: php: - '7.4' - - '8.0' - - '8.1' + doctrine-persistence: + - 'doctrine/persistence:^2.0' + - 'doctrine/persistence:^3.0' image: - 'mysql:8.0' - 'mysql:8.4' @@ -156,6 +158,7 @@ jobs: gh-client-secret: ${{ secrets.AUTOMATION_CLIENT_SECRET }} satis-network-key: ${{ secrets.SATIS_NETWORK_KEY }} satis-network-token: ${{ secrets.SATIS_NETWORK_TOKEN }} + composer-options: "--with ${{ matrix.doctrine-persistence }}" - name: Setup problem matchers for PHPUnit run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" @@ -194,8 +197,7 @@ jobs: matrix: php: - '7.4' - - '8.0' - - '8.1' + - '8.2' steps: - uses: actions/checkout@v5 with: diff --git a/composer.json b/composer.json index daebc88726..a355ab0750 100644 --- a/composer.json +++ b/composer.json @@ -45,6 +45,7 @@ "doctrine/dbal": "^2.13.0", "doctrine/orm": "^2.7", "doctrine/doctrine-bundle": "^2.0", + "doctrine/persistence": "^2.0 || ^3.0", "liip/imagine-bundle": "^2.3", "oneup/flysystem-bundle": "^4.4.2", "league/flysystem-memory": "^2.0.6", @@ -62,6 +63,7 @@ }, "require-dev": { "behat/behat": "^3.6.1", + "composer-runtime-api": "^2.0", "jenner/simple_fork": "^1.2", "friends-of-behat/mink-extension": "^2.4", "ibexa/ci-scripts": "^0.2@dev", diff --git a/phpstan-baseline-doctrine-persistence-v2.neon b/phpstan-baseline-doctrine-persistence-v2.neon new file mode 100644 index 0000000000..998fa138e4 --- /dev/null +++ b/phpstan-baseline-doctrine-persistence-v2.neon @@ -0,0 +1,7 @@ +parameters: + ignoreErrors: + - + message: '#^Call to function method_exists\(\) with Doctrine\\ORM\\EntityManagerInterface and ''merge'' will always evaluate to true\.$#' + identifier: function.alreadyNarrowedType + count: 1 + path: src/lib/Persistence/Doctrine/SiteAccessAwareEntityManager.php diff --git a/phpstan-baseline-doctrine-persistence-v3.neon b/phpstan-baseline-doctrine-persistence-v3.neon new file mode 100644 index 0000000000..35aaa35c0d --- /dev/null +++ b/phpstan-baseline-doctrine-persistence-v3.neon @@ -0,0 +1,13 @@ +parameters: + ignoreErrors: + - + message: '#^Call to function method_exists\(\) with Doctrine\\ORM\\EntityManagerInterface and ''isUninitializedObje…'' will always evaluate to true\.$#' + identifier: function.alreadyNarrowedType + count: 1 + path: src/lib/Persistence/Doctrine/SiteAccessAwareEntityManager.php + + - + message: '#^Method Doctrine\\Persistence\\ObjectManager\:\:clear\(\) invoked with 1 parameter, 0 required\.$#' + identifier: arguments.count + count: 1 + path: src/lib/Persistence/Doctrine/SiteAccessAwareEntityManager.php diff --git a/phpstan-baseline.neon.php b/phpstan-baseline.neon.php index eaab20f250..a720b235ae 100644 --- a/phpstan-baseline.neon.php +++ b/phpstan-baseline.neon.php @@ -6,6 +6,9 @@ */ declare(strict_types=1); +use Composer\InstalledVersions; +use Composer\Semver\VersionParser; + $includes = []; if (PHP_VERSION_ID < 80000) { $includes[] = __DIR__ . '/phpstan-baseline-7.4.neon'; @@ -27,6 +30,13 @@ $includes[] = __DIR__ . '/phpstan-baseline-lte-8.2.neon'; } +$versionParser = new VersionParser(); +if (InstalledVersions::satisfies($versionParser, 'doctrine/persistence', '2.*')) { + $includes[] = __DIR__ . '/phpstan-baseline-doctrine-persistence-v2.neon'; +} elseif (InstalledVersions::satisfies($versionParser, 'doctrine/persistence', '3.*')) { + $includes[] = __DIR__ . '/phpstan-baseline-doctrine-persistence-v3.neon'; +} + $config = []; $config['includes'] = $includes; diff --git a/src/lib/Persistence/Doctrine/SiteAccessAwareEntityManager.php b/src/lib/Persistence/Doctrine/SiteAccessAwareEntityManager.php index 03f1ecdaac..102bcd10d9 100644 --- a/src/lib/Persistence/Doctrine/SiteAccessAwareEntityManager.php +++ b/src/lib/Persistence/Doctrine/SiteAccessAwareEntityManager.php @@ -31,6 +31,8 @@ /** * @internal + * + * SiteAccessAwareEntityManager is a cross-compatible class supporting doctrine/persistence v2 and v3. */ final class SiteAccessAwareEntityManager implements EntityManagerInterface, ConfigScopeChangeSubscriber, ResetInterface { @@ -237,17 +239,17 @@ public function persist($object): void $this->getWrapped()->persist($object); } - /** - * @param object $object - */ public function remove($object): void { $this->getWrapped()->remove($object); } - public function clear(): void + /** + * @param string|null $objectName + */ + public function clear($objectName = null): void { - $this->getWrapped()->clear(); + $this->getWrapped()->clear($objectName); } /** @@ -258,9 +260,6 @@ public function detach($object): void $this->getWrapped()->detach($object); } - /** - * @param object $object - */ public function refresh($object, ?int $lockMode = null): void { $this->getWrapped()->refresh($object, $lockMode); @@ -271,25 +270,11 @@ public function flush(): void $this->getWrapped()->flush(); } - /** - * @template T of object - * - * @param class-string $className - * - * @return EntityRepository - */ public function getRepository($className): EntityRepository { return $this->getWrapped()->getRepository($className); } - /** - * @template T of object - * - * @param class-string $className - * - * @return ClassMetadata - */ public function getClassMetadata($className): ClassMetadata { return $this->getWrapped()->getClassMetadata($className); @@ -313,7 +298,12 @@ public function initializeObject($obj): void */ public function isUninitializedObject($value): bool { - return $this->getWrapped()->isUninitializedObject($value); + $entityManager = $this->getWrapped(); + + // workaround for doctrine/persistence v2 and v3 cross-compatibility + return method_exists($entityManager, 'isUninitializedObject') + ? $entityManager->isUninitializedObject($value) + : false; } /** @@ -323,4 +313,19 @@ public function contains($object): bool { return $this->getWrapped()->contains($object); } + + /** + * @param object $object + * + * @return object|null + */ + public function merge($object) + { + $entityManager = $this->getWrapped(); + + // workaround for doctrine/persistence v2 and v3 cross-compatibility + return method_exists($entityManager, 'merge') + ? $entityManager->merge($object) + : null; + } }