Skip to content

Commit 160ddd0

Browse files
committed
Adding support for __set_state magic method
1 parent 00d3828 commit 160ddd0

16 files changed

+231
-43
lines changed

src/ByteSequence.php

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@
66

77
final class ByteSequence implements StructuredField
88
{
9+
private function __construct(private string $value)
10+
{
11+
}
12+
13+
/**
14+
* @param array{value:string} $properties
15+
*/
16+
public static function __set_state(array $properties): self
17+
{
18+
return new self($properties['value']);
19+
}
20+
921
/**
1022
* Returns a new instance from a Base64 encoded string.
1123
*/
@@ -29,10 +41,6 @@ public static function fromDecoded(string $value): self
2941
return new self($value);
3042
}
3143

32-
private function __construct(private string $value)
33-
{
34-
}
35-
3644
/**
3745
* Returns the decoded string.
3846
*/

src/ByteSequenceTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,18 @@ public function it_can_encode_raw_field(): void
5050
self::assertSame($source, $item->encoded());
5151
self::assertSame(":$source:", $item->toHttpValue());
5252
}
53+
54+
/**
55+
* @test
56+
*/
57+
public function it_can_be_regenerated_with_eval(): void
58+
{
59+
$decoded = 'pretend this is binary content.';
60+
$item = ByteSequence::fromDecoded($decoded);
61+
62+
/** @var ByteSequence $generatedItem */
63+
$generatedItem = eval('return '.var_export($item, true).';');
64+
65+
self::assertEquals($item, $generatedItem);
66+
}
5367
}

src/Dictionary.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ private function __construct(
1919
) {
2020
}
2121

22+
/**
23+
* @param array{members:array<string, Item|InnerList>} $properties
24+
*/
25+
public static function __set_state(array $properties): self
26+
{
27+
return new self($properties['members']);
28+
}
29+
2230
/**
2331
* Returns a new instance from an associative iterable construct.
2432
*

src/DictionaryTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,4 +197,18 @@ public function it_can_handle_string_with_comma(): void
197197
self::assertSame($expected, $instance->toHttpValue());
198198
self::assertCount(2, $instance);
199199
}
200+
201+
/**
202+
* @test
203+
*/
204+
public function it_can_be_regenerated_with_eval(): void
205+
{
206+
$expected = 'a=foobar;test="bar, baz", b=toto';
207+
$instance = Dictionary::fromHttpValue($expected);
208+
209+
/** @var Dictionary $generatedInstance */
210+
$generatedInstance = eval('return '.var_export($instance, true).';');
211+
212+
self::assertEquals($instance, $generatedInstance);
213+
}
200214
}

src/InnerList.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ public function __construct(private Parameters $parameters, Item ...$members)
2121
$this->members = $members;
2222
}
2323

24+
/**
25+
* @param array{members:array<Item>, parameters:Parameters} $properties
26+
*/
27+
public static function __set_state(array $properties): self
28+
{
29+
return new self($properties['parameters'], ...$properties['members']);
30+
}
31+
2432
/**
2533
* @param iterable<Item|ByteSequence|Token|bool|int|float|string> $members
2634
* @param iterable<string,Item|ByteSequence|Token|bool|int|float|string> $parameters

src/InnerListTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,17 @@ public function it_can_merge_two_or_more_instances_to_yield_different_result():
153153
self::assertCount(3, $instance3);
154154
self::assertSame($expected->toHttpValue(), $instance3->toHttpValue());
155155
}
156+
157+
/**
158+
* @test
159+
*/
160+
public function it_can_be_regenerated_with_eval(): void
161+
{
162+
$instance = InnerList::fromMembers([false], ['foo' => 'bar']);
163+
164+
/** @var InnerList $generatedInstance */
165+
$generatedInstance = eval('return '.var_export($instance, true).';');
166+
167+
self::assertEquals($instance, $generatedInstance);
168+
}
156169
}

src/Item.php

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ private function __construct(
1212
) {
1313
}
1414

15+
/**
16+
* @param array{value:Token|ByteSequence|int|float|string|bool, parameters:Parameters} $properties
17+
*/
18+
public static function __set_state(array $properties): self
19+
{
20+
return new self($properties['value'], $properties['parameters']);
21+
}
22+
1523
/**
1624
* Returns a new instance from a value type and an iterable of key-value parameters.
1725
*
@@ -107,13 +115,11 @@ private static function parseToken(string $string): array
107115
$regexp .= '$';
108116
}
109117

110-
if (1 !== preg_match('/'.$regexp.'/i', $string, $matches)) {
111-
throw new SyntaxError("The HTTP textual representation `$string` for a token contains invalid characters.");
112-
}
118+
preg_match('/'.$regexp.'/i', $string, $found);
113119

114120
return [
115-
new Token($matches['token']),
116-
substr($string, strlen($matches['token'])),
121+
new Token($found['token']),
122+
substr($string, strlen($found['token'])),
117123
];
118124
}
119125

src/ItemTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,4 +189,34 @@ public function test_in_can_be_instantiated_using_bare_items(): void
189189

190190
self::assertEquals($item2, $item1);
191191
}
192+
193+
/**
194+
* @test
195+
*/
196+
public function it_will_fail_with_wrong_token(): void
197+
{
198+
$this->expectException(SyntaxError::class);
199+
200+
Item::fromHttpValue('foo,bar;a=3');
201+
}
202+
203+
/**
204+
* @test
205+
*/
206+
public function it_can_be_regenerated_with_eval(): void
207+
{
208+
$instance = Item::from('/terms', [
209+
'string' => '42',
210+
'integer' => 42,
211+
'float' => 4.2,
212+
'boolean' => true,
213+
'token' => new Token('forty-two'),
214+
'byte-sequence' => ByteSequence::fromDecoded('a42'),
215+
]);
216+
217+
/** @var Item $generatedInstance */
218+
$generatedInstance = eval('return '.var_export($instance, true).';');
219+
220+
self::assertEquals($instance, $generatedInstance);
221+
}
192222
}

src/OrderedList.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ public function __construct(Item|InnerList ...$members)
2121
$this->members = $members;
2222
}
2323

24+
/**
25+
* @param array{members:array<Item|InnerList>} $properties
26+
*/
27+
public static function __set_state(array $properties): self
28+
{
29+
return new self(...$properties['members']);
30+
}
31+
2432
/**
2533
* @param iterable<InnerList|Item|ByteSequence|Token|bool|int|float|string> $members
2634
*/

src/OrderedListTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,17 @@ public function it_can_merge_without_argument_and_not_throw(): void
156156
$instance->merge();
157157
self::assertCount(1, $instance);
158158
}
159+
160+
/**
161+
* @test
162+
*/
163+
public function it_can_be_regenerated_with_eval(): void
164+
{
165+
$instance = OrderedList::fromMembers([Item::from(false)]);
166+
167+
/** @var OrderedList $generatedInstance */
168+
$generatedInstance = eval('return '.var_export($instance, true).';');
169+
170+
self::assertEquals($instance, $generatedInstance);
171+
}
159172
}

0 commit comments

Comments
 (0)