From 44d8543c1142621c5db5bfe0f117dd48785f3ddc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 19 Feb 2026 15:35:04 +0000 Subject: [PATCH 1/2] Initial plan From af3bafc486e061faaf52784c01325bfb2d13f240 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 19 Feb 2026 16:04:49 +0000 Subject: [PATCH 2/2] Fix parent private property not being serialized/deserialized correctly Co-authored-by: jrbasso <26548+jrbasso@users.noreply.github.com> --- src/JsonSerializer/JsonSerializer.php | 36 +++++++++++++++++-- tests/JsonSerializerTest.php | 23 ++++++++++++ .../SupportClasses/ChildWithParentPrivate.php | 27 ++++++++++++++ 3 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 tests/SupportClasses/ChildWithParentPrivate.php diff --git a/src/JsonSerializer/JsonSerializer.php b/src/JsonSerializer/JsonSerializer.php index ec02718..4b16ea2 100644 --- a/src/JsonSerializer/JsonSerializer.php +++ b/src/JsonSerializer/JsonSerializer.php @@ -353,9 +353,41 @@ protected function getObjectProperties($ref, $value) foreach ($ref->getProperties() as $prop) { $props[] = $prop->getName(); } + + // Private properties of parent classes are not returned by getProperties() on the child class, + // so we traverse the parent chain to collect them. + $parentRef = $ref->getParentClass(); + while ($parentRef !== false) { + foreach ($parentRef->getProperties(\ReflectionProperty::IS_PRIVATE) as $prop) { + $props[] = $prop->getName(); + } + $parentRef = $parentRef->getParentClass(); + } + return array_unique(array_merge($props, array_keys(get_object_vars($value)))); } + /** + * Find a ReflectionProperty by traversing the class hierarchy (needed for parent private properties) + * + * @param ReflectionClass $ref + * @param string $name + * @return \ReflectionProperty + * @throws ReflectionException + */ + protected function getReflectionProperty($ref, $name) + { + $classRef = $ref; + while ($classRef !== false) { + try { + return $classRef->getProperty($name); + } catch (ReflectionException $e) { + $classRef = $classRef->getParentClass(); + } + } + throw new ReflectionException('Property ' . $name . ' not found in class ' . $ref->getName() . ' or its parents'); + } + /** * Extract the object data * @@ -369,7 +401,7 @@ protected function extractObjectData($value, $ref, $properties) $data = []; foreach ($properties as $property) { try { - $propRef = $ref->getProperty($property); + $propRef = $this->getReflectionProperty($ref, $property); $propRef->setAccessible(true); if (!$propRef->isInitialized($value)) { continue; @@ -515,7 +547,7 @@ protected function unserializeObject($value) $this->objectMapping[$this->objectMappingIndex++] = $obj; foreach ($value as $property => $propertyValue) { try { - $propRef = $ref->getProperty($property); + $propRef = $this->getReflectionProperty($ref, $property); $propRef->setAccessible(true); $propRef->setValue($obj, $this->unserializeData($propertyValue)); } catch (ReflectionException $e) { diff --git a/tests/JsonSerializerTest.php b/tests/JsonSerializerTest.php index 18b8f3c..f6bdb55 100644 --- a/tests/JsonSerializerTest.php +++ b/tests/JsonSerializerTest.php @@ -243,6 +243,29 @@ public function testUnserializeObjects() } + /** + * Test serialization/unserialization of objects with parent class private properties + * + * @return void + */ + public function testSerializeObjectWithParentPrivateProperty() + { + $obj = new SupportClasses\ChildWithParentPrivate(); + $serialized = $this->serializer->serialize($obj); + $this->assertStringContainsString('"parentPrivate":"parentPrivateValue"', $serialized); + $this->assertStringContainsString('"childPublic":"childPublicValue"', $serialized); + $this->assertStringContainsString('"childPrivate":"childPrivateValue"', $serialized); + $this->assertStringContainsString('"parentPublic":"parentPublicValue"', $serialized); + + /** @var SupportClasses\ChildWithParentPrivate $restored */ + $restored = $this->serializer->unserialize($serialized); + $this->assertInstanceOf(SupportClasses\ChildWithParentPrivate::class, $restored); + $this->assertSame('parentPrivateValue', $restored->getParentPrivate()); + $this->assertSame('childPublicValue', $restored->childPublic); + $this->assertSame('parentPublicValue', $restored->parentPublic); + } + + /** * Test serialization of Enums * diff --git a/tests/SupportClasses/ChildWithParentPrivate.php b/tests/SupportClasses/ChildWithParentPrivate.php new file mode 100644 index 0000000..a053982 --- /dev/null +++ b/tests/SupportClasses/ChildWithParentPrivate.php @@ -0,0 +1,27 @@ +parentPrivate; + } + + public function setParentPrivate($value) + { + $this->parentPrivate = $value; + } +} + +class ChildWithParentPrivate extends ParentWithPrivate +{ + public $childPublic = 'childPublicValue'; + + private $childPrivate = 'childPrivateValue'; +}