From 3027ce7dba8812472cd99322fc79ae7dcfae85a1 Mon Sep 17 00:00:00 2001 From: ngirardet Date: Fri, 14 Mar 2025 13:47:56 +0100 Subject: [PATCH 1/3] [PHP8.4]private(set) properties should be skipped to avoid resetting values. --- src/Proxy/ProxyFactory.php | 8 +++- .../ClassWithPrivateSetPropertiesTest.php | 39 +++++++++++++++++++ .../OrderPrivateSetProperties.php | 27 +++++++++++++ .../UserPrivateSetProperties.php | 25 ++++++++++++ 4 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 tests/Tests/ORM/Functional/Ticket/SkipPrivateSetProperties/ClassWithPrivateSetPropertiesTest.php create mode 100644 tests/Tests/ORM/Functional/Ticket/SkipPrivateSetProperties/OrderPrivateSetProperties.php create mode 100644 tests/Tests/ORM/Functional/Ticket/SkipPrivateSetProperties/UserPrivateSetProperties.php diff --git a/src/Proxy/ProxyFactory.php b/src/Proxy/ProxyFactory.php index b2d114a6698..85a7c23711e 100644 --- a/src/Proxy/ProxyFactory.php +++ b/src/Proxy/ProxyFactory.php @@ -43,8 +43,10 @@ use function strtr; use function substr; use function ucfirst; +use function version_compare; use const DIRECTORY_SEPARATOR; +use const PHP_VERSION; /** * This factory is used to create proxy objects for entities at runtime. @@ -263,7 +265,11 @@ private function getProxyFactory(string $className): Closure $name = $property->name; if ($property->isStatic() || (($class->hasField($name) || $class->hasAssociation($name)) && ! isset($identifiers[$name]))) { - continue; + if (version_compare(PHP_VERSION, '8.4', '<')) { + continue; + } elseif ($property->isPrivateSet() === false) { + continue; + } } $prefix = $property->isPrivate() ? "\0" . $property->class . "\0" : ($property->isProtected() ? "\0*\0" : ''); diff --git a/tests/Tests/ORM/Functional/Ticket/SkipPrivateSetProperties/ClassWithPrivateSetPropertiesTest.php b/tests/Tests/ORM/Functional/Ticket/SkipPrivateSetProperties/ClassWithPrivateSetPropertiesTest.php new file mode 100644 index 00000000000..c1b6f5bcb29 --- /dev/null +++ b/tests/Tests/ORM/Functional/Ticket/SkipPrivateSetProperties/ClassWithPrivateSetPropertiesTest.php @@ -0,0 +1,39 @@ +setUpEntitySchema([ + UserPrivateSetProperties::class, + OrderPrivateSetProperties::class, + ]); + } + + public function testEntityHydratation(): void + { + $user = new UserPrivateSetProperties('Some company'); + $this->_em->persist($user); + $order = new OrderPrivateSetProperties($user); + $this->_em->persist($order); + $this->_em->flush(); + $this->_em->clear(); + + $hydrated = $this->_em->getRepository(OrderPrivateSetProperties::class)->findAll(); + self::assertCount(1, $hydrated); + } +} diff --git a/tests/Tests/ORM/Functional/Ticket/SkipPrivateSetProperties/OrderPrivateSetProperties.php b/tests/Tests/ORM/Functional/Ticket/SkipPrivateSetProperties/OrderPrivateSetProperties.php new file mode 100644 index 00000000000..fe689f55809 --- /dev/null +++ b/tests/Tests/ORM/Functional/Ticket/SkipPrivateSetProperties/OrderPrivateSetProperties.php @@ -0,0 +1,27 @@ +company = $user->company; + } +} diff --git a/tests/Tests/ORM/Functional/Ticket/SkipPrivateSetProperties/UserPrivateSetProperties.php b/tests/Tests/ORM/Functional/Ticket/SkipPrivateSetProperties/UserPrivateSetProperties.php new file mode 100644 index 00000000000..0bf953c2b9b --- /dev/null +++ b/tests/Tests/ORM/Functional/Ticket/SkipPrivateSetProperties/UserPrivateSetProperties.php @@ -0,0 +1,25 @@ +company = $company; + } +} From 9f5c19f34fd015a740a9a541767d992eda098ed1 Mon Sep 17 00:00:00 2001 From: ngirardet Date: Fri, 14 Mar 2025 13:55:03 +0100 Subject: [PATCH 2/3] Refactoring with ticket ID --- .../DDC11871Order.php} | 10 +++++----- .../DDC11871Test.php} | 14 +++++++------- .../DDC11871User.php} | 6 +++--- 3 files changed, 15 insertions(+), 15 deletions(-) rename tests/Tests/ORM/Functional/Ticket/{SkipPrivateSetProperties/OrderPrivateSetProperties.php => DDC11871/DDC11871Order.php} (52%) rename tests/Tests/ORM/Functional/Ticket/{SkipPrivateSetProperties/ClassWithPrivateSetPropertiesTest.php => DDC11871/DDC11871Test.php} (57%) rename tests/Tests/ORM/Functional/Ticket/{SkipPrivateSetProperties/UserPrivateSetProperties.php => DDC11871/DDC11871User.php} (68%) diff --git a/tests/Tests/ORM/Functional/Ticket/SkipPrivateSetProperties/OrderPrivateSetProperties.php b/tests/Tests/ORM/Functional/Ticket/DDC11871/DDC11871Order.php similarity index 52% rename from tests/Tests/ORM/Functional/Ticket/SkipPrivateSetProperties/OrderPrivateSetProperties.php rename to tests/Tests/ORM/Functional/Ticket/DDC11871/DDC11871Order.php index fe689f55809..13bfb1d21cd 100644 --- a/tests/Tests/ORM/Functional/Ticket/SkipPrivateSetProperties/OrderPrivateSetProperties.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC11871/DDC11871Order.php @@ -2,13 +2,13 @@ declare(strict_types=1); -namespace Doctrine\Tests\ORM\Functional\Ticket\SkipPrivateSetProperties; +namespace Doctrine\Tests\ORM\Functional\Ticket\DDC11871; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity] -#[ORM\Table(name: 'Test_Class_With_Private_Set_Properties_Order')] -class OrderPrivateSetProperties +#[ORM\Table(name: 'DDC11871_Order')] +class DDC11871Order { #[ORM\Id] #[ORM\Column(type: 'integer')] @@ -19,8 +19,8 @@ class OrderPrivateSetProperties private(set) string $company; public function __construct( - #[ORM\ManyToOne(targetEntity: UserPrivateSetProperties::class, fetch: 'LAZY')] - private(set) UserPrivateSetProperties $user, + #[ORM\ManyToOne(targetEntity: DDC11871User::class, fetch: 'LAZY')] + private(set) DDC11871User $user, ) { $this->company = $user->company; } diff --git a/tests/Tests/ORM/Functional/Ticket/SkipPrivateSetProperties/ClassWithPrivateSetPropertiesTest.php b/tests/Tests/ORM/Functional/Ticket/DDC11871/DDC11871Test.php similarity index 57% rename from tests/Tests/ORM/Functional/Ticket/SkipPrivateSetProperties/ClassWithPrivateSetPropertiesTest.php rename to tests/Tests/ORM/Functional/Ticket/DDC11871/DDC11871Test.php index c1b6f5bcb29..df196a36427 100644 --- a/tests/Tests/ORM/Functional/Ticket/SkipPrivateSetProperties/ClassWithPrivateSetPropertiesTest.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC11871/DDC11871Test.php @@ -2,13 +2,13 @@ declare(strict_types=1); -namespace Doctrine\Tests\ORM\Functional\Ticket\SkipPrivateSetProperties; +namespace Doctrine\Tests\ORM\Functional\Ticket\DDC11871; use Doctrine\Tests\OrmFunctionalTestCase; use const PHP_VERSION_ID; -class ClassWithPrivateSetPropertiesTest extends OrmFunctionalTestCase +class DDC11871Test extends OrmFunctionalTestCase { public function setUp(): void { @@ -19,21 +19,21 @@ public function setUp(): void } $this->setUpEntitySchema([ - UserPrivateSetProperties::class, - OrderPrivateSetProperties::class, + DDC11871User::class, + DDC11871Order::class, ]); } public function testEntityHydratation(): void { - $user = new UserPrivateSetProperties('Some company'); + $user = new DDC11871User('Some company'); $this->_em->persist($user); - $order = new OrderPrivateSetProperties($user); + $order = new DDC11871Order($user); $this->_em->persist($order); $this->_em->flush(); $this->_em->clear(); - $hydrated = $this->_em->getRepository(OrderPrivateSetProperties::class)->findAll(); + $hydrated = $this->_em->getRepository(DDC11871Order::class)->findAll(); self::assertCount(1, $hydrated); } } diff --git a/tests/Tests/ORM/Functional/Ticket/SkipPrivateSetProperties/UserPrivateSetProperties.php b/tests/Tests/ORM/Functional/Ticket/DDC11871/DDC11871User.php similarity index 68% rename from tests/Tests/ORM/Functional/Ticket/SkipPrivateSetProperties/UserPrivateSetProperties.php rename to tests/Tests/ORM/Functional/Ticket/DDC11871/DDC11871User.php index 0bf953c2b9b..9e29160bcd0 100644 --- a/tests/Tests/ORM/Functional/Ticket/SkipPrivateSetProperties/UserPrivateSetProperties.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC11871/DDC11871User.php @@ -2,13 +2,13 @@ declare(strict_types=1); -namespace Doctrine\Tests\ORM\Functional\Ticket\SkipPrivateSetProperties; +namespace Doctrine\Tests\ORM\Functional\Ticket\DDC11871; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity] -#[ORM\Table(name: 'Test_Class_With_Private_Set_Properties_User')] -class UserPrivateSetProperties +#[ORM\Table(name: 'DDC11871_User')] +class DDC11871User { #[ORM\Id] #[ORM\Column(type: 'integer')] From 38ede2d822efaea88db01e05a21176249e27e332 Mon Sep 17 00:00:00 2001 From: ngirardet Date: Mon, 17 Mar 2025 08:56:29 +0100 Subject: [PATCH 3/3] Entity classes inlined into Test Ignore phpcs validation as it doesn't support property(set) yet. --- .../Ticket/DDC11871/DDC11871Order.php | 27 ------ .../Ticket/DDC11871/DDC11871Test.php | 39 --------- .../Ticket/DDC11871/DDC11871User.php | 25 ------ .../ORM/Functional/Ticket/DDC11871Test.php | 84 +++++++++++++++++++ 4 files changed, 84 insertions(+), 91 deletions(-) delete mode 100644 tests/Tests/ORM/Functional/Ticket/DDC11871/DDC11871Order.php delete mode 100644 tests/Tests/ORM/Functional/Ticket/DDC11871/DDC11871Test.php delete mode 100644 tests/Tests/ORM/Functional/Ticket/DDC11871/DDC11871User.php create mode 100644 tests/Tests/ORM/Functional/Ticket/DDC11871Test.php diff --git a/tests/Tests/ORM/Functional/Ticket/DDC11871/DDC11871Order.php b/tests/Tests/ORM/Functional/Ticket/DDC11871/DDC11871Order.php deleted file mode 100644 index 13bfb1d21cd..00000000000 --- a/tests/Tests/ORM/Functional/Ticket/DDC11871/DDC11871Order.php +++ /dev/null @@ -1,27 +0,0 @@ -company = $user->company; - } -} diff --git a/tests/Tests/ORM/Functional/Ticket/DDC11871/DDC11871Test.php b/tests/Tests/ORM/Functional/Ticket/DDC11871/DDC11871Test.php deleted file mode 100644 index df196a36427..00000000000 --- a/tests/Tests/ORM/Functional/Ticket/DDC11871/DDC11871Test.php +++ /dev/null @@ -1,39 +0,0 @@ -setUpEntitySchema([ - DDC11871User::class, - DDC11871Order::class, - ]); - } - - public function testEntityHydratation(): void - { - $user = new DDC11871User('Some company'); - $this->_em->persist($user); - $order = new DDC11871Order($user); - $this->_em->persist($order); - $this->_em->flush(); - $this->_em->clear(); - - $hydrated = $this->_em->getRepository(DDC11871Order::class)->findAll(); - self::assertCount(1, $hydrated); - } -} diff --git a/tests/Tests/ORM/Functional/Ticket/DDC11871/DDC11871User.php b/tests/Tests/ORM/Functional/Ticket/DDC11871/DDC11871User.php deleted file mode 100644 index 9e29160bcd0..00000000000 --- a/tests/Tests/ORM/Functional/Ticket/DDC11871/DDC11871User.php +++ /dev/null @@ -1,25 +0,0 @@ -company = $company; - } -} diff --git a/tests/Tests/ORM/Functional/Ticket/DDC11871Test.php b/tests/Tests/ORM/Functional/Ticket/DDC11871Test.php new file mode 100644 index 00000000000..fe026142752 --- /dev/null +++ b/tests/Tests/ORM/Functional/Ticket/DDC11871Test.php @@ -0,0 +1,84 @@ +setUpEntitySchema([ + DDC11871User::class, + DDC11871Order::class, + ]); + } + + public function testEntityHydratation(): void + { + $user = new DDC11871User('Some company'); + $this->_em->persist($user); + $order = new DDC11871Order($user); + $this->_em->persist($order); + $this->_em->flush(); + $this->_em->clear(); + + $hydrated = $this->_em->getRepository(DDC11871Order::class)->findAll(); + self::assertCount(1, $hydrated); + } +} + +#[ORM\Entity] +#[ORM\Table(name: 'DDC11871_User')] +class DDC11871User +{ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue(strategy: 'AUTO')] + public int $id; + + #[ORM\Column(type: 'string')] + private(set) string $company; // phpcs:ignore + + public function __construct(string $company) + { + $this->company = $company; + } +} + +#[ORM\Entity] +#[ORM\Table(name: 'DDC11871_Order')] +class DDC11871Order +{ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue(strategy: 'AUTO')] + public int $id; + + #[ORM\Column] + private(set) string $company; // phpcs:ignore + + public function __construct( + #[ORM\ManyToOne(targetEntity: DDC11871User::class, fetch: 'LAZY')] + private(set) DDC11871User $user, // phpcs:ignore + ) { + $this->company = $user->company; + } +}