384 lines
8.4 KiB
PHP
384 lines
8.4 KiB
PHP
|
<?php
|
||
|
|
||
|
/**
|
||
|
* Slim Framework (https://slimframework.com)
|
||
|
*
|
||
|
* @license https://github.com/slimphp/Slim-Psr7/blob/master/LICENSE.md (MIT License)
|
||
|
*/
|
||
|
|
||
|
declare(strict_types=1);
|
||
|
|
||
|
namespace Slim\Psr7;
|
||
|
|
||
|
use InvalidArgumentException;
|
||
|
use Psr\Http\Message\ServerRequestInterface;
|
||
|
use Psr\Http\Message\StreamInterface;
|
||
|
use Psr\Http\Message\UploadedFileInterface;
|
||
|
use Psr\Http\Message\UriInterface;
|
||
|
use Slim\Psr7\Interfaces\HeadersInterface;
|
||
|
|
||
|
use function get_class;
|
||
|
use function gettype;
|
||
|
use function is_array;
|
||
|
use function is_null;
|
||
|
use function is_object;
|
||
|
use function is_string;
|
||
|
use function ltrim;
|
||
|
use function parse_str;
|
||
|
use function preg_match;
|
||
|
use function sprintf;
|
||
|
use function str_replace;
|
||
|
|
||
|
class Request extends Message implements ServerRequestInterface
|
||
|
{
|
||
|
/**
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $method;
|
||
|
|
||
|
/**
|
||
|
* @var UriInterface
|
||
|
*/
|
||
|
protected $uri;
|
||
|
|
||
|
/**
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $requestTarget;
|
||
|
|
||
|
/**
|
||
|
* @var ?array
|
||
|
*/
|
||
|
protected $queryParams;
|
||
|
|
||
|
/**
|
||
|
* @var array
|
||
|
*/
|
||
|
protected $cookies;
|
||
|
|
||
|
/**
|
||
|
* @var array
|
||
|
*/
|
||
|
protected $serverParams;
|
||
|
|
||
|
/**
|
||
|
* @var array
|
||
|
*/
|
||
|
protected $attributes;
|
||
|
|
||
|
/**
|
||
|
* @var null|array|object
|
||
|
*/
|
||
|
protected $parsedBody;
|
||
|
|
||
|
/**
|
||
|
* @var UploadedFileInterface[]
|
||
|
*/
|
||
|
protected $uploadedFiles;
|
||
|
|
||
|
/**
|
||
|
* @param string $method The request method
|
||
|
* @param UriInterface $uri The request URI object
|
||
|
* @param HeadersInterface $headers The request headers collection
|
||
|
* @param array $cookies The request cookies collection
|
||
|
* @param array $serverParams The server environment variables
|
||
|
* @param StreamInterface $body The request body object
|
||
|
* @param array $uploadedFiles The request uploadedFiles collection
|
||
|
* @throws InvalidArgumentException on invalid HTTP method
|
||
|
*/
|
||
|
public function __construct(
|
||
|
$method,
|
||
|
UriInterface $uri,
|
||
|
HeadersInterface $headers,
|
||
|
array $cookies,
|
||
|
array $serverParams,
|
||
|
StreamInterface $body,
|
||
|
array $uploadedFiles = []
|
||
|
) {
|
||
|
$this->method = $this->filterMethod($method);
|
||
|
$this->uri = $uri;
|
||
|
$this->headers = $headers;
|
||
|
$this->cookies = $cookies;
|
||
|
$this->serverParams = $serverParams;
|
||
|
$this->attributes = [];
|
||
|
$this->body = $body;
|
||
|
$this->uploadedFiles = $uploadedFiles;
|
||
|
|
||
|
if (isset($serverParams['SERVER_PROTOCOL'])) {
|
||
|
$this->protocolVersion = str_replace('HTTP/', '', $serverParams['SERVER_PROTOCOL']);
|
||
|
}
|
||
|
|
||
|
if (!$this->headers->hasHeader('Host') || $this->uri->getHost() !== '') {
|
||
|
$this->headers->setHeader('Host', $this->uri->getHost());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* This method is applied to the cloned object after PHP performs an initial shallow-copy.
|
||
|
* This method completes a deep-copy by creating new objects for the cloned object's internal reference pointers.
|
||
|
*/
|
||
|
public function __clone()
|
||
|
{
|
||
|
$this->headers = clone $this->headers;
|
||
|
$this->body = clone $this->body;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritdoc}
|
||
|
*/
|
||
|
public function getMethod(): string
|
||
|
{
|
||
|
return $this->method;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritdoc}
|
||
|
*/
|
||
|
public function withMethod($method)
|
||
|
{
|
||
|
$method = $this->filterMethod($method);
|
||
|
$clone = clone $this;
|
||
|
$clone->method = $method;
|
||
|
|
||
|
return $clone;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Validate the HTTP method
|
||
|
*
|
||
|
* @param string $method
|
||
|
*
|
||
|
* @return string
|
||
|
*
|
||
|
* @throws InvalidArgumentException on invalid HTTP method.
|
||
|
*/
|
||
|
protected function filterMethod($method): string
|
||
|
{
|
||
|
/** @var mixed $method */
|
||
|
if (!is_string($method)) {
|
||
|
throw new InvalidArgumentException(sprintf(
|
||
|
'Unsupported HTTP method; must be a string, received %s',
|
||
|
(is_object($method) ? get_class($method) : gettype($method))
|
||
|
));
|
||
|
}
|
||
|
|
||
|
if (preg_match("/^[!#$%&'*+.^_`|~0-9a-z-]+$/i", $method) !== 1) {
|
||
|
throw new InvalidArgumentException(sprintf(
|
||
|
'Unsupported HTTP method "%s" provided',
|
||
|
$method
|
||
|
));
|
||
|
}
|
||
|
|
||
|
return $method;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritdoc}
|
||
|
*/
|
||
|
public function getRequestTarget(): string
|
||
|
{
|
||
|
if ($this->requestTarget) {
|
||
|
return $this->requestTarget;
|
||
|
}
|
||
|
|
||
|
if ($this->uri === null) {
|
||
|
return '/';
|
||
|
}
|
||
|
|
||
|
$path = $this->uri->getPath();
|
||
|
$path = '/' . ltrim($path, '/');
|
||
|
|
||
|
$query = $this->uri->getQuery();
|
||
|
if ($query) {
|
||
|
$path .= '?' . $query;
|
||
|
}
|
||
|
|
||
|
return $path;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritdoc}
|
||
|
*/
|
||
|
public function withRequestTarget($requestTarget)
|
||
|
{
|
||
|
if (preg_match('#\s#', $requestTarget)) {
|
||
|
throw new InvalidArgumentException(
|
||
|
'Invalid request target provided; must be a string and cannot contain whitespace'
|
||
|
);
|
||
|
}
|
||
|
|
||
|
$clone = clone $this;
|
||
|
$clone->requestTarget = $requestTarget;
|
||
|
|
||
|
return $clone;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritdoc}
|
||
|
*/
|
||
|
public function getUri(): UriInterface
|
||
|
{
|
||
|
return $this->uri;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritdoc}
|
||
|
*/
|
||
|
public function withUri(UriInterface $uri, $preserveHost = false)
|
||
|
{
|
||
|
$clone = clone $this;
|
||
|
$clone->uri = $uri;
|
||
|
|
||
|
if (!$preserveHost && $uri->getHost() !== '') {
|
||
|
$clone->headers->setHeader('Host', $uri->getHost());
|
||
|
return $clone;
|
||
|
}
|
||
|
|
||
|
if (($uri->getHost() !== '' && !$this->hasHeader('Host') || $this->getHeaderLine('Host') === '')) {
|
||
|
$clone->headers->setHeader('Host', $uri->getHost());
|
||
|
return $clone;
|
||
|
}
|
||
|
|
||
|
return $clone;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritdoc}
|
||
|
*/
|
||
|
public function getCookieParams(): array
|
||
|
{
|
||
|
return $this->cookies;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritdoc}
|
||
|
*/
|
||
|
public function withCookieParams(array $cookies)
|
||
|
{
|
||
|
$clone = clone $this;
|
||
|
$clone->cookies = $cookies;
|
||
|
|
||
|
return $clone;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritdoc}
|
||
|
*/
|
||
|
public function getQueryParams(): array
|
||
|
{
|
||
|
if (is_array($this->queryParams)) {
|
||
|
return $this->queryParams;
|
||
|
}
|
||
|
|
||
|
if ($this->uri === null) {
|
||
|
return [];
|
||
|
}
|
||
|
|
||
|
parse_str($this->uri->getQuery(), $this->queryParams); // <-- URL decodes data
|
||
|
assert(is_array($this->queryParams));
|
||
|
|
||
|
return $this->queryParams;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritdoc}
|
||
|
*/
|
||
|
public function withQueryParams(array $query)
|
||
|
{
|
||
|
$clone = clone $this;
|
||
|
$clone->queryParams = $query;
|
||
|
|
||
|
return $clone;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritdoc}
|
||
|
*/
|
||
|
public function getUploadedFiles(): array
|
||
|
{
|
||
|
return $this->uploadedFiles;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritdoc}
|
||
|
*/
|
||
|
public function withUploadedFiles(array $uploadedFiles)
|
||
|
{
|
||
|
$clone = clone $this;
|
||
|
$clone->uploadedFiles = $uploadedFiles;
|
||
|
|
||
|
return $clone;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritdoc}
|
||
|
*/
|
||
|
public function getServerParams(): array
|
||
|
{
|
||
|
return $this->serverParams;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritdoc}
|
||
|
*/
|
||
|
public function getAttributes(): array
|
||
|
{
|
||
|
return $this->attributes;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritdoc}
|
||
|
*/
|
||
|
public function getAttribute($name, $default = null)
|
||
|
{
|
||
|
return isset($this->attributes[$name]) ? $this->attributes[$name] : $default;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritdoc}
|
||
|
*/
|
||
|
public function withAttribute($name, $value)
|
||
|
{
|
||
|
$clone = clone $this;
|
||
|
$clone->attributes[$name] = $value;
|
||
|
|
||
|
return $clone;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritdoc}
|
||
|
*/
|
||
|
public function withoutAttribute($name)
|
||
|
{
|
||
|
$clone = clone $this;
|
||
|
|
||
|
unset($clone->attributes[$name]);
|
||
|
|
||
|
return $clone;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritdoc}
|
||
|
*/
|
||
|
public function getParsedBody()
|
||
|
{
|
||
|
return $this->parsedBody;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritdoc}
|
||
|
*/
|
||
|
public function withParsedBody($data)
|
||
|
{
|
||
|
/** @var mixed $data */
|
||
|
if (!is_null($data) && !is_object($data) && !is_array($data)) {
|
||
|
throw new InvalidArgumentException('Parsed body value must be an array, an object, or null');
|
||
|
}
|
||
|
|
||
|
$clone = clone $this;
|
||
|
$clone->parsedBody = $data;
|
||
|
|
||
|
return $clone;
|
||
|
}
|
||
|
}
|