Skip to content

Commit cdb3e6a

Browse files
authored
Support swow psr7-plus interface for http-message. (#5828)
1 parent ccfa7af commit cdb3e6a

File tree

7 files changed

+377
-32
lines changed

7 files changed

+377
-32
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
"hyperf/support": "~3.1.0",
1717
"hyperf/utils": "~3.1.0",
1818
"laminas/laminas-mime": "^2.7",
19-
"psr/http-message": "^1.0|^2.0"
19+
"psr/http-message": "^1.0|^2.0",
20+
"swow/psr7-plus": "^1.0"
2021
},
2122
"suggest": {
2223
"psr/container": "Required to replace RequestParserInterface."

src/Base/MessageTrait.php

Lines changed: 93 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public function getProtocolVersion(): string
5555
*
5656
* @param string $version HTTP protocol version
5757
*/
58-
public function withProtocolVersion($version): static
58+
public function withProtocolVersion(mixed $version): static
5959
{
6060
if ($this->protocol === $version) {
6161
return $this;
@@ -100,7 +100,7 @@ public function getHeaders(): array
100100
* name using a case-insensitive string comparison. Returns false if
101101
* no matching header name is found in the message.
102102
*/
103-
public function hasHeader($name): bool
103+
public function hasHeader(mixed $name): bool
104104
{
105105
return isset($this->headerNames[strtolower($name)]);
106106
}
@@ -117,7 +117,7 @@ public function hasHeader($name): bool
117117
* header. If the header does not appear in the message, this method MUST
118118
* return an empty array.
119119
*/
120-
public function getHeader($name): array
120+
public function getHeader(mixed $name): array
121121
{
122122
$name = strtolower($name);
123123

@@ -146,7 +146,7 @@ public function getHeader($name): array
146146
* concatenated together using a comma. If the header does not appear in
147147
* the message, this method MUST return an empty string.
148148
*/
149-
public function getHeaderLine($name): string
149+
public function getHeaderLine(mixed $name): string
150150
{
151151
return implode(', ', $this->getHeader($name));
152152
}
@@ -163,7 +163,7 @@ public function getHeaderLine($name): string
163163
* @param string|string[] $value header value(s)
164164
* @throws InvalidArgumentException for invalid header names or values
165165
*/
166-
public function withHeader($name, $value): static
166+
public function withHeader(mixed $name, mixed $value): static
167167
{
168168
if (! is_array($value)) {
169169
$value = [$value];
@@ -204,7 +204,7 @@ public function withHeaders(array $headers): static
204204
* @param string|string[] $value header value(s)
205205
* @throws InvalidArgumentException for invalid header names or values
206206
*/
207-
public function withAddedHeader($name, $value): static
207+
public function withAddedHeader(mixed $name, mixed $value): static
208208
{
209209
if (! is_array($value)) {
210210
$value = [$value];
@@ -234,7 +234,7 @@ public function withAddedHeader($name, $value): static
234234
*
235235
* @param string $name case-insensitive header field name to remove
236236
*/
237-
public function withoutHeader($name): static
237+
public function withoutHeader(mixed $name): static
238238
{
239239
$normalized = strtolower($name);
240240

@@ -324,7 +324,92 @@ public function isMultipart(): bool
324324
}
325325
}
326326

327-
private function setHeaders(array $headers): static
327+
public function setProtocolVersion(string $version): static
328+
{
329+
$this->protocol = $version;
330+
return $this;
331+
}
332+
333+
public function setHeader(string $name, mixed $value): static
334+
{
335+
if (! is_array($value)) {
336+
$value = [$value];
337+
}
338+
339+
$value = $this->trimHeaderValues($value);
340+
$normalized = strtolower($name);
341+
342+
if (isset($this->headerNames[$normalized])) {
343+
unset($this->headers[$this->headerNames[$normalized]]);
344+
}
345+
$this->headerNames[$normalized] = $name;
346+
$this->headers[$name] = $value;
347+
348+
return $this;
349+
}
350+
351+
public function addHeader(string $name, mixed $value): static
352+
{
353+
if (! is_array($value)) {
354+
$value = [$value];
355+
}
356+
357+
$value = $this->trimHeaderValues($value);
358+
$normalized = strtolower($name);
359+
360+
if (isset($this->headerNames[$normalized])) {
361+
$name = $this->headerNames[$normalized];
362+
$this->headers[$name] = array_merge($this->headers[$name], $value);
363+
} else {
364+
$this->headerNames[$normalized] = $name;
365+
$this->headers[$name] = $value;
366+
}
367+
368+
return $this;
369+
}
370+
371+
public function unsetHeader(string $name): static
372+
{
373+
$normalized = strtolower($name);
374+
375+
if (! isset($this->headerNames[$normalized])) {
376+
return $this;
377+
}
378+
379+
$name = $this->headerNames[$normalized];
380+
381+
unset($this->headers[$name], $this->headerNames[$normalized]);
382+
383+
return $this;
384+
}
385+
386+
public function getStandardHeaders(): array
387+
{
388+
$headers = $this->getHeaders();
389+
if (! $this->hasHeader('connection')) {
390+
$headers['Connection'] = [$this->shouldKeepAlive() ? 'keep-alive' : 'close'];
391+
}
392+
if (! $this->hasHeader('content-length')) {
393+
$headers['Content-Length'] = [(string) ($this->getBody()->getSize() ?? 0)];
394+
}
395+
return $headers;
396+
}
397+
398+
public function shouldKeepAlive(): bool
399+
{
400+
return strtolower($this->getHeaderLine('Connection')) === 'keep-alive';
401+
}
402+
403+
public function setBody(StreamInterface $body): static
404+
{
405+
$this->stream = $body;
406+
return $this;
407+
}
408+
409+
/**
410+
* @param array<string, array<string>|string> $headers
411+
*/
412+
public function setHeaders(array $headers): static
328413
{
329414
$this->headerNames = $this->headers = [];
330415
foreach ($headers as $header => $value) {

src/Base/Request.php

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@
1717
use Psr\Http\Message\RequestInterface;
1818
use Psr\Http\Message\StreamInterface;
1919
use Psr\Http\Message\UriInterface;
20+
use Swow\Psr7\Message\RequestPlusInterface;
2021

21-
class Request implements RequestInterface
22+
class Request implements RequestInterface, RequestPlusInterface
2223
{
2324
use MessageTrait;
2425

@@ -62,6 +63,11 @@ public function __construct(
6263
}
6364
}
6465

66+
public function __toString(): string
67+
{
68+
return $this->toString();
69+
}
70+
6571
/**
6672
* Retrieves the message's request target.
6773
* Retrieves the message's request-target either as it will appear (for
@@ -102,9 +108,9 @@ public function getRequestTarget(): string
102108
*
103109
* @see http://tools.ietf.org/html/rfc7230#section-5.3 (for the various
104110
* request-target forms allowed in request messages)
105-
* @param mixed $requestTarget
111+
* @param string $requestTarget
106112
*/
107-
public function withRequestTarget($requestTarget): static
113+
public function withRequestTarget(mixed $requestTarget): static
108114
{
109115
if (preg_match('#\s#', $requestTarget)) {
110116
throw new InvalidArgumentException('Invalid request target provided; cannot contain whitespace');
@@ -137,7 +143,7 @@ public function getMethod(): string
137143
* @param string $method case-sensitive method
138144
* @throws InvalidArgumentException for invalid HTTP methods
139145
*/
140-
public function withMethod($method): static
146+
public function withMethod(mixed $method): static
141147
{
142148
$method = strtoupper($method);
143149
$methods = ['GET', 'POST', 'PATCH', 'PUT', 'DELETE', 'HEAD'];
@@ -203,6 +209,58 @@ public function withUri(UriInterface $uri, $preserveHost = false): static
203209
return $new;
204210
}
205211

212+
public function setMethod(string $method): static
213+
{
214+
$method = strtoupper($method);
215+
$methods = ['GET', 'POST', 'PATCH', 'PUT', 'DELETE', 'HEAD'];
216+
if (! in_array($method, $methods)) {
217+
throw new InvalidArgumentException('Invalid Method');
218+
}
219+
$this->method = $method;
220+
return $this;
221+
}
222+
223+
public function setUri(UriInterface|string $uri, ?bool $preserveHost = null): static
224+
{
225+
$this->uri = $uri;
226+
227+
if (! $preserveHost) {
228+
$this->updateHostFromUri();
229+
}
230+
231+
return $this;
232+
}
233+
234+
public function setRequestTarget(string $requestTarget): static
235+
{
236+
if (preg_match('#\s#', $requestTarget)) {
237+
throw new InvalidArgumentException('Invalid request target provided; cannot contain whitespace');
238+
}
239+
240+
$this->requestTarget = $requestTarget;
241+
return $this;
242+
}
243+
244+
public function toString(bool $withoutBody = false): string
245+
{
246+
$headerString = '';
247+
if (! $withoutBody) {
248+
foreach ($this->getStandardHeaders() as $key => $values) {
249+
foreach ($values as $value) {
250+
$headerString .= sprintf("%s: %s\r\n", $key, $value);
251+
}
252+
}
253+
}
254+
return sprintf(
255+
"%s %s HTTP/%s\r\n%s\r\n%s",
256+
$this->getMethod(),
257+
$this->getUri()->getPath(),
258+
$this->getProtocolVersion(),
259+
$headerString,
260+
$this->getBody()
261+
);
262+
}
263+
206264
/**
207265
* Update Host Header according to Uri.
208266
*

src/Base/Response.php

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@
1313

1414
use InvalidArgumentException;
1515
use Psr\Http\Message\ResponseInterface;
16+
use Swow\Psr7\Message\ResponsePlusInterface;
1617

17-
class Response implements ResponseInterface
18+
class Response implements ResponseInterface, ResponsePlusInterface
1819
{
1920
use MessageTrait;
2021

@@ -122,10 +123,9 @@ public function getAttributes(): array
122123
*
123124
* @param string $name the attribute name
124125
* @param mixed $default default value to return if the attribute does not exist
125-
* @return mixed
126126
* @see getAttributes()
127127
*/
128-
public function getAttribute($name, $default = null)
128+
public function getAttribute(string $name, mixed $default = null): mixed
129129
{
130130
return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
131131
}
@@ -142,7 +142,7 @@ public function getAttribute($name, $default = null)
142142
* @param mixed $value the value of the attribute
143143
* @see getAttributes()
144144
*/
145-
public function withAttribute($name, $value): static
145+
public function withAttribute(string $name, mixed $value): static
146146
{
147147
$clone = clone $this;
148148
$clone->attributes[$name] = $value;
@@ -331,4 +331,32 @@ public function isEmpty(): bool
331331
{
332332
return in_array($this->statusCode, [204, 304]);
333333
}
334+
335+
public function toString(bool $withoutBody = false): string
336+
{
337+
$headerString = '';
338+
foreach ($this->getStandardHeaders() as $key => $values) {
339+
foreach ($values as $value) {
340+
$headerString .= sprintf("%s: %s\r\n", $key, $value);
341+
}
342+
}
343+
return sprintf(
344+
"HTTP/%s %s %s\r\n%s\r\n%s",
345+
$this->getProtocolVersion(),
346+
$this->getStatusCode(),
347+
$this->getReasonPhrase(),
348+
$headerString,
349+
$this->getBody()
350+
);
351+
}
352+
353+
public function setStatus(int $code, string $reasonPhrase = ''): static
354+
{
355+
$this->statusCode = $code;
356+
if (! $reasonPhrase && isset(self::$phrases[$code])) {
357+
$reasonPhrase = self::$phrases[$code];
358+
}
359+
$this->reasonPhrase = $reasonPhrase;
360+
return $this;
361+
}
334362
}

0 commit comments

Comments
 (0)