| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344 | <?phpdeclare(strict_types=1);namespace GuzzleHttp\Psr7;use InvalidArgumentException;use Psr\Http\Message\ServerRequestInterface;use Psr\Http\Message\StreamInterface;use Psr\Http\Message\UploadedFileInterface;use Psr\Http\Message\UriInterface;/** * Server-side HTTP request * * Extends the Request definition to add methods for accessing incoming data, * specifically server parameters, cookies, matched path parameters, query * string arguments, body parameters, and upload file information. * * "Attributes" are discovered via decomposing the request (and usually * specifically the URI path), and typically will be injected by the application. * * Requests are considered immutable; all methods that might change state are * implemented such that they retain the internal state of the current * message and return a new instance that contains the changed state. */class ServerRequest extends Request implements ServerRequestInterface{    /**     * @var array     */    private $attributes = [];    /**     * @var array     */    private $cookieParams = [];    /**     * @var array|object|null     */    private $parsedBody;    /**     * @var array     */    private $queryParams = [];    /**     * @var array     */    private $serverParams;    /**     * @var array     */    private $uploadedFiles = [];    /**     * @param string                               $method       HTTP method     * @param string|UriInterface                  $uri          URI     * @param array<string, string|string[]>       $headers      Request headers     * @param string|resource|StreamInterface|null $body         Request body     * @param string                               $version      Protocol version     * @param array                                $serverParams Typically the $_SERVER superglobal     */    public function __construct(        string $method,        $uri,        array $headers = [],        $body = null,        string $version = '1.1',        array $serverParams = []    ) {        $this->serverParams = $serverParams;        parent::__construct($method, $uri, $headers, $body, $version);    }    /**     * Return an UploadedFile instance array.     *     * @param array $files A array which respect $_FILES structure     *     * @throws InvalidArgumentException for unrecognized values     */    public static function normalizeFiles(array $files): array    {        $normalized = [];        foreach ($files as $key => $value) {            if ($value instanceof UploadedFileInterface) {                $normalized[$key] = $value;            } elseif (is_array($value) && isset($value['tmp_name'])) {                $normalized[$key] = self::createUploadedFileFromSpec($value);            } elseif (is_array($value)) {                $normalized[$key] = self::normalizeFiles($value);                continue;            } else {                throw new InvalidArgumentException('Invalid value in files specification');            }        }        return $normalized;    }    /**     * Create and return an UploadedFile instance from a $_FILES specification.     *     * If the specification represents an array of values, this method will     * delegate to normalizeNestedFileSpec() and return that return value.     *     * @param array $value $_FILES struct     *     * @return UploadedFileInterface|UploadedFileInterface[]     */    private static function createUploadedFileFromSpec(array $value)    {        if (is_array($value['tmp_name'])) {            return self::normalizeNestedFileSpec($value);        }        return new UploadedFile(            $value['tmp_name'],            (int) $value['size'],            (int) $value['error'],            $value['name'],            $value['type']        );    }    /**     * Normalize an array of file specifications.     *     * Loops through all nested files and returns a normalized array of     * UploadedFileInterface instances.     *     * @return UploadedFileInterface[]     */    private static function normalizeNestedFileSpec(array $files = []): array    {        $normalizedFiles = [];        foreach (array_keys($files['tmp_name']) as $key) {            $spec = [                'tmp_name' => $files['tmp_name'][$key],                'size'     => $files['size'][$key],                'error'    => $files['error'][$key],                'name'     => $files['name'][$key],                'type'     => $files['type'][$key],            ];            $normalizedFiles[$key] = self::createUploadedFileFromSpec($spec);        }        return $normalizedFiles;    }    /**     * Return a ServerRequest populated with superglobals:     * $_GET     * $_POST     * $_COOKIE     * $_FILES     * $_SERVER     */    public static function fromGlobals(): ServerRequestInterface    {        $method = $_SERVER['REQUEST_METHOD'] ?? 'GET';        $headers = getallheaders();        $uri = self::getUriFromGlobals();        $body = new CachingStream(new LazyOpenStream('php://input', 'r+'));        $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']) : '1.1';        $serverRequest = new ServerRequest($method, $uri, $headers, $body, $protocol, $_SERVER);        return $serverRequest            ->withCookieParams($_COOKIE)            ->withQueryParams($_GET)            ->withParsedBody($_POST)            ->withUploadedFiles(self::normalizeFiles($_FILES));    }    private static function extractHostAndPortFromAuthority(string $authority): array    {        $uri = 'http://' . $authority;        $parts = parse_url($uri);        if (false === $parts) {            return [null, null];        }        $host = $parts['host'] ?? null;        $port = $parts['port'] ?? null;        return [$host, $port];    }    /**     * Get a Uri populated with values from $_SERVER.     */    public static function getUriFromGlobals(): UriInterface    {        $uri = new Uri('');        $uri = $uri->withScheme(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http');        $hasPort = false;        if (isset($_SERVER['HTTP_HOST'])) {            [$host, $port] = self::extractHostAndPortFromAuthority($_SERVER['HTTP_HOST']);            if ($host !== null) {                $uri = $uri->withHost($host);            }            if ($port !== null) {                $hasPort = true;                $uri = $uri->withPort($port);            }        } elseif (isset($_SERVER['SERVER_NAME'])) {            $uri = $uri->withHost($_SERVER['SERVER_NAME']);        } elseif (isset($_SERVER['SERVER_ADDR'])) {            $uri = $uri->withHost($_SERVER['SERVER_ADDR']);        }        if (!$hasPort && isset($_SERVER['SERVER_PORT'])) {            $uri = $uri->withPort($_SERVER['SERVER_PORT']);        }        $hasQuery = false;        if (isset($_SERVER['REQUEST_URI'])) {            $requestUriParts = explode('?', $_SERVER['REQUEST_URI'], 2);            $uri = $uri->withPath($requestUriParts[0]);            if (isset($requestUriParts[1])) {                $hasQuery = true;                $uri = $uri->withQuery($requestUriParts[1]);            }        }        if (!$hasQuery && isset($_SERVER['QUERY_STRING'])) {            $uri = $uri->withQuery($_SERVER['QUERY_STRING']);        }        return $uri;    }    public function getServerParams(): array    {        return $this->serverParams;    }    public function getUploadedFiles(): array    {        return $this->uploadedFiles;    }    public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface    {        $new = clone $this;        $new->uploadedFiles = $uploadedFiles;        return $new;    }    public function getCookieParams(): array    {        return $this->cookieParams;    }    public function withCookieParams(array $cookies): ServerRequestInterface    {        $new = clone $this;        $new->cookieParams = $cookies;        return $new;    }    public function getQueryParams(): array    {        return $this->queryParams;    }    public function withQueryParams(array $query): ServerRequestInterface    {        $new = clone $this;        $new->queryParams = $query;        return $new;    }    /**     * {@inheritdoc}     *     * @return array|object|null     */    public function getParsedBody()    {        return $this->parsedBody;    }    public function withParsedBody($data): ServerRequestInterface    {        $new = clone $this;        $new->parsedBody = $data;        return $new;    }    public function getAttributes(): array    {        return $this->attributes;    }    /**     * {@inheritdoc}     *     * @return mixed     */    public function getAttribute($attribute, $default = null)    {        if (false === array_key_exists($attribute, $this->attributes)) {            return $default;        }        return $this->attributes[$attribute];    }    public function withAttribute($attribute, $value): ServerRequestInterface    {        $new = clone $this;        $new->attributes[$attribute] = $value;        return $new;    }    public function withoutAttribute($attribute): ServerRequestInterface    {        if (false === array_key_exists($attribute, $this->attributes)) {            return $this;        }        $new = clone $this;        unset($new->attributes[$attribute]);        return $new;    }}
 |