4
4
5
5
namespace Bakame \Http \StructuredFields ;
6
6
7
+ use DateTimeImmutable ;
8
+ use DateTimeInterface ;
9
+ use DateTimeZone ;
7
10
use Stringable ;
8
11
use function count ;
9
12
use function in_array ;
23
26
final class Item implements StructuredField, ParameterAccess
24
27
{
25
28
private function __construct (
26
- private readonly Token |ByteSequence |int |float |string |bool $ value ,
29
+ private readonly Token |ByteSequence |DateTimeImmutable | int |float |string |bool $ value ,
27
30
private readonly Parameters $ parameters
28
31
) {
29
32
}
@@ -54,7 +57,7 @@ public static function fromPair(array $pair): self
54
57
* @param iterable<string,Item|DataType> $parameters
55
58
*/
56
59
public static function from (
57
- Token |ByteSequence |Stringable |int |float |string |bool $ value ,
60
+ Token |ByteSequence |Stringable |DateTimeInterface | int |float |string |bool $ value ,
58
61
iterable $ parameters = []
59
62
): self {
60
63
return new self (self ::filterValue ($ value ), Parameters::fromAssociative ($ parameters ));
@@ -149,6 +152,7 @@ public static function fromHttpValue(Stringable|string $httpValue): self
149
152
'" ' === $ itemString [0 ] => self ::parseString ($ itemString ),
150
153
': ' === $ itemString [0 ] => self ::parseBytesSequence ($ itemString ),
151
154
'? ' === $ itemString [0 ] => self ::parseBoolean ($ itemString ),
155
+ '@ ' === $ itemString [0 ] => self ::parseDate ($ itemString ),
152
156
1 === preg_match ('/^(-?\d)/ ' , $ itemString ) => self ::parseNumber ($ itemString ),
153
157
1 === preg_match ('/^([a-z*])/i ' , $ itemString ) => self ::parseToken ($ itemString ),
154
158
default => throw new SyntaxError ('The HTTP textual representation " ' .$ httpValue .'" for an item is unknown or unsupported. ' ),
@@ -232,6 +236,24 @@ private static function parseNumber(string $string): array
232
236
return [$ number , substr ($ string , strlen ($ found ['number ' ]))];
233
237
}
234
238
239
+ /**
240
+ * Parses an HTTP textual representation of an Item as a Data Type number.
241
+ *
242
+ * @return array{0:DateTimeImmutable, 1:string}
243
+ */
244
+ private static function parseDate (string $ string ): array
245
+ {
246
+ [$ timestamp , $ parameters ] = self ::parseNumber (substr ($ string , 1 ));
247
+ if (!is_int ($ timestamp )) {
248
+ throw new SyntaxError ("The HTTP textual representation \"$ string \" for a date contains invalid characters. " );
249
+ }
250
+
251
+ return [
252
+ (new DateTimeImmutable ('NOW ' , new DateTimeZone ('UTC ' )))->setTimestamp ($ timestamp ),
253
+ $ parameters ,
254
+ ];
255
+ }
256
+
235
257
/**
236
258
* Parses an HTTP textual representation of an Item as a String Data Type.
237
259
*
@@ -291,17 +313,18 @@ private function serializeDecimal(float $value): string
291
313
/**
292
314
* Returns the underlying value decoded.
293
315
*/
294
- public function value (): Token |ByteSequence |int |float |string |bool
316
+ public function value (): Token |ByteSequence |DateTimeImmutable | int |float |string |bool
295
317
{
296
318
return $ this ->value ;
297
319
}
298
320
299
- public function withValue (Token |ByteSequence |Stringable |int |float |string |bool $ value ): self
321
+ public function withValue (Token |ByteSequence |DateTimeInterface | Stringable |int |float |string |bool $ value ): self
300
322
{
301
323
$ newValue = self ::filterValue ($ value );
302
324
303
325
if (
304
326
($ newValue === $ this ->value )
327
+ || ($ newValue instanceof DateTimeImmutable && $ this ->value instanceof DateTimeImmutable && $ this ->value == $ newValue )
305
328
|| ($ newValue instanceof Token && $ this ->value instanceof Token && $ this ->value ->value === $ newValue ->value )
306
329
|| ($ newValue instanceof ByteSequence && $ this ->value instanceof ByteSequence && $ this ->value ->encoded () === $ newValue ->encoded ())
307
330
) {
@@ -316,29 +339,19 @@ public function parameters(): Parameters
316
339
return clone $ this ->parameters ;
317
340
}
318
341
319
- public function prependParameter (string $ name , Item |ByteSequence |Token |bool |int |float |string $ member ): static
342
+ public function prependParameter (string $ name , Item |ByteSequence |Token |DateTimeInterface | Stringable | bool |int |float |string $ member ): static
320
343
{
321
- $ parameters = clone $ this ->parameters ;
322
-
323
- return $ this ->withParameters ($ parameters ->prepend ($ name , $ member ));
344
+ return $ this ->withParameters ($ this ->parameters ()->prepend ($ name , $ member ));
324
345
}
325
346
326
- public function appendParameter (string $ name , Item |ByteSequence |Token |Stringable |bool |int |float |string $ member ): static
347
+ public function appendParameter (string $ name , Item |ByteSequence |Token |DateTimeInterface | Stringable |bool |int |float |string $ member ): static
327
348
{
328
- $ parameters = clone $ this ->parameters ;
329
-
330
- return $ this ->withParameters ($ parameters ->append ($ name , $ member ));
349
+ return $ this ->withParameters ($ this ->parameters ()->append ($ name , $ member ));
331
350
}
332
351
333
- public function withoutParameter (string $ name ): static
352
+ public function withoutParameter (string ... $ name ): static
334
353
{
335
- if (!$ this ->parameters ->has ($ name )) {
336
- return $ this ;
337
- }
338
-
339
- $ parameters = clone $ this ->parameters ;
340
-
341
- return $ this ->withParameters ($ parameters ->delete ($ name ));
354
+ return $ this ->withParameters ($ this ->parameters ()->delete (...$ name ));
342
355
}
343
356
344
357
public function withParameters (Parameters $ parameters ): static
@@ -363,6 +376,7 @@ public function toHttpValue(): string
363
376
is_float ($ this ->value ) => $ this ->serializeDecimal ($ this ->value ),
364
377
is_bool ($ this ->value ) => '? ' .($ this ->value ? '1 ' : '0 ' ),
365
378
$ this ->value instanceof Token => $ this ->value ->value ,
379
+ $ this ->value instanceof DateTimeImmutable => '@ ' .$ this ->value ->getTimestamp (),
366
380
default => ': ' .$ this ->value ->encoded ().': ' ,
367
381
}.$ this ->parameters ->toHttpValue ();
368
382
}
@@ -397,12 +411,19 @@ public function isByteSequence(): bool
397
411
return $ this ->value instanceof ByteSequence;
398
412
}
399
413
400
- private static function filterValue (float |bool |int |string |Token |ByteSequence |Stringable $ value ): ByteSequence |string |Token |int |bool |float
414
+ public function isDate (): bool
415
+ {
416
+ return $ this ->value instanceof DateTimeImmutable;
417
+ }
418
+
419
+ private static function filterValue (float |bool |int |string |Token |ByteSequence |DateTimeInterface |Stringable $ value ): ByteSequence |DateTimeImmutable |Token |string |int |bool |float
401
420
{
402
421
return match (true ) {
403
422
is_int ($ value ) => self ::filterInteger ($ value ),
404
423
is_float ($ value ) => self ::filterDecimal ($ value ),
405
424
is_string ($ value ) || $ value instanceof Stringable => self ::filterString ($ value ),
425
+ $ value instanceof DateTimeImmutable => $ value ,
426
+ $ value instanceof DateTimeInterface => DateTimeImmutable::createFromInterface ($ value ),
406
427
default => $ value ,
407
428
};
408
429
}
0 commit comments