Skip to content

Commit 33a5791

Browse files
committed
[refactor] optimize ImmutableBase reflection and initialization flow
1. Introduce static caching for reflection properties and object modes to reduce repeated reflection overhead. 2. Optimize walkProperties() by caching property lists per class for faster repeated traversal. 3. Extract property initialization logic into analyzeClass() for improved structure and maintainability. 4. Refactor toArray() logic with analyzeClassForToArray() callback to clarify responsibility boundaries. 5. Isolate enum resolution into analyzeEnum() for better readability and error handling. 6. Improve PHPDoc and method-level documentation across ImmutableBase.php.
1 parent 002dfdb commit 33a5791

3 files changed

Lines changed: 386 additions & 367 deletions

File tree

CHANGELOG.md

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

3+
## [v3.1.3] - 2025-12-14
4+
5+
### Changed
6+
7+
- Refactored core logic in `src/ImmutableBase.php` to introduce static caching for reflection properties and modes, enhancing performance for repeated operations.
8+
- Optimized `walkProperties()` method to cache property lists per class, significantly reducing reflection overhead on subsequent calls.
9+
- Enhanced property initialization by extracting logic into dedicated `analyzeClass()` method for better code organization and maintainability.
10+
- Improved `toArray()` implementation with new `analyzeClassForToArray()` callback method, providing cleaner separation of concerns.
11+
- Updated enum resolution with dedicated `analyzeEnum()` method, improving readability and error handling for enum value assignments.
12+
- Enhanced PHPDoc documentation and method descriptions throughout `ImmutableBase.php` for better developer experience and API clarity.
13+
314
## [v3.1.2] - 2025-11-24
415

516
### Fixed

benchmarks/defaultBench.php

Lines changed: 94 additions & 224 deletions
Original file line numberDiff line numberDiff line change
@@ -6,261 +6,131 @@
66

77
require_once dirname(__DIR__) . '/vendor/autoload.php';
88

9-
use Tests\Enum;
10-
use Tests\DataTransferObjects\Basic;
11-
use Tests\DataTransferObjects\Advanced;
9+
use ReallifeKip\ImmutableBase\Attributes\ArrayOf;
10+
use ReallifeKip\ImmutableBase\Objects\DataTransferObject;
1211

13-
final class defaultBench
12+
/** @BeforeMethods({"setUp"}) */
13+
class defaultBench
1414
{
15-
private array $nullableData;
16-
private array $basicData;
17-
private array $advancedDataByArray;
18-
private array $advancedDataByInstance;
19-
private array $modifyBasicData;
20-
private array $modifyAdvancedDataByArray;
21-
private array $modifyAdvancedDataByInstance;
22-
public function __construct()
23-
{
24-
$this->nullableData = [
25-
'nullable_str' => null,
26-
'nullable_int' => null,
27-
'nullable_array' => null,
28-
'nullable_object' => null,
29-
'nullable_float' => null,
30-
'nullable_bool' => null,
31-
'nullable_enum' => null
32-
];
33-
$this->basicData = [
34-
'string' => 'string',
35-
'int' => 1,
36-
'array' => [1,2,3],
37-
'object' => (object)[1,2,3],
38-
'float' => 1.1,
39-
'bool' => true,
40-
'enum' => Enum::ONE
41-
] + $this->nullableData;
42-
$this->advancedDataByArray = $this->basicData + [
43-
'basic' => $this->basicData,
44-
'arrayOfBasics' => [
45-
$this->basicData,
46-
$this->basicData
47-
],
48-
'union' => 'string',
49-
'unionNullable' => 'string',
50-
];
51-
$this->advancedDataByInstance = $this->basicData + [
52-
'basic' => new Basic($this->basicData),
53-
'arrayOfBasics' => [
54-
new Basic($this->basicData),
55-
new Basic($this->basicData)
56-
],
57-
'union' => 'string',
58-
'unionNullable' => 'string',
15+
private array $simpleData;
16+
private array $nestedData;
17+
private array $collectionData_100;
18+
private array $collectionData_1000;
19+
private array $recursiveData_10;
20+
private array $recursiveData_50;
21+
private string $jsonString;
22+
23+
public function setUp(): void
24+
{
25+
$this->simpleData = [
26+
'name' => 'Kip',
27+
'age' => 30,
28+
'active' => true,
29+
'score' => 99.5
5930
];
60-
$this->modifyBasicData = [
61-
'string' => 'string_',
62-
'int' => 2,
63-
'array' => [4,5,6],
64-
'object' => (object)[4,5,6],
65-
'float' => 2.2,
66-
'bool' => false,
67-
'enum' => Enum::TWO,
68-
'nullable_str' => 'string_',
69-
'nullable_int' => 2,
70-
'nullable_array' => [1,2,3],
71-
'nullable_object' => (object)[1,2,3],
72-
'nullable_float' => 2.2,
73-
'nullable_bool' => false,
74-
'nullable_enum' => Enum::TWO,
31+
32+
$this->nestedData = [
33+
'simple' => $this->simpleData,
34+
'type' => 'A'
7535
];
76-
$this->modifyAdvancedDataByArray = $this->modifyBasicData + [
77-
'basic' => $this->modifyBasicData,
78-
'arrayOfBasics' => [
79-
$this->modifyBasicData,
80-
$this->modifyBasicData
81-
],
82-
'union' => 123,
83-
'unionNullable' => null
36+
$this->collectionData_100 = [
37+
'list' => array_fill(0, 100, $this->simpleData)
8438
];
85-
$this->modifyAdvancedDataByInstance = $this->modifyBasicData + [
86-
'basic' => new Basic($this->modifyBasicData),
87-
'arrayOfBasics' => [
88-
new Basic($this->modifyBasicData),
89-
new Basic($this->modifyBasicData)
90-
],
91-
'union' => 123,
92-
'unionNullable' => null
39+
$this->collectionData_1000 = [
40+
'list' => array_fill(0, 1000, $this->simpleData)
9341
];
94-
}
95-
96-
/**
97-
* 建立給定深度的深層巢狀 Advanced 陣列結構(用於測試深層建構與遞迴處理)。
98-
*/
99-
private function buildDeepArray(int $depth = 5): array
100-
{
101-
$base = $this->basicData;
102-
$base['basic'] = $this->basicData;
103-
$base['arrayOfBasics'] = [$this->basicData];
104-
$base['union'] = 'string';
105-
$base['unionNullable'] = 'string';
106-
107-
$current = $base;
108-
for ($i = 1; $i < $depth; $i++) {
109-
$current = $this->basicData;
110-
$current['basic'] = $current;
111-
$current['arrayOfBasics'] = [$current];
112-
$current['union'] = 'string';
113-
$current['unionNullable'] = 'string';
42+
$this->recursiveData_10 = $this->buildRecursiveData(10);
43+
$this->recursiveData_50 = $this->buildRecursiveData(50);
44+
$this->jsonString = json_encode($this->simpleData);
45+
}
46+
private function buildRecursiveData(int $depth): array
47+
{
48+
$current = null;
49+
for ($i = $depth; $i > 0; $i--) {
50+
$current = [
51+
'id' => $i,
52+
'child' => $current
53+
];
11454
}
115-
11655
return $current;
11756
}
118-
119-
/**
120-
* 建立給定深度的深層巢狀 Advanced 實例結構(由底層陣列遞迴轉換為 Advanced 實例)。
121-
*/
122-
private function buildDeepInstance(int $depth = 5): Advanced
57+
public function benchGentleSimpleFromArray(): void
12358
{
124-
$base = $this->basicData;
125-
$base['basic'] = $this->basicData;
126-
$base['arrayOfBasics'] = [$this->basicData];
127-
$base['union'] = 'string';
128-
$base['unionNullable'] = 'string';
129-
130-
$current = $base;
131-
for ($i = 1; $i < $depth; $i++) {
132-
$current = $this->basicData;
133-
$current['arrayOfBasics'] = [$current];
134-
$current['basic'] = $current instanceof Basic ? $current : $current;
135-
$current['union'] = 'string';
136-
$current['unionNullable'] = 'string';
137-
}
138-
139-
$convert = function ($node) use (&$convert) {
140-
if (is_array($node) && isset($node['basic'])) {
141-
$node['basic'] = $convert($node['basic']);
142-
if (is_array($node['arrayOfBasics'])) {
143-
$node['arrayOfBasics'] = array_map($convert, $node['arrayOfBasics']);
144-
}
145-
return new Advanced($node);
146-
}
147-
return $node;
148-
};
149-
150-
return $convert($current);
59+
SimpleDTO::fromArray($this->simpleData);
15160
}
152-
153-
/**
154-
* 從深層陣列(深度 5)建立 Advanced,測量深層建構的成本與行為。
155-
*/
156-
public function benchConstructDeepAdvancedFromArray(): void
61+
public function benchGentleSimpleFromJson(): void
15762
{
158-
$deep = $this->buildDeepArray(5);
159-
new Advanced($deep);
63+
SimpleDTO::fromJson($this->jsonString);
16064
}
161-
162-
/**
163-
* 從深層實例建立 Advanced(先生成深層實例再由其陣列重建),用以測量實例->陣列->重建 的成本。
164-
*/
165-
public function benchConstructDeepAdvancedFromInstance(): void
65+
public function benchModerateNested(): void
16666
{
167-
$deep = $this->buildDeepInstance();
168-
new Advanced($deep->toArray());
67+
NestedDTO::fromArray($this->nestedData);
16968
}
170-
171-
/**
172-
* 深層 Advanced 執行 toArray(),測量遞迴序列化深層巢狀結構的效能與輸出正確性。
173-
*/
174-
public function benchToArrayDeepAdvanced(): array
69+
public function benchModerateToArray(): void
17570
{
176-
$deep = $this->buildDeepInstance();
177-
return $deep->toArray();
71+
$dto = NestedDTO::fromArray($this->nestedData);
72+
$dto->toArray();
17873
}
179-
180-
/**
181-
* 在深層 Advanced 上執行 with(),測量複製與巢狀欄位覆寫在深層結構上的成本。
182-
*/
183-
public function benchWithDeepAdvanced(): void
74+
public function benchModerateWith(): void
18475
{
185-
$deep = $this->buildDeepInstance();
186-
$deep->with($deep->toArray());
76+
$dto = NestedDTO::fromArray($this->nestedData);
77+
$dto->with([
78+
'simple' => [
79+
'name' => 'New Name'
80+
]
81+
]);
18782
}
188-
189-
/**
190-
* 從陣列建立 Basic 的建構效能測試(測量單層 DTO 建構成本)。
191-
*/
192-
public function benchConstructBasicFromArray(): void
83+
public function benchViolentCollection_100(): void
19384
{
194-
new Basic($this->basicData);
85+
CollectionDTO::fromArray($this->collectionData_100);
19586
}
196-
197-
/**
198-
* 從巢狀陣列建立 Advanced 的建構效能測試(測量解析巢狀陣列為 DTO 的成本)。
199-
*/
200-
public function benchConstructAdvancedFromArray(): void
87+
public function benchViolentCollection_1000(): void
20188
{
202-
new Advanced($this->advancedDataByArray);
89+
CollectionDTO::fromArray($this->collectionData_1000);
20390
}
204-
205-
/**
206-
* 從巢狀實例建立 Advanced 的建構效能測試(測量接受已初始化 instance 的情況)。
207-
*/
208-
public function benchConstructAdvancedFromInstance(): void
91+
public function benchHellRecursion_10(): void
20992
{
210-
new Advanced($this->advancedDataByInstance);
93+
RecursiveDTO::fromArray($this->recursiveData_10);
21194
}
212-
213-
/**
214-
* 在 Basic 上呼叫 toArray(),測量單層 DTO 序列化為陣列的成本。
215-
*/
216-
public function benchToArrayBasic(): array
95+
public function benchHellRecursion_50(): void
21796
{
218-
$basic = new Basic($this->basicData);
219-
return $basic->toArray();
97+
RecursiveDTO::fromArray($this->recursiveData_50);
22098
}
221-
222-
/**
223-
* 在由陣列建立的 Advanced 上呼叫 toArray(),測量巢狀 DTO 轉陣列的效能。
224-
*/
225-
public function benchToArrayAdvancedByArray(): array
99+
public function benchHellRecursionToArray_50(): void
226100
{
227-
$advanced = new Advanced($this->advancedDataByArray);
228-
return $advanced->toArray();
101+
$dto = RecursiveDTO::fromArray($this->recursiveData_50);
102+
$dto->toArray();
229103
}
104+
}
105+
enum BenchEnum: string
106+
{
107+
case A = 'A';
108+
case B = 'B';
109+
case C = 'C';
110+
}
230111

231-
/**
232-
* 在由實例建立的 Advanced 上呼叫 toArray(),測量巢狀實例序列化的效能。
233-
*/
234-
public function benchToArrayAdvancedByInstance(): array
235-
{
236-
$advanced = new Advanced($this->advancedDataByInstance);
237-
return $advanced->toArray();
238-
}
112+
class SimpleDTO extends DataTransferObject
113+
{
114+
public readonly string $name;
115+
public readonly int $age;
116+
public readonly bool $active;
117+
public readonly ?float $score;
118+
}
239119

240-
/**
241-
* 在 Basic 上執行 with(),測量建立新 immutable 實例並覆寫單層欄位的成本。
242-
*/
243-
public function benchWithBasic(): void
244-
{
245-
$basic = new Basic($this->basicData);
246-
$basic->with($this->modifyBasicData);
247-
}
120+
class NestedDTO extends DataTransferObject
121+
{
122+
public readonly SimpleDTO $simple;
123+
public readonly BenchEnum $type;
124+
}
248125

249-
/**
250-
* 在由陣列建立的 Advanced 上執行 with(),測量巢狀欄位覆寫與新實例建立的成本。
251-
*/
252-
public function benchWithAdvancedByArray(): void
253-
{
254-
$advanced = new Advanced($this->advancedDataByArray);
255-
$advanced->with($this->modifyAdvancedDataByArray);
256-
}
126+
class CollectionDTO extends DataTransferObject
127+
{
128+
#[ArrayOf(SimpleDTO::class)]
129+
public readonly array $list;
130+
}
257131

258-
/**
259-
* 在由實例建立的 Advanced 上執行 with(),測量接受巢狀 instance 作為修改來源時的成本。
260-
*/
261-
public function benchWithAdvancedByInstance(): void
262-
{
263-
$advanced = new Advanced($this->advancedDataByInstance);
264-
$advanced->with($this->modifyAdvancedDataByInstance);
265-
}
132+
class RecursiveDTO extends DataTransferObject
133+
{
134+
public readonly int $id;
135+
public readonly ?RecursiveDTO $child;
266136
}

0 commit comments

Comments
 (0)