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