Update website

This commit is contained in:
Guilhem Lavaux 2024-11-19 09:35:33 +01:00
parent bb4b0f9be8
commit 011b183e28
4263 changed files with 3014 additions and 720369 deletions

View file

@ -1,22 +0,0 @@
The MIT License (MIT)
Copyright (c) 2015 Slim Framework
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,75 +0,0 @@
{
"name": "slim/psr7",
"type": "library",
"description": "Strict PSR-7 implementation",
"keywords": ["psr7","psr-7","http"],
"homepage": "https://www.slimframework.com",
"license": "MIT",
"authors": [
{
"name": "Josh Lockhart",
"email": "hello@joshlockhart.com",
"homepage": "http://joshlockhart.com"
},
{
"name": "Andrew Smith",
"email": "a.smith@silentworks.co.uk",
"homepage": "http://silentworks.co.uk"
},
{
"name": "Rob Allen",
"email": "rob@akrabat.com",
"homepage": "http://akrabat.com"
},
{
"name": "Pierre Berube",
"email": "pierre@lgse.com",
"homepage": "http://www.lgse.com"
}
],
"require": {
"php": "^7.2 || ^8.0",
"fig/http-message-util": "^1.1.5",
"psr/http-factory": "^1.0",
"psr/http-message": "^1.0",
"ralouphie/getallheaders": "^3",
"symfony/polyfill-php80": "^1.22"
},
"require-dev": {
"ext-json": "*",
"adriansuter/php-autoload-override": "^1.2",
"http-interop/http-factory-tests": "^0.9.0",
"php-http/psr7-integration-tests": "dev-master",
"phpstan/phpstan": "^0.12",
"phpunit/phpunit": "^8.5 || ^9.5",
"squizlabs/php_codesniffer": "^3.6",
"weirdan/prophecy-shim": "^1.0 || ^2.0.2"
},
"provide": {
"psr/http-message-implementation": "1.0",
"psr/http-factory-implementation": "1.0"
},
"autoload": {
"psr-4": {
"Slim\\Psr7\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Slim\\Tests\\Psr7\\": "tests"
}
},
"scripts": {
"test": [
"@phpunit",
"@phpcs",
"@phpstan"
],
"phpunit": "phpunit",
"phpcs": "phpcs",
"phpstan": "phpstan analyse src --memory-limit=-1"
},
"config": {
"sort-packages": true
}
}

View file

@ -1,221 +0,0 @@
<?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 function array_key_exists;
use function array_replace;
use function count;
use function explode;
use function gmdate;
use function in_array;
use function is_array;
use function is_string;
use function preg_split;
use function rtrim;
use function strtolower;
use function strtotime;
use function urldecode;
use function urlencode;
class Cookies
{
/**
* Cookies from HTTP request
*
* @var array
*/
protected $requestCookies = [];
/**
* Cookies for HTTP response
*
* @var array
*/
protected $responseCookies = [];
/**
* Default cookie properties
*
* @var array
*/
protected $defaults = [
'value' => '',
'domain' => null,
'hostonly' => null,
'path' => null,
'expires' => null,
'secure' => false,
'httponly' => false,
'samesite' => null
];
/**
* @param array $cookies
*/
public function __construct(array $cookies = [])
{
$this->requestCookies = $cookies;
}
/**
* Set default cookie properties
*
* @param array $settings
*
* @return static
*/
public function setDefaults(array $settings): self
{
$this->defaults = array_replace($this->defaults, $settings);
return $this;
}
/**
* Get cookie
*
* @param string $name
* @param string|array|null $default
* @return mixed|null
*/
public function get(string $name, $default = null)
{
return array_key_exists($name, $this->requestCookies) ? $this->requestCookies[$name] : $default;
}
/**
* Set cookie
*
* @param string $name
* @param string|array $value
* @return static
*/
public function set(string $name, $value): self
{
if (!is_array($value)) {
$value = ['value' => $value];
}
$this->responseCookies[$name] = array_replace($this->defaults, $value);
return $this;
}
/**
* Convert all response cookies into an associate array of header values
*
* @return array
*/
public function toHeaders(): array
{
$headers = [];
foreach ($this->responseCookies as $name => $properties) {
$headers[] = $this->toHeader($name, $properties);
}
return $headers;
}
/**
* Convert to `Set-Cookie` header
*
* @param string $name Cookie name
* @param array $properties Cookie properties
*
* @return string
*/
protected function toHeader(string $name, array $properties): string
{
$result = urlencode($name) . '=' . urlencode($properties['value']);
if (isset($properties['domain'])) {
$result .= '; domain=' . $properties['domain'];
}
if (isset($properties['path'])) {
$result .= '; path=' . $properties['path'];
}
if (isset($properties['expires'])) {
if (is_string($properties['expires'])) {
$timestamp = strtotime($properties['expires']);
} else {
$timestamp = (int) $properties['expires'];
}
if ($timestamp && $timestamp !== 0) {
$result .= '; expires=' . gmdate('D, d-M-Y H:i:s e', $timestamp);
}
}
if (isset($properties['secure']) && $properties['secure']) {
$result .= '; secure';
}
if (isset($properties['hostonly']) && $properties['hostonly']) {
$result .= '; HostOnly';
}
if (isset($properties['httponly']) && $properties['httponly']) {
$result .= '; HttpOnly';
}
if (isset($properties['samesite']) && in_array(strtolower($properties['samesite']), ['lax', 'strict'], true)) {
// While strtolower is needed for correct comparison, the RFC doesn't care about case
$result .= '; SameSite=' . $properties['samesite'];
}
return $result;
}
/**
* Parse cookie values from header value
*
* Returns an associative array of cookie names and values
*
* @param string|array $header
*
* @return array
*/
public static function parseHeader($header): array
{
if (is_array($header)) {
$header = isset($header[0]) ? $header[0] : '';
}
if (!is_string($header)) {
throw new InvalidArgumentException('Cannot parse Cookie data. Header value must be a string.');
}
$header = rtrim($header, "\r\n");
$pieces = preg_split('@[;]\s*@', $header);
$cookies = [];
if (is_array($pieces)) {
foreach ($pieces as $cookie) {
$cookie = explode('=', $cookie, 2);
if (count($cookie) === 2) {
$key = urldecode($cookie[0]);
$value = urldecode($cookie[1]);
if (!isset($cookies[$key])) {
$cookies[$key] = $value;
}
}
}
}
return $cookies;
}
}

View file

@ -1,55 +0,0 @@
<?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 function array_merge;
use function microtime;
use function time;
class Environment
{
/**
* @param array $data Array of custom environment keys and values
*
* @return array
*/
public static function mock(array $data = []): array
{
if (
(isset($data['HTTPS']) && $data['HTTPS'] !== 'off')
|| ((isset($data['REQUEST_SCHEME']) && $data['REQUEST_SCHEME'] === 'https'))
) {
$scheme = 'https';
$port = 443;
} else {
$scheme = 'http';
$port = 80;
}
return array_merge([
'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
'HTTP_ACCEPT_LANGUAGE' => 'en-US,en;q=0.8',
'HTTP_USER_AGENT' => 'Slim Framework',
'QUERY_STRING' => '',
'REMOTE_ADDR' => '127.0.0.1',
'REQUEST_METHOD' => 'GET',
'REQUEST_SCHEME' => $scheme,
'REQUEST_TIME' => time(),
'REQUEST_TIME_FLOAT' => microtime(true),
'REQUEST_URI' => '',
'SCRIPT_NAME' => '',
'SERVER_NAME' => 'localhost',
'SERVER_PORT' => $port,
'SERVER_PROTOCOL' => 'HTTP/1.1',
], $data);
}
}

View file

@ -1,73 +0,0 @@
<?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\Factory;
use InvalidArgumentException;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UriFactoryInterface;
use Psr\Http\Message\UriInterface;
use Slim\Psr7\Headers;
use Slim\Psr7\Request;
use function is_string;
class RequestFactory implements RequestFactoryInterface
{
/**
* @var StreamFactoryInterface|StreamFactory
*/
protected $streamFactory;
/**
* @var UriFactoryInterface|UriFactory
*/
protected $uriFactory;
/**
* @param StreamFactoryInterface|null $streamFactory
* @param UriFactoryInterface|null $uriFactory
*/
public function __construct(?StreamFactoryInterface $streamFactory = null, ?UriFactoryInterface $uriFactory = null)
{
if (!isset($streamFactory)) {
$streamFactory = new StreamFactory();
}
if (!isset($uriFactory)) {
$uriFactory = new UriFactory();
}
$this->streamFactory = $streamFactory;
$this->uriFactory = $uriFactory;
}
/**
* {@inheritdoc}
*/
public function createRequest(string $method, $uri): RequestInterface
{
if (is_string($uri)) {
$uri = $this->uriFactory->createUri($uri);
}
if (!$uri instanceof UriInterface) {
throw new InvalidArgumentException(
'Parameter 2 of RequestFactory::createRequest() must be a string or a compatible UriInterface.'
);
}
$body = $this->streamFactory->createStream();
return new Request($method, $uri, new Headers(), [], [], $body);
}
}

View file

@ -1,35 +0,0 @@
<?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\Factory;
use Fig\Http\Message\StatusCodeInterface;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Slim\Psr7\Response;
class ResponseFactory implements ResponseFactoryInterface
{
/**
* {@inheritdoc}
*/
public function createResponse(
int $code = StatusCodeInterface::STATUS_OK,
string $reasonPhrase = ''
): ResponseInterface {
$res = new Response($code);
if ($reasonPhrase !== '') {
$res = $res->withStatus($code, $reasonPhrase);
}
return $res;
}
}

View file

@ -1,124 +0,0 @@
<?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\Factory;
use InvalidArgumentException;
use Psr\Http\Message\ServerRequestFactoryInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UriFactoryInterface;
use Psr\Http\Message\UriInterface;
use Slim\Psr7\Cookies;
use Slim\Psr7\Headers;
use Slim\Psr7\Request;
use Slim\Psr7\Stream;
use Slim\Psr7\UploadedFile;
use function current;
use function explode;
use function fopen;
use function in_array;
use function is_string;
class ServerRequestFactory implements ServerRequestFactoryInterface
{
/**
* @var StreamFactoryInterface|StreamFactory
*/
protected $streamFactory;
/**
* @var UriFactoryInterface|UriFactory
*/
protected $uriFactory;
/**
* @param StreamFactoryInterface|null $streamFactory
* @param UriFactoryInterface|null $uriFactory
*/
public function __construct(?StreamFactoryInterface $streamFactory = null, ?UriFactoryInterface $uriFactory = null)
{
if (!isset($streamFactory)) {
$streamFactory = new StreamFactory();
}
if (!isset($uriFactory)) {
$uriFactory = new UriFactory();
}
$this->streamFactory = $streamFactory;
$this->uriFactory = $uriFactory;
}
/**
* {@inheritdoc}
*/
public function createServerRequest(string $method, $uri, array $serverParams = []): ServerRequestInterface
{
if (is_string($uri)) {
$uri = $this->uriFactory->createUri($uri);
}
if (!$uri instanceof UriInterface) {
throw new InvalidArgumentException('URI must either be string or instance of ' . UriInterface::class);
}
$body = $this->streamFactory->createStream();
$headers = new Headers();
$cookies = [];
if (!empty($serverParams)) {
$headers = Headers::createFromGlobals();
$cookies = Cookies::parseHeader($headers->getHeader('Cookie', []));
}
return new Request($method, $uri, $headers, $cookies, $serverParams, $body);
}
/**
* Create new ServerRequest from environment.
*
* @internal This method is not part of PSR-17
*
* @return Request
*/
public static function createFromGlobals(): Request
{
$method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET';
$uri = (new UriFactory())->createFromGlobals($_SERVER);
$headers = Headers::createFromGlobals();
$cookies = Cookies::parseHeader($headers->getHeader('Cookie', []));
// Cache the php://input stream as it cannot be re-read
$cacheResource = fopen('php://temp', 'wb+');
$cache = $cacheResource ? new Stream($cacheResource) : null;
$body = (new StreamFactory())->createStreamFromFile('php://input', 'r', $cache);
$uploadedFiles = UploadedFile::createFromGlobals($_SERVER);
$request = new Request($method, $uri, $headers, $cookies, $_SERVER, $body, $uploadedFiles);
$contentTypes = $request->getHeader('Content-Type') ?? [];
$parsedContentType = '';
foreach ($contentTypes as $contentType) {
$fragments = explode(';', $contentType);
$parsedContentType = current($fragments);
}
$contentTypesWithParsedBodies = ['application/x-www-form-urlencoded', 'multipart/form-data'];
if ($method === 'POST' && in_array($parsedContentType, $contentTypesWithParsedBodies)) {
return $request->withParsedBody($_POST);
}
return $request;
}
}

View file

@ -1,95 +0,0 @@
<?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\Factory;
use InvalidArgumentException;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\StreamInterface;
use RuntimeException;
use Slim\Psr7\Stream;
use ValueError;
use function fopen;
use function fwrite;
use function is_resource;
use function restore_error_handler;
use function rewind;
use function set_error_handler;
class StreamFactory implements StreamFactoryInterface
{
/**
* {@inheritdoc}
*
* @throws RuntimeException
*/
public function createStream(string $content = ''): StreamInterface
{
$resource = fopen('php://temp', 'rw+');
if (!is_resource($resource)) {
throw new RuntimeException('StreamFactory::createStream() could not open temporary file stream.');
}
fwrite($resource, $content);
rewind($resource);
return $this->createStreamFromResource($resource);
}
/**
* {@inheritdoc}
*/
public function createStreamFromFile(
string $filename,
string $mode = 'r',
StreamInterface $cache = null
): StreamInterface {
set_error_handler(
static function (int $errno, string $errstr) use ($filename, $mode): void {
throw new RuntimeException(
"Unable to open $filename using mode $mode: $errstr",
$errno
);
}
);
try {
$resource = fopen($filename, $mode);
} catch (ValueError $exception) {
throw new RuntimeException("Unable to open $filename using mode $mode: " . $exception->getMessage());
} finally {
restore_error_handler();
}
if (!is_resource($resource)) {
throw new RuntimeException(
"StreamFactory::createStreamFromFile() could not create resource from file `$filename`"
);
}
return new Stream($resource, $cache);
}
/**
* {@inheritdoc}
*/
public function createStreamFromResource($resource, StreamInterface $cache = null): StreamInterface
{
if (!is_resource($resource)) {
throw new InvalidArgumentException(
'Parameter 1 of StreamFactory::createStreamFromResource() must be a resource.'
);
}
return new Stream($resource, $cache);
}
}

View file

@ -1,47 +0,0 @@
<?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\Factory;
use InvalidArgumentException;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UploadedFileFactoryInterface;
use Psr\Http\Message\UploadedFileInterface;
use Slim\Psr7\UploadedFile;
use function is_string;
use const UPLOAD_ERR_OK;
class UploadedFileFactory implements UploadedFileFactoryInterface
{
/**
* {@inheritdoc}
*/
public function createUploadedFile(
StreamInterface $stream,
?int $size = null,
int $error = UPLOAD_ERR_OK,
?string $clientFilename = null,
?string $clientMediaType = null
): UploadedFileInterface {
$file = $stream->getMetadata('uri');
if (!is_string($file) || !$stream->isReadable()) {
throw new InvalidArgumentException('File is not readable.');
}
if ($size === null) {
$size = $stream->getSize();
}
return new UploadedFile($stream, $clientFilename, $clientMediaType, $size, $error);
}
}

View file

@ -1,116 +0,0 @@
<?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\Factory;
use InvalidArgumentException;
use Psr\Http\Message\UriFactoryInterface;
use Psr\Http\Message\UriInterface;
use Slim\Psr7\Uri;
use function count;
use function explode;
use function parse_url;
use function preg_match;
use function strpos;
use function strstr;
use function substr;
use const PHP_URL_QUERY;
class UriFactory implements UriFactoryInterface
{
/**
* {@inheritdoc}
*/
public function createUri(string $uri = ''): UriInterface
{
$parts = parse_url($uri);
if ($parts === false) {
throw new InvalidArgumentException('URI cannot be parsed');
}
$scheme = $parts['scheme'] ?? '';
$user = $parts['user'] ?? '';
$pass = $parts['pass'] ?? '';
$host = $parts['host'] ?? '';
$port = $parts['port'] ?? null;
$path = $parts['path'] ?? '';
$query = $parts['query'] ?? '';
$fragment = $parts['fragment'] ?? '';
return new Uri($scheme, $host, $port, $path, $query, $fragment, $user, $pass);
}
/**
* Create new Uri from environment.
*
* @internal This method is not part of PSR-17
*
* @param array $globals The global server variables.
*
* @return Uri
*/
public function createFromGlobals(array $globals): Uri
{
// Scheme
$https = isset($globals['HTTPS']) ? $globals['HTTPS'] : false;
$scheme = !$https || $https === 'off' ? 'http' : 'https';
// Authority: Username and password
$username = isset($globals['PHP_AUTH_USER']) ? $globals['PHP_AUTH_USER'] : '';
$password = isset($globals['PHP_AUTH_PW']) ? $globals['PHP_AUTH_PW'] : '';
// Authority: Host
$host = '';
if (isset($globals['HTTP_HOST'])) {
$host = $globals['HTTP_HOST'];
} elseif (isset($globals['SERVER_NAME'])) {
$host = $globals['SERVER_NAME'];
}
// Authority: Port
$port = !empty($globals['SERVER_PORT']) ? (int)$globals['SERVER_PORT'] : ($scheme === 'https' ? 443 : 80);
if (preg_match('/^(\[[a-fA-F0-9:.]+])(:\d+)?\z/', $host, $matches)) {
$host = $matches[1];
if (isset($matches[2])) {
$port = (int) substr($matches[2], 1);
}
} else {
$pos = strpos($host, ':');
if ($pos !== false) {
$port = (int) substr($host, $pos + 1);
$host = strstr($host, ':', true);
}
}
// Query string
$queryString = '';
if (isset($globals['QUERY_STRING'])) {
$queryString = $globals['QUERY_STRING'];
}
// Request URI
$requestUri = '';
if (isset($globals['REQUEST_URI'])) {
$uriFragments = explode('?', $globals['REQUEST_URI']);
$requestUri = $uriFragments[0];
if ($queryString === '' && count($uriFragments) > 1) {
$queryString = parse_url('http://www.example.com' . $globals['REQUEST_URI'], PHP_URL_QUERY) ?? '';
}
}
// Build Uri and return
return new Uri($scheme, $host, $port, $requestUri, $queryString, '', $username, $password);
}
}

View file

@ -1,105 +0,0 @@
<?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 function array_merge;
use function is_array;
use function is_string;
class Header
{
/**
* @var string
*/
private $originalName;
/**
* @var string
*/
private $normalizedName;
/**
* @var array
*/
private $values;
/**
* Header constructor.
*
* @param string $originalName
* @param string $normalizedName
* @param array $values
*/
public function __construct(string $originalName, string $normalizedName, array $values)
{
$this->originalName = $originalName;
$this->normalizedName = $normalizedName;
$this->values = $values;
}
/**
* @return string
*/
public function getOriginalName(): string
{
return $this->originalName;
}
/**
* @return string
*/
public function getNormalizedName(): string
{
return $this->normalizedName;
}
/**
* @param string $value
*
* @return self
*/
public function addValue(string $value): self
{
$this->values[] = $value;
return $this;
}
/**
* @param array|string $values
*
* @return self
*/
public function addValues($values): self
{
if (is_string($values)) {
return $this->addValue($values);
}
if (!is_array($values)) {
throw new InvalidArgumentException('Parameter 1 of Header::addValues() should be a string or an array.');
}
$this->values = array_merge($this->values, $values);
return $this;
}
/**
* @return array
*/
public function getValues(): array
{
return $this->values;
}
}

View file

@ -1,321 +0,0 @@
<?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 Slim\Psr7\Interfaces\HeadersInterface;
use function base64_encode;
use function function_exists;
use function getallheaders;
use function is_array;
use function is_numeric;
use function is_string;
use function preg_match;
use function strpos;
use function strtolower;
use function strtr;
use function substr;
use function trim;
class Headers implements HeadersInterface
{
/**
* @var array
*/
protected $globals;
/**
* @var Header[]
*/
protected $headers;
/**
* @param array $headers
* @param array $globals
*/
final public function __construct(array $headers = [], ?array $globals = null)
{
$this->globals = $globals ?? $_SERVER;
$this->setHeaders($headers);
}
/**
* {@inheritdoc}
*/
public function addHeader($name, $value): HeadersInterface
{
[$values, $originalName, $normalizedName] = $this->prepareHeader($name, $value);
if (isset($this->headers[$normalizedName])) {
$header = $this->headers[$normalizedName];
$header->addValues($values);
} else {
$this->headers[$normalizedName] = new Header($originalName, $normalizedName, $values);
}
return $this;
}
/**
* {@inheritdoc}
*/
public function removeHeader(string $name): HeadersInterface
{
$name = $this->normalizeHeaderName($name);
unset($this->headers[$name]);
return $this;
}
/**
* {@inheritdoc}
*/
public function getHeader(string $name, $default = []): array
{
$name = $this->normalizeHeaderName($name);
if (isset($this->headers[$name])) {
$header = $this->headers[$name];
return $header->getValues();
}
if (empty($default)) {
return $default;
}
$this->validateHeader($name, $default);
return $this->trimHeaderValue($default);
}
/**
* {@inheritdoc}
*/
public function setHeader($name, $value): HeadersInterface
{
[$values, $originalName, $normalizedName] = $this->prepareHeader($name, $value);
// Ensure we preserve original case if the header already exists in the stack
if (isset($this->headers[$normalizedName])) {
$existingHeader = $this->headers[$normalizedName];
$originalName = $existingHeader->getOriginalName();
}
$this->headers[$normalizedName] = new Header($originalName, $normalizedName, $values);
return $this;
}
/**
* {@inheritdoc}
*/
public function setHeaders(array $headers): HeadersInterface
{
$this->headers = [];
foreach ($this->parseAuthorizationHeader($headers) as $name => $value) {
$this->addHeader($name, $value);
}
return $this;
}
/**
* {@inheritdoc}
*/
public function hasHeader(string $name): bool
{
$name = $this->normalizeHeaderName($name);
return isset($this->headers[$name]);
}
/**
* {@inheritdoc}
*/
public function getHeaders(bool $originalCase = false): array
{
$headers = [];
foreach ($this->headers as $header) {
$name = $originalCase ? $header->getOriginalName() : $header->getNormalizedName();
$headers[$name] = $header->getValues();
}
return $headers;
}
/**
* @param string $name
* @param bool $preserveCase
* @return string
*/
protected function normalizeHeaderName(string $name, bool $preserveCase = false): string
{
$name = strtr($name, '_', '-');
if (!$preserveCase) {
$name = strtolower($name);
}
if (strpos(strtolower($name), 'http-') === 0) {
$name = substr($name, 5);
}
return $name;
}
/**
* Parse incoming headers and determine Authorization header from original headers
*
* @param array $headers
* @return array
*/
protected function parseAuthorizationHeader(array $headers): array
{
$hasAuthorizationHeader = false;
foreach ($headers as $name => $value) {
if (strtolower($name) === 'authorization') {
$hasAuthorizationHeader = true;
break;
}
}
if (!$hasAuthorizationHeader) {
if (isset($this->globals['REDIRECT_HTTP_AUTHORIZATION'])) {
$headers['Authorization'] = $this->globals['REDIRECT_HTTP_AUTHORIZATION'];
} elseif (isset($this->globals['PHP_AUTH_USER'])) {
$pw = isset($this->globals['PHP_AUTH_PW']) ? $this->globals['PHP_AUTH_PW'] : '';
$headers['Authorization'] = 'Basic ' . base64_encode($this->globals['PHP_AUTH_USER'] . ':' . $pw);
} elseif (isset($this->globals['PHP_AUTH_DIGEST'])) {
$headers['Authorization'] = $this->globals['PHP_AUTH_DIGEST'];
}
}
return $headers;
}
/**
* @param array|string $value
*
* @return array
*/
protected function trimHeaderValue($value): array
{
$items = is_array($value) ? $value : [$value];
$result = [];
foreach ($items as $item) {
$result[] = trim((string) $item, " \t");
}
return $result;
}
/**
* @param string $name
* @param array|string $value
*
* @throws InvalidArgumentException
*
* @return array
*/
protected function prepareHeader($name, $value): array
{
$this->validateHeader($name, $value);
$values = $this->trimHeaderValue($value);
$originalName = $this->normalizeHeaderName($name, true);
$normalizedName = $this->normalizeHeaderName($name);
return [$values, $originalName, $normalizedName];
}
/**
* Make sure the header complies with RFC 7230.
*
* Header names must be a non-empty string consisting of token characters.
*
* Header values must be strings consisting of visible characters with all optional
* leading and trailing whitespace stripped. This method will always strip such
* optional whitespace. Note that the method does not allow folding whitespace within
* the values as this was deprecated for almost all instances by the RFC.
*
* header-field = field-name ":" OWS field-value OWS
* field-name = 1*( "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / "^"
* / "_" / "`" / "|" / "~" / %x30-39 / ( %x41-5A / %x61-7A ) )
* OWS = *( SP / HTAB )
* field-value = *( ( %x21-7E / %x80-FF ) [ 1*( SP / HTAB ) ( %x21-7E / %x80-FF ) ] )
*
* @see https://tools.ietf.org/html/rfc7230#section-3.2.4
*
* @param string $name
* @param array|string $value
*
* @throws InvalidArgumentException;
*/
protected function validateHeader($name, $value): void
{
$this->validateHeaderName($name);
$this->validateHeaderValue($value);
}
/**
* @param mixed $name
*
* @throws InvalidArgumentException
*/
protected function validateHeaderName($name): void
{
if (!is_string($name) || preg_match("@^[!#$%&'*+.^_`|~0-9A-Za-z-]+$@", $name) !== 1) {
throw new InvalidArgumentException('Header name must be an RFC 7230 compatible string.');
}
}
/**
* @param mixed $value
*
* @throws InvalidArgumentException
*/
protected function validateHeaderValue($value): void
{
$items = is_array($value) ? $value : [$value];
if (empty($items)) {
throw new InvalidArgumentException(
'Header values must be a string or an array of strings, empty array given.'
);
}
$pattern = "@^[ \t\x21-\x7E\x80-\xFF]*$@";
foreach ($items as $item) {
$hasInvalidType = !is_numeric($item) && !is_string($item);
$rejected = $hasInvalidType || preg_match($pattern, (string) $item) !== 1;
if ($rejected) {
throw new InvalidArgumentException(
'Header values must be RFC 7230 compatible strings.'
);
}
}
}
/**
* @return static
*/
public static function createFromGlobals()
{
$headers = null;
if (function_exists('getallheaders')) {
$headers = getallheaders();
}
if (!is_array($headers)) {
$headers = [];
}
return new static($headers);
}
}

View file

@ -1,90 +0,0 @@
<?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\Interfaces;
use InvalidArgumentException;
interface HeadersInterface
{
/**
* Add header value
*
* This method appends the value to the existing array of values
*
* @param string $name
* @param array|string $value
*
* @return HeadersInterface
*
* @throws InvalidArgumentException
*/
public function addHeader($name, $value): HeadersInterface;
/**
* Remove header value
*
* @param string $name
* @return HeadersInterface
*/
public function removeHeader(string $name): HeadersInterface;
/**
* Get header value or values.
* If the array has a single value it will return that single value.
* If the array has multiple values, it will return an array of values.
*
* @param string $name
* @param string[] $default
*
* @return array
*/
public function getHeader(string $name, $default = []): array;
/**
* Replaces the existing header value with the new value.
*
* @param string $name
* @param array|string $value
*
* @return HeadersInterface
*
* @throws InvalidArgumentException
*/
public function setHeader($name, $value): HeadersInterface;
/**
* Replaces all existing headers with the new values.
*
* @param array $headers
*
* @return HeadersInterface
*
* @throws InvalidArgumentException
*/
public function setHeaders(array $headers): HeadersInterface;
/**
* Is the header present in the stack.
*
* @param string $name
* @return bool
*/
public function hasHeader(string $name): bool;
/**
* Return all headers in the stack.
*
* @param bool $originalCase
*
* @return array
*/
public function getHeaders(bool $originalCase): array;
}

View file

@ -1,191 +0,0 @@
<?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\MessageInterface;
use Psr\Http\Message\StreamInterface;
use Slim\Psr7\Interfaces\HeadersInterface;
use function array_keys;
use function header;
use function header_remove;
use function implode;
use function sprintf;
abstract class Message implements MessageInterface
{
/**
* @var string
*/
protected $protocolVersion = '1.1';
/**
* @var array
*/
protected static $validProtocolVersions = [
'1.0' => true,
'1.1' => true,
'2.0' => true,
'2' => true,
];
/**
* @var HeadersInterface
*/
protected $headers;
/**
* @var StreamInterface
*/
protected $body;
/**
* Disable magic setter to ensure immutability
*
* @param string $name The property name
* @param mixed $value The property value
*
* @return void
*/
public function __set($name, $value): void
{
// Do nothing
}
/**
* {@inheritdoc}
*/
public function getProtocolVersion(): string
{
return $this->protocolVersion;
}
/**
* @return static
* {@inheritdoc}
*/
public function withProtocolVersion($version)
{
if (!isset(self::$validProtocolVersions[$version])) {
throw new InvalidArgumentException(
'Invalid HTTP version. Must be one of: '
. implode(', ', array_keys(self::$validProtocolVersions))
);
}
$clone = clone $this;
$clone->protocolVersion = $version;
return $clone;
}
/**
* {@inheritdoc}
*/
public function getHeaders(): array
{
return $this->headers->getHeaders(true);
}
/**
* {@inheritdoc}
*/
public function hasHeader($name): bool
{
return $this->headers->hasHeader($name);
}
/**
* {@inheritdoc}
*/
public function getHeader($name): array
{
return $this->headers->getHeader($name);
}
/**
* {@inheritdoc}
*/
public function getHeaderLine($name): string
{
$values = $this->headers->getHeader($name);
return implode(',', $values);
}
/**
* @return static
* {@inheritdoc}
*/
public function withHeader($name, $value)
{
$clone = clone $this;
$clone->headers->setHeader($name, $value);
if ($this instanceof Response && $this->body instanceof NonBufferedBody) {
header(sprintf('%s: %s', $name, $clone->getHeaderLine($name)));
}
return $clone;
}
/**
* @return static
* {@inheritdoc}
*/
public function withAddedHeader($name, $value)
{
$clone = clone $this;
$clone->headers->addHeader($name, $value);
if ($this instanceof Response && $this->body instanceof NonBufferedBody) {
header(sprintf('%s: %s', $name, $clone->getHeaderLine($name)));
}
return $clone;
}
/**
* @return static
* {@inheritdoc}
*/
public function withoutHeader($name)
{
$clone = clone $this;
$clone->headers->removeHeader($name);
if ($this instanceof Response && $this->body instanceof NonBufferedBody) {
header_remove($name);
}
return $clone;
}
/**
* {@inheritdoc}
*/
public function getBody(): StreamInterface
{
return $this->body;
}
/**
* @return static
* {@inheritdoc}
*/
public function withBody(StreamInterface $body)
{
$clone = clone $this;
$clone->body = $body;
return $clone;
}
}

View file

@ -1,153 +0,0 @@
<?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 Psr\Http\Message\StreamInterface;
use RuntimeException;
use function flush;
use function ob_get_clean;
use function ob_get_level;
use function strlen;
use const SEEK_SET;
class NonBufferedBody implements StreamInterface
{
/**
* {@inheritdoc}
*/
public function __toString(): string
{
return '';
}
/**
* {@inheritdoc}
*/
public function close(): void
{
throw new RuntimeException('A NonBufferedBody is not closable.');
}
/**
* {@inheritdoc}
*/
public function detach()
{
return null;
}
/**
* {@inheritdoc}
*/
public function getSize(): ?int
{
return null;
}
/**
* {@inheritdoc}
*/
public function tell(): int
{
return 0;
}
/**
* {@inheritdoc}
*/
public function eof(): bool
{
return true;
}
/**
* {@inheritdoc}
*/
public function isSeekable(): bool
{
return false;
}
/**
* {@inheritdoc}
*/
public function seek($offset, $whence = SEEK_SET): void
{
throw new RuntimeException('A NonBufferedBody is not seekable.');
}
/**
* {@inheritdoc}
*/
public function rewind(): void
{
throw new RuntimeException('A NonBufferedBody is not rewindable.');
}
/**
* {@inheritdoc}
*/
public function isWritable(): bool
{
return true;
}
/**
* {@inheritdoc}
*/
public function write($string): int
{
$buffered = '';
while (0 < ob_get_level()) {
$buffered = ob_get_clean() . $buffered;
}
echo $buffered . $string;
flush();
return strlen($string) + strlen($buffered);
}
/**
* {@inheritdoc}
*/
public function isReadable(): bool
{
return false;
}
/**
* {@inheritdoc}
*/
public function read($length): string
{
throw new RuntimeException('A NonBufferedBody is not readable.');
}
/**
* {@inheritdoc}
*/
public function getContents(): string
{
return '';
}
/**
* {@inheritdoc}
*/
public function getMetadata($key = null): ?array
{
return null;
}
}

View file

@ -1,383 +0,0 @@
<?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;
}
}

View file

@ -1,224 +0,0 @@
<?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 Fig\Http\Message\StatusCodeInterface;
use InvalidArgumentException;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
use Slim\Psr7\Factory\StreamFactory;
use Slim\Psr7\Interfaces\HeadersInterface;
use function is_integer;
use function is_object;
use function is_string;
use function method_exists;
class Response extends Message implements ResponseInterface
{
/**
* @var int
*/
protected $status = StatusCodeInterface::STATUS_OK;
/**
* @var string
*/
protected $reasonPhrase = '';
/**
* @var array
*/
protected static $messages = [
// Informational 1xx
StatusCodeInterface::STATUS_CONTINUE => 'Continue',
StatusCodeInterface::STATUS_SWITCHING_PROTOCOLS => 'Switching Protocols',
StatusCodeInterface::STATUS_PROCESSING => 'Processing',
// Successful 2xx
StatusCodeInterface::STATUS_OK => 'OK',
StatusCodeInterface::STATUS_CREATED => 'Created',
StatusCodeInterface::STATUS_ACCEPTED => 'Accepted',
StatusCodeInterface::STATUS_NON_AUTHORITATIVE_INFORMATION => 'Non-Authoritative Information',
StatusCodeInterface::STATUS_NO_CONTENT => 'No Content',
StatusCodeInterface::STATUS_RESET_CONTENT => 'Reset Content',
StatusCodeInterface::STATUS_PARTIAL_CONTENT => 'Partial Content',
StatusCodeInterface::STATUS_MULTI_STATUS => 'Multi-Status',
StatusCodeInterface::STATUS_ALREADY_REPORTED => 'Already Reported',
StatusCodeInterface::STATUS_IM_USED => 'IM Used',
// Redirection 3xx
StatusCodeInterface::STATUS_MULTIPLE_CHOICES => 'Multiple Choices',
StatusCodeInterface::STATUS_MOVED_PERMANENTLY => 'Moved Permanently',
StatusCodeInterface::STATUS_FOUND => 'Found',
StatusCodeInterface::STATUS_SEE_OTHER => 'See Other',
StatusCodeInterface::STATUS_NOT_MODIFIED => 'Not Modified',
StatusCodeInterface::STATUS_USE_PROXY => 'Use Proxy',
StatusCodeInterface::STATUS_RESERVED => '(Unused)',
StatusCodeInterface::STATUS_TEMPORARY_REDIRECT => 'Temporary Redirect',
StatusCodeInterface::STATUS_PERMANENT_REDIRECT => 'Permanent Redirect',
// Client Error 4xx
StatusCodeInterface::STATUS_BAD_REQUEST => 'Bad Request',
StatusCodeInterface::STATUS_UNAUTHORIZED => 'Unauthorized',
StatusCodeInterface::STATUS_PAYMENT_REQUIRED => 'Payment Required',
StatusCodeInterface::STATUS_FORBIDDEN => 'Forbidden',
StatusCodeInterface::STATUS_NOT_FOUND => 'Not Found',
StatusCodeInterface::STATUS_METHOD_NOT_ALLOWED => 'Method Not Allowed',
StatusCodeInterface::STATUS_NOT_ACCEPTABLE => 'Not Acceptable',
StatusCodeInterface::STATUS_PROXY_AUTHENTICATION_REQUIRED => 'Proxy Authentication Required',
StatusCodeInterface::STATUS_REQUEST_TIMEOUT => 'Request Timeout',
StatusCodeInterface::STATUS_CONFLICT => 'Conflict',
StatusCodeInterface::STATUS_GONE => 'Gone',
StatusCodeInterface::STATUS_LENGTH_REQUIRED => 'Length Required',
StatusCodeInterface::STATUS_PRECONDITION_FAILED => 'Precondition Failed',
StatusCodeInterface::STATUS_PAYLOAD_TOO_LARGE => 'Request Entity Too Large',
StatusCodeInterface::STATUS_URI_TOO_LONG => 'Request-URI Too Long',
StatusCodeInterface::STATUS_UNSUPPORTED_MEDIA_TYPE => 'Unsupported Media Type',
StatusCodeInterface::STATUS_RANGE_NOT_SATISFIABLE => 'Requested Range Not Satisfiable',
StatusCodeInterface::STATUS_EXPECTATION_FAILED => 'Expectation Failed',
StatusCodeInterface::STATUS_IM_A_TEAPOT => 'I\'m a teapot',
StatusCodeInterface::STATUS_MISDIRECTED_REQUEST => 'Misdirected Request',
StatusCodeInterface::STATUS_UNPROCESSABLE_ENTITY => 'Unprocessable Entity',
StatusCodeInterface::STATUS_LOCKED => 'Locked',
StatusCodeInterface::STATUS_FAILED_DEPENDENCY => 'Failed Dependency',
StatusCodeInterface::STATUS_UPGRADE_REQUIRED => 'Upgrade Required',
StatusCodeInterface::STATUS_PRECONDITION_REQUIRED => 'Precondition Required',
StatusCodeInterface::STATUS_TOO_MANY_REQUESTS => 'Too Many Requests',
StatusCodeInterface::STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE => 'Request Header Fields Too Large',
444 => 'Connection Closed Without Response',
StatusCodeInterface::STATUS_UNAVAILABLE_FOR_LEGAL_REASONS => 'Unavailable For Legal Reasons',
499 => 'Client Closed Request',
// Server Error 5xx
StatusCodeInterface::STATUS_INTERNAL_SERVER_ERROR => 'Internal Server Error',
StatusCodeInterface::STATUS_NOT_IMPLEMENTED => 'Not Implemented',
StatusCodeInterface::STATUS_BAD_GATEWAY => 'Bad Gateway',
StatusCodeInterface::STATUS_SERVICE_UNAVAILABLE => 'Service Unavailable',
StatusCodeInterface::STATUS_GATEWAY_TIMEOUT => 'Gateway Timeout',
StatusCodeInterface::STATUS_VERSION_NOT_SUPPORTED => 'HTTP Version Not Supported',
StatusCodeInterface::STATUS_VARIANT_ALSO_NEGOTIATES => 'Variant Also Negotiates',
StatusCodeInterface::STATUS_INSUFFICIENT_STORAGE => 'Insufficient Storage',
StatusCodeInterface::STATUS_LOOP_DETECTED => 'Loop Detected',
StatusCodeInterface::STATUS_NOT_EXTENDED => 'Not Extended',
StatusCodeInterface::STATUS_NETWORK_AUTHENTICATION_REQUIRED => 'Network Authentication Required',
599 => 'Network Connect Timeout Error',
];
/**
* @param int $status The response status code.
* @param HeadersInterface|null $headers The response headers.
* @param StreamInterface|null $body The response body.
*/
public function __construct(
int $status = StatusCodeInterface::STATUS_OK,
?HeadersInterface $headers = null,
?StreamInterface $body = null
) {
$this->status = $this->filterStatus($status);
$this->headers = $headers ? $headers : new Headers([], []);
$this->body = $body ? $body : (new StreamFactory())->createStream();
}
/**
* 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;
}
/**
* {@inheritdoc}
*/
public function getStatusCode(): int
{
return $this->status;
}
/**
* {@inheritdoc}
*/
public function withStatus($code, $reasonPhrase = '')
{
$code = $this->filterStatus($code);
$reasonPhrase = $this->filterReasonPhrase($reasonPhrase);
$clone = clone $this;
$clone->status = $code;
$clone->reasonPhrase = $reasonPhrase;
return $clone;
}
/**
* {@inheritdoc}
*/
public function getReasonPhrase(): string
{
if ($this->reasonPhrase !== '') {
return $this->reasonPhrase;
}
if (isset(static::$messages[$this->status])) {
return static::$messages[$this->status];
}
return '';
}
/**
* Filter HTTP status code.
*
* @param int $status HTTP status code.
*
* @return int
*
* @throws InvalidArgumentException If an invalid HTTP status code is provided.
*/
protected function filterStatus($status): int
{
if (!is_integer($status) || $status < StatusCodeInterface::STATUS_CONTINUE || $status > 599) {
throw new InvalidArgumentException('Invalid HTTP status code.');
}
return $status;
}
/**
* Filter Reason Phrase
*
* @param mixed $reasonPhrase
*
* @return string
*
* @throws InvalidArgumentException
*/
protected function filterReasonPhrase($reasonPhrase = ''): string
{
if (is_object($reasonPhrase) && method_exists($reasonPhrase, '__toString')) {
$reasonPhrase = (string) $reasonPhrase;
}
if (!is_string($reasonPhrase)) {
throw new InvalidArgumentException('Response reason phrase must be a string.');
}
if (strpos($reasonPhrase, "\r") || strpos($reasonPhrase, "\n")) {
throw new InvalidArgumentException(
'Reason phrase contains one of the following prohibited characters: \r \n'
);
}
return $reasonPhrase;
}
}

View file

@ -1,420 +0,0 @@
<?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\StreamInterface;
use RuntimeException;
use function fclose;
use function feof;
use function fread;
use function fseek;
use function fstat;
use function ftell;
use function fwrite;
use function is_array;
use function is_resource;
use function is_string;
use function pclose;
use function rewind;
use function stream_get_contents;
use function stream_get_meta_data;
use function strstr;
use const SEEK_SET;
class Stream implements StreamInterface
{
/**
* Bit mask to determine if the stream is a pipe
*
* This is octal as per header stat.h
*/
public const FSTAT_MODE_S_IFIFO = 0010000;
/**
* The underlying stream resource
*
* @var resource|null
*/
protected $stream;
/**
* @var array|null
*/
protected $meta;
/**
* @var bool|null
*/
protected $readable;
/**
* @var bool|null
*/
protected $writable;
/**
* @var bool|null
*/
protected $seekable;
/**
* @var null|int
*/
protected $size;
/**
* @var bool|null
*/
protected $isPipe;
/**
* @var bool
*/
protected $finished = false;
/**
* @var StreamInterface | null
*/
protected $cache;
/**
* @param resource $stream A PHP resource handle.
* @param StreamInterface $cache A stream to cache $stream (useful for non-seekable streams)
*
* @throws InvalidArgumentException If argument is not a resource.
*/
public function __construct($stream, StreamInterface $cache = null)
{
$this->attach($stream);
if ($cache && (!$cache->isSeekable() || !$cache->isWritable())) {
throw new RuntimeException('Cache stream must be seekable and writable');
}
$this->cache = $cache;
}
/**
* {@inheritdoc}
*/
public function getMetadata($key = null)
{
if (!$this->stream) {
return null;
}
$this->meta = stream_get_meta_data($this->stream);
if (!$key) {
return $this->meta;
}
return isset($this->meta[$key]) ? $this->meta[$key] : null;
}
/**
* Attach new resource to this object.
*
* @internal This method is not part of the PSR-7 standard.
*
* @param resource $stream A PHP resource handle.
*
* @throws InvalidArgumentException If argument is not a valid PHP resource.
*
* @return void
*/
protected function attach($stream): void
{
if (!is_resource($stream)) {
throw new InvalidArgumentException(__METHOD__ . ' argument must be a valid PHP resource');
}
if ($this->stream) {
$this->detach();
}
$this->stream = $stream;
}
/**
* {@inheritdoc}
*/
public function detach()
{
$oldResource = $this->stream;
$this->stream = null;
$this->meta = null;
$this->readable = null;
$this->writable = null;
$this->seekable = null;
$this->size = null;
$this->isPipe = null;
$this->cache = null;
$this->finished = false;
return $oldResource;
}
/**
* {@inheritdoc}
*/
public function __toString(): string
{
if (!$this->stream) {
return '';
}
if ($this->cache && $this->finished) {
$this->cache->rewind();
return $this->cache->getContents();
}
if ($this->isSeekable()) {
$this->rewind();
}
return $this->getContents();
}
/**
* {@inheritdoc}
*/
public function close(): void
{
if ($this->stream) {
if ($this->isPipe()) {
pclose($this->stream);
} else {
fclose($this->stream);
}
}
$this->detach();
}
/**
* {@inheritdoc}
*/
public function getSize(): ?int
{
if ($this->stream && !$this->size) {
$stats = fstat($this->stream);
if ($stats) {
$this->size = isset($stats['size']) && !$this->isPipe() ? $stats['size'] : null;
}
}
return $this->size;
}
/**
* {@inheritdoc}
*/
public function tell(): int
{
$position = false;
if ($this->stream) {
$position = ftell($this->stream);
}
if ($position === false || $this->isPipe()) {
throw new RuntimeException('Could not get the position of the pointer in stream.');
}
return $position;
}
/**
* {@inheritdoc}
*/
public function eof(): bool
{
return $this->stream ? feof($this->stream) : true;
}
/**
* {@inheritdoc}
*/
public function isReadable(): bool
{
if ($this->readable === null) {
if ($this->isPipe()) {
$this->readable = true;
} else {
$this->readable = false;
if ($this->stream) {
$mode = $this->getMetadata('mode');
if (strstr($mode, 'r') !== false || strstr($mode, '+') !== false) {
$this->readable = true;
}
}
}
}
return $this->readable;
}
/**
* {@inheritdoc}
*/
public function isWritable(): bool
{
if ($this->writable === null) {
$this->writable = false;
if ($this->stream) {
$mode = $this->getMetadata('mode');
if (strstr($mode, 'w') !== false || strstr($mode, '+') !== false) {
$this->writable = true;
}
}
}
return $this->writable;
}
/**
* {@inheritdoc}
*/
public function isSeekable(): bool
{
if ($this->seekable === null) {
$this->seekable = false;
if ($this->stream) {
$this->seekable = !$this->isPipe() && $this->getMetadata('seekable');
}
}
return $this->seekable;
}
/**
* {@inheritdoc}
*/
public function seek($offset, $whence = SEEK_SET): void
{
if (!$this->isSeekable() || $this->stream && fseek($this->stream, $offset, $whence) === -1) {
throw new RuntimeException('Could not seek in stream.');
}
}
/**
* {@inheritdoc}
*/
public function rewind(): void
{
if (!$this->isSeekable() || $this->stream && rewind($this->stream) === false) {
throw new RuntimeException('Could not rewind stream.');
}
}
/**
* {@inheritdoc}
*/
public function read($length): string
{
$data = false;
if ($this->isReadable() && $this->stream) {
$data = fread($this->stream, $length);
}
if (is_string($data)) {
if ($this->cache) {
$this->cache->write($data);
}
if ($this->eof()) {
$this->finished = true;
}
return $data;
}
throw new RuntimeException('Could not read from stream.');
}
/**
* {@inheritdoc}
*/
public function write($string)
{
$written = false;
if ($this->isWritable() && $this->stream) {
$written = fwrite($this->stream, $string);
}
if ($written !== false) {
$this->size = null;
return $written;
}
throw new RuntimeException('Could not write to stream.');
}
/**
* {@inheritdoc}
*/
public function getContents(): string
{
if ($this->cache && $this->finished) {
$this->cache->rewind();
return $this->cache->getContents();
}
$contents = false;
if ($this->stream) {
$contents = stream_get_contents($this->stream);
}
if (is_string($contents)) {
if ($this->cache) {
$this->cache->write($contents);
}
if ($this->eof()) {
$this->finished = true;
}
return $contents;
}
throw new RuntimeException('Could not get contents of stream.');
}
/**
* Returns whether or not the stream is a pipe.
*
* @internal This method is not part of the PSR-7 standard.
*
* @return bool
*/
public function isPipe(): bool
{
if ($this->isPipe === null) {
$this->isPipe = false;
if ($this->stream) {
$stats = fstat($this->stream);
if (is_array($stats)) {
$this->isPipe = isset($stats['mode']) && ($stats['mode'] & self::FSTAT_MODE_S_IFIFO) !== 0;
}
}
}
return $this->isPipe;
}
}

View file

@ -1,293 +0,0 @@
<?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\StreamInterface;
use Psr\Http\Message\UploadedFileInterface;
use RuntimeException;
use Slim\Psr7\Factory\StreamFactory;
use function copy;
use function dirname;
use function is_array;
use function is_string;
use function is_uploaded_file;
use function is_writable;
use function move_uploaded_file;
use function rename;
use function sprintf;
use function strpos;
use function unlink;
use const UPLOAD_ERR_OK;
class UploadedFile implements UploadedFileInterface
{
/**
* The client-provided full path to the file
*
* @var string
*/
protected $file;
/**
* The client-provided file name.
*
* @var string|null
*/
protected $name;
/**
* The client-provided media type of the file.
*
* @var string|null
*/
protected $type;
/**
* @var int|null
*/
protected $size;
/**
* A valid PHP UPLOAD_ERR_xxx code for the file upload.
*
* @var int
*/
protected $error = UPLOAD_ERR_OK;
/**
* Indicates if the upload is from a SAPI environment.
*
* @var bool
*/
protected $sapi = false;
/**
* @var StreamInterface|null
*/
protected $stream;
/**
* Indicates if the uploaded file has already been moved.
*
* @var bool
*/
protected $moved = false;
/**
* @param string|StreamInterface $fileNameOrStream The full path to the uploaded file provided by the client,
* or a StreamInterface instance.
* @param string|null $name The file name.
* @param string|null $type The file media type.
* @param int|null $size The file size in bytes.
* @param int $error The UPLOAD_ERR_XXX code representing the status of the upload.
* @param bool $sapi Indicates if the upload is in a SAPI environment.
*/
final public function __construct(
$fileNameOrStream,
?string $name = null,
?string $type = null,
?int $size = null,
int $error = UPLOAD_ERR_OK,
bool $sapi = false
) {
if ($fileNameOrStream instanceof StreamInterface) {
$file = $fileNameOrStream->getMetadata('uri');
if (!is_string($file)) {
throw new InvalidArgumentException('No URI associated with the stream.');
}
$this->file = $file;
$this->stream = $fileNameOrStream;
} elseif (is_string($fileNameOrStream)) {
$this->file = $fileNameOrStream;
} else {
throw new InvalidArgumentException(
'Please provide a string (full path to the uploaded file) or an instance of StreamInterface.'
);
}
$this->name = $name;
$this->type = $type;
$this->size = $size;
$this->error = $error;
$this->sapi = $sapi;
}
/**
* {@inheritdoc}
*/
public function getStream()
{
if ($this->moved) {
throw new RuntimeException(sprintf('Uploaded file %s has already been moved', $this->name));
}
if (!$this->stream) {
$this->stream = (new StreamFactory())->createStreamFromFile($this->file);
}
return $this->stream;
}
/**
* {@inheritdoc}
*/
public function moveTo($targetPath): void
{
if ($this->moved) {
throw new RuntimeException('Uploaded file already moved');
}
$targetIsStream = strpos($targetPath, '://') > 0;
if (!$targetIsStream && !is_writable(dirname($targetPath))) {
throw new InvalidArgumentException('Upload target path is not writable');
}
if ($targetIsStream) {
if (!copy($this->file, $targetPath)) {
throw new RuntimeException(sprintf('Error moving uploaded file %s to %s', $this->name, $targetPath));
}
if (!unlink($this->file)) {
throw new RuntimeException(sprintf('Error removing uploaded file %s', $this->name));
}
} elseif ($this->sapi) {
if (!is_uploaded_file($this->file)) {
throw new RuntimeException(sprintf('%s is not a valid uploaded file', $this->file));
}
if (!move_uploaded_file($this->file, $targetPath)) {
throw new RuntimeException(sprintf('Error moving uploaded file %s to %s', $this->name, $targetPath));
}
} else {
if (!rename($this->file, $targetPath)) {
throw new RuntimeException(sprintf('Error moving uploaded file %s to %s', $this->name, $targetPath));
}
}
$this->moved = true;
}
/**
* {@inheritdoc}
*/
public function getError(): int
{
return $this->error;
}
/**
* {@inheritdoc}
*/
public function getClientFilename(): ?string
{
return $this->name;
}
/**
* {@inheritdoc}
*/
public function getClientMediaType(): ?string
{
return $this->type;
}
/**
* {@inheritdoc}
*/
public function getSize(): ?int
{
return $this->size;
}
/**
* Returns the client-provided full path to the file
*
* @internal This method is not part of the PSR-7 standard
*
* @return string
*/
public function getFilePath(): string
{
return $this->file;
}
/**
* Create a normalized tree of UploadedFile instances from the Environment.
*
* @internal This method is not part of the PSR-7 standard.
*
* @param array $globals The global server variables.
*
* @return array A normalized tree of UploadedFile instances or null if none are provided.
*/
public static function createFromGlobals(array $globals): array
{
if (isset($globals['slim.files']) && is_array($globals['slim.files'])) {
return $globals['slim.files'];
}
if (!empty($_FILES)) {
return static::parseUploadedFiles($_FILES);
}
return [];
}
/**
* Parse a non-normalized, i.e. $_FILES superglobal, tree of uploaded file data.
*
* @internal This method is not part of the PSR-7 standard.
*
* @param array $uploadedFiles The non-normalized tree of uploaded file data.
*
* @return array A normalized tree of UploadedFile instances.
*/
private static function parseUploadedFiles(array $uploadedFiles): array
{
$parsed = [];
foreach ($uploadedFiles as $field => $uploadedFile) {
if (!isset($uploadedFile['error'])) {
if (is_array($uploadedFile)) {
$parsed[$field] = static::parseUploadedFiles($uploadedFile);
}
continue;
}
$parsed[$field] = [];
if (!is_array($uploadedFile['error'])) {
$parsed[$field] = new static(
$uploadedFile['tmp_name'],
isset($uploadedFile['name']) ? $uploadedFile['name'] : null,
isset($uploadedFile['type']) ? $uploadedFile['type'] : null,
isset($uploadedFile['size']) ? $uploadedFile['size'] : null,
$uploadedFile['error'],
true
);
} else {
$subArray = [];
foreach ($uploadedFile['error'] as $fileIdx => $error) {
// Normalize sub array and re-parse to move the input's key name up a level
$subArray[$fileIdx]['name'] = $uploadedFile['name'][$fileIdx];
$subArray[$fileIdx]['type'] = $uploadedFile['type'][$fileIdx];
$subArray[$fileIdx]['tmp_name'] = $uploadedFile['tmp_name'][$fileIdx];
$subArray[$fileIdx]['error'] = $uploadedFile['error'][$fileIdx];
$subArray[$fileIdx]['size'] = $uploadedFile['size'][$fileIdx];
$parsed[$field] = static::parseUploadedFiles($subArray);
}
}
}
return $parsed;
}
}

View file

@ -1,495 +0,0 @@
<?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\UriInterface;
use function filter_var;
use function is_integer;
use function is_null;
use function is_object;
use function is_string;
use function ltrim;
use function method_exists;
use function preg_replace_callback;
use function rawurlencode;
use function str_replace;
use function strtolower;
use const FILTER_FLAG_IPV6;
use const FILTER_VALIDATE_IP;
class Uri implements UriInterface
{
public const SUPPORTED_SCHEMES = [
'' => null,
'http' => 80,
'https' => 443
];
/**
* Uri scheme (without "://" suffix)
*
* @var string
*/
protected $scheme = '';
/**
* @var string
*/
protected $user = '';
/**
* @var string
*/
protected $password = '';
/**
* @var string
*/
protected $host = '';
/**
* @var null|int
*/
protected $port;
/**
* @var string
*/
protected $path = '';
/**
* Uri query string (without "?" prefix)
*
* @var string
*/
protected $query = '';
/**
* Uri fragment string (without "#" prefix)
*
* @var string
*/
protected $fragment = '';
/**
* @param string $scheme Uri scheme.
* @param string $host Uri host.
* @param int $port Uri port number.
* @param string $path Uri path.
* @param string $query Uri query string.
* @param string $fragment Uri fragment.
* @param string $user Uri user.
* @param string $password Uri password.
*/
public function __construct(
string $scheme,
string $host,
?int $port = null,
string $path = '/',
string $query = '',
string $fragment = '',
string $user = '',
string $password = ''
) {
$this->scheme = $this->filterScheme($scheme);
$this->host = $this->filterHost($host);
$this->port = $this->filterPort($port);
$this->path = $this->filterPath($path);
$this->query = $this->filterQuery($query);
$this->fragment = $this->filterFragment($fragment);
$this->user = $user;
$this->password = $password;
}
/**
* {@inheritdoc}
*/
public function getScheme(): string
{
return $this->scheme;
}
/**
* {@inheritdoc}
*/
public function withScheme($scheme)
{
$scheme = $this->filterScheme($scheme);
$clone = clone $this;
$clone->scheme = $scheme;
return $clone;
}
/**
* Filter Uri scheme.
*
* @param mixed $scheme Raw Uri scheme.
*
* @return string
*
* @throws InvalidArgumentException If the Uri scheme is not a string.
* @throws InvalidArgumentException If Uri scheme is not exists in SUPPORTED_SCHEMES
*/
protected function filterScheme($scheme): string
{
if (!is_string($scheme)) {
throw new InvalidArgumentException('Uri scheme must be a string.');
}
$scheme = str_replace('://', '', strtolower($scheme));
if (!key_exists($scheme, self::SUPPORTED_SCHEMES)) {
throw new InvalidArgumentException(
'Uri scheme must be one of: "' . implode('", "', array_keys(static::SUPPORTED_SCHEMES)) . '"'
);
}
return $scheme;
}
/**
* {@inheritdoc}
*/
public function getAuthority(): string
{
$userInfo = $this->getUserInfo();
$host = $this->getHost();
$port = $this->getPort();
return ($userInfo !== '' ? $userInfo . '@' : '') . $host . ($port !== null ? ':' . $port : '');
}
/**
* {@inheritdoc}
*/
public function getUserInfo(): string
{
$info = $this->user;
if (isset($this->password) && $this->password !== '') {
$info .= ':' . $this->password;
}
return $info;
}
/**
* {@inheritdoc}
*/
public function withUserInfo($user, $password = null)
{
$clone = clone $this;
$clone->user = $this->filterUserInfo($user);
if ($clone->user !== '') {
$clone->password = $this->filterUserInfo($password);
} else {
$clone->password = '';
}
return $clone;
}
/**
* Filters the user info string.
*
* Returns the percent-encoded query string.
*
* @param string|null $info The raw uri query string.
*
* @return string
*/
protected function filterUserInfo(?string $info = null): string
{
if (!is_string($info)) {
return '';
}
$match = preg_replace_callback(
'/(?:[^a-zA-Z0-9_\-\.~!\$&\'\(\)\*\+,;=]+|%(?![A-Fa-f0-9]{2}))/u',
function ($match) {
return rawurlencode($match[0]);
},
$info
);
return is_string($match) ? $match : '';
}
/**
* {@inheritdoc}
*/
public function getHost(): string
{
return $this->host;
}
/**
* {@inheritdoc}
*/
public function withHost($host)
{
$clone = clone $this;
$clone->host = $this->filterHost($host);
return $clone;
}
/**
* Filter Uri host.
*
* If the supplied host is an IPv6 address, then it is converted to a reference
* as per RFC 2373.
*
* @param mixed $host The host to filter.
*
* @return string
*
* @throws InvalidArgumentException for invalid host names.
*/
protected function filterHost($host): string
{
if (is_object($host) && method_exists($host, '__toString')) {
$host = (string) $host;
}
if (!is_string($host)) {
throw new InvalidArgumentException('Uri host must be a string');
}
if (filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
$host = '[' . $host . ']';
}
return strtolower($host);
}
/**
* {@inheritdoc}
*/
public function getPort(): ?int
{
return $this->port && !$this->hasStandardPort() ? $this->port : null;
}
/**
* {@inheritdoc}
*/
public function withPort($port)
{
$port = $this->filterPort($port);
$clone = clone $this;
$clone->port = $port;
return $clone;
}
/**
* Does this Uri use a standard port?
*
* @return bool
*/
protected function hasStandardPort(): bool
{
return static::SUPPORTED_SCHEMES[$this->scheme] === $this->port;
}
/**
* Filter Uri port.
*
* @param null|int $port The Uri port number.
*
* @return null|int
*
* @throws InvalidArgumentException If the port is invalid.
*/
protected function filterPort($port): ?int
{
if (is_null($port) || (is_integer($port) && ($port >= 1 && $port <= 65535))) {
return $port;
}
throw new InvalidArgumentException('Uri port must be null or an integer between 1 and 65535 (inclusive)');
}
/**
* {@inheritdoc}
*/
public function getPath(): string
{
return $this->path;
}
/**
* {@inheritdoc}
*/
public function withPath($path)
{
if (!is_string($path)) {
throw new InvalidArgumentException('Uri path must be a string');
}
$clone = clone $this;
$clone->path = $this->filterPath($path);
return $clone;
}
/**
* Filter Uri path.
*
* This method percent-encodes all reserved characters in the provided path string.
* This method will NOT double-encode characters that are already percent-encoded.
*
* @param string $path The raw uri path.
*
* @return string The RFC 3986 percent-encoded uri path.
*
* @link http://www.faqs.org/rfcs/rfc3986.html
*/
protected function filterPath($path): string
{
$match = preg_replace_callback(
'/(?:[^a-zA-Z0-9_\-\.~:@&=\+\$,\/;%]+|%(?![A-Fa-f0-9]{2}))/',
function ($match) {
return rawurlencode($match[0]);
},
$path
);
return is_string($match) ? $match : '';
}
/**
* {@inheritdoc}
*/
public function getQuery(): string
{
return $this->query;
}
/**
* {@inheritdoc}
*/
public function withQuery($query)
{
$query = ltrim($this->filterQuery($query), '?');
$clone = clone $this;
$clone->query = $query;
return $clone;
}
/**
* Filters the query string of a URI.
*
* Returns the percent-encoded query string.
*
* @param mixed $query The raw uri query string.
*
* @return string
*/
protected function filterQuery($query): string
{
if (is_object($query) && method_exists($query, '__toString')) {
$query = (string) $query;
}
if (!is_string($query)) {
throw new InvalidArgumentException('Uri query must be a string.');
}
$match = preg_replace_callback(
'/(?:[^a-zA-Z0-9_\-\.~!\$&\'\(\)\*\+,;=%:@\/\?]+|%(?![A-Fa-f0-9]{2}))/',
function ($match) {
return rawurlencode($match[0]);
},
$query
);
return is_string($match) ? $match : '';
}
/**
* {@inheritdoc}
*/
public function getFragment(): string
{
return $this->fragment;
}
/**
* {@inheritdoc}
*/
public function withFragment($fragment)
{
$fragment = $this->filterFragment($fragment);
$clone = clone $this;
$clone->fragment = $fragment;
return $clone;
}
/**
* Filters fragment of a URI.
*
* Returns the percent-encoded fragment.
*
* @param mixed $fragment The raw uri query string.
*
* @return string
*/
protected function filterFragment($fragment): string
{
if (is_object($fragment) && method_exists($fragment, '__toString')) {
$fragment = (string) $fragment;
}
if (!is_string($fragment)) {
throw new InvalidArgumentException('Uri fragment must be a string.');
}
$fragment = ltrim($fragment, '#');
$match = preg_replace_callback(
'/(?:[^a-zA-Z0-9_\-\.~!\$&\'\(\)\*\+,;=%:@\/\?]+|%(?![A-Fa-f0-9]{2}))/',
function ($match) {
return rawurlencode($match[0]);
},
$fragment
);
return is_string($match) ? $match : '';
}
/**
* {@inheritdoc}
*/
public function __toString(): string
{
$scheme = $this->getScheme();
$authority = $this->getAuthority();
$path = $this->getPath();
$query = $this->getQuery();
$fragment = $this->getFragment();
$path = '/' . ltrim($path, '/');
return ($scheme !== '' ? $scheme . ':' : '')
. ($authority !== '' ? '//' . $authority : '')
. $path
. ($query !== '' ? '?' . $query : '')
. ($fragment !== '' ? '#' . $fragment : '');
}
}