You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit e1bcfa3
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: CHANGELOG.md
+26Lines changed: 26 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,5 +1,31 @@
1
1
# CHANGELOG
2
2
3
+
## [v4.5.0] - 2026-05-28
4
+
5
+
### Added
6
+
7
+
-**`prepareInput()` — input preprocessing before hydration.** Declare `protected static prepareInput(array $data): array` in any subclass to normalize, transform, or derive input values. Runs after `defaultValues()` and `#[Defaults]` are merged, before type resolution and hydration.
8
+
9
+
```php
10
+
readonly class CreateUserDTO extends DataTransferObject
11
+
{
12
+
public string $email;
13
+
#[Defaults('member')]
14
+
public string $role;
15
+
16
+
protected static function prepareInput(array $data): array
-`$data` already contains merged defaults at call time — both `defaultValues()` and `#[Defaults]` values are visible and transformable.
25
+
- Only keys already present in `$data` are written back (`array_intersect_key` guard prevents phantom-property injection).
26
+
- Each class in the inheritance chain that declares its own `prepareInput()` runs in order from root to concrete class, so parent transforms apply before child transforms.
27
+
-`with()` does not invoke `prepareInput()` — preparation applies only to untrusted external input; `with()` callers are trusted code.
F1["fromArray() / fromJson()\n― DTO / VO ―"]:::constructor
247
+
F2["from()\n― SVO ―"]:::constructor
248
+
249
+
F1 & F2 --> DEF1
250
+
251
+
DEF1["#[Defaults]"]:::defaults
252
+
DEF1 --> DEF2
253
+
DEF2["defaultValues()"]:::defaults
254
+
255
+
DEF2 --> P["prepareInput()"]:::prepare
256
+
257
+
P -->|DTO| I([instance]):::output
258
+
P -->|VO / SVO| VAL["validate()"]:::defaults
259
+
VAL --> I
260
+
```
261
+
227
262
> 🔗 Want a quick try? [JSON to ImmutableBase Converter](https://json-to-immutablebase-object-converter.reallife-kip.com) lets you paste JSON and generate IB classes instantly!
**`$data` already contains defaults.** Both `defaultValues()` and `#[Defaults]` values are merged into `$data` before `prepareInput()` is called — you can read and transform them.
527
+
528
+
**Phantom-key injection is blocked.** Only keys already present in `$data` are written back. Returning a new key from `prepareInput()` is silently ignored.
529
+
530
+
```php
531
+
protected static function prepareInput(array $data): array
532
+
{
533
+
return [
534
+
'email' => strtolower($data['email']),
535
+
'phantom' => 'injected', // ignored — 'phantom' is not a property
536
+
];
537
+
}
538
+
```
539
+
540
+
**Inheritance chain stacks.** Each class in the hierarchy that declares its own `prepareInput()` runs in order from root to concrete. The parent's output feeds into the child's input.
541
+
542
+
```php
543
+
readonly class ParentDTO extends DataTransferObject
544
+
{
545
+
public string $name;
546
+
547
+
protected static function prepareInput(array $data): array
548
+
{
549
+
return ['name' => trim($data['name'])];
550
+
}
551
+
}
552
+
553
+
readonly class ChildDTO extends ParentDTO
554
+
{
555
+
protected static function prepareInput(array $data): array
556
+
{
557
+
return ['name' => strtoupper($data['name'])]; // runs after parent trim
558
+
}
559
+
}
560
+
```
561
+
562
+
**`with()` does not run `prepareInput()`.**`prepareInput()` is designed to sanitize untrusted external input (HTTP, CSV, API payloads). `with()` is a programmatic mutation by the developer, who is already the trusted caller — preparation is not applicable.
0 commit comments