File indexing completed on 2025-01-26 05:29:13
0001 <?php 0002 0003 namespace GuzzleHttp\Psr7; 0004 0005 use InvalidArgumentException; 0006 use Psr\Http\Message\ServerRequestInterface; 0007 use Psr\Http\Message\UriInterface; 0008 use Psr\Http\Message\StreamInterface; 0009 use Psr\Http\Message\UploadedFileInterface; 0010 0011 /** 0012 * Server-side HTTP request 0013 * 0014 * Extends the Request definition to add methods for accessing incoming data, 0015 * specifically server parameters, cookies, matched path parameters, query 0016 * string arguments, body parameters, and upload file information. 0017 * 0018 * "Attributes" are discovered via decomposing the request (and usually 0019 * specifically the URI path), and typically will be injected by the application. 0020 * 0021 * Requests are considered immutable; all methods that might change state are 0022 * implemented such that they retain the internal state of the current 0023 * message and return a new instance that contains the changed state. 0024 */ 0025 class ServerRequest extends Request implements ServerRequestInterface 0026 { 0027 /** 0028 * @var array 0029 */ 0030 private $attributes = []; 0031 0032 /** 0033 * @var array 0034 */ 0035 private $cookieParams = []; 0036 0037 /** 0038 * @var null|array|object 0039 */ 0040 private $parsedBody; 0041 0042 /** 0043 * @var array 0044 */ 0045 private $queryParams = []; 0046 0047 /** 0048 * @var array 0049 */ 0050 private $serverParams; 0051 0052 /** 0053 * @var array 0054 */ 0055 private $uploadedFiles = []; 0056 0057 /** 0058 * @param string $method HTTP method 0059 * @param string|UriInterface $uri URI 0060 * @param array $headers Request headers 0061 * @param string|null|resource|StreamInterface $body Request body 0062 * @param string $version Protocol version 0063 * @param array $serverParams Typically the $_SERVER superglobal 0064 */ 0065 public function __construct( 0066 $method, 0067 $uri, 0068 array $headers = [], 0069 $body = null, 0070 $version = '1.1', 0071 array $serverParams = [] 0072 ) { 0073 $this->serverParams = $serverParams; 0074 0075 parent::__construct($method, $uri, $headers, $body, $version); 0076 } 0077 0078 /** 0079 * Return an UploadedFile instance array. 0080 * 0081 * @param array $files A array which respect $_FILES structure 0082 * @throws InvalidArgumentException for unrecognized values 0083 * @return array 0084 */ 0085 public static function normalizeFiles(array $files) 0086 { 0087 $normalized = []; 0088 0089 foreach ($files as $key => $value) { 0090 if ($value instanceof UploadedFileInterface) { 0091 $normalized[$key] = $value; 0092 } elseif (is_array($value) && isset($value['tmp_name'])) { 0093 $normalized[$key] = self::createUploadedFileFromSpec($value); 0094 } elseif (is_array($value)) { 0095 $normalized[$key] = self::normalizeFiles($value); 0096 continue; 0097 } else { 0098 throw new InvalidArgumentException('Invalid value in files specification'); 0099 } 0100 } 0101 0102 return $normalized; 0103 } 0104 0105 /** 0106 * Create and return an UploadedFile instance from a $_FILES specification. 0107 * 0108 * If the specification represents an array of values, this method will 0109 * delegate to normalizeNestedFileSpec() and return that return value. 0110 * 0111 * @param array $value $_FILES struct 0112 * @return array|UploadedFileInterface 0113 */ 0114 private static function createUploadedFileFromSpec(array $value) 0115 { 0116 if (is_array($value['tmp_name'])) { 0117 return self::normalizeNestedFileSpec($value); 0118 } 0119 0120 return new UploadedFile( 0121 $value['tmp_name'], 0122 (int) $value['size'], 0123 (int) $value['error'], 0124 $value['name'], 0125 $value['type'] 0126 ); 0127 } 0128 0129 /** 0130 * Normalize an array of file specifications. 0131 * 0132 * Loops through all nested files and returns a normalized array of 0133 * UploadedFileInterface instances. 0134 * 0135 * @param array $files 0136 * @return UploadedFileInterface[] 0137 */ 0138 private static function normalizeNestedFileSpec(array $files = []) 0139 { 0140 $normalizedFiles = []; 0141 0142 foreach (array_keys($files['tmp_name']) as $key) { 0143 $spec = [ 0144 'tmp_name' => $files['tmp_name'][$key], 0145 'size' => $files['size'][$key], 0146 'error' => $files['error'][$key], 0147 'name' => $files['name'][$key], 0148 'type' => $files['type'][$key], 0149 ]; 0150 $normalizedFiles[$key] = self::createUploadedFileFromSpec($spec); 0151 } 0152 0153 return $normalizedFiles; 0154 } 0155 0156 /** 0157 * Return a ServerRequest populated with superglobals: 0158 * $_GET 0159 * $_POST 0160 * $_COOKIE 0161 * $_FILES 0162 * $_SERVER 0163 * 0164 * @return ServerRequestInterface 0165 */ 0166 public static function fromGlobals() 0167 { 0168 $method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET'; 0169 $headers = function_exists('getallheaders') ? getallheaders() : []; 0170 $uri = self::getUriFromGlobals(); 0171 $body = new LazyOpenStream('php://input', 'r+'); 0172 $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']) : '1.1'; 0173 0174 $serverRequest = new ServerRequest($method, $uri, $headers, $body, $protocol, $_SERVER); 0175 0176 return $serverRequest 0177 ->withCookieParams($_COOKIE) 0178 ->withQueryParams($_GET) 0179 ->withParsedBody($_POST) 0180 ->withUploadedFiles(self::normalizeFiles($_FILES)); 0181 } 0182 0183 /** 0184 * Get a Uri populated with values from $_SERVER. 0185 * 0186 * @return UriInterface 0187 */ 0188 public static function getUriFromGlobals() { 0189 $uri = new Uri(''); 0190 0191 $uri = $uri->withScheme(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http'); 0192 0193 $hasPort = false; 0194 if (isset($_SERVER['HTTP_HOST'])) { 0195 $hostHeaderParts = explode(':', $_SERVER['HTTP_HOST']); 0196 $uri = $uri->withHost($hostHeaderParts[0]); 0197 if (isset($hostHeaderParts[1])) { 0198 $hasPort = true; 0199 $uri = $uri->withPort($hostHeaderParts[1]); 0200 } 0201 } elseif (isset($_SERVER['SERVER_NAME'])) { 0202 $uri = $uri->withHost($_SERVER['SERVER_NAME']); 0203 } elseif (isset($_SERVER['SERVER_ADDR'])) { 0204 $uri = $uri->withHost($_SERVER['SERVER_ADDR']); 0205 } 0206 0207 if (!$hasPort && isset($_SERVER['SERVER_PORT'])) { 0208 $uri = $uri->withPort($_SERVER['SERVER_PORT']); 0209 } 0210 0211 $hasQuery = false; 0212 if (isset($_SERVER['REQUEST_URI'])) { 0213 $requestUriParts = explode('?', $_SERVER['REQUEST_URI']); 0214 $uri = $uri->withPath($requestUriParts[0]); 0215 if (isset($requestUriParts[1])) { 0216 $hasQuery = true; 0217 $uri = $uri->withQuery($requestUriParts[1]); 0218 } 0219 } 0220 0221 if (!$hasQuery && isset($_SERVER['QUERY_STRING'])) { 0222 $uri = $uri->withQuery($_SERVER['QUERY_STRING']); 0223 } 0224 0225 return $uri; 0226 } 0227 0228 0229 /** 0230 * {@inheritdoc} 0231 */ 0232 public function getServerParams() 0233 { 0234 return $this->serverParams; 0235 } 0236 0237 /** 0238 * {@inheritdoc} 0239 */ 0240 public function getUploadedFiles() 0241 { 0242 return $this->uploadedFiles; 0243 } 0244 0245 /** 0246 * {@inheritdoc} 0247 */ 0248 public function withUploadedFiles(array $uploadedFiles) 0249 { 0250 $new = clone $this; 0251 $new->uploadedFiles = $uploadedFiles; 0252 0253 return $new; 0254 } 0255 0256 /** 0257 * {@inheritdoc} 0258 */ 0259 public function getCookieParams() 0260 { 0261 return $this->cookieParams; 0262 } 0263 0264 /** 0265 * {@inheritdoc} 0266 */ 0267 public function withCookieParams(array $cookies) 0268 { 0269 $new = clone $this; 0270 $new->cookieParams = $cookies; 0271 0272 return $new; 0273 } 0274 0275 /** 0276 * {@inheritdoc} 0277 */ 0278 public function getQueryParams() 0279 { 0280 return $this->queryParams; 0281 } 0282 0283 /** 0284 * {@inheritdoc} 0285 */ 0286 public function withQueryParams(array $query) 0287 { 0288 $new = clone $this; 0289 $new->queryParams = $query; 0290 0291 return $new; 0292 } 0293 0294 /** 0295 * {@inheritdoc} 0296 */ 0297 public function getParsedBody() 0298 { 0299 return $this->parsedBody; 0300 } 0301 0302 /** 0303 * {@inheritdoc} 0304 */ 0305 public function withParsedBody($data) 0306 { 0307 $new = clone $this; 0308 $new->parsedBody = $data; 0309 0310 return $new; 0311 } 0312 0313 /** 0314 * {@inheritdoc} 0315 */ 0316 public function getAttributes() 0317 { 0318 return $this->attributes; 0319 } 0320 0321 /** 0322 * {@inheritdoc} 0323 */ 0324 public function getAttribute($attribute, $default = null) 0325 { 0326 if (false === array_key_exists($attribute, $this->attributes)) { 0327 return $default; 0328 } 0329 0330 return $this->attributes[$attribute]; 0331 } 0332 0333 /** 0334 * {@inheritdoc} 0335 */ 0336 public function withAttribute($attribute, $value) 0337 { 0338 $new = clone $this; 0339 $new->attributes[$attribute] = $value; 0340 0341 return $new; 0342 } 0343 0344 /** 0345 * {@inheritdoc} 0346 */ 0347 public function withoutAttribute($attribute) 0348 { 0349 if (false === array_key_exists($attribute, $this->attributes)) { 0350 return $this; 0351 } 0352 0353 $new = clone $this; 0354 unset($new->attributes[$attribute]); 0355 0356 return $new; 0357 } 0358 }