Skip to content
This repository was archived by the owner on Jan 29, 2020. It is now read-only.

Commit d9b2fb8

Browse files
committed
Imports emitters from zend-diactoros
Imports the `EmitterInterface` and emitter implementations from zend-diactoros, to allow them to be used with any PSR-7 implementation. Adds psr/http-message-implementation as a required dependency, and zend-diactoros as a development dependency to allow testing accurately.
1 parent 05689be commit d9b2fb8

13 files changed

+1403
-7
lines changed

composer.json

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
{
22
"name": "zendframework/zend-serverhandler-runner",
3-
"description": "<package description; should match repository description>",
3+
"description": "Execute PSR-15 RequestHandlerInterface instances and emit responses they generate.",
44
"license": "BSD-3-Clause",
55
"keywords": [
66
"components",
77
"zf",
88
"zendframework",
9-
"<individual keywords for current package>"
9+
"psr-7",
10+
"psr-15"
1011
],
1112
"support": {
1213
"docs": "https://docs.zendframework.com/zend-serverhandler-runner/",
@@ -17,11 +18,15 @@
1718
"forum": "https://discourse.zendframework.com/c/questions/components"
1819
},
1920
"require": {
20-
"php": "^7.1"
21+
"php": "^7.1",
22+
"psr/http-message": "^1.0",
23+
"psr/http-message-implementation": "^1.0",
24+
"psr/http-server-handler": "^1.0"
2125
},
2226
"require-dev": {
2327
"phpunit/phpunit": "^6.3",
24-
"zendframework/zend-coding-standard": "~1.0.0"
28+
"zendframework/zend-coding-standard": "~1.0.0",
29+
"zendframework/zend-diactoros": "^1.7"
2530
},
2631
"autoload": {
2732
"psr-4": {
@@ -31,7 +36,11 @@
3136
"autoload-dev": {
3237
"psr-4": {
3338
"ZendTest\\ServerHandler\\Runner\\": "test/"
34-
}
39+
},
40+
"files": [
41+
"test/TestAsset/Functions.php",
42+
"test/TestAsset/SapiResponse.php"
43+
]
3544
},
3645
"config": {
3746
"sort-packages": true

composer.lock

Lines changed: 158 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Emitter/EmitterInterface.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
/**
3+
* @see https://github.com/zendframework/zend-serverhandler-runner for the canonical source repository
4+
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
5+
* @license https://github.com/zendframework/zend-serverhandler-runner/blob/master/LICENSE.md New BSD License
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace Zend\ServerHandler\Runner\Emitter;
11+
12+
use Psr\Http\Message\ResponseInterface;
13+
14+
interface EmitterInterface
15+
{
16+
/**
17+
* Emit a response.
18+
*
19+
* Emits a response, including status line, headers, and the message body,
20+
* according to the environment.
21+
*
22+
* Implementations of this method may be written in such a way as to have
23+
* side effects, such as usage of header() or pushing output to the
24+
* output buffer.
25+
*
26+
* Implementations MAY raise exceptions if they are unable to emit the
27+
* response; e.g., if headers have already been sent.
28+
*/
29+
public function emit(ResponseInterface $response) : void;
30+
}

src/Emitter/SapiEmitter.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
/**
3+
* @see https://github.com/zendframework/zend-serverhandler-runner for the canonical source repository
4+
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
5+
* @license https://github.com/zendframework/zend-serverhandler-runner/blob/master/LICENSE.md New BSD License
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace Zend\ServerHandler\Runner\Emitter;
11+
12+
use Psr\Http\Message\ResponseInterface;
13+
14+
class SapiEmitter implements EmitterInterface
15+
{
16+
use SapiEmitterTrait;
17+
18+
/**
19+
* Emits a response for a PHP SAPI environment.
20+
*
21+
* Emits the status line and headers via the header() function, and the
22+
* body content via the output buffer.
23+
*/
24+
public function emit(ResponseInterface $response) : void
25+
{
26+
$this->assertNoPreviousOutput();
27+
28+
$this->emitHeaders($response);
29+
$this->emitStatusLine($response);
30+
$this->emitBody($response);
31+
}
32+
33+
/**
34+
* Emit the message body.
35+
*/
36+
private function emitBody(ResponseInterface $response) : void
37+
{
38+
echo $response->getBody();
39+
}
40+
}

src/Emitter/SapiEmitterTrait.php

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?php
2+
/**
3+
* @see https://github.com/zendframework/zend-serverhandler-runner for the canonical source repository
4+
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
5+
* @license https://github.com/zendframework/zend-serverhandler-runner/blob/master/LICENSE.md New BSD License
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace Zend\ServerHandler\Runner\Emitter;
11+
12+
use Psr\Http\Message\ResponseInterface;
13+
use Zend\ServerHandler\Runner\Exception\EmitterException;
14+
15+
trait SapiEmitterTrait
16+
{
17+
/**
18+
* Checks to see if content has previously been sent.
19+
*
20+
* If either headers have been sent or the output buffer contains content,
21+
* raises an exception.
22+
*
23+
* @throws EmitterException if headers have already been sent.
24+
* @throws EmitterException if output is present in the output buffer.
25+
*/
26+
private function assertNoPreviousOutput()
27+
{
28+
if (headers_sent()) {
29+
throw EmitterException::forHeadersSent();
30+
}
31+
32+
if (ob_get_level() > 0 && ob_get_length() > 0) {
33+
throw EmitterException::forOutputSent();
34+
}
35+
}
36+
37+
/**
38+
* Emit the status line.
39+
*
40+
* Emits the status line using the protocol version and status code from
41+
* the response; if a reason phrase is available, it, too, is emitted.
42+
*
43+
* It is important to mention that this method should be called after
44+
* `emitHeaders()` in order to prevent PHP from changing the status code of
45+
* the emitted response.
46+
*
47+
* @see \Zend\ServerHandler\Runner\Emitter\SapiEmitterTrait::emitHeaders()
48+
*/
49+
private function emitStatusLine(ResponseInterface $response) : void
50+
{
51+
$reasonPhrase = $response->getReasonPhrase();
52+
$statusCode = $response->getStatusCode();
53+
54+
header(sprintf(
55+
'HTTP/%s %d%s',
56+
$response->getProtocolVersion(),
57+
$statusCode,
58+
($reasonPhrase ? ' ' . $reasonPhrase : '')
59+
), true, $statusCode);
60+
}
61+
62+
/**
63+
* Emit response headers.
64+
*
65+
* Loops through each header, emitting each; if the header value
66+
* is an array with multiple values, ensures that each is sent
67+
* in such a way as to create aggregate headers (instead of replace
68+
* the previous).
69+
*/
70+
private function emitHeaders(ResponseInterface $response) : void
71+
{
72+
$statusCode = $response->getStatusCode();
73+
74+
foreach ($response->getHeaders() as $header => $values) {
75+
$name = $this->filterHeader($header);
76+
$first = $name === 'Set-Cookie' ? false : true;
77+
foreach ($values as $value) {
78+
header(sprintf(
79+
'%s: %s',
80+
$name,
81+
$value
82+
), $first, $statusCode);
83+
$first = false;
84+
}
85+
}
86+
}
87+
88+
/**
89+
* Filter a header name to wordcase
90+
*/
91+
private function filterHeader(string $header) : string
92+
{
93+
$filtered = str_replace('-', ' ', $header);
94+
$filtered = ucwords($filtered);
95+
return str_replace(' ', '-', $filtered);
96+
}
97+
}

0 commit comments

Comments
 (0)