4
4
5
5
namespace Bakame \Http \StructuredFields ;
6
6
7
+ use ArrayAccess ;
7
8
use DateTimeInterface ;
8
9
use Iterator ;
10
+ use LogicException ;
9
11
use Stringable ;
10
12
use function array_key_exists ;
11
13
use function array_keys ;
15
17
use function is_array ;
16
18
17
19
/**
20
+ * @see https://www.rfc-editor.org/rfc/rfc8941.html#section-3.2
21
+ *
18
22
* @implements MemberOrderedMap<string, Value|InnerList<int, Value>>
23
+ * @implements ArrayAccess<string, Value|InnerList<int, Value>>
19
24
* @phpstan-import-type DataType from Item
20
25
*/
21
- final class Dictionary implements MemberOrderedMap
26
+ final class Dictionary implements MemberOrderedMap, ArrayAccess
22
27
{
23
28
/** @var array<string, Value|InnerList<int, Value>> */
24
29
private array $ members = [];
25
30
26
31
/**
27
- * @param iterable<string, InnerList<int, Value>|Value|DataType> $members
32
+ * @param iterable<string, InnerList<int, Value>|iterable<Value|DataType>| Value|DataType> $members
28
33
*/
29
34
private function __construct (iterable $ members = [])
30
35
{
31
36
foreach ($ members as $ key => $ member ) {
32
- $ this ->set ($ key, $ member );
37
+ $ this ->members [MapKey:: fromString ($ key)-> value ] = self :: filterMember ( $ member );
33
38
}
34
39
}
35
40
41
+ /**
42
+ * @param StructuredField|iterable<Value|DataType>|DataType $member
43
+ */
44
+ private static function filterMember (iterable |StructuredField |Token |ByteSequence |DateTimeInterface |Stringable |string |int |float |bool $ member ): InnerList |Value
45
+ {
46
+ return match (true ) {
47
+ $ member instanceof InnerList, $ member instanceof Value => $ member ,
48
+ is_iterable ($ member ) => InnerList::fromList ($ member ),
49
+ default => Item::from ($ member ),
50
+ };
51
+ }
52
+
36
53
/**
37
54
* Returns a new instance.
38
55
*/
@@ -47,7 +64,7 @@ public static function create(): self
47
64
* its keys represent the dictionary entry key
48
65
* its values represent the dictionary entry value
49
66
*
50
- * @param iterable<string, InnerList<int, Value>|Value|DataType> $members
67
+ * @param iterable<string, InnerList<int, Value>|list<Value|DataType>| Value|DataType> $members
51
68
*/
52
69
public static function fromAssociative (iterable $ members ): self
53
70
{
@@ -61,20 +78,19 @@ public static function fromAssociative(iterable $members): self
61
78
* the first member represents the instance entry key
62
79
* the second member represents the instance entry value
63
80
*
64
- * @param MemberOrderedMap<string, Value|InnerList<int, Value>>|iterable<array{0:string, 1:InnerList<int, Value>|Value|DataType}> $pairs
81
+ * @param MemberOrderedMap<string, Value|InnerList<int, Value>>|iterable<array{0:string, 1:InnerList<int, Value>|list<Value|DataType>| Value|DataType}> $pairs
65
82
*/
66
- public static function fromPairs (MemberOrderedMap | iterable $ pairs ): self
83
+ public static function fromPairs (iterable $ pairs ): self
67
84
{
68
85
if ($ pairs instanceof MemberOrderedMap) {
69
86
$ pairs = $ pairs ->toPairs ();
70
87
}
71
88
72
- $ instance = new self ();
73
- foreach ($ pairs as $ pair ) {
74
- $ instance ->set (...$ pair );
75
- }
76
-
77
- return $ instance ;
89
+ return new self ((function (iterable $ pairs ) {
90
+ foreach ($ pairs as [$ key , $ member ]) {
91
+ yield $ key => $ member ;
92
+ }
93
+ })($ pairs ));
78
94
}
79
95
80
96
/**
@@ -84,12 +100,11 @@ public static function fromPairs(MemberOrderedMap|iterable $pairs): self
84
100
*/
85
101
public static function fromHttpValue (Stringable |string $ httpValue ): self
86
102
{
87
- $ instance = new self ();
88
- foreach (Parser::parseDictionary ($ httpValue ) as $ key => $ value ) {
89
- $ instance ->set ($ key , is_array ($ value ) ? InnerList::fromList (...$ value ) : $ value );
90
- }
91
-
92
- return $ instance ;
103
+ return new self ((function (iterable $ pairs ) {
104
+ foreach ($ pairs as $ key => $ value ) {
105
+ yield $ key => is_array ($ value ) ? InnerList::fromList (...$ value ) : $ value ;
106
+ }
107
+ })(Parser::parseDictionary ($ httpValue )));
93
108
}
94
109
95
110
public function toHttpValue (): string
@@ -206,79 +221,64 @@ public function pair(int $index): array
206
221
// @codeCoverageIgnoreEnd
207
222
}
208
223
209
- /**
210
- * @throws SyntaxError If the string key is not a valid
211
- */
212
- public function set (string $ key , StructuredField |Token |ByteSequence |DateTimeInterface |Stringable |string |int |float |bool $ member ): static
224
+ public function add (string $ key , StructuredField |Token |ByteSequence |DateTimeInterface |Stringable |string |int |float |bool $ member ): static
213
225
{
214
- $ this ->members [MapKey::fromString ($ key )->value ] = self ::filterMember ($ member );
226
+ $ members = $ this ->members ;
227
+ $ members [MapKey::fromString ($ key )->value ] = self ::filterMember ($ member );
215
228
216
- return $ this ;
217
- }
218
-
219
- private static function filterMember (StructuredField |Token |ByteSequence |DateTimeInterface |Stringable |string |int |float |bool $ member ): InnerList |Value
220
- {
221
- return match (true ) {
222
- $ member instanceof InnerList, $ member instanceof Value => $ member ,
223
- $ member instanceof StructuredField => throw new InvalidArgument ('Expecting a " ' .Value::class.'" or a " ' .InnerList::class.'" instance; received a " ' .$ member ::class.'" instead. ' ),
224
- default => Item::from ($ member ),
225
- };
229
+ return new self ($ members );
226
230
}
227
231
228
- public function delete (string ...$ keys ): static
232
+ public function remove (string ...$ keys ): static
229
233
{
234
+ $ members = $ this ->members ;
230
235
foreach ($ keys as $ key ) {
231
- unset($ this -> members [$ key ]);
236
+ unset($ members [$ key ]);
232
237
}
233
238
234
- return $ this ;
235
- }
236
-
237
- public function clear (): static
238
- {
239
- $ this ->members = [];
240
-
241
- return $ this ;
239
+ return new self ($ members );
242
240
}
243
241
244
242
public function append (string $ key , StructuredField |Token |ByteSequence |DateTimeInterface |Stringable |string |int |float |bool $ member ): static
245
243
{
246
- unset($ this ->members [$ key ]);
244
+ $ members = $ this ->members ;
245
+ unset($ members [$ key ]);
247
246
248
- return $ this -> set ( $ key , $ member );
247
+ return new self ([... $ members , $ key => self :: filterMember ( $ member)] );
249
248
}
250
249
251
250
public function prepend (string $ key , StructuredField |Token |ByteSequence |DateTimeInterface |Stringable |string |int |float |bool $ member ): static
252
251
{
253
- unset($ this ->members [$ key ]);
252
+ $ members = $ this ->members ;
253
+ unset($ members [$ key ]);
254
254
255
- $ this ->members = [...[MapKey::fromString ($ key )->value => self ::filterMember ($ member )], ...$ this ->members ];
256
-
257
- return $ this ;
255
+ return new self ([$ key => self ::filterMember ($ member ), ...$ members ]);
258
256
}
259
257
260
258
/**
261
259
* @param iterable<string, InnerList<int, Value>|Value|DataType> ...$others
262
260
*/
263
261
public function mergeAssociative (iterable ...$ others ): static
264
262
{
263
+ $ members = $ this ->members ;
265
264
foreach ($ others as $ other ) {
266
- $ this -> members = [...$ this -> members , ...self ::fromAssociative ($ other )->members ];
265
+ $ members = [...$ members , ...self ::fromAssociative ($ other )->members ];
267
266
}
268
267
269
- return $ this ;
268
+ return new self ( $ members ) ;
270
269
}
271
270
272
271
/**
273
272
* @param MemberOrderedMap<string, Value|InnerList<int, Value>>|iterable<array{0:string, 1:InnerList<int, Value>|Value|DataType}> ...$others
274
273
*/
275
274
public function mergePairs (MemberOrderedMap |iterable ...$ others ): static
276
275
{
276
+ $ members = $ this ->members ;
277
277
foreach ($ others as $ other ) {
278
- $ this -> members = [...$ this -> members , ...self ::fromPairs ($ other )->members ];
278
+ $ members = [...$ members , ...self ::fromPairs ($ other )->members ];
279
279
}
280
280
281
- return $ this ;
281
+ return new self ( $ members ) ;
282
282
}
283
283
284
284
/**
@@ -299,23 +299,13 @@ public function offsetGet(mixed $offset): InnerList|Value
299
299
return $ this ->get ($ offset );
300
300
}
301
301
302
- /**
303
- * @param string $offset
304
- */
305
302
public function offsetUnset (mixed $ offset ): void
306
303
{
307
- $ this -> delete ( $ offset );
304
+ throw new LogicException ( self ::class. ' instance can not be updated using ' .ArrayAccess::class. ' methods. ' );
308
305
}
309
306
310
- /**
311
- * @param InnerList<int, Value>|Value|DataType $value
312
- */
313
307
public function offsetSet (mixed $ offset , mixed $ value ): void
314
308
{
315
- if (!is_string ($ offset )) {
316
- throw new SyntaxError ('The offset for a dictionary member is expected to be a string; " ' .gettype ($ offset ).'" given. ' );
317
- }
318
-
319
- $ this ->set ($ offset , $ value );
309
+ throw new LogicException (self ::class.' instance can not be updated using ' .ArrayAccess::class.' methods. ' );
320
310
}
321
311
}
0 commit comments