1<?php 2 3declare(strict_types=1); 4 5/* 6 * This file is part of the TYPO3 CMS project. 7 * 8 * It is free software; you can redistribute it and/or modify it under 9 * the terms of the GNU General Public License, either version 2 10 * of the License, or any later version. 11 * 12 * For the full copyright and license information, please read the 13 * LICENSE.txt file that was distributed with this source code. 14 * 15 * The TYPO3 project - inspiring people to share! 16 */ 17 18namespace TYPO3\CMS\Core\Http; 19 20use Psr\Http\Message\ResponseInterface; 21use Psr\Http\Message\ServerRequestInterface; 22use Psr\Http\Server\RequestHandlerInterface; 23use TYPO3\CMS\Core\Core\ApplicationInterface; 24 25/** 26 * @internal 27 */ 28abstract class AbstractApplication implements ApplicationInterface 29{ 30 private const MULTI_LINE_HEADERS = [ 31 'set-cookie', 32 ]; 33 34 /** 35 * @var RequestHandlerInterface|null 36 */ 37 protected $requestHandler; 38 39 /** 40 * Outputs content 41 * 42 * @param ResponseInterface $response 43 */ 44 protected function sendResponse(ResponseInterface $response) 45 { 46 if ($response instanceof NullResponse) { 47 return; 48 } 49 50 // @todo This requires some merge strategy or header callback handling 51 if (!headers_sent()) { 52 // If the response code was not changed by legacy code (still is 200) 53 // then allow the PSR-7 response object to explicitly set it. 54 // Otherwise let legacy code take precedence. 55 // This code path can be deprecated once we expose the response object to third party code 56 if (http_response_code() === 200) { 57 header('HTTP/' . $response->getProtocolVersion() . ' ' . $response->getStatusCode() . ' ' . $response->getReasonPhrase()); 58 } 59 60 foreach ($response->getHeaders() as $name => $values) { 61 if (in_array(strtolower($name), self::MULTI_LINE_HEADERS, true)) { 62 foreach ($values as $value) { 63 header($name . ': ' . $value, false); 64 } 65 } else { 66 header($name . ': ' . implode(', ', $values)); 67 } 68 } 69 } 70 $body = $response->getBody(); 71 if ($body instanceof SelfEmittableStreamInterface) { 72 // Optimization for streams that use php functions like readfile() as fastpath for serving files. 73 $body->emit(); 74 } else { 75 echo $body->__toString(); 76 } 77 } 78 79 /** 80 * @param ServerRequestInterface $request 81 * @return ResponseInterface 82 */ 83 protected function handle(ServerRequestInterface $request): ResponseInterface 84 { 85 return $this->requestHandler->handle($request); 86 } 87 88 /** 89 * Set up the application and shut it down afterwards 90 * 91 * @param callable $execute 92 */ 93 final public function run(callable $execute = null) 94 { 95 try { 96 $response = $this->handle( 97 ServerRequestFactory::fromGlobals() 98 ); 99 if ($execute !== null) { 100 call_user_func($execute); 101 } 102 } catch (ImmediateResponseException $exception) { 103 $response = $exception->getResponse(); 104 } 105 106 $this->sendResponse($response); 107 } 108} 109