@@ -34,11 +34,14 @@ final class Extractor implements \ArrayAccess, \IteratorAggregate
3434{
3535 /**
3636 * @param null|boolean|integer|float|string|array<mixed> $data
37- * @param (int|string)[] $path
37+ * @param (int|string)[] $pathUntilFirstNullEncounter
38+ * @param (int|string)[] $entireAccessPath
39+ * @param bool $isKey
3840 */
3941 private function __construct (
4042 private readonly null |bool |int |float |string |array $ data ,
41- private readonly array $ path ,
43+ private readonly array $ pathUntilFirstNullEncounter ,
44+ private readonly array $ entireAccessPath ,
4245 private readonly bool $ isKey
4346 ) {
4447 }
@@ -49,15 +52,20 @@ private function __construct(
4952 */
5053 public static function for (null |bool |int |float |string |array $ data ): self
5154 {
52- return new self ($ data , [], false );
55+ return new self ($ data , [], [], false );
5356 }
5457
5558 /**
5659 * @param int|string $key
5760 */
5861 private function forKey (int |string $ key ): self
5962 {
60- return new self ($ key , [...$ this ->path , $ key ], true );
63+ return new self (
64+ data: $ key ,
65+ pathUntilFirstNullEncounter: [...$ this ->entireAccessPath , $ key ],
66+ entireAccessPath: [...$ this ->entireAccessPath , $ key ],
67+ isKey: true
68+ );
6169 }
6270
6371 /**
@@ -66,7 +74,7 @@ private function forKey(int|string $key): self
6674 */
6775 public function getPath (): array
6876 {
69- return $ this ->path ;
77+ return $ this ->entireAccessPath ;
7078 }
7179
7280 /**
@@ -76,15 +84,15 @@ public function getPath(): array
7684 public function bool (): bool
7785 {
7886 if ($ this ->data === null ) {
79- throw ExtractorException::becauseDataIsRequiredButNullWasPassed ($ this ->path );
87+ throw ExtractorException::becauseDataIsRequiredButNullWasPassed ($ this ->pathUntilFirstNullEncounter );
8088 }
8189
8290 if (is_bool ($ this ->data )) {
8391 return $ this ->data ;
8492 }
8593
8694 throw ExtractorException::becauseDataDidNotMatchExpectedType (
87- path: $ this ->path ,
95+ path: $ this ->pathUntilFirstNullEncounter ,
8896 expectedType: 'bool ' ,
8997 attemptedData: $ this ->data ,
9098 isKey: $ this ->isKey
@@ -102,7 +110,7 @@ public function boolOrNull(): bool|null
102110 }
103111
104112 throw ExtractorException::becauseDataDidNotMatchExpectedType (
105- path: $ this ->path ,
113+ path: $ this ->pathUntilFirstNullEncounter ,
106114 expectedType: 'bool or null ' ,
107115 attemptedData: $ this ->data ,
108116 isKey: $ this ->isKey
@@ -116,15 +124,15 @@ public function boolOrNull(): bool|null
116124 public function int (): int
117125 {
118126 if ($ this ->data === null ) {
119- throw ExtractorException::becauseDataIsRequiredButNullWasPassed ($ this ->path );
127+ throw ExtractorException::becauseDataIsRequiredButNullWasPassed ($ this ->pathUntilFirstNullEncounter );
120128 }
121129
122130 if (is_int ($ this ->data )) {
123131 return $ this ->data ;
124132 }
125133
126134 throw ExtractorException::becauseDataDidNotMatchExpectedType (
127- path: $ this ->path ,
135+ path: $ this ->pathUntilFirstNullEncounter ,
128136 expectedType: 'int ' ,
129137 attemptedData: $ this ->data ,
130138 isKey: $ this ->isKey
@@ -142,7 +150,7 @@ public function intOrNull(): int|null
142150 }
143151
144152 throw ExtractorException::becauseDataDidNotMatchExpectedType (
145- path: $ this ->path ,
153+ path: $ this ->pathUntilFirstNullEncounter ,
146154 expectedType: 'int or null ' ,
147155 attemptedData: $ this ->data ,
148156 isKey: $ this ->isKey
@@ -156,15 +164,15 @@ public function intOrNull(): int|null
156164 public function float (): float
157165 {
158166 if ($ this ->data === null ) {
159- throw ExtractorException::becauseDataIsRequiredButNullWasPassed ($ this ->path );
167+ throw ExtractorException::becauseDataIsRequiredButNullWasPassed ($ this ->pathUntilFirstNullEncounter );
160168 }
161169
162170 if (is_float ($ this ->data )) {
163171 return $ this ->data ;
164172 }
165173
166174 throw ExtractorException::becauseDataDidNotMatchExpectedType (
167- path: $ this ->path ,
175+ path: $ this ->pathUntilFirstNullEncounter ,
168176 expectedType: 'float ' ,
169177 attemptedData: $ this ->data ,
170178 isKey: $ this ->isKey
@@ -182,7 +190,7 @@ public function floatOrNull(): float|null
182190 }
183191
184192 throw ExtractorException::becauseDataDidNotMatchExpectedType (
185- path: $ this ->path ,
193+ path: $ this ->pathUntilFirstNullEncounter ,
186194 expectedType: 'float or null ' ,
187195 attemptedData: $ this ->data ,
188196 isKey: $ this ->isKey
@@ -196,15 +204,15 @@ public function floatOrNull(): float|null
196204 public function intOrFloat (): int |float
197205 {
198206 if ($ this ->data === null ) {
199- throw ExtractorException::becauseDataIsRequiredButNullWasPassed ($ this ->path );
207+ throw ExtractorException::becauseDataIsRequiredButNullWasPassed ($ this ->pathUntilFirstNullEncounter );
200208 }
201209
202210 if (is_int ($ this ->data ) || is_float ($ this ->data )) {
203211 return $ this ->data ;
204212 }
205213
206214 throw ExtractorException::becauseDataDidNotMatchExpectedType (
207- path: $ this ->path ,
215+ path: $ this ->pathUntilFirstNullEncounter ,
208216 expectedType: 'int or float ' ,
209217 attemptedData: $ this ->data ,
210218 isKey: $ this ->isKey
@@ -222,7 +230,7 @@ public function intOrfloatOrNull(): int|float|null
222230 }
223231
224232 throw ExtractorException::becauseDataDidNotMatchExpectedType (
225- path: $ this ->path ,
233+ path: $ this ->pathUntilFirstNullEncounter ,
226234 expectedType: 'int or float or null ' ,
227235 attemptedData: $ this ->data ,
228236 isKey: $ this ->isKey
@@ -236,15 +244,15 @@ public function intOrfloatOrNull(): int|float|null
236244 public function string (): string
237245 {
238246 if ($ this ->data === null ) {
239- throw ExtractorException::becauseDataIsRequiredButNullWasPassed ($ this ->path );
247+ throw ExtractorException::becauseDataIsRequiredButNullWasPassed ($ this ->pathUntilFirstNullEncounter );
240248 }
241249
242250 if (is_string ($ this ->data )) {
243251 return $ this ->data ;
244252 }
245253
246254 throw ExtractorException::becauseDataDidNotMatchExpectedType (
247- path: $ this ->path ,
255+ path: $ this ->pathUntilFirstNullEncounter ,
248256 expectedType: 'string ' ,
249257 attemptedData: $ this ->data ,
250258 isKey: $ this ->isKey
@@ -262,7 +270,7 @@ public function stringOrNull(): string|null
262270 }
263271
264272 throw ExtractorException::becauseDataDidNotMatchExpectedType (
265- path: $ this ->path ,
273+ path: $ this ->pathUntilFirstNullEncounter ,
266274 expectedType: 'string or null ' ,
267275 attemptedData: $ this ->data ,
268276 isKey: $ this ->isKey
@@ -276,15 +284,15 @@ public function stringOrNull(): string|null
276284 public function array (): array
277285 {
278286 if ($ this ->data === null ) {
279- throw ExtractorException::becauseDataIsRequiredButNullWasPassed ($ this ->path );
287+ throw ExtractorException::becauseDataIsRequiredButNullWasPassed ($ this ->pathUntilFirstNullEncounter );
280288 }
281289
282290 if (is_array ($ this ->data )) {
283291 return $ this ->data ;
284292 }
285293
286294 throw ExtractorException::becauseDataDidNotMatchExpectedType (
287- path: $ this ->path ,
295+ path: $ this ->pathUntilFirstNullEncounter ,
288296 expectedType: 'array ' ,
289297 attemptedData: $ this ->data ,
290298 isKey: $ this ->isKey
@@ -302,7 +310,7 @@ public function arrayOrNull(): null|array
302310 }
303311
304312 throw ExtractorException::becauseDataDidNotMatchExpectedType (
305- path: $ this ->path ,
313+ path: $ this ->pathUntilFirstNullEncounter ,
306314 expectedType: 'array or null ' ,
307315 attemptedData: $ this ->data ,
308316 isKey: $ this ->isKey
@@ -327,14 +335,29 @@ public function offsetExists(mixed $offset): bool
327335 public function offsetGet (mixed $ offset ): mixed
328336 {
329337 if ($ this ->data === null ) {
330- return new self (null , [...$ this ->path , $ offset ], false );
338+ return new self (
339+ data: null ,
340+ pathUntilFirstNullEncounter: $ this ->pathUntilFirstNullEncounter ,
341+ entireAccessPath: [...$ this ->entireAccessPath , $ offset ],
342+ isKey: false
343+ );
331344 }
332345
333346 $ data = $ this ->array ();
334347
335348 return array_key_exists ($ offset , $ data )
336- ? new self ($ data [$ offset ], [...$ this ->path , $ offset ], false )
337- : new self (null , [...$ this ->path , $ offset ], false );
349+ ? new self (
350+ data: $ data [$ offset ],
351+ pathUntilFirstNullEncounter: [...$ this ->entireAccessPath , $ offset ],
352+ entireAccessPath: [...$ this ->entireAccessPath , $ offset ],
353+ isKey: false
354+ )
355+ : new self (
356+ data: null ,
357+ pathUntilFirstNullEncounter: [...$ this ->entireAccessPath , $ offset ],
358+ entireAccessPath: [...$ this ->entireAccessPath , $ offset ],
359+ isKey: false
360+ );
338361 }
339362
340363 /**
@@ -378,7 +401,12 @@ public function getIterator(): \Traversable
378401 {
379402 if ($ this ->data !== null ) {
380403 foreach ($ this ->array () as $ key => $ value ) {
381- yield $ this ->forKey ($ key ) => new self ($ value , [...$ this ->path , $ key ], false );
404+ yield $ this ->forKey ($ key ) => new self (
405+ data: $ value ,
406+ pathUntilFirstNullEncounter: [...$ this ->entireAccessPath , $ key ],
407+ entireAccessPath: [...$ this ->entireAccessPath , $ key ],
408+ isKey: false
409+ );
382410 }
383411 }
384412 }
0 commit comments