friendship ended with social-app. php is my new best friend
1<?php
2/**
3 * Class SapiEmitter
4 *
5 * @created 22.10.2022
6 * @author smiley <smiley@chillerlan.net>
7 * @copyright 2022 smiley
8 * @license MIT
9 */
10declare(strict_types=1);
11
12namespace chillerlan\HTTP\Utils\Emitter;
13
14use chillerlan\HTTP\Utils\HeaderUtil;
15use RuntimeException;
16use function header, headers_sent, is_array, ob_get_length, ob_get_level, sprintf;
17
18class SapiEmitter extends ResponseEmitterAbstract{
19
20 public function emit():void{
21
22 if(ob_get_level() > 0 && ob_get_length() > 0){
23 throw new RuntimeException('Output has been emitted previously; cannot emit response.');
24 }
25
26 if(headers_sent($file, $line)){
27 throw new RuntimeException(sprintf('Headers already sent in file %s on line %s.', $file, $line));
28 }
29
30 $this->emitHeaders();
31 $this->emitBody();
32 }
33
34 /**
35 * Emits the headers
36 *
37 * There are two special-case header calls. The first is a header
38 * that starts with the string "HTTP/" (case is not significant),
39 * which will be used to figure out the HTTP status code to send.
40 * For example, if you have configured Apache to use a PHP script
41 * to handle requests for missing files (using the ErrorDocument
42 * directive), you may want to make sure that your script generates
43 * the proper status code.
44 *
45 * The second special case is the "Location:" header.
46 * Not only does it send this header back to the browser,
47 * but it also returns a REDIRECT (302) status code to the browser
48 * unless the 201 or a 3xx status code has already been set.
49 *
50 * @see https://www.php.net/manual/en/function.header.php
51 */
52 protected function emitHeaders():void{
53 $headers = HeaderUtil::normalize($this->response->getHeaders());
54
55 foreach($headers as $name => $value){
56
57 if($name === 'Set-Cookie'){
58 continue;
59 }
60
61 $this->sendHeader(sprintf('%s: %s', $name, $value), true);
62 }
63
64 if(isset($headers['Set-Cookie']) && is_array($headers['Set-Cookie'])){
65
66 foreach($headers['Set-Cookie'] as $cookie){
67 $this->sendHeader(sprintf('Set-Cookie: %s', $cookie), false);
68 }
69
70 }
71
72 // Set the status _after_ the headers, because of PHP's "helpful" behavior with location headers.
73 // See https://github.com/slimphp/Slim/issues/1730
74 $this->sendHeader($this->getStatusLine(), true, $this->response->getStatusCode());
75 }
76
77 /**
78 * Allow to intercept header calls in tests
79 *
80 * @codeCoverageIgnore (overridden in test)
81 */
82 protected function sendHeader(string $header, bool $replace, int $response_code = 0):void{
83 header($header, $replace, $response_code);
84 }
85
86}