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