getStatusCode(), [204, 205, 304], true); } /** * Normalize response headers * @return static */ public function normalizeHeaders() { $response = $this; # Empty if ($response->isEmptyByStatus()) { $response = $response ->withoutHeader('Content-Type') ->withoutHeader('Content-Length'); } else { if (! $response->hasHeader('Content-Type')) { $response = $response->withHeader('Content-Type', 'text/html; charset=UTF-8'); } } # Cookies $cookies = []; foreach ($response->getCookies() as $cookie) { $cookies[] = $cookie->__toString(); } if (sizeof($cookies) > 0) { $response = $response->withHeader('Set-Cookie', $cookies); } return $response; } /** * Send response * @return void */ public function send() { $response = $this->normalizeHeaders(); $status = $response->getStatusCode(); # Headers if (! headers_sent()) { # Status header(sprintf( 'HTTP/%s %s %s', $response->getProtocolVersion(), $status, $response->getReasonPhrase() ), true, $status); $headers = bff::filter('http.response.headers', $response->getHeaders()); foreach ($headers as $name => $values) { $replace = (0 === strcasecmp($name, 'Content-Type')); foreach ($values as $value) { header(sprintf('%s: %s', $name, $value), $replace, $status); } } } # Body if (! $response->isEmptyByStatus($status)) { $body = $response->getBody(); if ($body->isSeekable()) { $body->rewind(); } $chunkSize = bff::filter('http.response.chunkSize', 4096); $contentLength = (int)$response->getHeaderLine('Content-Length'); if (! $contentLength) { $contentLength = $body->getSize(); } if ($contentLength) { $amountToRead = (int)$contentLength; while ($amountToRead > 0 && ! $body->eof()) { $data = $body->read(min($chunkSize, $amountToRead)); echo $data; $amountToRead -= strlen($data); if (connection_status() !== CONNECTION_NORMAL) { break; } } } else { while (! $body->eof()) { echo $body->read($chunkSize); if (connection_status() !== CONNECTION_NORMAL) { break; } } } # StreamTemporary: delete temporary file $body->close(); } # Send response to client (FPM only) if (function_exists('fastcgi_finish_request')) { //fastcgi_finish_request(); } } /** * Inject HTML code * @param string $code code to inject * @param string $tag - 'body', 'head' * @param string $position - position 'after:open', 'before:close' * @return static */ public function injectHtml(string $code, string $tag = 'body', string $position = 'after:open') { if (! in_array(mb_strtolower($tag), ['body','head'])) { return $this; } $html = (string) $this->getBody(); $pos = false; switch ($position) { case 'before:close': { # X $pos = mb_strripos($html, ''); } break; case 'after:close': { # X $pos = mb_strripos($html, ''); if ($pos !== false) { $pos += mb_strlen(''); } } break; case 'after:open': { # X $pos = mb_stripos($html, '<' . $tag); if ($pos !== false) { $pos = mb_stripos($html, '>', $pos) + 1; } } break; } if ($pos !== false) { $body = new Stream('php://temp', 'wb+'); $body->write( mb_substr($html, 0, $pos) . $code . mb_substr($html, $pos) ); $body->rewind(); return $this->withBody($body); } return $this; } /** * Throws the response in a ResponseException instance. * @throws ResponseException */ public function throw() { throw ResponseException::init($this); } }