@@ -18,21 +18,31 @@ final class Shape implements Constraint
18
18
private array $ constraints ;
19
19
/** @var list<non-empty-string> */
20
20
private array $ optional ;
21
+ /** @var array<non-empty-string, mixed> */
22
+ private array $ defaults ;
23
+ /** @var array<non-empty-string, non-empty-string> */
24
+ private array $ rename ;
21
25
/** @var ?callable(non-empty-string): non-empty-string */
22
26
private $ message ;
23
27
24
28
/**
25
29
* @param non-empty-array<non-empty-string, Constraint<mixed, mixed>> $constraints
26
30
* @param list<non-empty-string> $optional
31
+ * @param array<non-empty-string, mixed> $defaults
32
+ * @param array<non-empty-string, non-empty-string> $rename
27
33
* @param ?callable(non-empty-string): non-empty-string $message
28
34
*/
29
35
private function __construct (
30
36
array $ constraints ,
31
37
array $ optional ,
32
- ?callable $ message = null ,
38
+ array $ defaults ,
39
+ array $ rename ,
40
+ ?callable $ message ,
33
41
) {
34
42
$ this ->constraints = $ constraints ;
35
43
$ this ->optional = $ optional ;
44
+ $ this ->defaults = $ defaults ;
45
+ $ this ->rename = $ rename ;
36
46
$ this ->message = $ message ;
37
47
}
38
48
@@ -48,7 +58,13 @@ public function __invoke(mixed $value): Validation
48
58
*/
49
59
public static function of (string $ key , Constraint $ constraint ): self
50
60
{
51
- return new self ([$ key => $ constraint ], []);
61
+ return new self (
62
+ [$ key => $ constraint ],
63
+ [],
64
+ [],
65
+ [],
66
+ null ,
67
+ );
52
68
}
53
69
54
70
/**
@@ -59,7 +75,13 @@ public function with(string $key, Constraint $constraint): self
59
75
$ constraints = $ this ->constraints ;
60
76
$ constraints [$ key ] = $ constraint ;
61
77
62
- return new self ($ constraints , $ this ->optional , $ this ->message );
78
+ return new self (
79
+ $ constraints ,
80
+ $ this ->optional ,
81
+ $ this ->defaults ,
82
+ $ this ->rename ,
83
+ $ this ->message ,
84
+ );
63
85
}
64
86
65
87
/**
@@ -75,15 +97,67 @@ public function optional(string $key, Constraint $constraint = null): self
75
97
$ constraints [$ key ] = $ constraint ;
76
98
}
77
99
78
- return new self ($ constraints , $ optional , $ this ->message );
100
+ return new self (
101
+ $ constraints ,
102
+ $ optional ,
103
+ $ this ->defaults ,
104
+ $ this ->rename ,
105
+ $ this ->message ,
106
+ );
107
+ }
108
+
109
+ /**
110
+ * @param non-empty-string $key
111
+ */
112
+ public function default (string $ key , mixed $ value ): self
113
+ {
114
+ if (!\in_array ($ key , $ this ->optional , true )) {
115
+ throw new \LogicException ("No optional key $ key defined " );
116
+ }
117
+
118
+ $ defaults = $ this ->defaults ;
119
+ /** @psalm-suppress MixedAssignment */
120
+ $ defaults [$ key ] = $ value ;
121
+
122
+ return new self (
123
+ $ this ->constraints ,
124
+ $ this ->optional ,
125
+ $ defaults ,
126
+ $ this ->rename ,
127
+ $ this ->message ,
128
+ );
129
+ }
130
+
131
+ /**
132
+ * @param non-empty-string $from
133
+ * @param non-empty-string $to
134
+ */
135
+ public function rename (string $ from , string $ to ): self
136
+ {
137
+ $ rename = $ this ->rename ;
138
+ $ rename [$ from ] = $ to ;
139
+
140
+ return new self (
141
+ $ this ->constraints ,
142
+ $ this ->optional ,
143
+ $ this ->defaults ,
144
+ $ rename ,
145
+ $ this ->message ,
146
+ );
79
147
}
80
148
81
149
/**
82
150
* @param callable(non-empty-string): non-empty-string $message
83
151
*/
84
152
public function withKeyFailure (callable $ message ): self
85
153
{
86
- return new self ($ this ->constraints , $ this ->optional , $ message );
154
+ return new self (
155
+ $ this ->constraints ,
156
+ $ this ->optional ,
157
+ $ this ->defaults ,
158
+ $ this ->rename ,
159
+ $ message ,
160
+ );
87
161
}
88
162
89
163
public function and (Constraint $ constraint ): Constraint
@@ -140,10 +214,15 @@ private function validate(array $value): Validation
140
214
141
215
$ validation = $ validation ->and (
142
216
$ keyValidation ->and ($ ofType )($ value ),
143
- static function ($ array , $ value ) use ($ key , $ optional ) {
217
+ function ($ array , $ value ) use ($ key , $ optional ) {
218
+ $ concreteKey = $ this ->rename [$ key ] ?? $ key ;
219
+
144
220
if ($ value !== $ optional ) {
145
221
/** @psalm-suppress MixedAssignment */
146
- $ array [$ key ] = $ value ;
222
+ $ array [$ concreteKey ] = $ value ;
223
+ } else if (\array_key_exists ($ key , $ this ->defaults )) {
224
+ /** @psalm-suppress MixedAssignment */
225
+ $ array [$ concreteKey ] = $ this ->defaults [$ key ];
147
226
}
148
227
149
228
return $ array ;
0 commit comments