Skip to content

Commit 3925c08

Browse files
committed
Refactor converting HTTPWG rulesets
1 parent 6897d27 commit 3925c08

File tree

2 files changed

+174
-145
lines changed

2 files changed

+174
-145
lines changed
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
<?php
2+
3+
namespace gapple\Tests\StructuredFields\Httpwg;
4+
5+
use gapple\StructuredFields\Bytes;
6+
use gapple\StructuredFields\Date;
7+
use gapple\StructuredFields\Dictionary;
8+
use gapple\StructuredFields\DisplayString;
9+
use gapple\StructuredFields\InnerList;
10+
use gapple\StructuredFields\Item;
11+
use gapple\StructuredFields\OuterList;
12+
use gapple\StructuredFields\Parameters;
13+
use gapple\StructuredFields\Token;
14+
use ParagonIE\ConstantTime\Base32;
15+
16+
/**
17+
* @phpstan-type ExpectedItem array{ExpectedBareValue, ExpectedParameters}
18+
* @phpstan-type ExpectedDictionary array<array{string, ExpectedTuple}>
19+
* @phpstan-type ExpectedOuterList array<ExpectedTuple>
20+
* @phpstan-type ExpectedInnerList array{array<ExpectedItem>, ExpectedParameters}
21+
* @phpstan-type ExpectedTuple ExpectedItem|ExpectedInnerList
22+
* @phpstan-type ExpectedBareValue bool|int|float|string|ExpectedTypedValue
23+
* @phpstan-type ExpectedTypedValue object{__type: 'binary'|'date'|'displaystring'|'token', value: int|string}
24+
* @phpstan-type ExpectedParameters array<array{string, ExpectedBareValue}>
25+
*/
26+
class HttpwgRuleExpectedConverter
27+
{
28+
/**
29+
* Convert the expected value of an item tuple.
30+
*
31+
* @param ExpectedItem $item
32+
* @return Item
33+
*/
34+
public static function item(array $item): Item
35+
{
36+
return new Item(self::value($item[0]), self::parameters($item[1]));
37+
}
38+
39+
/**
40+
* Convert the expected values of a dictionary.
41+
*
42+
* @param ExpectedDictionary $dictionary
43+
* @return Dictionary
44+
*/
45+
public static function dictionary(array $dictionary): Dictionary
46+
{
47+
$output = new Dictionary();
48+
49+
foreach ($dictionary as $value) {
50+
// Null byte is not supported as first character of property name.
51+
if (strpos($value[0], "\0") === 0) {
52+
throw new \UnexpectedValueException();
53+
}
54+
55+
if (is_array($value[1][0])) {
56+
$output->{$value[0]} = self::innerList($value[1]);
57+
} else {
58+
$output->{$value[0]} = self::item($value[1]);
59+
}
60+
}
61+
62+
return $output;
63+
}
64+
65+
/**
66+
* Convert the expected values of a list.
67+
*
68+
* @param ExpectedOuterList $list
69+
* @return OuterList
70+
*/
71+
public static function list(array $list): OuterList
72+
{
73+
$output = new OuterList();
74+
75+
foreach ($list as $value) {
76+
if (is_array($value[0])) {
77+
$output[] = self::innerList($value);
78+
} else {
79+
$output[] = self::item($value);
80+
}
81+
}
82+
83+
return $output;
84+
}
85+
86+
/**
87+
* Convert the expected values of a parameters map.
88+
*
89+
* @param ExpectedParameters $parameters
90+
* @return Parameters
91+
*/
92+
private static function parameters(array $parameters): Parameters
93+
{
94+
$output = new Parameters();
95+
96+
foreach ($parameters as $value) {
97+
// Null byte is not supported as first character of property name.
98+
if (strpos($value[0], "\0") === 0) {
99+
throw new \UnexpectedValueException();
100+
}
101+
102+
$output->{$value[0]} = self::value($value[1]);
103+
}
104+
105+
return $output;
106+
}
107+
108+
/**
109+
* Convert the expected values of an inner list tuple.
110+
*
111+
* @param ExpectedInnerList $innerList
112+
* @return InnerList
113+
*/
114+
private static function innerList(array $innerList): InnerList
115+
{
116+
$outputList = [];
117+
118+
foreach ($innerList[0] as $value) {
119+
$outputList[] = new Item(self::value($value[0]), self::parameters($value[1]));
120+
}
121+
122+
return new InnerList($outputList, self::parameters($innerList[1]));
123+
}
124+
125+
/**
126+
* Convert any encoded special values to typed objects.
127+
*
128+
* @param ExpectedBareValue $data
129+
* The expected bare value.
130+
* @return bool|int|float|string|Bytes|Date|DisplayString|Token
131+
*/
132+
private static function value($data)
133+
{
134+
if (!is_object($data)) {
135+
return $data;
136+
}
137+
138+
if (property_exists($data, '__type')) {
139+
switch ($data->__type) {
140+
case 'binary':
141+
assert(is_string($data->value));
142+
return new Bytes(Base32::decodeUpper($data->value));
143+
case 'date':
144+
assert(is_int($data->value));
145+
return new Date($data->value);
146+
case 'displaystring':
147+
assert(is_string($data->value));
148+
return new DisplayString($data->value);
149+
case 'token':
150+
assert(is_string($data->value));
151+
return new Token($data->value);
152+
}
153+
}
154+
155+
throw new \UnexpectedValueException("Unknown value type");
156+
}
157+
}

tests/Httpwg/HttpwgTest.php

Lines changed: 17 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,9 @@
22

33
namespace gapple\Tests\StructuredFields\Httpwg;
44

5-
use gapple\StructuredFields\Bytes;
6-
use gapple\StructuredFields\Date;
7-
use gapple\StructuredFields\Dictionary;
8-
use gapple\StructuredFields\DisplayString;
9-
use gapple\StructuredFields\InnerList;
10-
use gapple\StructuredFields\Item;
11-
use gapple\StructuredFields\OuterList;
12-
use gapple\StructuredFields\Parameters;
13-
use gapple\StructuredFields\Token;
145
use gapple\Tests\StructuredFields\Rule;
156
use gapple\Tests\StructuredFields\RulesetTest;
16-
use ParagonIE\ConstantTime\Base32;
177

18-
/**
19-
* @phpstan-type ExpectedParameters array<array{string, mixed}>
20-
* @phpstan-type ExpectedTuple array{mixed, ExpectedParameters}
21-
* @phpstan-type ExpectedInnerList array{array<ExpectedTuple>, ExpectedParameters}
22-
* @phpstan-type ExpectedOuterList array<ExpectedTuple>
23-
* @phpstan-type ExpectedDictionary array<array{string, ExpectedTuple}>
24-
*/
258
abstract class HttpwgTest extends RulesetTest
269
{
2710
/**
@@ -51,16 +34,28 @@ protected function rulesetDataProvider(): array
5134
}
5235

5336
$dataset = [];
54-
foreach ($rules as $rule) {
55-
if (isset($rule->expected)) {
37+
foreach ($rules as $rawRule) {
38+
if (isset($rawRule->expected)) {
5639
try {
57-
$rule->expected = self::{"convertExpected" . ucfirst($rule->header_type)}($rule->expected);
58-
} catch (\UnexpectedValueException $e) {
40+
switch ($rawRule->header_type) {
41+
case 'item':
42+
$rawRule->expected = HttpwgRuleExpectedConverter::item($rawRule->expected);
43+
break;
44+
case 'list':
45+
$rawRule->expected = HttpwgRuleExpectedConverter::list($rawRule->expected);
46+
break;
47+
case 'dictionary':
48+
$rawRule->expected = HttpwgRuleExpectedConverter::dictionary($rawRule->expected);
49+
break;
50+
default:
51+
throw new \UnexpectedValueException('Unknown header type');
52+
}
53+
} catch (\UnexpectedValueException | \AssertionError $e) {
5954
// Skip rules that cannot be parsed.
6055
continue;
6156
}
6257
}
63-
$rule = Rule::fromClass($rule);
58+
$rule = Rule::fromClass($rawRule);
6459

6560
if (isset($dataset[$rule->name])) {
6661
user_error(
@@ -74,127 +69,4 @@ protected function rulesetDataProvider(): array
7469

7570
return $dataset;
7671
}
77-
78-
/**
79-
* Convert the expected value of an item tuple.
80-
*
81-
* @param ExpectedTuple $item
82-
* @return Item
83-
*/
84-
private static function convertExpectedItem(array $item): Item
85-
{
86-
return new Item(self::convertValue($item[0]), self::convertParameters($item[1]));
87-
}
88-
89-
/**
90-
* Convert the expected values of a parameters map.
91-
*
92-
* @param ExpectedParameters $parameters
93-
* @return Parameters
94-
*/
95-
private static function convertParameters(array $parameters): Parameters
96-
{
97-
$output = new Parameters();
98-
99-
foreach ($parameters as $value) {
100-
// Null byte is not supported as first character of property name.
101-
if (strpos($value[0], "\0") === 0) {
102-
throw new \UnexpectedValueException();
103-
}
104-
105-
$output->{$value[0]} = self::convertValue($value[1]);
106-
}
107-
108-
return $output;
109-
}
110-
111-
/**
112-
* Convert the expected values of an inner list tuple.
113-
*
114-
* @param ExpectedInnerList $innerList
115-
* @return InnerList
116-
*/
117-
private static function convertInnerList(array $innerList): InnerList
118-
{
119-
$outputList = [];
120-
121-
foreach ($innerList[0] as $value) {
122-
$outputList[] = new Item(self::convertValue($value[0]), self::convertParameters($value[1]));
123-
}
124-
125-
return new InnerList($outputList, self::convertParameters($innerList[1]));
126-
}
127-
128-
/**
129-
* Convert the expected values of a list.
130-
*
131-
* @param ExpectedOuterList $list
132-
* @return OuterList
133-
*/
134-
private static function convertExpectedList(array $list): OuterList
135-
{
136-
$output = new OuterList();
137-
138-
foreach ($list as $value) {
139-
if (is_array($value[0])) {
140-
$output[] = self::convertInnerList($value);
141-
} else {
142-
$output[] = self::convertExpectedItem($value);
143-
}
144-
}
145-
146-
return $output;
147-
}
148-
149-
/**
150-
* Convert the expected values of a dictionary.
151-
*
152-
* @param ExpectedDictionary $dictionary
153-
* @return Dictionary
154-
*/
155-
private static function convertExpectedDictionary(array $dictionary): Dictionary
156-
{
157-
$output = new Dictionary();
158-
159-
foreach ($dictionary as $value) {
160-
// Null byte is not supported as first character of property name.
161-
if (strpos($value[0], "\0") === 0) {
162-
throw new \UnexpectedValueException();
163-
}
164-
165-
if (is_array($value[1][0])) {
166-
$output->{$value[0]} = self::convertInnerList($value[1]);
167-
} else {
168-
$output->{$value[0]} = self::convertExpectedItem($value[1]);
169-
}
170-
}
171-
172-
return $output;
173-
}
174-
175-
/**
176-
* Convert any encoded special values to typed objects.
177-
*
178-
* @param mixed $data
179-
* The expected bare value.
180-
* @return mixed
181-
*/
182-
private static function convertValue($data)
183-
{
184-
if (is_object($data) && property_exists($data, '__type')) {
185-
/** @var \stdClass $data */
186-
switch ($data->__type) {
187-
case 'token':
188-
return new Token($data->value);
189-
case 'binary':
190-
return new Bytes(Base32::decodeUpper($data->value));
191-
case 'date':
192-
return new Date($data->value);
193-
case 'displaystring':
194-
return new DisplayString($data->value);
195-
}
196-
}
197-
198-
return $data;
199-
}
20072
}

0 commit comments

Comments
 (0)