Skip to content

Commit 9c6d6aa

Browse files
committed
Merge branch 'psr-7'
2 parents 4c806c7 + bb2ba89 commit 9c6d6aa

21 files changed

+236
-103
lines changed

.php-cs-fixer.dist.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
->setRules([
1919
'is_null' => true,
2020
'native_constant_invocation' => true,
21+
'no_superfluous_phpdoc_tags' => ['allow_mixed' => true],
2122
'yoda_style' => ['equal' => false, 'identical' => false],
2223
])
2324
->setFinder($finder)

src/Concept/ConvertibleEnumeration.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
use LogicException;
88

99
/**
10-
* Base class for enumerations that use static array properties to convert
11-
* constants to and from their names
10+
* Base class for enumerations that use static arrays to map constants to and
11+
* from their names
1212
*
1313
* @template TValue of array-key
1414
*
@@ -71,4 +71,12 @@ final public static function cases(): array
7171
{
7272
return static::$ValueMap;
7373
}
74+
75+
/**
76+
* @inheritDoc
77+
*/
78+
final public static function hasValue($value): bool
79+
{
80+
return isset(static::$NameMap[$value]);
81+
}
7482
}

src/Concept/Enumeration.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,18 @@ public static function cases(): array
2424
{
2525
return self::constants();
2626
}
27+
28+
/**
29+
* @inheritDoc
30+
*/
31+
public static function hasValue($value): bool
32+
{
33+
if (
34+
(is_int($value) || is_string($value)) &&
35+
isset(self::constantNames()[$value])
36+
) {
37+
return true;
38+
}
39+
return in_array($value, self::constants(), true);
40+
}
2741
}

src/Concept/ReflectiveEnumeration.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
use ReflectionClass;
99

1010
/**
11-
* Base class for enumerations that use reflection to convert constants to and
12-
* from their names
11+
* Base class for enumerations that use reflection to map constants to and from
12+
* their names
1313
*
1414
* @template TValue of array-key
1515
*
@@ -111,4 +111,15 @@ final public static function cases(): array
111111
}
112112
return self::$ValueMaps[static::class];
113113
}
114+
115+
/**
116+
* @inheritDoc
117+
*/
118+
final public static function hasValue($value): bool
119+
{
120+
if (!isset(self::$NameMaps[static::class])) {
121+
self::loadMaps();
122+
}
123+
return isset(self::$NameMaps[static::class][$value]);
124+
}
114125
}

src/Concern/IsCatalog.php

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,25 @@ trait IsCatalog
1616
*/
1717
private static array $Constants = [];
1818

19+
/**
20+
* @var array<class-string<static>,array<TValue&array-key,string[]|string>>
21+
*/
22+
private static array $ConstantNames = [];
23+
1924
/**
2025
* @return array<string,TValue>
2126
*/
2227
protected static function constants(): array
2328
{
24-
return self::$Constants[static::class]
25-
?? (self::$Constants[static::class] = self::getConstants());
29+
return self::$Constants[static::class] ??= self::getConstants();
30+
}
31+
32+
/**
33+
* @return array<TValue&array-key,string[]|string>
34+
*/
35+
protected static function constantNames(): array
36+
{
37+
return self::$ConstantNames[static::class] ??= self::getConstantNames();
2638
}
2739

2840
/**
@@ -39,5 +51,27 @@ private static function getConstants(): array
3951
return $constants ?? [];
4052
}
4153

54+
/**
55+
* @return array<TValue&array-key,string[]|string>
56+
*/
57+
private static function getConstantNames(): array
58+
{
59+
foreach (self::constants() as $name => $value) {
60+
if (!(is_int($value) || is_string($value))) {
61+
continue;
62+
}
63+
if (!isset($names[$value])) {
64+
$names[$value] = $name;
65+
continue;
66+
}
67+
if (!is_array($names[$value])) {
68+
$names[$value] = (array) $names[$value];
69+
}
70+
$names[$value][] = $name;
71+
}
72+
73+
return $names ?? [];
74+
}
75+
4276
final private function __construct() {}
4377
}

src/Contract/IEnumeration.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,11 @@ interface IEnumeration
1515
* @return array<string,TValue>
1616
*/
1717
public static function cases(): array;
18+
19+
/**
20+
* True if the class has a public constant with the given value
21+
*
22+
* @param TValue $value
23+
*/
24+
public static function hasValue($value): bool;
1825
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Lkrms\Http\Catalog;
4+
5+
use Lkrms\Concept\Enumeration;
6+
7+
/**
8+
* HTTP protocol versions
9+
*
10+
* @extends Enumeration<string>
11+
*/
12+
final class HttpProtocolVersion extends Enumeration
13+
{
14+
public const HTTP_1_0 = '1.0';
15+
16+
public const HTTP_1_1 = '1.1';
17+
}

src/Http/Catalog/HttpRequestMethod.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
namespace Lkrms\Http\Catalog;
44

5-
use Lkrms\Concept\Dictionary;
5+
use Lkrms\Concept\Enumeration;
66

77
/**
88
* HTTP request methods
99
*
10-
* @extends Dictionary<string>
10+
* @extends Enumeration<string>
1111
*/
12-
final class HttpRequestMethod extends Dictionary
12+
final class HttpRequestMethod extends Enumeration
1313
{
1414
public const GET = 'GET';
1515

src/Http/HttpMessage.php

Lines changed: 71 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Lkrms\Contract\IImmutable;
77
use Lkrms\Exception\InvalidArgumentException;
88
use Lkrms\Exception\InvalidArgumentTypeException;
9+
use Lkrms\Http\Catalog\HttpProtocolVersion;
910
use Lkrms\Http\Contract\HttpHeadersInterface;
1011
use Psr\Http\Message\MessageInterface;
1112
use Psr\Http\Message\StreamInterface;
@@ -32,29 +33,11 @@ abstract class HttpMessage implements MessageInterface, IImmutable
3233
public function __construct(
3334
$body = null,
3435
$headers = null,
35-
string $protocolVersion = '1.1'
36+
string $version = '1.1'
3637
) {
37-
$this->ProtocolVersion = $protocolVersion;
38-
39-
if ($headers instanceof HttpHeadersInterface) {
40-
$this->Headers = $headers;
41-
} elseif (is_array($headers) || $headers === null) {
42-
$this->Headers = new HttpHeaders($headers ?: []);
43-
} else {
44-
throw new InvalidArgumentTypeException(2, 'headers', 'HttpHeadersInterface|array<string,string[]>|null', $headers);
45-
}
46-
47-
if ($body instanceof StreamInterface) {
48-
$this->Body = $body;
49-
} elseif (is_string($body) || $body === null) {
50-
$this->Body = Stream::fromContents((string) $body);
51-
} else {
52-
try {
53-
$this->Body = new Stream($body);
54-
} catch (InvalidArgumentException $ex) {
55-
throw new InvalidArgumentTypeException(1, 'body', 'StreamInterface|resource|string|null', $body);
56-
}
57-
}
38+
$this->ProtocolVersion = $this->filterProtocolVersion($version);
39+
$this->Headers = $this->filterHeaders($headers);
40+
$this->Body = $this->filterBody($body);
5841
}
5942

6043
/**
@@ -65,14 +48,6 @@ public function getProtocolVersion(): string
6548
return $this->ProtocolVersion;
6649
}
6750

68-
/**
69-
* @inheritDoc
70-
*/
71-
public function withProtocolVersion(string $version): self
72-
{
73-
return $this->with('ProtocolVersion', $version);
74-
}
75-
7651
/**
7752
* @inheritDoc
7853
*/
@@ -105,6 +80,22 @@ public function getHeaderLine(string $name): string
10580
return $this->Headers->getHeaderLine($name);
10681
}
10782

83+
/**
84+
* @inheritDoc
85+
*/
86+
public function getBody(): StreamInterface
87+
{
88+
return $this->Body;
89+
}
90+
91+
/**
92+
* @inheritDoc
93+
*/
94+
public function withProtocolVersion(string $version): self
95+
{
96+
return $this->with('ProtocolVersion', $this->filterProtocolVersion($version));
97+
}
98+
10899
/**
109100
* @inheritDoc
110101
*/
@@ -132,16 +123,61 @@ public function withoutHeader(string $name): self
132123
/**
133124
* @inheritDoc
134125
*/
135-
public function getBody(): StreamInterface
126+
public function withBody(StreamInterface $body): self
136127
{
137-
return $this->Body;
128+
return $this->with('Body', $body);
129+
}
130+
131+
private function filterProtocolVersion(string $version): string
132+
{
133+
if (!HttpProtocolVersion::hasValue($version)) {
134+
throw new InvalidArgumentException(
135+
sprintf('Invalid HTTP protocol version: %s', $version)
136+
);
137+
}
138+
return $version;
138139
}
139140

140141
/**
141-
* @inheritDoc
142+
* @param HttpHeadersInterface|array<string,string[]>|null $headers
142143
*/
143-
public function withBody(StreamInterface $body): self
144+
private function filterHeaders($headers): HttpHeadersInterface
144145
{
145-
return $this->with('Body', $body);
146+
if ($headers instanceof HttpHeadersInterface) {
147+
return $headers;
148+
}
149+
if (is_array($headers) || $headers === null) {
150+
return new HttpHeaders($headers ?: []);
151+
}
152+
// @phpstan-ignore-next-line
153+
throw new InvalidArgumentTypeException(
154+
1,
155+
'headers',
156+
'HttpHeadersInterface|array<string,string[]>|null',
157+
$headers
158+
);
159+
}
160+
161+
/**
162+
* @param StreamInterface|resource|string|null $body
163+
*/
164+
private function filterBody($body): StreamInterface
165+
{
166+
if ($body instanceof StreamInterface) {
167+
return $body;
168+
}
169+
if (is_string($body) || $body === null) {
170+
return Stream::fromContents((string) $body);
171+
}
172+
try {
173+
return new Stream($body);
174+
} catch (InvalidArgumentException $ex) {
175+
throw new InvalidArgumentTypeException(
176+
1,
177+
'body',
178+
'StreamInterface|resource|string|null',
179+
$body
180+
);
181+
}
146182
}
147183
}

0 commit comments

Comments
 (0)