Skip to content

Commit a428293

Browse files
committed
[fix] Readonly
1.修復 PHP 8.1 ~ 8.3 子類無法初始化父類 readonly 屬性導致非預期的繼承意外
1 parent 7ab42ee commit a428293

File tree

2 files changed

+25
-2
lines changed

2 files changed

+25
-2
lines changed

CHANGELOG

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# CHANGELOG
22

3+
## [v2.3.1] - 2025-08-18
4+
5+
- **修復 PHP 8.1 ~ 8.3 子類無法初始化父類 readonly 屬性導致非預期的繼承意外**
6+
37
## [v2.3.0] - 2025-08-07
48

59
### 🎉 新功能

src/ImmutableBase.php

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace ReallifeKip\ImmutableBase;
66

7+
use Closure;
78
use Exception;
89
use ReflectionClass;
910
use JsonSerializable;
@@ -57,9 +58,11 @@ abstract class ImmutableBase implements JsonSerializable
5758
{
5859
/** @var ReflectionClass[] $reflectionsCache */
5960
private static array $reflectionsCache = [];
61+
private static array $classBoundSetter = [];
6062
public function __construct(array $data = [])
6163
{
62-
$this->walkProperties(function (\ReflectionProperty $property) use ($data): void {
64+
$thisClass = (new ReflectionClass($this))->getName();
65+
$this->walkProperties(function (\ReflectionProperty $property) use ($thisClass, $data) {
6366
try {
6467
$key = $property->getName();
6568
/** @var \ReflectionNamedType|\ReflectionUnionType $type */
@@ -94,7 +97,23 @@ public function __construct(array $data = [])
9497
$exists => $this->valueDecide($type, $data[$key]),
9598
default => null
9699
};
97-
$property->setValue($this, $value);
100+
$declaring = $property->getDeclaringClass()->getName();
101+
$isOwn = ($declaring === $thisClass);
102+
if (!$isOwn && $property->isReadOnly()) {
103+
if ($property->isInitialized($this)) {
104+
return;
105+
}
106+
$assign = self::$classBoundSetter[$declaring] ??= Closure::bind(
107+
function (object $obj, string $prop, mixed $val): void {
108+
$obj->$prop = $val;
109+
},
110+
null,
111+
$declaring
112+
);
113+
$assign($this, $property->getName(), $value);
114+
} else {
115+
$property->setValue($this, $value);
116+
}
98117
} catch (Exception $e) {
99118
if ($msg = $e->getMessage()) {
100119
throw new Exception("$key $msg");

0 commit comments

Comments
 (0)