diff --git a/src/Serializers/Native.php b/src/Serializers/Native.php index 59a1bc6c..73254902 100644 --- a/src/Serializers/Native.php +++ b/src/Serializers/Native.php @@ -504,6 +504,10 @@ protected function mapByReference(&$data) continue; } + if (PHP_VERSION >= 8.1 && $property->isReadOnly() && $property->class !== $reflection->name) { + continue; + } + $value = $property->getValue($instance); if (is_array($value) || is_object($value)) { diff --git a/tests/SerializerPhp81Test.php b/tests/SerializerPhp81Test.php index 41c6a6ed..bd356131 100644 --- a/tests/SerializerPhp81Test.php +++ b/tests/SerializerPhp81Test.php @@ -183,6 +183,28 @@ enum SerializerScopedBackedEnum: string { ); })->with('serializers'); +test('readonly properties declared in parent', function () { + $childWithDefaultValue = new SerializerPhp81Child(); + + $f = static function () use ($childWithDefaultValue) { + return $childWithDefaultValue; + }; + + $f = s($f); + + expect($f()->property)->toBe(1); + + $child = new SerializerPhp81Child(100); + + $f = static function () use ($child) { + return $child; + }; + + $f = s($f); + + expect($f()->property)->toBe(100); +})->with('serializers'); + test('first-class callable with closures', function () { $f = function ($value) { return $value; @@ -584,6 +606,15 @@ enum SerializerScopedBackedEnum: string { interface SerializerPhp81HasId {} interface SerializerPhp81HasName {} +class SerializerPhp81Child extends SerializerPhp81Parent {} + +class SerializerPhp81Parent +{ + public function __construct( + public readonly int $property = 1, + ) {} +} + class SerializerPhp81Service implements SerializerPhp81HasId, SerializerPhp81HasName { final public const X = 'foo';