Skip to content

Commit dfc9ca8

Browse files
committed
[refactor] 重構/性能/可讀性
1.重構核心邏輯優化性能 2.提高代碼可讀性
1 parent 7189921 commit dfc9ca8

2 files changed

Lines changed: 46 additions & 36 deletions

File tree

CHANGELOG.md

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

3+
## [v3.0.2] - 2025-10-17
4+
5+
- 重構核心邏輯優化性能
6+
- 提高代碼可讀性
7+
38
## [v3.0.1] - 2025-10-16
49

510
- 新增測試情境

src/ImmutableBase.php

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Throwable;
1010
use ReflectionClass;
1111
use ReflectionNamedType;
12+
use ReflectionProperty;
1213
use ReflectionUnionType;
1314

1415
/**
@@ -53,11 +54,14 @@ public function __construct(string $class = '')
5354

5455
abstract class ImmutableBase
5556
{
57+
private int $mode;
58+
private ReflectionClass $ref;
5659
/** @var ReflectionClass[] $reflectionsCache */
5760
private static array $reflectionsCache = [];
5861
private static array $classBoundSetter = [];
5962
public function __construct(array $data = [])
6063
{
64+
$this->constructInitialize();
6165
$this->walkProperties(function (\ReflectionProperty $property) use ($data) {
6266
try {
6367
$key = $property->name;
@@ -105,9 +109,22 @@ public function __construct(array $data = [])
105109
}
106110
});
107111
}
112+
private function constructInitialize()
113+
{
114+
$this->ref ??= self::getReflection($this);
115+
foreach ($this->ref->getAttributes() as $attr) {
116+
$set[$attr->name] = true;
117+
}
118+
$this->mode ??= match (true) {
119+
isset($set[DataTransferObject::class]) => 1,
120+
isset($set[ValueObject::class]) => 2,
121+
isset($set[Entity::class]) => 3,
122+
default => throw new Exception('ImmutableBase 子類必須使用 DataTransferObject、ValueObject 或 Entity 任一標註'),
123+
};
124+
}
108125
private function propertyInitialize(\ReflectionProperty $property, mixed $value): void
109126
{
110-
$declaring = $property->getDeclaringClass()->getName();
127+
$declaring = $property->class;
111128
if ($declaring !== $this::class && $property->isReadOnly()) {
112129
if ($property->isInitialized($this)) {
113130
return;
@@ -116,7 +133,7 @@ private function propertyInitialize(\ReflectionProperty $property, mixed $value)
116133
fn (object $obj, string $prop, mixed $val) => $obj->$prop = $val,
117134
null,
118135
$declaring
119-
))($this, $property->getName(), $value);
136+
))($this, $property->name, $value);
120137
} else {
121138
$property->setValue($this, $value);
122139
}
@@ -132,36 +149,23 @@ private static function getReflection(object $obj): ReflectionClass
132149
*/
133150
private function walkProperties(callable $callback): void
134151
{
135-
$ref = self::getReflection($this);
136-
$attrs = array_map(fn ($attr) => $attr->getName(), $ref->getAttributes());
137-
$set = array_flip($attrs);
138-
$mode = match (true) {
139-
isset($set[DataTransferObject::class]) => 1,
140-
isset($set[ValueObject::class]) => 2,
141-
isset($set[Entity::class]) => 3,
142-
default => throw new Exception('ImmutableBase 子類必須使用 DataTransferObject、ValueObject 或 Entity 任一標註'),
143-
};
144-
$chain = [];
145-
for ($c = $ref; $c && $c->getName() !== self::class; $c = $c->getParentClass()) {
146-
$chain[] = $c;
152+
$properties = [];
153+
for ($c = $this->ref; $c && $c->name !== self::class; $c = $c->getParentClass()) {
154+
array_unshift($properties, ...$c->getProperties());
147155
}
148-
$chain = array_reverse($chain);
149-
foreach ($chain as $cls) {
150-
$properties = $cls->getProperties();
151-
array_filter($properties, fn ($p) => $p->getName() === $cls->getName() || $cls->getName() !== self::class);
152-
foreach ($properties as $p) {
153-
$isPublic = $p->isPublic();
154-
$propertyName = $p->getName();
155-
$className = $p->getDeclaringClass()->getName();
156-
if ($mode === 1) {
157-
if (!$isPublic || !$p->isReadOnly()) {
158-
throw new Exception("$className $propertyName 必須為 public 且 readonly");
159-
}
160-
} elseif ($isPublic) {
161-
throw new Exception("$className $propertyName 不允許為 public");
156+
foreach ($properties as $p) {
157+
/** @var ReflectionProperty $p */
158+
$isPublic = $p->isPublic();
159+
$propertyName = $p->name;
160+
$className = $p->class;
161+
if ($this->mode === 1) {
162+
if (!$isPublic || !$p->isReadOnly()) {
163+
throw new Exception("$className $propertyName 必須為 public 且 readonly");
162164
}
163-
$callback($p);
165+
} elseif ($isPublic) {
166+
throw new Exception("$className $propertyName 不允許為 public");
164167
}
168+
$callback($p);
165169
}
166170
}
167171

@@ -176,7 +180,7 @@ final public function with(array $data): static
176180
$ref = self::getReflection($this);
177181
foreach ($ref->getProperties() as $property) {
178182
try {
179-
$name = $property->getName();
183+
$name = $property->name;
180184
$type = $property->getType();
181185
$newData[$name] = array_key_exists($name, $data) ?
182186
$this->valueDecide($type, $data[$name]) :
@@ -196,7 +200,7 @@ final public function toArray(): array
196200
{
197201
$properties = [];
198202
$this->walkProperties(function (\ReflectionProperty $property) use (&$properties) {
199-
$properties[$property->getName()] = is_array($value = $property->getValue($this)) ?
203+
$properties[$property->name] = is_array($value = $property->getValue($this)) ?
200204
array_map([$this, 'toArrayOrValue'], $value) :
201205
$this->toArrayOrValue($value);
202206
});
@@ -222,25 +226,26 @@ private function valueDecide(ReflectionNamedType|ReflectionUnionType $type, mixe
222226
$this->builtinTypeValidate($value, $type->getName()) === false &&
223227
!$this->validNullValue($type, $value)
224228
) {
225-
throw new Exception("型別錯誤,期望:{$type->getName()},傳入:".(is_object($value) ? get_class($value) : gettype($value)));
229+
throw new Exception("型別錯誤,期望:{$type->getName()},傳入:".(is_object($value) ? $value::class : gettype($value)));
226230
}
227231
}
228232
return $value;
229233
}
230234
private function unionTypeDecide(ReflectionUnionType $type, mixed $value)
231235
{
232-
$names = array_map(fn ($e) => $e->getName(), $type->getTypes());
236+
$types = $type->getTypes();
237+
$names = array_map(fn ($e) => $e->getName(), $types);
233238
if (!in_array('array', $names, true) && is_array($value)) {
234239
throw new Exception('型別為複合且不包含array,須傳入已實例化的物件。');
235240
}
236-
foreach ($type->getTypes() as $t) {
241+
foreach ($types as $t) {
237242
try {
238243
return $this->valueDecide($t, $value);
239244
} catch (Exception) {
240245
}
241246
}
242247
$excepts = implode('|', $names);
243-
$valueType = is_object($value) ? get_class($value) : gettype($value);
248+
$valueType = is_object($value) ? $value::class : gettype($value);
244249
throw new Exception("型別錯誤,期望:{$excepts},傳入:{$valueType}");
245250
}
246251
private function namedTypeDecide(ReflectionNamedType $type, mixed $value)
@@ -257,7 +262,7 @@ private function namedTypeDecide(ReflectionNamedType $type, mixed $value)
257262
throw new Exception("$value 不是 $class 的期望值");
258263
}
259264
})(),
260-
default => throw new Exception("型別錯誤,期望:{$class},傳入:" . (is_object($value) ? get_class($value) : gettype($value)))
265+
default => throw new Exception("型別錯誤,期望:{$class},傳入:" . (is_object($value) ? $value::class : gettype($value)))
261266
};
262267
}
263268
private function validNullValue(ReflectionNamedType $type, $value)

0 commit comments

Comments
 (0)