From 1057aac5c1ea4a24ee1a036c9d363543b43f4c10 Mon Sep 17 00:00:00 2001 From: Lukasz Cybula Date: Wed, 15 Apr 2026 12:19:11 +0200 Subject: [PATCH 1/3] Allowed doctrine/doctrine-bundle ^3.0 --- composer.json | 2 +- tests/_support/project/config/packages/doctrine.yaml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 8c42e1e..bf65714 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,7 @@ "codeception/module-doctrine": "^3.1", "codeception/module-symfony": "*", "doctrine/dbal": "^3.4|^4.0", - "doctrine/doctrine-bundle": "^2.4", + "doctrine/doctrine-bundle": "^2.4|^3.0", "doctrine/lexer": "^1.2|^2.0|^3.0", "doctrine/orm": "^2.13|^3.0", "doctrine/persistence": "^2.0|^3.0|^4.0", diff --git a/tests/_support/project/config/packages/doctrine.yaml b/tests/_support/project/config/packages/doctrine.yaml index 5cb3468..10318e6 100644 --- a/tests/_support/project/config/packages/doctrine.yaml +++ b/tests/_support/project/config/packages/doctrine.yaml @@ -6,7 +6,6 @@ doctrine: charset: UTF8 path: '%kernel.project_dir%/var/data.sqlite' orm: - auto_generate_proxy_classes: true naming_strategy: doctrine.orm.naming_strategy.underscore auto_mapping: true mappings: From 9f3ae611bfa3631b692ba71ecaaa4ce8d5dba7fa Mon Sep 17 00:00:00 2001 From: Lukasz Cybula Date: Thu, 7 May 2026 13:04:47 +0200 Subject: [PATCH 2/3] Added support for PHP 8.4+ lazy ghost objects --- .gitignore | 1 + composer.json | 1 + phpstan.neon | 13 ++---- .../ORM/Subscriber/EntitySubscriber.php | 8 ++++ tests/_support/project/src/Entity/Article.php | 13 ++++++ tests/_support/project/src/Entity/Post.php | 6 +++ .../Resources/config/doctrine/Article.orm.xml | 2 + .../Resources/config/doctrine/Post.orm.xml | 2 + tests/functional/Entity/ProxyLoadingCest.php | 44 +++++++++++++++++++ 9 files changed, 81 insertions(+), 9 deletions(-) create mode 100644 tests/functional/Entity/ProxyLoadingCest.php diff --git a/.gitignore b/.gitignore index 18ecc0e..69f6d2c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ vendor composer.lock .phpcs-cache +tests/_support/project/config/reference.php tests/_support/project/public/files/* !tests/_support/project/public/files/.gitkeep tests/_support/project/var/cache/* diff --git a/composer.json b/composer.json index bf65714..9368710 100644 --- a/composer.json +++ b/composer.json @@ -69,6 +69,7 @@ "twig/twig": "^3.7" }, "conflict": { + "doctrine/collections": "<1.7", "doctrine/persistence": "<2.0", "symfony/property-info": ">=8.0" }, diff --git a/phpstan.neon b/phpstan.neon index 42ad403..1a88940 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -9,6 +9,7 @@ parameters: - src - tests inferPrivatePropertyTypeFromConstructor: true + reportUnmatchedIgnoredErrors: false excludePaths: - 'tests/_support/_generated' - 'tests/_support/project/var' @@ -18,36 +19,30 @@ parameters: - message: '#^Parameter \#1 \$errors of method Tests\\FSi\\App\\Controller\\[a-zA-z]{1,}Controller::formErrorsToMessage\(\).{1,}$#' path: 'tests/_support/project/src/Controller' - reportUnmatched: false - message: '#^PHPDoc tag \@param for parameter \$event contains generic type Doctrine\\Persistence\\Event\\LifecycleEventArgs but class Doctrine\\Persistence\\Event\\LifecycleEventArgs is not generic\.$#' path: 'src/Integration/Doctrine/ORM/Subscriber/EntitySubscriber.php' - reportUnmatched: false - message: '#^Call to an undefined method Doctrine\\ORM\\Event\\\w+::getEntityManager\(\)\.$#' path: 'src/Integration/Doctrine/ORM/Subscriber/EntitySubscriber.php' - reportUnmatched: false - message: '#^Call to function method_exists\(\) with Doctrine\\ORM\\Event\\\w+ and ''getObjectManager'' will always evaluate to true\.$#' path: 'src/Integration/Doctrine/ORM/Subscriber/EntitySubscriber.php' - reportUnmatched: false + - + message: '#^Call to an undefined method ReflectionClass\::\w+\(\)\.$#' + path: 'src/Integration/Doctrine/ORM/Subscriber/EntitySubscriber.php' - message: '#.*Symfony\\Component\\Routing\\RouteCollectionBuilder.*#' path: '%currentWorkingDirectory%/tests/_support/project/src/Kernel.php' - reportUnmatched: false - message: '#Doctrine\\ORM\\Mapping\\EmbeddedClassMapping( not found)?#' path: '%currentWorkingDirectory%/src/Integration/Doctrine/ORM/Entity/EmptyEmbeddableVerifier.php' - reportUnmatched: false - identifier: 'trait.unused' path: '%currentWorkingDirectory%/src/Integration/Doctrine/ORM/ProxyTrait.php' - reportUnmatched: false - identifier: 'property.unusedType' path: 'tests/_support/project/src/Entity' - reportUnmatched: false - identifier: 'arrayValues.list' path: 'tests/_support/Module/DbModule.php' - reportUnmatched: false diff --git a/src/Integration/Doctrine/ORM/Subscriber/EntitySubscriber.php b/src/Integration/Doctrine/ORM/Subscriber/EntitySubscriber.php index ce21fc4..3ecc8b5 100644 --- a/src/Integration/Doctrine/ORM/Subscriber/EntitySubscriber.php +++ b/src/Integration/Doctrine/ORM/Subscriber/EntitySubscriber.php @@ -23,12 +23,15 @@ use FSi\Component\Translatable\Entity\TranslationLoader; use FSi\Component\Translatable\Entity\TranslationUpdater; use FSi\Component\Translatable\LocaleProvider; +use ReflectionClass; use function array_walk; use function get_class; use function in_array; use function method_exists; +use const PHP_VERSION_ID; + final class EntitySubscriber { private LocaleProvider $localeProvider; @@ -233,6 +236,11 @@ private function initializeProxy(object $object): void { if (true === $object instanceof Proxy && false === $object->__isInitialized()) { $object->__load(); + } elseif (PHP_VERSION_ID >= 80400) { + $reflector = new ReflectionClass($object); + if (true === $reflector->isUninitializedLazyObject($object)) { + $reflector->initializeLazyObject($object); + } } } } diff --git a/tests/_support/project/src/Entity/Article.php b/tests/_support/project/src/Entity/Article.php index 7848c3d..a4c1ef9 100644 --- a/tests/_support/project/src/Entity/Article.php +++ b/tests/_support/project/src/Entity/Article.php @@ -31,12 +31,17 @@ class Article * @var Collection */ private Collection $comments; + /** + * @var Collection + */ + private Collection $posts; public function __construct(?string $title = null, ?string $description = null) { $this->title = $title; $this->description = $description; $this->comments = new ArrayCollection(); + $this->posts = new ArrayCollection(); } public function getId(): ?int @@ -141,4 +146,12 @@ public function removeComment(Comment $comment): void { $this->comments->removeElement($comment); } + + /** + * @return list + */ + public function getPosts(): array + { + return $this->posts->getValues(); + } } diff --git a/tests/_support/project/src/Entity/Post.php b/tests/_support/project/src/Entity/Post.php index 820488f..425a8a3 100644 --- a/tests/_support/project/src/Entity/Post.php +++ b/tests/_support/project/src/Entity/Post.php @@ -17,6 +17,7 @@ class Post private ?string $locale; private ?string $title; private ?string $content; + private ?Article $article = null; public function __construct(?string $title = null, ?string $content = null) { @@ -58,4 +59,9 @@ public function setContent(?string $content): void { $this->content = $content; } + + public function getArticle(): ?Article + { + return $this->article; + } } diff --git a/tests/_support/project/src/Resources/config/doctrine/Article.orm.xml b/tests/_support/project/src/Resources/config/doctrine/Article.orm.xml index 63ed301..48400b7 100644 --- a/tests/_support/project/src/Resources/config/doctrine/Article.orm.xml +++ b/tests/_support/project/src/Resources/config/doctrine/Article.orm.xml @@ -12,6 +12,8 @@ + + diff --git a/tests/_support/project/src/Resources/config/doctrine/Post.orm.xml b/tests/_support/project/src/Resources/config/doctrine/Post.orm.xml index 9a7a810..0390515 100644 --- a/tests/_support/project/src/Resources/config/doctrine/Post.orm.xml +++ b/tests/_support/project/src/Resources/config/doctrine/Post.orm.xml @@ -10,6 +10,8 @@ + + diff --git a/tests/functional/Entity/ProxyLoadingCest.php b/tests/functional/Entity/ProxyLoadingCest.php new file mode 100644 index 0000000..f8ed640 --- /dev/null +++ b/tests/functional/Entity/ProxyLoadingCest.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Tests\FSi\Component\Translatable\Entity; + +use DateTimeImmutable; +use Tests\FSi\App\Entity\Article; +use Tests\FSi\App\Entity\Post; +use Tests\FSi\FunctionalTester; + +final class ProxyLoadingCest +{ + public function testProxyLoading(FunctionalTester $I): void + { + $articleId = $I->haveInRepository(Article::class, [ + 'locale' => 'en', + 'publicationDate' => new DateTimeImmutable(), + 'title' => 'Title', + 'description' => 'Description', + ]); + $article = $I->grabEntityFromRepository(Article::class, ['id' => $articleId]); + + $postId = $I->haveInRepository(Post::class, [ + 'locale' => 'en', + 'title' => 'Title', + 'content' => 'Content', + 'article' => $article, + ]); + + $I->clearEntityManager(); + + $post = $I->grabEntityFromRepository(Post::class, ['id' => $postId]); + $I->assertInstanceOf(Article::class, $post->getArticle()); + $I->flushToDatabase(); + } +} From f9fb9bb769f250c6fcf3f80aaec89f53aca5b0d9 Mon Sep 17 00:00:00 2001 From: Lukasz Cybula Date: Thu, 7 May 2026 13:05:01 +0200 Subject: [PATCH 3/3] Upgraded phpunit --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 9368710..1367f9c 100644 --- a/composer.json +++ b/composer.json @@ -44,7 +44,7 @@ "phpstan/phpstan": "^2.1.46", "phpstan/phpstan-beberlei-assert": "^2.0", "phpstan/phpstan-phpunit": "^2.0", - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "^10.0", "psr/http-client": "^1.0", "squizlabs/php_codesniffer": "^4.0", "symfony/asset": "^4.4.30|^5.4|^6.0|^7.0",