From 2f0d2f2b3c76b9dc7a60d0671d7bd42285828ee2 Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Tue, 8 Sep 2026 19:33:27 +0200 Subject: [PATCH 1/2] IBX-11842: Fixed SiteAccessAwareEntityManager::find() ignoring lock mode and lock version (#777) For more details see https://ibexa.atlassian.net/browse/IBX-11842 and https://github.com/ibexa/core/pull/777 Key changes: * Fixed `SiteAccessAwareEntityManager::find()` silently dropping the `$lockMode` and `$lockVersion` arguments, causing requested pessimistic/optimistic locks (e.g. `LockMode::PESSIMISTIC_WRITE`) to never be applied. * Added a scoped PHPStan `ignoreErrors` entry to accommodate `find()`'s wider signature against `ObjectManager::find()`. * Added unit test coverage verifying lock mode and lock version are forwarded to the wrapped entity manager. --------- Co-Authored-By: Claude Fable 5 --- phpstan.neon.dist | 7 ++ .../Doctrine/SiteAccessAwareEntityManager.php | 17 ++++- .../SiteAccessAwareEntityManagerTest.php | 68 +++++++++++++++++++ 3 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 tests/lib/Persistence/Doctrine/SiteAccessAwareEntityManagerTest.php diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 6a8bbef7fe..747dc89132 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -17,6 +17,13 @@ parameters: - tests/* - message: "#^Cannot call method warning\\(\\) on Psr\\\\Log\\\\LoggerInterface\\|null\\.$#" + - + # ObjectManager::find() declares 2 parameters, but the runtime EntityManager + # supports $lockMode and $lockVersion, forwarded like Doctrine\ORM\Decorator\EntityManagerDecorator does + message: '#^Method Doctrine\\Persistence\\ObjectManager\:\:find\(\) invoked with 4 parameters, 2 required\.$#' + identifier: arguments.count + count: 1 + path: src/lib/Persistence/Doctrine/SiteAccessAwareEntityManager.php paths: - src - tests diff --git a/src/lib/Persistence/Doctrine/SiteAccessAwareEntityManager.php b/src/lib/Persistence/Doctrine/SiteAccessAwareEntityManager.php index 102bcd10d9..e4a1b68b1d 100644 --- a/src/lib/Persistence/Doctrine/SiteAccessAwareEntityManager.php +++ b/src/lib/Persistence/Doctrine/SiteAccessAwareEntityManager.php @@ -226,9 +226,22 @@ public function getCache(): ?Cache return $this->getWrapped()->getCache(); } - public function find($className, $id): ?object + /** + * @template T of object + * + * @phpstan-param class-string $className + * @phpstan-param \Doctrine\DBAL\LockMode::*|null $lockMode + * + * @param string $className + * @param mixed $id + * @param int|null $lockMode + * @param int|null $lockVersion + * + * @return T|null + */ + public function find($className, $id, ?int $lockMode = null, ?int $lockVersion = null): ?object { - return $this->getWrapped()->find($className, $id); + return $this->getWrapped()->find($className, $id, $lockMode, $lockVersion); } /** diff --git a/tests/lib/Persistence/Doctrine/SiteAccessAwareEntityManagerTest.php b/tests/lib/Persistence/Doctrine/SiteAccessAwareEntityManagerTest.php new file mode 100644 index 0000000000..f814d473af --- /dev/null +++ b/tests/lib/Persistence/Doctrine/SiteAccessAwareEntityManagerTest.php @@ -0,0 +1,68 @@ +wrappedEntityManager = $this->createMock(EntityManagerInterface::class); + + $entityManagerFactory = $this->createMock(EntityManagerFactory::class); + $entityManagerFactory + ->method('getEntityManager') + ->willReturn($this->wrappedEntityManager); + + $this->entityManager = new SiteAccessAwareEntityManager($entityManagerFactory); + } + + public function testFindForwardsLockModeAndLockVersion(): void + { + $entity = new stdClass(); + + $this->wrappedEntityManager + ->expects(self::once()) + ->method('find') + ->with(stdClass::class, 1, LockMode::PESSIMISTIC_WRITE, 2) + ->willReturn($entity); + + self::assertSame( + $entity, + $this->entityManager->find(stdClass::class, 1, LockMode::PESSIMISTIC_WRITE, 2) + ); + } + + public function testFindWithoutLockArgumentsForwardsNullDefaults(): void + { + $entity = new stdClass(); + + $this->wrappedEntityManager + ->expects(self::once()) + ->method('find') + ->with(stdClass::class, 1, null, null) + ->willReturn($entity); + + self::assertSame( + $entity, + $this->entityManager->find(stdClass::class, 1) + ); + } +} From e89c28624ab408a0edeec70d60d2dd23b24473bc Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Fri, 11 Sep 2026 16:50:34 +0200 Subject: [PATCH 2/2] IBX-11778: Updated GitHub Actions workflows to org standard (#818) For more details see https://ibexa.atlassian.net/browse/IBX-11778 and https://github.com/ibexa/core/pull/818 Key changes: * Renamed the Backend CI workflow from "CI" to "Backend CI" and added a bare `workflow_dispatch` trigger for manual runs. * Passed an explicit `php-version` matching the job matrix to all `composer-install` steps in `backend-ci.yaml`. * Guarded the code style check with `set -euo pipefail` so a `check-cs` failure piped into `cs2pr` is no longer masked. * Bumped the SHA-pinned `docker/login-action` to v4.6.0 in `gha-docker-solr.yaml`. --- .github/workflows/backend-ci.yaml | 13 +++++++++++-- .github/workflows/gha-docker-solr.yaml | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/backend-ci.yaml b/.github/workflows/backend-ci.yaml index 00f0eb9ebd..97804be916 100644 --- a/.github/workflows/backend-ci.yaml +++ b/.github/workflows/backend-ci.yaml @@ -1,10 +1,11 @@ -name: CI +name: Backend CI on: push: branches: - '[0-9]+.[0-9]+' pull_request: ~ + workflow_dispatch: ~ jobs: cs-fix: @@ -19,13 +20,17 @@ jobs: - uses: ibexa/gh-workflows/actions/composer-install@main with: + php-version: ${{ matrix.php }} gh-client-id: ${{ secrets.AUTOMATION_CLIENT_ID }} gh-client-secret: ${{ secrets.AUTOMATION_CLIENT_SECRET }} satis-network-key: ${{ secrets.SATIS_NETWORK_KEY }} satis-network-token: ${{ secrets.SATIS_NETWORK_TOKEN }} - name: Run code style check - run: composer run-script check-cs -- --format=checkstyle | cs2pr + shell: bash + run: | + set -euo pipefail + composer run-script check-cs -- --format=checkstyle | cs2pr tests: name: Unit tests & SQLite integration tests @@ -45,6 +50,7 @@ jobs: - uses: ibexa/gh-workflows/actions/composer-install@main with: + php-version: ${{ matrix.php }} gh-client-id: ${{ secrets.AUTOMATION_CLIENT_ID }} gh-client-secret: ${{ secrets.AUTOMATION_CLIENT_SECRET }} satis-network-key: ${{ secrets.SATIS_NETWORK_KEY }} @@ -101,6 +107,7 @@ jobs: - uses: ibexa/gh-workflows/actions/composer-install@main with: + php-version: ${{ matrix.php }} gh-client-id: ${{ secrets.AUTOMATION_CLIENT_ID }} gh-client-secret: ${{ secrets.AUTOMATION_CLIENT_SECRET }} satis-network-key: ${{ secrets.SATIS_NETWORK_KEY }} @@ -158,6 +165,7 @@ jobs: - uses: ibexa/gh-workflows/actions/composer-install@main with: + php-version: ${{ matrix.php }} gh-client-id: ${{ secrets.AUTOMATION_CLIENT_ID }} gh-client-secret: ${{ secrets.AUTOMATION_CLIENT_SECRET }} satis-network-key: ${{ secrets.SATIS_NETWORK_KEY }} @@ -222,6 +230,7 @@ jobs: - name: Install Composer dependencies uses: ibexa/gh-workflows/actions/composer-install@main with: + php-version: ${{ matrix.php }} gh-client-id: ${{ secrets.AUTOMATION_CLIENT_ID }} gh-client-secret: ${{ secrets.AUTOMATION_CLIENT_SECRET }} satis-network-key: ${{ secrets.SATIS_NETWORK_KEY }} diff --git a/.github/workflows/gha-docker-solr.yaml b/.github/workflows/gha-docker-solr.yaml index e76c2c7d86..1f203f832c 100644 --- a/.github/workflows/gha-docker-solr.yaml +++ b/.github/workflows/gha-docker-solr.yaml @@ -66,7 +66,7 @@ jobs: CORES_SETUP: single - name: Log in to the Container registry - uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: registry: ghcr.io username: ${{ github.actor }}