1<?php 2 3/* 4 * This file is part of the Symfony package. 5 * 6 * (c) Fabien Potencier <fabien@symfony.com> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Symfony\Bridge\PsrHttpMessage\Factory; 13 14use Psr\Http\Message\ResponseFactoryInterface; 15use Psr\Http\Message\ServerRequestFactoryInterface; 16use Psr\Http\Message\StreamFactoryInterface; 17use Psr\Http\Message\UploadedFileFactoryInterface; 18use Psr\Http\Message\UploadedFileInterface; 19use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface; 20use Symfony\Component\HttpFoundation\BinaryFileResponse; 21use Symfony\Component\HttpFoundation\File\UploadedFile; 22use Symfony\Component\HttpFoundation\Request; 23use Symfony\Component\HttpFoundation\Response; 24use Symfony\Component\HttpFoundation\StreamedResponse; 25 26/** 27 * Builds Psr\HttpMessage instances using a PSR-17 implementation. 28 * 29 * @author Antonio J. García Lagar <aj@garcialagar.es> 30 */ 31class PsrHttpFactory implements HttpMessageFactoryInterface 32{ 33 private $serverRequestFactory; 34 private $streamFactory; 35 private $uploadedFileFactory; 36 private $responseFactory; 37 38 public function __construct(ServerRequestFactoryInterface $serverRequestFactory, StreamFactoryInterface $streamFactory, UploadedFileFactoryInterface $uploadedFileFactory, ResponseFactoryInterface $responseFactory) 39 { 40 $this->serverRequestFactory = $serverRequestFactory; 41 $this->streamFactory = $streamFactory; 42 $this->uploadedFileFactory = $uploadedFileFactory; 43 $this->responseFactory = $responseFactory; 44 } 45 46 /** 47 * {@inheritdoc} 48 */ 49 public function createRequest(Request $symfonyRequest) 50 { 51 $request = $this->serverRequestFactory->createServerRequest( 52 $symfonyRequest->getMethod(), 53 $symfonyRequest->getSchemeAndHttpHost().$symfonyRequest->getRequestUri(), 54 $symfonyRequest->server->all() 55 ); 56 57 foreach ($symfonyRequest->headers->all() as $name => $value) { 58 $request = $request->withHeader($name, $value); 59 } 60 61 if (PHP_VERSION_ID < 50600) { 62 $body = $this->streamFactory->createStreamFromFile('php://temp', 'wb+'); 63 $body->write($symfonyRequest->getContent()); 64 } else { 65 $body = $this->streamFactory->createStreamFromResource($symfonyRequest->getContent(true)); 66 } 67 68 $request = $request 69 ->withBody($body) 70 ->withUploadedFiles($this->getFiles($symfonyRequest->files->all())) 71 ->withCookieParams($symfonyRequest->cookies->all()) 72 ->withQueryParams($symfonyRequest->query->all()) 73 ->withParsedBody($symfonyRequest->request->all()) 74 ; 75 76 foreach ($symfonyRequest->attributes->all() as $key => $value) { 77 $request = $request->withAttribute($key, $value); 78 } 79 80 return $request; 81 } 82 83 /** 84 * Converts Symfony uploaded files array to the PSR one. 85 * 86 * @param array $uploadedFiles 87 * 88 * @return array 89 */ 90 private function getFiles(array $uploadedFiles) 91 { 92 $files = array(); 93 94 foreach ($uploadedFiles as $key => $value) { 95 if (null === $value) { 96 $files[$key] = $this->uploadedFileFactory->createUploadedFile($this->streamFactory->createStream(), 0, UPLOAD_ERR_NO_FILE); 97 continue; 98 } 99 if ($value instanceof UploadedFile) { 100 $files[$key] = $this->createUploadedFile($value); 101 } else { 102 $files[$key] = $this->getFiles($value); 103 } 104 } 105 106 return $files; 107 } 108 109 /** 110 * Creates a PSR-7 UploadedFile instance from a Symfony one. 111 * 112 * @param UploadedFile $symfonyUploadedFile 113 * 114 * @return UploadedFileInterface 115 */ 116 private function createUploadedFile(UploadedFile $symfonyUploadedFile) 117 { 118 return $this->uploadedFileFactory->createUploadedFile( 119 $this->streamFactory->createStreamFromFile( 120 $symfonyUploadedFile->getRealPath() 121 ), 122 (int) $symfonyUploadedFile->getSize(), 123 $symfonyUploadedFile->getError(), 124 $symfonyUploadedFile->getClientOriginalName(), 125 $symfonyUploadedFile->getClientMimeType() 126 ); 127 } 128 129 /** 130 * {@inheritdoc} 131 */ 132 public function createResponse(Response $symfonyResponse) 133 { 134 $response = $this->responseFactory->createResponse($symfonyResponse->getStatusCode()); 135 136 if ($symfonyResponse instanceof BinaryFileResponse) { 137 $stream = $this->streamFactory->createStreamFromFile( 138 $symfonyResponse->getFile()->getPathname() 139 ); 140 } else { 141 $stream = $this->streamFactory->createStreamFromFile('php://temp', 'wb+'); 142 if ($symfonyResponse instanceof StreamedResponse) { 143 ob_start(function ($buffer) use ($stream) { 144 $stream->write($buffer); 145 146 return ''; 147 }); 148 149 $symfonyResponse->sendContent(); 150 ob_end_clean(); 151 } else { 152 $stream->write($symfonyResponse->getContent()); 153 } 154 } 155 156 $response = $response->withBody($stream); 157 158 $headers = $symfonyResponse->headers->all(); 159 $cookies = $symfonyResponse->headers->getCookies(); 160 if (!empty($cookies)) { 161 $headers['Set-Cookie'] = array(); 162 163 foreach ($cookies as $cookie) { 164 $headers['Set-Cookie'][] = $cookie->__toString(); 165 } 166 } 167 168 foreach ($headers as $name => $value) { 169 $response = $response->withHeader($name, $value); 170 } 171 172 $protocolVersion = $symfonyResponse->getProtocolVersion(); 173 $response = $response->withProtocolVersion($protocolVersion); 174 175 return $response; 176 } 177} 178