From e39770af94d0991ee7d6d28547e9b9762e60a801 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Wed, 1 Jul 2026 10:42:31 -0400 Subject: [PATCH] Add regression tests for uninitialized typed properties in repository Repository hydration serializes an empty model (via anydataset-db's PreFetchTrait) to enumerate fields, and save() serializes the model to build the INSERT. When a model has non-nullable typed properties with no default, both paths used to raise "Typed property ... must not be accessed before initialization". Fixed in byjg/serializer 6.0.2; these tests cover both the get() and save() paths and guard against a regression. --- tests/Model/UsersTyped.php | 20 ++++++++++++++++++ tests/RepositoryTest.php | 42 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 tests/Model/UsersTyped.php diff --git a/tests/Model/UsersTyped.php b/tests/Model/UsersTyped.php new file mode 100644 index 0000000..07eb430 --- /dev/null +++ b/tests/Model/UsersTyped.php @@ -0,0 +1,20 @@ +assertEquals('2015-05-02 00:00:00', $users->getCreatedate()); } + /** + * Regression: a model with non-nullable typed properties (no default) is "uninitialized" on + * `new`. The hydration path serializes an empty instance to enumerate the fields, which used + * to raise "must not be accessed before initialization". + */ + public function testGetIntoUninitializedTypedModel() + { + $mapper = new Mapper(UsersTyped::class, 'users', 'id'); + $repository = new Repository($this->repository->getExecutor(), $mapper); + + $user = $repository->get(1); + $this->assertInstanceOf(UsersTyped::class, $user); + $this->assertEquals(1, $user->id); + $this->assertEquals('John Doe', $user->name); + $this->assertEquals('2015-05-02 00:00:00', $user->createdate); + } + + /** + * Regression: saving an entity whose non-nullable typed PK is left uninitialized (it is an + * auto-increment column) must not raise "must not be accessed before initialization" when the + * entity is serialized to build the INSERT. The uninitialized property is simply omitted. + */ + public function testSaveObjectWithUninitializedTypedProperty() + { + $mapper = new Mapper(UsersTyped::class, 'users', 'id'); + $repository = new Repository($this->repository->getExecutor(), $mapper); + + $user = new UsersTyped(); + $user->name = 'Newbie'; + // $user->id and $user->createdate are intentionally left uninitialized: both must be + // omitted from the INSERT instead of raising an error while serializing the entity. + + $repository->save($user); + + // save() writes the auto-increment id back onto the entity, proving the INSERT ran and the + // uninitialized properties were serialized (omitted) without raising an error. createdate + // stays uninitialized (save only writes back the PK), so it is not read here. + $this->assertEquals(4, $user->id); + $this->assertEquals('Newbie', $user->name); + } + public function testGetByFilter() { $users = $this->repository->getByFilter('id = :id', ['id' => 1]);