| 1 | efrain | 1 | <?php
 | 
        
           |  |  | 2 |   | 
        
           |  |  | 3 | declare(strict_types=1);
 | 
        
           |  |  | 4 |   | 
        
           |  |  | 5 | namespace GuzzleHttp\Psr7;
 | 
        
           |  |  | 6 |   | 
        
           |  |  | 7 | use InvalidArgumentException;
 | 
        
           |  |  | 8 | use Psr\Http\Message\ServerRequestInterface;
 | 
        
           |  |  | 9 | use Psr\Http\Message\StreamInterface;
 | 
        
           |  |  | 10 | use Psr\Http\Message\UploadedFileInterface;
 | 
        
           |  |  | 11 | use Psr\Http\Message\UriInterface;
 | 
        
           |  |  | 12 |   | 
        
           |  |  | 13 | /**
 | 
        
           |  |  | 14 |  * Server-side HTTP request
 | 
        
           |  |  | 15 |  *
 | 
        
           |  |  | 16 |  * Extends the Request definition to add methods for accessing incoming data,
 | 
        
           |  |  | 17 |  * specifically server parameters, cookies, matched path parameters, query
 | 
        
           |  |  | 18 |  * string arguments, body parameters, and upload file information.
 | 
        
           |  |  | 19 |  *
 | 
        
           |  |  | 20 |  * "Attributes" are discovered via decomposing the request (and usually
 | 
        
           |  |  | 21 |  * specifically the URI path), and typically will be injected by the application.
 | 
        
           |  |  | 22 |  *
 | 
        
           |  |  | 23 |  * Requests are considered immutable; all methods that might change state are
 | 
        
           |  |  | 24 |  * implemented such that they retain the internal state of the current
 | 
        
           |  |  | 25 |  * message and return a new instance that contains the changed state.
 | 
        
           |  |  | 26 |  */
 | 
        
           |  |  | 27 | class ServerRequest extends Request implements ServerRequestInterface
 | 
        
           |  |  | 28 | {
 | 
        
           |  |  | 29 |     /**
 | 
        
           |  |  | 30 |      * @var array
 | 
        
           |  |  | 31 |      */
 | 
        
           |  |  | 32 |     private $attributes = [];
 | 
        
           |  |  | 33 |   | 
        
           |  |  | 34 |     /**
 | 
        
           |  |  | 35 |      * @var array
 | 
        
           |  |  | 36 |      */
 | 
        
           |  |  | 37 |     private $cookieParams = [];
 | 
        
           |  |  | 38 |   | 
        
           |  |  | 39 |     /**
 | 
        
           |  |  | 40 |      * @var array|object|null
 | 
        
           |  |  | 41 |      */
 | 
        
           |  |  | 42 |     private $parsedBody;
 | 
        
           |  |  | 43 |   | 
        
           |  |  | 44 |     /**
 | 
        
           |  |  | 45 |      * @var array
 | 
        
           |  |  | 46 |      */
 | 
        
           |  |  | 47 |     private $queryParams = [];
 | 
        
           |  |  | 48 |   | 
        
           |  |  | 49 |     /**
 | 
        
           |  |  | 50 |      * @var array
 | 
        
           |  |  | 51 |      */
 | 
        
           |  |  | 52 |     private $serverParams;
 | 
        
           |  |  | 53 |   | 
        
           |  |  | 54 |     /**
 | 
        
           |  |  | 55 |      * @var array
 | 
        
           |  |  | 56 |      */
 | 
        
           |  |  | 57 |     private $uploadedFiles = [];
 | 
        
           |  |  | 58 |   | 
        
           |  |  | 59 |     /**
 | 
        
           |  |  | 60 |      * @param string                               $method       HTTP method
 | 
        
           |  |  | 61 |      * @param string|UriInterface                  $uri          URI
 | 
        
           | 1441 | ariadna | 62 |      * @param (string|string[])[]                  $headers      Request headers
 | 
        
           | 1 | efrain | 63 |      * @param string|resource|StreamInterface|null $body         Request body
 | 
        
           |  |  | 64 |      * @param string                               $version      Protocol version
 | 
        
           |  |  | 65 |      * @param array                                $serverParams Typically the $_SERVER superglobal
 | 
        
           |  |  | 66 |      */
 | 
        
           |  |  | 67 |     public function __construct(
 | 
        
           |  |  | 68 |         string $method,
 | 
        
           |  |  | 69 |         $uri,
 | 
        
           |  |  | 70 |         array $headers = [],
 | 
        
           |  |  | 71 |         $body = null,
 | 
        
           |  |  | 72 |         string $version = '1.1',
 | 
        
           |  |  | 73 |         array $serverParams = []
 | 
        
           |  |  | 74 |     ) {
 | 
        
           |  |  | 75 |         $this->serverParams = $serverParams;
 | 
        
           |  |  | 76 |   | 
        
           |  |  | 77 |         parent::__construct($method, $uri, $headers, $body, $version);
 | 
        
           |  |  | 78 |     }
 | 
        
           |  |  | 79 |   | 
        
           |  |  | 80 |     /**
 | 
        
           |  |  | 81 |      * Return an UploadedFile instance array.
 | 
        
           |  |  | 82 |      *
 | 
        
           |  |  | 83 |      * @param array $files An array which respect $_FILES structure
 | 
        
           |  |  | 84 |      *
 | 
        
           |  |  | 85 |      * @throws InvalidArgumentException for unrecognized values
 | 
        
           |  |  | 86 |      */
 | 
        
           |  |  | 87 |     public static function normalizeFiles(array $files): array
 | 
        
           |  |  | 88 |     {
 | 
        
           |  |  | 89 |         $normalized = [];
 | 
        
           |  |  | 90 |   | 
        
           |  |  | 91 |         foreach ($files as $key => $value) {
 | 
        
           |  |  | 92 |             if ($value instanceof UploadedFileInterface) {
 | 
        
           |  |  | 93 |                 $normalized[$key] = $value;
 | 
        
           |  |  | 94 |             } elseif (is_array($value) && isset($value['tmp_name'])) {
 | 
        
           |  |  | 95 |                 $normalized[$key] = self::createUploadedFileFromSpec($value);
 | 
        
           |  |  | 96 |             } elseif (is_array($value)) {
 | 
        
           |  |  | 97 |                 $normalized[$key] = self::normalizeFiles($value);
 | 
        
           |  |  | 98 |                 continue;
 | 
        
           |  |  | 99 |             } else {
 | 
        
           |  |  | 100 |                 throw new InvalidArgumentException('Invalid value in files specification');
 | 
        
           |  |  | 101 |             }
 | 
        
           |  |  | 102 |         }
 | 
        
           |  |  | 103 |   | 
        
           |  |  | 104 |         return $normalized;
 | 
        
           |  |  | 105 |     }
 | 
        
           |  |  | 106 |   | 
        
           |  |  | 107 |     /**
 | 
        
           |  |  | 108 |      * Create and return an UploadedFile instance from a $_FILES specification.
 | 
        
           |  |  | 109 |      *
 | 
        
           |  |  | 110 |      * If the specification represents an array of values, this method will
 | 
        
           |  |  | 111 |      * delegate to normalizeNestedFileSpec() and return that return value.
 | 
        
           |  |  | 112 |      *
 | 
        
           |  |  | 113 |      * @param array $value $_FILES struct
 | 
        
           |  |  | 114 |      *
 | 
        
           |  |  | 115 |      * @return UploadedFileInterface|UploadedFileInterface[]
 | 
        
           |  |  | 116 |      */
 | 
        
           |  |  | 117 |     private static function createUploadedFileFromSpec(array $value)
 | 
        
           |  |  | 118 |     {
 | 
        
           |  |  | 119 |         if (is_array($value['tmp_name'])) {
 | 
        
           |  |  | 120 |             return self::normalizeNestedFileSpec($value);
 | 
        
           |  |  | 121 |         }
 | 
        
           |  |  | 122 |   | 
        
           |  |  | 123 |         return new UploadedFile(
 | 
        
           |  |  | 124 |             $value['tmp_name'],
 | 
        
           |  |  | 125 |             (int) $value['size'],
 | 
        
           |  |  | 126 |             (int) $value['error'],
 | 
        
           |  |  | 127 |             $value['name'],
 | 
        
           |  |  | 128 |             $value['type']
 | 
        
           |  |  | 129 |         );
 | 
        
           |  |  | 130 |     }
 | 
        
           |  |  | 131 |   | 
        
           |  |  | 132 |     /**
 | 
        
           |  |  | 133 |      * Normalize an array of file specifications.
 | 
        
           |  |  | 134 |      *
 | 
        
           |  |  | 135 |      * Loops through all nested files and returns a normalized array of
 | 
        
           |  |  | 136 |      * UploadedFileInterface instances.
 | 
        
           |  |  | 137 |      *
 | 
        
           |  |  | 138 |      * @return UploadedFileInterface[]
 | 
        
           |  |  | 139 |      */
 | 
        
           |  |  | 140 |     private static function normalizeNestedFileSpec(array $files = []): array
 | 
        
           |  |  | 141 |     {
 | 
        
           |  |  | 142 |         $normalizedFiles = [];
 | 
        
           |  |  | 143 |   | 
        
           |  |  | 144 |         foreach (array_keys($files['tmp_name']) as $key) {
 | 
        
           |  |  | 145 |             $spec = [
 | 
        
           |  |  | 146 |                 'tmp_name' => $files['tmp_name'][$key],
 | 
        
           | 1441 | ariadna | 147 |                 'size' => $files['size'][$key] ?? null,
 | 
        
           |  |  | 148 |                 'error' => $files['error'][$key] ?? null,
 | 
        
           |  |  | 149 |                 'name' => $files['name'][$key] ?? null,
 | 
        
           |  |  | 150 |                 'type' => $files['type'][$key] ?? null,
 | 
        
           | 1 | efrain | 151 |             ];
 | 
        
           |  |  | 152 |             $normalizedFiles[$key] = self::createUploadedFileFromSpec($spec);
 | 
        
           |  |  | 153 |         }
 | 
        
           |  |  | 154 |   | 
        
           |  |  | 155 |         return $normalizedFiles;
 | 
        
           |  |  | 156 |     }
 | 
        
           |  |  | 157 |   | 
        
           |  |  | 158 |     /**
 | 
        
           |  |  | 159 |      * Return a ServerRequest populated with superglobals:
 | 
        
           |  |  | 160 |      * $_GET
 | 
        
           |  |  | 161 |      * $_POST
 | 
        
           |  |  | 162 |      * $_COOKIE
 | 
        
           |  |  | 163 |      * $_FILES
 | 
        
           |  |  | 164 |      * $_SERVER
 | 
        
           |  |  | 165 |      */
 | 
        
           |  |  | 166 |     public static function fromGlobals(): ServerRequestInterface
 | 
        
           |  |  | 167 |     {
 | 
        
           |  |  | 168 |         $method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
 | 
        
           |  |  | 169 |         $headers = getallheaders();
 | 
        
           |  |  | 170 |         $uri = self::getUriFromGlobals();
 | 
        
           |  |  | 171 |         $body = new CachingStream(new LazyOpenStream('php://input', 'r+'));
 | 
        
           |  |  | 172 |         $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']) : '1.1';
 | 
        
           |  |  | 173 |   | 
        
           |  |  | 174 |         $serverRequest = new ServerRequest($method, $uri, $headers, $body, $protocol, $_SERVER);
 | 
        
           |  |  | 175 |   | 
        
           |  |  | 176 |         return $serverRequest
 | 
        
           |  |  | 177 |             ->withCookieParams($_COOKIE)
 | 
        
           |  |  | 178 |             ->withQueryParams($_GET)
 | 
        
           |  |  | 179 |             ->withParsedBody($_POST)
 | 
        
           |  |  | 180 |             ->withUploadedFiles(self::normalizeFiles($_FILES));
 | 
        
           |  |  | 181 |     }
 | 
        
           |  |  | 182 |   | 
        
           |  |  | 183 |     private static function extractHostAndPortFromAuthority(string $authority): array
 | 
        
           |  |  | 184 |     {
 | 
        
           | 1441 | ariadna | 185 |         $uri = 'http://'.$authority;
 | 
        
           | 1 | efrain | 186 |         $parts = parse_url($uri);
 | 
        
           |  |  | 187 |         if (false === $parts) {
 | 
        
           |  |  | 188 |             return [null, null];
 | 
        
           |  |  | 189 |         }
 | 
        
           |  |  | 190 |   | 
        
           |  |  | 191 |         $host = $parts['host'] ?? null;
 | 
        
           |  |  | 192 |         $port = $parts['port'] ?? null;
 | 
        
           |  |  | 193 |   | 
        
           |  |  | 194 |         return [$host, $port];
 | 
        
           |  |  | 195 |     }
 | 
        
           |  |  | 196 |   | 
        
           |  |  | 197 |     /**
 | 
        
           |  |  | 198 |      * Get a Uri populated with values from $_SERVER.
 | 
        
           |  |  | 199 |      */
 | 
        
           |  |  | 200 |     public static function getUriFromGlobals(): UriInterface
 | 
        
           |  |  | 201 |     {
 | 
        
           |  |  | 202 |         $uri = new Uri('');
 | 
        
           |  |  | 203 |   | 
        
           |  |  | 204 |         $uri = $uri->withScheme(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http');
 | 
        
           |  |  | 205 |   | 
        
           |  |  | 206 |         $hasPort = false;
 | 
        
           |  |  | 207 |         if (isset($_SERVER['HTTP_HOST'])) {
 | 
        
           |  |  | 208 |             [$host, $port] = self::extractHostAndPortFromAuthority($_SERVER['HTTP_HOST']);
 | 
        
           |  |  | 209 |             if ($host !== null) {
 | 
        
           |  |  | 210 |                 $uri = $uri->withHost($host);
 | 
        
           |  |  | 211 |             }
 | 
        
           |  |  | 212 |   | 
        
           |  |  | 213 |             if ($port !== null) {
 | 
        
           |  |  | 214 |                 $hasPort = true;
 | 
        
           |  |  | 215 |                 $uri = $uri->withPort($port);
 | 
        
           |  |  | 216 |             }
 | 
        
           |  |  | 217 |         } elseif (isset($_SERVER['SERVER_NAME'])) {
 | 
        
           |  |  | 218 |             $uri = $uri->withHost($_SERVER['SERVER_NAME']);
 | 
        
           |  |  | 219 |         } elseif (isset($_SERVER['SERVER_ADDR'])) {
 | 
        
           |  |  | 220 |             $uri = $uri->withHost($_SERVER['SERVER_ADDR']);
 | 
        
           |  |  | 221 |         }
 | 
        
           |  |  | 222 |   | 
        
           |  |  | 223 |         if (!$hasPort && isset($_SERVER['SERVER_PORT'])) {
 | 
        
           |  |  | 224 |             $uri = $uri->withPort($_SERVER['SERVER_PORT']);
 | 
        
           |  |  | 225 |         }
 | 
        
           |  |  | 226 |   | 
        
           |  |  | 227 |         $hasQuery = false;
 | 
        
           |  |  | 228 |         if (isset($_SERVER['REQUEST_URI'])) {
 | 
        
           |  |  | 229 |             $requestUriParts = explode('?', $_SERVER['REQUEST_URI'], 2);
 | 
        
           |  |  | 230 |             $uri = $uri->withPath($requestUriParts[0]);
 | 
        
           |  |  | 231 |             if (isset($requestUriParts[1])) {
 | 
        
           |  |  | 232 |                 $hasQuery = true;
 | 
        
           |  |  | 233 |                 $uri = $uri->withQuery($requestUriParts[1]);
 | 
        
           |  |  | 234 |             }
 | 
        
           |  |  | 235 |         }
 | 
        
           |  |  | 236 |   | 
        
           |  |  | 237 |         if (!$hasQuery && isset($_SERVER['QUERY_STRING'])) {
 | 
        
           |  |  | 238 |             $uri = $uri->withQuery($_SERVER['QUERY_STRING']);
 | 
        
           |  |  | 239 |         }
 | 
        
           |  |  | 240 |   | 
        
           |  |  | 241 |         return $uri;
 | 
        
           |  |  | 242 |     }
 | 
        
           |  |  | 243 |   | 
        
           |  |  | 244 |     public function getServerParams(): array
 | 
        
           |  |  | 245 |     {
 | 
        
           |  |  | 246 |         return $this->serverParams;
 | 
        
           |  |  | 247 |     }
 | 
        
           |  |  | 248 |   | 
        
           |  |  | 249 |     public function getUploadedFiles(): array
 | 
        
           |  |  | 250 |     {
 | 
        
           |  |  | 251 |         return $this->uploadedFiles;
 | 
        
           |  |  | 252 |     }
 | 
        
           |  |  | 253 |   | 
        
           |  |  | 254 |     public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface
 | 
        
           |  |  | 255 |     {
 | 
        
           |  |  | 256 |         $new = clone $this;
 | 
        
           |  |  | 257 |         $new->uploadedFiles = $uploadedFiles;
 | 
        
           |  |  | 258 |   | 
        
           |  |  | 259 |         return $new;
 | 
        
           |  |  | 260 |     }
 | 
        
           |  |  | 261 |   | 
        
           |  |  | 262 |     public function getCookieParams(): array
 | 
        
           |  |  | 263 |     {
 | 
        
           |  |  | 264 |         return $this->cookieParams;
 | 
        
           |  |  | 265 |     }
 | 
        
           |  |  | 266 |   | 
        
           |  |  | 267 |     public function withCookieParams(array $cookies): ServerRequestInterface
 | 
        
           |  |  | 268 |     {
 | 
        
           |  |  | 269 |         $new = clone $this;
 | 
        
           |  |  | 270 |         $new->cookieParams = $cookies;
 | 
        
           |  |  | 271 |   | 
        
           |  |  | 272 |         return $new;
 | 
        
           |  |  | 273 |     }
 | 
        
           |  |  | 274 |   | 
        
           |  |  | 275 |     public function getQueryParams(): array
 | 
        
           |  |  | 276 |     {
 | 
        
           |  |  | 277 |         return $this->queryParams;
 | 
        
           |  |  | 278 |     }
 | 
        
           |  |  | 279 |   | 
        
           |  |  | 280 |     public function withQueryParams(array $query): ServerRequestInterface
 | 
        
           |  |  | 281 |     {
 | 
        
           |  |  | 282 |         $new = clone $this;
 | 
        
           |  |  | 283 |         $new->queryParams = $query;
 | 
        
           |  |  | 284 |   | 
        
           |  |  | 285 |         return $new;
 | 
        
           |  |  | 286 |     }
 | 
        
           |  |  | 287 |   | 
        
           |  |  | 288 |     /**
 | 
        
           |  |  | 289 |      * @return array|object|null
 | 
        
           |  |  | 290 |      */
 | 
        
           |  |  | 291 |     public function getParsedBody()
 | 
        
           |  |  | 292 |     {
 | 
        
           |  |  | 293 |         return $this->parsedBody;
 | 
        
           |  |  | 294 |     }
 | 
        
           |  |  | 295 |   | 
        
           |  |  | 296 |     public function withParsedBody($data): ServerRequestInterface
 | 
        
           |  |  | 297 |     {
 | 
        
           |  |  | 298 |         $new = clone $this;
 | 
        
           |  |  | 299 |         $new->parsedBody = $data;
 | 
        
           |  |  | 300 |   | 
        
           |  |  | 301 |         return $new;
 | 
        
           |  |  | 302 |     }
 | 
        
           |  |  | 303 |   | 
        
           |  |  | 304 |     public function getAttributes(): array
 | 
        
           |  |  | 305 |     {
 | 
        
           |  |  | 306 |         return $this->attributes;
 | 
        
           |  |  | 307 |     }
 | 
        
           |  |  | 308 |   | 
        
           |  |  | 309 |     /**
 | 
        
           |  |  | 310 |      * @return mixed
 | 
        
           |  |  | 311 |      */
 | 
        
           |  |  | 312 |     public function getAttribute($attribute, $default = null)
 | 
        
           |  |  | 313 |     {
 | 
        
           |  |  | 314 |         if (false === array_key_exists($attribute, $this->attributes)) {
 | 
        
           |  |  | 315 |             return $default;
 | 
        
           |  |  | 316 |         }
 | 
        
           |  |  | 317 |   | 
        
           |  |  | 318 |         return $this->attributes[$attribute];
 | 
        
           |  |  | 319 |     }
 | 
        
           |  |  | 320 |   | 
        
           |  |  | 321 |     public function withAttribute($attribute, $value): ServerRequestInterface
 | 
        
           |  |  | 322 |     {
 | 
        
           |  |  | 323 |         $new = clone $this;
 | 
        
           |  |  | 324 |         $new->attributes[$attribute] = $value;
 | 
        
           |  |  | 325 |   | 
        
           |  |  | 326 |         return $new;
 | 
        
           |  |  | 327 |     }
 | 
        
           |  |  | 328 |   | 
        
           |  |  | 329 |     public function withoutAttribute($attribute): ServerRequestInterface
 | 
        
           |  |  | 330 |     {
 | 
        
           |  |  | 331 |         if (false === array_key_exists($attribute, $this->attributes)) {
 | 
        
           |  |  | 332 |             return $this;
 | 
        
           |  |  | 333 |         }
 | 
        
           |  |  | 334 |   | 
        
           |  |  | 335 |         $new = clone $this;
 | 
        
           |  |  | 336 |         unset($new->attributes[$attribute]);
 | 
        
           |  |  | 337 |   | 
        
           |  |  | 338 |         return $new;
 | 
        
           |  |  | 339 |     }
 | 
        
           |  |  | 340 | }
 |