File manager - Edit - /home/opticamezl/www/newok/laminas-diactoros.tar
Back
COPYRIGHT.md 0000644 00000000133 15173734626 0006451 0 ustar 00 Copyright (c) 2020 Laminas Project a Series of LF Projects, LLC. (https://getlaminas.org/) PATCHES.txt 0000644 00000000405 15173734626 0006411 0 ustar 00 This file was automatically generated by Composer Patches (https://github.com/cweagans/composer-patches) Patches applied to this directory: Fixes HTTP Multiline Header Termination Source: ./build/composer_patches/4.4.4-2024-04-13_php-laminas-diactoros.patch LICENSE.md 0000644 00000002732 15173734626 0006172 0 ustar 00 Copyright (c) 2020 Laminas Project a Series of LF Projects, LLC. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Laminas Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. src/AbstractSerializer.php 0000644 00000010752 15173734626 0011664 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use Psr\Http\Message\StreamInterface; use function array_pop; use function implode; use function ltrim; use function preg_match; use function sprintf; use function str_replace; use function ucwords; /** * Provides base functionality for request and response de/serialization * strategies, including functionality for retrieving a line at a time from * the message, splitting headers from the body, and serializing headers. */ abstract class AbstractSerializer { const CR = "\r"; const EOL = "\r\n"; const LF = "\n"; /** * Retrieve a single line from the stream. * * Retrieves a line from the stream; a line is defined as a sequence of * characters ending in a CRLF sequence. * * @throws Exception\DeserializationException if the sequence contains a CR * or LF in isolation, or ends in a CR. */ protected static function getLine(StreamInterface $stream) : string { $line = ''; $crFound = false; while (! $stream->eof()) { $char = $stream->read(1); if ($crFound && $char === self::LF) { $crFound = false; break; } // CR NOT followed by LF if ($crFound && $char !== self::LF) { throw Exception\DeserializationException::forUnexpectedCarriageReturn(); } // LF in isolation if (! $crFound && $char === self::LF) { throw Exception\DeserializationException::forUnexpectedLineFeed(); } // CR found; do not append if ($char === self::CR) { $crFound = true; continue; } // Any other character: append $line .= $char; } // CR found at end of stream if ($crFound) { throw Exception\DeserializationException::forUnexpectedEndOfHeaders(); } return $line; } /** * Split the stream into headers and body content. * * Returns an array containing two elements * * - The first is an array of headers * - The second is a StreamInterface containing the body content * * @throws Exception\DeserializationException For invalid headers. */ protected static function splitStream(StreamInterface $stream) : array { $headers = []; $currentHeader = false; while ($line = self::getLine($stream)) { if (preg_match(';^(?P<name>[!#$%&\'*+.^_`\|~0-9a-zA-Z-]+):(?P<value>.*)$;', $line, $matches)) { $currentHeader = $matches['name']; if (! isset($headers[$currentHeader])) { $headers[$currentHeader] = []; } $headers[$currentHeader][] = ltrim($matches['value']); continue; } if (! $currentHeader) { throw Exception\DeserializationException::forInvalidHeader(); } if (! preg_match('#^[ \t]#', $line)) { throw Exception\DeserializationException::forInvalidHeaderContinuation(); } // Append continuation to last header value found $value = array_pop($headers[$currentHeader]); $headers[$currentHeader][] = $value . ltrim($line); } // use RelativeStream to avoid copying initial stream into memory return [$headers, new RelativeStream($stream, $stream->tell())]; } /** * Serialize headers to string values. */ protected static function serializeHeaders(array $headers) : string { $lines = []; foreach ($headers as $header => $values) { $normalized = self::filterHeader($header); foreach ($values as $value) { $lines[] = sprintf('%s: %s', $normalized, $value); } } return implode("\r\n", $lines); } /** * Filter a header name to wordcase */ protected static function filterHeader($header) : string { $filtered = str_replace('-', ' ', $header); $filtered = ucwords($filtered); return str_replace(' ', '-', $filtered); } } src/Request.php 0000644 00000004107 15173734626 0007514 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\StreamInterface; use Psr\Http\Message\UriInterface; use function strtolower; /** * HTTP Request encapsulation * * 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 Request implements RequestInterface { use RequestTrait; /** * @param null|string|UriInterface $uri URI for the request, if any. * @param null|string $method HTTP method for the request, if any. * @param string|resource|StreamInterface $body Message body, if any. * @param array $headers Headers for the message, if any. * @throws Exception\InvalidArgumentException for any invalid value. */ public function __construct($uri = null, string $method = null, $body = 'php://temp', array $headers = []) { $this->initialize($uri, $method, $body, $headers); } /** * {@inheritdoc} */ public function getHeaders() : array { $headers = $this->headers; if (! $this->hasHeader('host') && $this->uri->getHost() ) { $headers['Host'] = [$this->getHostFromUri()]; } return $headers; } /** * {@inheritdoc} */ public function getHeader($header) : array { if (! $this->hasHeader($header)) { if (strtolower($header) === 'host' && $this->uri->getHost() ) { return [$this->getHostFromUri()]; } return []; } $header = $this->headerNames[strtolower($header)]; return $this->headers[$header]; } } src/ConfigProvider.php 0000644 00000003051 15173734626 0011001 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use Psr\Http\Message\ServerRequestFactoryInterface; use Psr\Http\Message\RequestFactoryInterface; use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\StreamFactoryInterface; use Psr\Http\Message\UploadedFileFactoryInterface; use Psr\Http\Message\UriFactoryInterface; class ConfigProvider { /** * Retrieve configuration for laminas-diactoros. * * @return array */ public function __invoke() : array { return [ 'dependencies' => $this->getDependencies(), ]; } /** * Returns the container dependencies. * Maps factory interfaces to factories. */ public function getDependencies() : array { return [ 'invokables' => [ RequestFactoryInterface::class => RequestFactory::class, ResponseFactoryInterface::class => ResponseFactory::class, StreamFactoryInterface::class => StreamFactory::class, ServerRequestFactoryInterface::class => ServerRequestFactory::class, UploadedFileFactoryInterface::class => UploadedFileFactory::class, UriFactoryInterface::class => UriFactory::class ], ]; } } src/ServerRequest.php 0000644 00000014677 15173734626 0010720 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\StreamInterface; use Psr\Http\Message\UploadedFileInterface; use Psr\Http\Message\UriInterface; use function array_key_exists; use function is_array; /** * 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 implements ServerRequestInterface { use RequestTrait; /** * @var array */ private $attributes = []; /** * @var array */ private $cookieParams = []; /** * @var null|array|object */ private $parsedBody; /** * @var array */ private $queryParams = []; /** * @var array */ private $serverParams; /** * @var array */ private $uploadedFiles; /** * @param array $serverParams Server parameters, typically from $_SERVER * @param array $uploadedFiles Upload file information, a tree of UploadedFiles * @param null|string|UriInterface $uri URI for the request, if any. * @param null|string $method HTTP method for the request, if any. * @param string|resource|StreamInterface $body Message body, if any. * @param array $headers Headers for the message, if any. * @param array $cookies Cookies for the message, if any. * @param array $queryParams Query params for the message, if any. * @param null|array|object $parsedBody The deserialized body parameters, if any. * @param string $protocol HTTP protocol version. * @throws Exception\InvalidArgumentException for any invalid value. */ public function __construct( array $serverParams = [], array $uploadedFiles = [], $uri = null, string $method = null, $body = 'php://input', array $headers = [], array $cookies = [], array $queryParams = [], $parsedBody = null, string $protocol = '1.1' ) { $this->validateUploadedFiles($uploadedFiles); if ($body === 'php://input') { $body = new PhpInputStream(); } $this->initialize($uri, $method, $body, $headers); $this->serverParams = $serverParams; $this->uploadedFiles = $uploadedFiles; $this->cookieParams = $cookies; $this->queryParams = $queryParams; $this->parsedBody = $parsedBody; $this->protocol = $protocol; } /** * {@inheritdoc} */ public function getServerParams() : array { return $this->serverParams; } /** * {@inheritdoc} */ public function getUploadedFiles() : array { return $this->uploadedFiles; } /** * {@inheritdoc} */ public function withUploadedFiles(array $uploadedFiles) : ServerRequest { $this->validateUploadedFiles($uploadedFiles); $new = clone $this; $new->uploadedFiles = $uploadedFiles; return $new; } /** * {@inheritdoc} */ public function getCookieParams() : array { return $this->cookieParams; } /** * {@inheritdoc} */ public function withCookieParams(array $cookies) : ServerRequest { $new = clone $this; $new->cookieParams = $cookies; return $new; } /** * {@inheritdoc} */ public function getQueryParams() : array { return $this->queryParams; } /** * {@inheritdoc} */ public function withQueryParams(array $query) : ServerRequest { $new = clone $this; $new->queryParams = $query; return $new; } /** * {@inheritdoc} */ public function getParsedBody() { return $this->parsedBody; } /** * {@inheritdoc} */ public function withParsedBody($data) : ServerRequest { if (! is_array($data) && ! is_object($data) && null !== $data) { throw new Exception\InvalidArgumentException(sprintf( '%s expects a null, array, or object argument; received %s', __METHOD__, gettype($data) )); } $new = clone $this; $new->parsedBody = $data; return $new; } /** * {@inheritdoc} */ public function getAttributes() : array { return $this->attributes; } /** * {@inheritdoc} */ public function getAttribute($attribute, $default = null) { if (! array_key_exists($attribute, $this->attributes)) { return $default; } return $this->attributes[$attribute]; } /** * {@inheritdoc} */ public function withAttribute($attribute, $value) : ServerRequest { $new = clone $this; $new->attributes[$attribute] = $value; return $new; } /** * {@inheritdoc} */ public function withoutAttribute($attribute) : ServerRequest { $new = clone $this; unset($new->attributes[$attribute]); return $new; } /** * Recursively validate the structure in an uploaded files array. * * @throws Exception\InvalidArgumentException if any leaf is not an UploadedFileInterface instance. */ private function validateUploadedFiles(array $uploadedFiles) : void { foreach ($uploadedFiles as $file) { if (is_array($file)) { $this->validateUploadedFiles($file); continue; } if (! $file instanceof UploadedFileInterface) { throw new Exception\InvalidArgumentException('Invalid leaf in uploaded files structure'); } } } } src/StreamFactory.php 0000644 00000002343 15173734626 0010647 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use Psr\Http\Message\StreamFactoryInterface; use Psr\Http\Message\StreamInterface; use function fopen; use function fwrite; use function get_resource_type; use function is_resource; use function rewind; class StreamFactory implements StreamFactoryInterface { /** * {@inheritDoc} */ public function createStream(string $content = '') : StreamInterface { $resource = fopen('php://temp', 'r+'); fwrite($resource, $content); rewind($resource); return $this->createStreamFromResource($resource); } /** * {@inheritDoc} */ public function createStreamFromFile(string $file, string $mode = 'r') : StreamInterface { return new Stream($file, $mode); } /** * {@inheritDoc} */ public function createStreamFromResource($resource) : StreamInterface { return new Stream($resource); } } src/Request/ArraySerializer.php 0000644 00000005662 15173734626 0012633 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros\Request; use Laminas\Diactoros\Exception; use Laminas\Diactoros\Request; use Laminas\Diactoros\Stream; use Psr\Http\Message\RequestInterface; use Throwable; use function sprintf; /** * Serialize or deserialize request messages to/from arrays. * * This class provides functionality for serializing a RequestInterface instance * to an array, as well as the reverse operation of creating a Request instance * from an array representing a message. */ final class ArraySerializer { /** * Serialize a request message to an array. */ public static function toArray(RequestInterface $request) : array { return [ 'method' => $request->getMethod(), 'request_target' => $request->getRequestTarget(), 'uri' => (string) $request->getUri(), 'protocol_version' => $request->getProtocolVersion(), 'headers' => $request->getHeaders(), 'body' => (string) $request->getBody(), ]; } /** * Deserialize a request array to a request instance. * * @throws Exception\DeserializationException when cannot deserialize response */ public static function fromArray(array $serializedRequest) : Request { try { $uri = self::getValueFromKey($serializedRequest, 'uri'); $method = self::getValueFromKey($serializedRequest, 'method'); $body = new Stream('php://memory', 'wb+'); $body->write(self::getValueFromKey($serializedRequest, 'body')); $headers = self::getValueFromKey($serializedRequest, 'headers'); $requestTarget = self::getValueFromKey($serializedRequest, 'request_target'); $protocolVersion = self::getValueFromKey($serializedRequest, 'protocol_version'); return (new Request($uri, $method, $body, $headers)) ->withRequestTarget($requestTarget) ->withProtocolVersion($protocolVersion); } catch (Throwable $exception) { throw Exception\DeserializationException::forRequestFromArray($exception); } } /** * @return mixed * @throws Exception\DeserializationException */ private static function getValueFromKey(array $data, string $key, string $message = null) { if (isset($data[$key])) { return $data[$key]; } if ($message === null) { $message = sprintf('Missing "%s" key in serialized request', $key); } throw new Exception\DeserializationException($message); } } src/Request/Serializer.php 0000644 00000010754 15173734626 0011632 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros\Request; use Laminas\Diactoros\AbstractSerializer; use Laminas\Diactoros\Exception; use Laminas\Diactoros\Request; use Laminas\Diactoros\Stream; use Laminas\Diactoros\Uri; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\StreamInterface; use function preg_match; use function sprintf; /** * Serialize (cast to string) or deserialize (cast string to Request) messages. * * This class provides functionality for serializing a RequestInterface instance * to a string, as well as the reverse operation of creating a Request instance * from a string/stream representing a message. */ final class Serializer extends AbstractSerializer { /** * Deserialize a request string to a request instance. * * Internally, casts the message to a stream and invokes fromStream(). * * @throws Exception\SerializationException when errors occur parsing the message. */ public static function fromString(string $message) : Request { $stream = new Stream('php://temp', 'wb+'); $stream->write($message); return self::fromStream($stream); } /** * Deserialize a request stream to a request instance. * * @throws Exception\InvalidArgumentException if the message stream is not * readable or seekable. * @throws Exception\SerializationException if an invalid request line is detected. */ public static function fromStream(StreamInterface $stream) : Request { if (! $stream->isReadable() || ! $stream->isSeekable()) { throw new Exception\InvalidArgumentException('Message stream must be both readable and seekable'); } $stream->rewind(); [$method, $requestTarget, $version] = self::getRequestLine($stream); $uri = self::createUriFromRequestTarget($requestTarget); [$headers, $body] = self::splitStream($stream); return (new Request($uri, $method, $body, $headers)) ->withProtocolVersion($version) ->withRequestTarget($requestTarget); } /** * Serialize a request message to a string. */ public static function toString(RequestInterface $request) : string { $httpMethod = $request->getMethod(); $headers = self::serializeHeaders($request->getHeaders()); $body = (string) $request->getBody(); $format = '%s %s HTTP/%s%s%s'; if (! empty($headers)) { $headers = "\r\n" . $headers; } if (! empty($body)) { $headers .= "\r\n\r\n"; } return sprintf( $format, $httpMethod, $request->getRequestTarget(), $request->getProtocolVersion(), $headers, $body ); } /** * Retrieve the components of the request line. * * Retrieves the first line of the stream and parses it, raising an * exception if it does not follow specifications; if valid, returns a list * with the method, target, and version, in that order. * * @throws Exception\SerializationException */ private static function getRequestLine(StreamInterface $stream) : array { $requestLine = self::getLine($stream); if (! preg_match( '#^(?P<method>[!\#$%&\'*+.^_`|~a-zA-Z0-9-]+) (?P<target>[^\s]+) HTTP/(?P<version>[1-9]\d*\.\d+)$#', $requestLine, $matches )) { throw Exception\SerializationException::forInvalidRequestLine(); } return [$matches['method'], $matches['target'], $matches['version']]; } /** * Create and return a Uri instance based on the provided request target. * * If the request target is of authority or asterisk form, an empty Uri * instance is returned; otherwise, the value is used to create and return * a new Uri instance. */ private static function createUriFromRequestTarget(string $requestTarget) : Uri { if (preg_match('#^https?://#', $requestTarget)) { return new Uri($requestTarget); } if (preg_match('#^(\*|[^/])#', $requestTarget)) { return new Uri(); } return new Uri($requestTarget); } } src/Uri.php 0000644 00000042531 15173734626 0006626 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use Psr\Http\Message\UriInterface; use function array_keys; use function explode; use function get_class; use function gettype; use function implode; use function is_numeric; use function is_object; use function is_string; use function ltrim; use function parse_url; use function preg_match; use function preg_replace; use function preg_replace_callback; use function rawurlencode; use function sprintf; use function str_split; use function strpos; use function strtolower; use function substr; /** * Implementation of Psr\Http\UriInterface. * * Provides a value object representing a URI for HTTP requests. * * Instances of this class are considered immutable; all methods that * might change state are implemented such that they retain the internal * state of the current instance and return a new instance that contains the * changed state. */ class Uri implements UriInterface { /** * Sub-delimiters used in user info, query strings and fragments. * * @const string */ const CHAR_SUB_DELIMS = '!\$&\'\(\)\*\+,;='; /** * Unreserved characters used in user info, paths, query strings, and fragments. * * @const string */ const CHAR_UNRESERVED = 'a-zA-Z0-9_\-\.~\pL'; /** * @var int[] Array indexed by valid scheme names to their corresponding ports. */ protected $allowedSchemes = [ 'http' => 80, 'https' => 443, ]; /** * @var string */ private $scheme = ''; /** * @var string */ private $userInfo = ''; /** * @var string */ private $host = ''; /** * @var int */ private $port; /** * @var string */ private $path = ''; /** * @var string */ private $query = ''; /** * @var string */ private $fragment = ''; /** * generated uri string cache * @var string|null */ private $uriString; public function __construct(string $uri = '') { if ('' === $uri) { return; } $this->parseUri($uri); } /** * Operations to perform on clone. * * Since cloning usually is for purposes of mutation, we reset the * $uriString property so it will be re-calculated. */ public function __clone() { $this->uriString = null; } /** * {@inheritdoc} */ public function __toString() : string { if (null !== $this->uriString) { return $this->uriString; } $this->uriString = static::createUriString( $this->scheme, $this->getAuthority(), $this->getPath(), // Absolute URIs should use a "/" for an empty path $this->query, $this->fragment ); return $this->uriString; } /** * {@inheritdoc} */ public function getScheme() : string { return $this->scheme; } /** * {@inheritdoc} */ public function getAuthority() : string { if ('' === $this->host) { return ''; } $authority = $this->host; if ('' !== $this->userInfo) { $authority = $this->userInfo . '@' . $authority; } if ($this->isNonStandardPort($this->scheme, $this->host, $this->port)) { $authority .= ':' . $this->port; } return $authority; } /** * Retrieve the user-info part of the URI. * * This value is percent-encoded, per RFC 3986 Section 3.2.1. * * {@inheritdoc} */ public function getUserInfo() : string { return $this->userInfo; } /** * {@inheritdoc} */ public function getHost() : string { return $this->host; } /** * {@inheritdoc} */ public function getPort() : ?int { return $this->isNonStandardPort($this->scheme, $this->host, $this->port) ? $this->port : null; } /** * {@inheritdoc} */ public function getPath() : string { return $this->path; } /** * {@inheritdoc} */ public function getQuery() : string { return $this->query; } /** * {@inheritdoc} */ public function getFragment() : string { return $this->fragment; } /** * {@inheritdoc} */ public function withScheme($scheme) : UriInterface { if (! is_string($scheme)) { throw new Exception\InvalidArgumentException(sprintf( '%s expects a string argument; received %s', __METHOD__, is_object($scheme) ? get_class($scheme) : gettype($scheme) )); } $scheme = $this->filterScheme($scheme); if ($scheme === $this->scheme) { // Do nothing if no change was made. return $this; } $new = clone $this; $new->scheme = $scheme; return $new; } /** * Create and return a new instance containing the provided user credentials. * * The value will be percent-encoded in the new instance, but with measures * taken to prevent double-encoding. * * {@inheritdoc} */ public function withUserInfo($user, $password = null) : UriInterface { if (! is_string($user)) { throw new Exception\InvalidArgumentException(sprintf( '%s expects a string user argument; received %s', __METHOD__, is_object($user) ? get_class($user) : gettype($user) )); } if (null !== $password && ! is_string($password)) { throw new Exception\InvalidArgumentException(sprintf( '%s expects a string or null password argument; received %s', __METHOD__, is_object($password) ? get_class($password) : gettype($password) )); } $info = $this->filterUserInfoPart($user); if (null !== $password) { $info .= ':' . $this->filterUserInfoPart($password); } if ($info === $this->userInfo) { // Do nothing if no change was made. return $this; } $new = clone $this; $new->userInfo = $info; return $new; } /** * {@inheritdoc} */ public function withHost($host) : UriInterface { if (! is_string($host)) { throw new Exception\InvalidArgumentException(sprintf( '%s expects a string argument; received %s', __METHOD__, is_object($host) ? get_class($host) : gettype($host) )); } if ($host === $this->host) { // Do nothing if no change was made. return $this; } $new = clone $this; $new->host = strtolower($host); return $new; } /** * {@inheritdoc} */ public function withPort($port) : UriInterface { if ($port !== null) { if (! is_numeric($port) || is_float($port)) { throw new Exception\InvalidArgumentException(sprintf( 'Invalid port "%s" specified; must be an integer, an integer string, or null', is_object($port) ? get_class($port) : gettype($port) )); } $port = (int) $port; } if ($port === $this->port) { // Do nothing if no change was made. return $this; } if ($port !== null && ($port < 1 || $port > 65535)) { throw new Exception\InvalidArgumentException(sprintf( 'Invalid port "%d" specified; must be a valid TCP/UDP port', $port )); } $new = clone $this; $new->port = $port; return $new; } /** * {@inheritdoc} */ public function withPath($path) : UriInterface { if (! is_string($path)) { throw new Exception\InvalidArgumentException( 'Invalid path provided; must be a string' ); } if (strpos($path, '?') !== false) { throw new Exception\InvalidArgumentException( 'Invalid path provided; must not contain a query string' ); } if (strpos($path, '#') !== false) { throw new Exception\InvalidArgumentException( 'Invalid path provided; must not contain a URI fragment' ); } $path = $this->filterPath($path); if ($path === $this->path) { // Do nothing if no change was made. return $this; } $new = clone $this; $new->path = $path; return $new; } /** * {@inheritdoc} */ public function withQuery($query) : UriInterface { if (! is_string($query)) { throw new Exception\InvalidArgumentException( 'Query string must be a string' ); } if (strpos($query, '#') !== false) { throw new Exception\InvalidArgumentException( 'Query string must not include a URI fragment' ); } $query = $this->filterQuery($query); if ($query === $this->query) { // Do nothing if no change was made. return $this; } $new = clone $this; $new->query = $query; return $new; } /** * {@inheritdoc} */ public function withFragment($fragment) : UriInterface { if (! is_string($fragment)) { throw new Exception\InvalidArgumentException(sprintf( '%s expects a string argument; received %s', __METHOD__, is_object($fragment) ? get_class($fragment) : gettype($fragment) )); } $fragment = $this->filterFragment($fragment); if ($fragment === $this->fragment) { // Do nothing if no change was made. return $this; } $new = clone $this; $new->fragment = $fragment; return $new; } /** * Parse a URI into its parts, and set the properties */ private function parseUri(string $uri) : void { $parts = parse_url($uri); if (false === $parts) { throw new Exception\InvalidArgumentException( 'The source URI string appears to be malformed' ); } $this->scheme = isset($parts['scheme']) ? $this->filterScheme($parts['scheme']) : ''; $this->userInfo = isset($parts['user']) ? $this->filterUserInfoPart($parts['user']) : ''; $this->host = isset($parts['host']) ? strtolower($parts['host']) : ''; $this->port = isset($parts['port']) ? $parts['port'] : null; $this->path = isset($parts['path']) ? $this->filterPath($parts['path']) : ''; $this->query = isset($parts['query']) ? $this->filterQuery($parts['query']) : ''; $this->fragment = isset($parts['fragment']) ? $this->filterFragment($parts['fragment']) : ''; if (isset($parts['pass'])) { $this->userInfo .= ':' . $parts['pass']; } } /** * Create a URI string from its various parts */ private static function createUriString( string $scheme, string $authority, string $path, string $query, string $fragment ) : string { $uri = ''; if ('' !== $scheme) { $uri .= sprintf('%s:', $scheme); } if ('' !== $authority) { $uri .= '//' . $authority; } if ('' !== $path && '/' !== substr($path, 0, 1)) { $path = '/' . $path; } $uri .= $path; if ('' !== $query) { $uri .= sprintf('?%s', $query); } if ('' !== $fragment) { $uri .= sprintf('#%s', $fragment); } return $uri; } /** * Is a given port non-standard for the current scheme? */ private function isNonStandardPort(string $scheme, string $host, ?int $port) : bool { if ('' === $scheme) { return '' === $host || null !== $port; } if ('' === $host || null === $port) { return false; } return ! isset($this->allowedSchemes[$scheme]) || $port !== $this->allowedSchemes[$scheme]; } /** * Filters the scheme to ensure it is a valid scheme. * * @param string $scheme Scheme name. * @return string Filtered scheme. */ private function filterScheme(string $scheme) : string { $scheme = strtolower($scheme); $scheme = preg_replace('#:(//)?$#', '', $scheme); if ('' === $scheme) { return ''; } if (! isset($this->allowedSchemes[$scheme])) { throw new Exception\InvalidArgumentException(sprintf( 'Unsupported scheme "%s"; must be any empty string or in the set (%s)', $scheme, implode(', ', array_keys($this->allowedSchemes)) )); } return $scheme; } /** * Filters a part of user info in a URI to ensure it is properly encoded. * * @param string $part * @return string */ private function filterUserInfoPart(string $part) : string { $part = $this->filterInvalidUtf8($part); // Note the addition of `%` to initial charset; this allows `|` portion // to match and thus prevent double-encoding. return preg_replace_callback( '/(?:[^%' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . ']+|%(?![A-Fa-f0-9]{2}))/u', [$this, 'urlEncodeChar'], $part ); } /** * Filters the path of a URI to ensure it is properly encoded. */ private function filterPath(string $path) : string { $path = $this->filterInvalidUtf8($path); $path = preg_replace_callback( '/(?:[^' . self::CHAR_UNRESERVED . ')(:@&=\+\$,\/;%]+|%(?![A-Fa-f0-9]{2}))/u', [$this, 'urlEncodeChar'], $path ); if ('' === $path) { // No path return $path; } if ($path[0] !== '/') { // Relative path return $path; } // Ensure only one leading slash, to prevent XSS attempts. return '/' . ltrim($path, '/'); } /** * Encode invalid UTF-8 characters in given string. All other characters are unchanged. */ private function filterInvalidUtf8(string $string) : string { // check if given string contains only valid UTF-8 characters if (preg_match('//u', $string)) { return $string; } $letters = str_split($string); foreach ($letters as $i => $letter) { if (! preg_match('//u', $letter)) { $letters[$i] = $this->urlEncodeChar([$letter]); } } return implode('', $letters); } /** * Filter a query string to ensure it is propertly encoded. * * Ensures that the values in the query string are properly urlencoded. */ private function filterQuery(string $query) : string { if ('' !== $query && strpos($query, '?') === 0) { $query = substr($query, 1); } $parts = explode('&', $query); foreach ($parts as $index => $part) { [$key, $value] = $this->splitQueryValue($part); if ($value === null) { $parts[$index] = $this->filterQueryOrFragment($key); continue; } $parts[$index] = sprintf( '%s=%s', $this->filterQueryOrFragment($key), $this->filterQueryOrFragment($value) ); } return implode('&', $parts); } /** * Split a query value into a key/value tuple. * * @param string $value * @return array A value with exactly two elements, key and value */ private function splitQueryValue(string $value) : array { $data = explode('=', $value, 2); if (! isset($data[1])) { $data[] = null; } return $data; } /** * Filter a fragment value to ensure it is properly encoded. */ private function filterFragment(string $fragment) : string { if ('' !== $fragment && strpos($fragment, '#') === 0) { $fragment = '%23' . substr($fragment, 1); } return $this->filterQueryOrFragment($fragment); } /** * Filter a query string key or value, or a fragment. */ private function filterQueryOrFragment(string $value) : string { $value = $this->filterInvalidUtf8($value); return preg_replace_callback( '/(?:[^' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . '%:@\/\?]+|%(?![A-Fa-f0-9]{2}))/u', [$this, 'urlEncodeChar'], $value ); } /** * URL encode a character returned by a regex. */ private function urlEncodeChar(array $matches) : string { return rawurlencode($matches[0]); } } src/functions/marshal_uri_from_sapi.legacy.php 0000644 00000001164 15173734626 0015704 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Zend\Diactoros; use function Laminas\Diactoros\marshalUriFromSapi as laminas_marshalUriFromSapi; /** * @deprecated Use Laminas\Diactoros\marshalUriFromSapi instead */ function marshalUriFromSapi(array $server, array $headers) : Uri { return laminas_marshalUriFromSapi(...func_get_args()); } src/functions/create_uploaded_file.legacy.php 0000644 00000001153 15173734626 0015454 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Zend\Diactoros; use function Laminas\Diactoros\createUploadedFile as laminas_createUploadedFile; /** * @deprecated Use Laminas\Diactoros\createUploadedFile instead */ function createUploadedFile(array $spec) : UploadedFile { return laminas_createUploadedFile(...func_get_args()); } src/functions/marshal_headers_from_sapi.php 0000644 00000003153 15173734626 0015255 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use function array_key_exists; use function is_string; use function strpos; use function strtolower; use function strtr; use function substr; /** * @param array $server Values obtained from the SAPI (generally `$_SERVER`). * @return array Header/value pairs */ function marshalHeadersFromSapi(array $server) : array { $headers = []; foreach ($server as $key => $value) { if (! is_string($key)) { continue; } if ($value === '') { continue; } // Apache prefixes environment variables with REDIRECT_ // if they are added by rewrite rules if (strpos($key, 'REDIRECT_') === 0) { $key = substr($key, 9); // We will not overwrite existing variables with the // prefixed versions, though if (array_key_exists($key, $server)) { continue; } } if (strpos($key, 'HTTP_') === 0) { $name = strtr(strtolower(substr($key, 5)), '_', '-'); $headers[$name] = $value; continue; } if (strpos($key, 'CONTENT_') === 0) { $name = strtr(strtolower($key), '_', '-'); $headers[$name] = $value; continue; } } return $headers; } src/functions/marshal_protocol_version_from_sapi.legacy.php 0000644 00000001243 15173734626 0020511 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Zend\Diactoros; use function Laminas\Diactoros\marshalProtocolVersionFromSapi as laminas_marshalProtocolVersionFromSapi; /** * @deprecated Use Laminas\Diactoros\marshalProtocolVersionFromSapi instead */ function marshalProtocolVersionFromSapi(array $server) : string { return laminas_marshalProtocolVersionFromSapi(...func_get_args()); } src/functions/marshal_protocol_version_from_sapi.php 0000644 00000001760 15173734626 0017252 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use function preg_match; /** * Return HTTP protocol version (X.Y) as discovered within a `$_SERVER` array. * * @throws Exception\UnrecognizedProtocolVersionException if the * $server['SERVER_PROTOCOL'] value is malformed. */ function marshalProtocolVersionFromSapi(array $server) : string { if (! isset($server['SERVER_PROTOCOL'])) { return '1.1'; } if (! preg_match('#^(HTTP/)?(?P<version>[1-9]\d*(?:\.\d)?)$#', $server['SERVER_PROTOCOL'], $matches)) { throw Exception\UnrecognizedProtocolVersionException::forVersion( (string) $server['SERVER_PROTOCOL'] ); } return $matches['version']; } src/functions/create_uploaded_file.php 0000644 00000002250 15173734626 0014210 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; /** * Create an uploaded file instance from an array of values. * * @param array $spec A single $_FILES entry. * @throws Exception\InvalidArgumentException if one or more of the tmp_name, * size, or error keys are missing from $spec. */ function createUploadedFile(array $spec) : UploadedFile { if (! isset($spec['tmp_name']) || ! isset($spec['size']) || ! isset($spec['error']) ) { throw new Exception\InvalidArgumentException(sprintf( '$spec provided to %s MUST contain each of the keys "tmp_name",' . ' "size", and "error"; one or more were missing', __FUNCTION__ )); } return new UploadedFile( $spec['tmp_name'], (int) $spec['size'], $spec['error'], $spec['name'] ?? null, $spec['type'] ?? null ); } src/functions/marshal_method_from_sapi.legacy.php 0000644 00000001166 15173734626 0016367 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Zend\Diactoros; use function Laminas\Diactoros\marshalMethodFromSapi as laminas_marshalMethodFromSapi; /** * @deprecated Use Laminas\Diactoros\marshalMethodFromSapi instead */ function marshalMethodFromSapi(array $server) : string { return laminas_marshalMethodFromSapi(...func_get_args()); } src/functions/parse_cookie_header.php 0000644 00000002376 15173734626 0014055 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use function preg_match_all; use function urldecode; /** * Parse a cookie header according to RFC 6265. * * PHP will replace special characters in cookie names, which results in other cookies not being available due to * overwriting. Thus, the server request should take the cookies from the request header instead. * * @param string $cookieHeader A string cookie header value. * @return array key/value cookie pairs. */ function parseCookieHeader($cookieHeader) : array { preg_match_all('( (?:^\\n?[ \t]*|;[ ]) (?P<name>[!#$%&\'*+-.0-9A-Z^_`a-z|~]+) = (?P<DQUOTE>"?) (?P<value>[\x21\x23-\x2b\x2d-\x3a\x3c-\x5b\x5d-\x7e]*) (?P=DQUOTE) (?=\\n?[ \t]*$|;[ ]) )x', $cookieHeader, $matches, PREG_SET_ORDER); $cookies = []; foreach ($matches as $match) { $cookies[$match['name']] = urldecode($match['value']); } return $cookies; } src/functions/normalize_uploaded_files.legacy.php 0000644 00000001246 15173734626 0016377 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Zend\Diactoros; use Psr\Http\Message\UploadedFileInterface; use function Laminas\Diactoros\normalizeUploadedFiles as laminas_normalizeUploadedFiles; /** * @deprecated Use Laminas\Diactoros\normalizeUploadedFiles instead */ function normalizeUploadedFiles(array $files) : array { return laminas_normalizeUploadedFiles(...func_get_args()); } src/functions/normalize_server.php 0000644 00000003455 15173734626 0013467 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use function is_callable; /** * Marshal the $_SERVER array * * Pre-processes and returns the $_SERVER superglobal. In particularly, it * attempts to detect the Authorization header, which is often not aggregated * correctly under various SAPI/httpd combinations. * * @param null|callable $apacheRequestHeaderCallback Callback that can be used to * retrieve Apache request headers. This defaults to * `apache_request_headers` under the Apache mod_php. * @return array Either $server verbatim, or with an added HTTP_AUTHORIZATION header. */ function normalizeServer(array $server, callable $apacheRequestHeaderCallback = null) : array { if (null === $apacheRequestHeaderCallback && is_callable('apache_request_headers')) { $apacheRequestHeaderCallback = 'apache_request_headers'; } // If the HTTP_AUTHORIZATION value is already set, or the callback is not // callable, we return verbatim if (isset($server['HTTP_AUTHORIZATION']) || ! is_callable($apacheRequestHeaderCallback) ) { return $server; } $apacheRequestHeaders = $apacheRequestHeaderCallback(); if (isset($apacheRequestHeaders['Authorization'])) { $server['HTTP_AUTHORIZATION'] = $apacheRequestHeaders['Authorization']; return $server; } if (isset($apacheRequestHeaders['authorization'])) { $server['HTTP_AUTHORIZATION'] = $apacheRequestHeaders['authorization']; return $server; } return $server; } src/functions/marshal_headers_from_sapi.legacy.php 0000644 00000001172 15173734626 0016517 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Zend\Diactoros; use function Laminas\Diactoros\marshalHeadersFromSapi as laminas_marshalHeadersFromSapi; /** * @deprecated Use Laminas\Diactoros\marshalHeadersFromSapi instead */ function marshalHeadersFromSapi(array $server) : array { return laminas_marshalHeadersFromSapi(...func_get_args()); } src/functions/normalize_uploaded_files.php 0000644 00000010331 15173734626 0015127 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use Psr\Http\Message\UploadedFileInterface; use function is_array; /** * Normalize uploaded files * * Transforms each value into an UploadedFile instance, and ensures that nested * arrays are normalized. * * @return UploadedFileInterface[] * @throws Exception\InvalidArgumentException for unrecognized values */ function normalizeUploadedFiles(array $files) : array { /** * Traverse a nested tree of uploaded file specifications. * * @param string[]|array[] $tmpNameTree * @param int[]|array[] $sizeTree * @param int[]|array[] $errorTree * @param string[]|array[]|null $nameTree * @param string[]|array[]|null $typeTree * @return UploadedFile[]|array[] */ $recursiveNormalize = function ( array $tmpNameTree, array $sizeTree, array $errorTree, array $nameTree = null, array $typeTree = null ) use (&$recursiveNormalize) : array { $normalized = []; foreach ($tmpNameTree as $key => $value) { if (is_array($value)) { // Traverse $normalized[$key] = $recursiveNormalize( $tmpNameTree[$key], $sizeTree[$key], $errorTree[$key], $nameTree[$key] ?? null, $typeTree[$key] ?? null ); continue; } $normalized[$key] = createUploadedFile([ 'tmp_name' => $tmpNameTree[$key], 'size' => $sizeTree[$key], 'error' => $errorTree[$key], 'name' => $nameTree[$key] ?? null, 'type' => $typeTree[$key] ?? null, ]); } return $normalized; }; /** * Normalize an array of file specifications. * * Loops through all nested files (as determined by receiving an array to the * `tmp_name` key of a `$_FILES` specification) and returns a normalized array * of UploadedFile instances. * * This function normalizes a `$_FILES` array representing a nested set of * uploaded files as produced by the php-fpm SAPI, CGI SAPI, or mod_php * SAPI. * * @param array $files * @return UploadedFile[] */ $normalizeUploadedFileSpecification = function (array $files = []) use (&$recursiveNormalize) : array { if (! isset($files['tmp_name']) || ! is_array($files['tmp_name']) || ! isset($files['size']) || ! is_array($files['size']) || ! isset($files['error']) || ! is_array($files['error']) ) { throw new Exception\InvalidArgumentException(sprintf( '$files provided to %s MUST contain each of the keys "tmp_name",' . ' "size", and "error", with each represented as an array;' . ' one or more were missing or non-array values', __FUNCTION__ )); } return $recursiveNormalize( $files['tmp_name'], $files['size'], $files['error'], $files['name'] ?? null, $files['type'] ?? null ); }; $normalized = []; foreach ($files as $key => $value) { if ($value instanceof UploadedFileInterface) { $normalized[$key] = $value; continue; } if (is_array($value) && isset($value['tmp_name']) && is_array($value['tmp_name'])) { $normalized[$key] = $normalizeUploadedFileSpecification($value); continue; } if (is_array($value) && isset($value['tmp_name'])) { $normalized[$key] = createUploadedFile($value); continue; } if (is_array($value)) { $normalized[$key] = normalizeUploadedFiles($value); continue; } throw new Exception\InvalidArgumentException('Invalid value in files specification'); } return $normalized; } src/functions/parse_cookie_header.legacy.php 0000644 00000001141 15173734626 0015305 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Zend\Diactoros; use function Laminas\Diactoros\parseCookieHeader as laminas_parseCookieHeader; /** * @deprecated Use Laminas\Diactoros\parseCookieHeader instead */ function parseCookieHeader($cookieHeader) : array { return laminas_parseCookieHeader(...func_get_args()); } src/functions/marshal_method_from_sapi.php 0000644 00000001010 15173734626 0015110 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; /** * Retrieve the request method from the SAPI parameters. */ function marshalMethodFromSapi(array $server) : string { return $server['REQUEST_METHOD'] ?? 'GET'; } src/functions/marshal_uri_from_sapi.php 0000644 00000015243 15173734626 0014444 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use function array_change_key_case; use function array_key_exists; use function explode; use function implode; use function is_array; use function ltrim; use function preg_match; use function preg_replace; use function strlen; use function strpos; use function strtolower; use function substr; /** * Marshal a Uri instance based on the values presnt in the $_SERVER array and headers. * * @param array $server SAPI parameters * @param array $headers HTTP request headers */ function marshalUriFromSapi(array $server, array $headers) : Uri { /** * Retrieve a header value from an array of headers using a case-insensitive lookup. * * @param array $headers Key/value header pairs * @param mixed $default Default value to return if header not found * @return mixed */ $getHeaderFromArray = function (string $name, array $headers, $default = null) { $header = strtolower($name); $headers = array_change_key_case($headers, CASE_LOWER); if (array_key_exists($header, $headers)) { $value = is_array($headers[$header]) ? implode(', ', $headers[$header]) : $headers[$header]; return $value; } return $default; }; /** * Marshal the host and port from HTTP headers and/or the PHP environment. * * @return array Array of two items, host and port, in that order (can be * passed to a list() operation). */ $marshalHostAndPort = function (array $headers, array $server) use ($getHeaderFromArray) : array { /** * @param string|array $host * @return array Array of two items, host and port, in that order (can be * passed to a list() operation). */ $marshalHostAndPortFromHeader = function ($host) { if (is_array($host)) { $host = implode(', ', $host); } $port = null; // works for regname, IPv4 & IPv6 if (preg_match('|\:(\d+)$|', $host, $matches)) { $host = substr($host, 0, -1 * (strlen($matches[1]) + 1)); $port = (int) $matches[1]; } return [$host, $port]; }; /** * @return array Array of two items, host and port, in that order (can be * passed to a list() operation). */ $marshalIpv6HostAndPort = function (array $server, ?int $port) : array { $host = '[' . $server['SERVER_ADDR'] . ']'; $port = $port ?: 80; if ($port . ']' === substr($host, strrpos($host, ':') + 1)) { // The last digit of the IPv6-Address has been taken as port // Unset the port so the default port can be used $port = null; } return [$host, $port]; }; static $defaults = ['', null]; $forwardedHost = $getHeaderFromArray('x-forwarded-host', $headers, false); if ($forwardedHost !== false) { return $marshalHostAndPortFromHeader($forwardedHost); } $host = $getHeaderFromArray('host', $headers, false); if ($host !== false) { return $marshalHostAndPortFromHeader($host); } if (! isset($server['SERVER_NAME'])) { return $defaults; } $host = $server['SERVER_NAME']; $port = isset($server['SERVER_PORT']) ? (int) $server['SERVER_PORT'] : null; if (! isset($server['SERVER_ADDR']) || ! preg_match('/^\[[0-9a-fA-F\:]+\]$/', $host) ) { return [$host, $port]; } // Misinterpreted IPv6-Address // Reported for Safari on Windows return $marshalIpv6HostAndPort($server, $port); }; /** * Detect the path for the request * * Looks at a variety of criteria in order to attempt to autodetect the base * request path, including: * * - IIS7 UrlRewrite environment * - REQUEST_URI * - ORIG_PATH_INFO * * From Laminas\Http\PhpEnvironment\Request class */ $marshalRequestPath = function (array $server) : string { // IIS7 with URL Rewrite: make sure we get the unencoded url // (double slash problem). $iisUrlRewritten = $server['IIS_WasUrlRewritten'] ?? null; $unencodedUrl = $server['UNENCODED_URL'] ?? ''; if ('1' === $iisUrlRewritten && ! empty($unencodedUrl)) { return $unencodedUrl; } $requestUri = $server['REQUEST_URI'] ?? null; if ($requestUri !== null) { return preg_replace('#^[^/:]+://[^/]+#', '', $requestUri); } $origPathInfo = $server['ORIG_PATH_INFO'] ?? null; if (empty($origPathInfo)) { return '/'; } return $origPathInfo; }; $uri = new Uri(''); // URI scheme $scheme = 'http'; $marshalHttpsValue = function ($https) : bool { if (is_bool($https)) { return $https; } if (! is_string($https)) { throw new Exception\InvalidArgumentException(sprintf( 'SAPI HTTPS value MUST be a string or boolean; received %s', gettype($https) )); } return 'on' === strtolower($https); }; if (array_key_exists('HTTPS', $server)) { $https = $marshalHttpsValue($server['HTTPS']); } elseif (array_key_exists('https', $server)) { $https = $marshalHttpsValue($server['https']); } else { $https = false; } if ($https || strtolower($getHeaderFromArray('x-forwarded-proto', $headers, '')) === 'https' ) { $scheme = 'https'; } $uri = $uri->withScheme($scheme); // Set the host [$host, $port] = $marshalHostAndPort($headers, $server); if (! empty($host)) { $uri = $uri->withHost($host); if (! empty($port)) { $uri = $uri->withPort($port); } } // URI path $path = $marshalRequestPath($server); // Strip query string $path = explode('?', $path, 2)[0]; // URI query $query = ''; if (isset($server['QUERY_STRING'])) { $query = ltrim($server['QUERY_STRING'], '?'); } // URI fragment $fragment = ''; if (strpos($path, '#') !== false) { [$path, $fragment] = explode('#', $path, 2); } return $uri ->withPath($path) ->withFragment($fragment) ->withQuery($query); } src/functions/normalize_server.legacy.php 0000644 00000001205 15173734626 0014721 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Zend\Diactoros; use function Laminas\Diactoros\normalizeServer as laminas_normalizeServer; /** * @deprecated Use Laminas\Diactoros\normalizeServer instead */ function normalizeServer(array $server, callable $apacheRequestHeaderCallback = null) : array { return laminas_normalizeServer(...func_get_args()); } src/Stream.php 0000644 00000021151 15173734626 0007315 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use Psr\Http\Message\StreamInterface; use RuntimeException; use function array_key_exists; use function fclose; use function feof; use function fopen; use function fread; use function fseek; use function fstat; use function ftell; use function fwrite; use function get_resource_type; use function is_int; use function is_resource; use function is_string; use function restore_error_handler; use function set_error_handler; use function stream_get_contents; use function stream_get_meta_data; use function strstr; use const E_WARNING; use const SEEK_SET; /** * Implementation of PSR HTTP streams */ class Stream implements StreamInterface { /** * A list of allowed stream resource types that are allowed to instantiate a Stream */ private const ALLOWED_STREAM_RESOURCE_TYPES = ['gd', 'stream']; /** * @var resource|null */ protected $resource; /** * @var string|resource */ protected $stream; /** * @param string|resource $stream * @param string $mode Mode with which to open stream * @throws Exception\InvalidArgumentException */ public function __construct($stream, string $mode = 'r') { $this->setStream($stream, $mode); } /** * {@inheritdoc} */ public function __toString() : string { if (! $this->isReadable()) { return ''; } try { if ($this->isSeekable()) { $this->rewind(); } return $this->getContents(); } catch (RuntimeException $e) { return ''; } } /** * {@inheritdoc} */ public function close() : void { if (! $this->resource) { return; } $resource = $this->detach(); fclose($resource); } /** * {@inheritdoc} */ public function detach() { $resource = $this->resource; $this->resource = null; return $resource; } /** * Attach a new stream/resource to the instance. * * @param string|resource $resource * @param string $mode * @throws Exception\InvalidArgumentException for stream identifier that cannot be * cast to a resource * @throws Exception\InvalidArgumentException for non-resource stream */ public function attach($resource, string $mode = 'r') : void { $this->setStream($resource, $mode); } /** * {@inheritdoc} */ public function getSize() : ?int { if (null === $this->resource) { return null; } $stats = fstat($this->resource); if ($stats !== false) { return $stats['size']; } return null; } /** * {@inheritdoc} */ public function tell() : int { if (! $this->resource) { throw Exception\UntellableStreamException::dueToMissingResource(); } $result = ftell($this->resource); if (! is_int($result)) { throw Exception\UntellableStreamException::dueToPhpError(); } return $result; } /** * {@inheritdoc} */ public function eof() : bool { if (! $this->resource) { return true; } return feof($this->resource); } /** * {@inheritdoc} */ public function isSeekable() : bool { if (! $this->resource) { return false; } $meta = stream_get_meta_data($this->resource); return $meta['seekable']; } /** * {@inheritdoc} */ public function seek($offset, $whence = SEEK_SET) : void { if (! $this->resource) { throw Exception\UnseekableStreamException::dueToMissingResource(); } if (! $this->isSeekable()) { throw Exception\UnseekableStreamException::dueToConfiguration(); } $result = fseek($this->resource, $offset, $whence); if (0 !== $result) { throw Exception\UnseekableStreamException::dueToPhpError(); } } /** * {@inheritdoc} */ public function rewind() : void { $this->seek(0); } /** * {@inheritdoc} */ public function isWritable() : bool { if (! $this->resource) { return false; } $meta = stream_get_meta_data($this->resource); $mode = $meta['mode']; return ( strstr($mode, 'x') || strstr($mode, 'w') || strstr($mode, 'c') || strstr($mode, 'a') || strstr($mode, '+') ); } /** * {@inheritdoc} */ public function write($string) : int { if (! $this->resource) { throw Exception\UnwritableStreamException::dueToMissingResource(); } if (! $this->isWritable()) { throw Exception\UnwritableStreamException::dueToConfiguration(); } $result = fwrite($this->resource, $string); if (false === $result) { throw Exception\UnwritableStreamException::dueToPhpError(); } return $result; } /** * {@inheritdoc} */ public function isReadable() : bool { if (! $this->resource) { return false; } $meta = stream_get_meta_data($this->resource); $mode = $meta['mode']; return (strstr($mode, 'r') || strstr($mode, '+')); } /** * {@inheritdoc} */ public function read($length) : string { if (! $this->resource) { throw Exception\UnreadableStreamException::dueToMissingResource(); } if (! $this->isReadable()) { throw Exception\UnreadableStreamException::dueToConfiguration(); } $result = fread($this->resource, $length); if (false === $result) { throw Exception\UnreadableStreamException::dueToPhpError(); } return $result; } /** * {@inheritdoc} */ public function getContents() : string { if (! $this->isReadable()) { throw Exception\UnreadableStreamException::dueToConfiguration(); } $result = stream_get_contents($this->resource); if (false === $result) { throw Exception\UnreadableStreamException::dueToPhpError(); } return $result; } /** * {@inheritdoc} */ public function getMetadata($key = null) { if (null === $key) { return stream_get_meta_data($this->resource); } $metadata = stream_get_meta_data($this->resource); if (! array_key_exists($key, $metadata)) { return null; } return $metadata[$key]; } /** * Set the internal stream resource. * * @param string|resource $stream String stream target or stream resource. * @param string $mode Resource mode for stream target. * @throws Exception\InvalidArgumentException for invalid streams or resources. */ private function setStream($stream, string $mode = 'r') : void { $error = null; $resource = $stream; if (is_string($stream)) { set_error_handler(function ($e) use (&$error) { if ($e !== E_WARNING) { return; } $error = $e; }); $resource = fopen($stream, $mode); restore_error_handler(); } if ($error) { throw new Exception\InvalidArgumentException('Invalid stream reference provided'); } if (! $this->isValidStreamResourceType($resource)) { throw new Exception\InvalidArgumentException( 'Invalid stream provided; must be a string stream identifier or stream resource' ); } if ($stream !== $resource) { $this->stream = $stream; } $this->resource = $resource; } /** * Determine if a resource is one of the resource types allowed to instantiate a Stream * * @param resource $resource Stream resource. */ private function isValidStreamResourceType($resource): bool { return ( is_resource($resource) && in_array(get_resource_type($resource), self::ALLOWED_STREAM_RESOURCE_TYPES, true) ); } } src/ServerRequestFactory.php 0000644 00000006020 15173734626 0012227 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use Psr\Http\Message\ServerRequestFactoryInterface; use Psr\Http\Message\ServerRequestInterface; use function array_key_exists; use function is_callable; /** * Class for marshaling a request object from the current PHP environment. * * Logic largely refactored from the Laminas Laminas\Http\PhpEnvironment\Request class. * * @copyright Copyright (c) 2005-2015 Laminas (https://www.zend.com) * @license https://getlaminas.org/license/new-bsd New BSD License */ class ServerRequestFactory implements ServerRequestFactoryInterface { /** * Function to use to get apache request headers; present only to simplify mocking. * * @var callable */ private static $apacheRequestHeaders = 'apache_request_headers'; /** * Create a request from the supplied superglobal values. * * If any argument is not supplied, the corresponding superglobal value will * be used. * * The ServerRequest created is then passed to the fromServer() method in * order to marshal the request URI and headers. * * @see fromServer() * @param array $server $_SERVER superglobal * @param array $query $_GET superglobal * @param array $body $_POST superglobal * @param array $cookies $_COOKIE superglobal * @param array $files $_FILES superglobal * @return ServerRequest */ public static function fromGlobals( array $server = null, array $query = null, array $body = null, array $cookies = null, array $files = null ) : ServerRequest { $server = normalizeServer( $server ?: $_SERVER, is_callable(self::$apacheRequestHeaders) ? self::$apacheRequestHeaders : null ); $files = normalizeUploadedFiles($files ?: $_FILES); $headers = marshalHeadersFromSapi($server); if (null === $cookies && array_key_exists('cookie', $headers)) { $cookies = parseCookieHeader($headers['cookie']); } return new ServerRequest( $server, $files, marshalUriFromSapi($server, $headers), marshalMethodFromSapi($server), 'php://input', $headers, $cookies ?: $_COOKIE, $query ?: $_GET, $body ?: $_POST, marshalProtocolVersionFromSapi($server) ); } /** * {@inheritDoc} */ public function createServerRequest(string $method, $uri, array $serverParams = []) : ServerRequestInterface { $uploadedFiles = []; return new ServerRequest( $serverParams, $uploadedFiles, $uri, $method, 'php://temp' ); } } src/PhpInputStream.php 0000644 00000003461 15173734626 0011011 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use function stream_get_contents; /** * Caching version of php://input */ class PhpInputStream extends Stream { /** * @var string */ private $cache = ''; /** * @var bool */ private $reachedEof = false; /** * @param string|resource $stream */ public function __construct($stream = 'php://input') { parent::__construct($stream, 'r'); } /** * {@inheritdoc} */ public function __toString() : string { if ($this->reachedEof) { return $this->cache; } $this->getContents(); return $this->cache; } /** * {@inheritdoc} */ public function isWritable() : bool { return false; } /** * {@inheritdoc} */ public function read($length) : string { $content = parent::read($length); if (! $this->reachedEof) { $this->cache .= $content; } if ($this->eof()) { $this->reachedEof = true; } return $content; } /** * {@inheritdoc} */ public function getContents($maxLength = -1) : string { if ($this->reachedEof) { return $this->cache; } $contents = stream_get_contents($this->resource, $maxLength); $this->cache .= $contents; if ($maxLength === -1 || $this->eof()) { $this->reachedEof = true; } return $contents; } } src/CallbackStream.php 0000644 00000006545 15173734626 0010744 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use Psr\Http\Message\StreamInterface; use function array_key_exists; use const SEEK_SET; /** * Implementation of PSR HTTP streams */ class CallbackStream implements StreamInterface { /** * @var callable|null */ protected $callback; /** * @param callable $callback * @throws Exception\InvalidArgumentException */ public function __construct(callable $callback) { $this->attach($callback); } /** * {@inheritdoc} */ public function __toString() : string { return $this->getContents(); } /** * {@inheritdoc} */ public function close() : void { $this->callback = null; } /** * {@inheritdoc} */ public function detach() : ?callable { $callback = $this->callback; $this->callback = null; return $callback; } /** * Attach a new callback to the instance. */ public function attach(callable $callback) : void { $this->callback = $callback; } /** * {@inheritdoc} */ public function getSize() : ?int { return null; } /** * {@inheritdoc} */ public function tell() : int { throw Exception\UntellableStreamException::forCallbackStream(); } /** * {@inheritdoc} */ public function eof() : bool { return empty($this->callback); } /** * {@inheritdoc} */ public function isSeekable() : bool { return false; } /** * {@inheritdoc} */ public function seek($offset, $whence = SEEK_SET) { throw Exception\UnseekableStreamException::forCallbackStream(); } /** * {@inheritdoc} */ public function rewind() : void { throw Exception\UnrewindableStreamException::forCallbackStream(); } /** * {@inheritdoc} */ public function isWritable() : bool { return false; } /** * {@inheritdoc} */ public function write($string) : void { throw Exception\UnwritableStreamException::forCallbackStream(); } /** * {@inheritdoc} */ public function isReadable() : bool { return false; } /** * {@inheritdoc} */ public function read($length) : string { throw Exception\UnreadableStreamException::forCallbackStream(); } /** * {@inheritdoc} */ public function getContents() : string { $callback = $this->detach(); $contents = $callback ? $callback() : ''; return (string) $contents; } /** * {@inheritdoc} */ public function getMetadata($key = null) { $metadata = [ 'eof' => $this->eof(), 'stream_type' => 'callback', 'seekable' => false ]; if (null === $key) { return $metadata; } if (! array_key_exists($key, $metadata)) { return null; } return $metadata[$key]; } } src/MessageTrait.php 0000644 00000031200 15173734626 0010446 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use Psr\Http\Message\MessageInterface; use Psr\Http\Message\StreamInterface; use function array_map; use function array_merge; use function get_class; use function gettype; use function implode; use function is_array; use function is_object; use function is_resource; use function is_string; use function preg_match; use function sprintf; use function strtolower; /** * Trait implementing the various methods defined in MessageInterface. * * @see https://github.com/php-fig/http-message/tree/master/src/MessageInterface.php */ trait MessageTrait { /** * List of all registered headers, as key => array of values. * * @var array */ protected $headers = []; /** * Map of normalized header name to original name used to register header. * * @var array */ protected $headerNames = []; /** * @var string */ private $protocol = '1.1'; /** * @var StreamInterface */ private $stream; /** * Retrieves the HTTP protocol version as a string. * * The string MUST contain only the HTTP version number (e.g., "1.1", "1.0"). * * @return string HTTP protocol version. */ public function getProtocolVersion() : string { return $this->protocol; } /** * Return an instance with the specified HTTP protocol version. * * The version string MUST contain only the HTTP version number (e.g., * "1.1", "1.0"). * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * new protocol version. * * @param string $version HTTP protocol version * @return static */ public function withProtocolVersion($version) : MessageInterface { $this->validateProtocolVersion($version); $new = clone $this; $new->protocol = $version; return $new; } /** * Retrieves all message headers. * * The keys represent the header name as it will be sent over the wire, and * each value is an array of strings associated with the header. * * // Represent the headers as a string * foreach ($message->getHeaders() as $name => $values) { * echo $name . ": " . implode(", ", $values); * } * * // Emit headers iteratively: * foreach ($message->getHeaders() as $name => $values) { * foreach ($values as $value) { * header(sprintf('%s: %s', $name, $value), false); * } * } * * @return array Returns an associative array of the message's headers. Each * key MUST be a header name, and each value MUST be an array of strings. */ public function getHeaders() : array { return $this->headers; } /** * Checks if a header exists by the given case-insensitive name. * * @param string $header Case-insensitive header name. * @return bool Returns true if any header names match the given header * name using a case-insensitive string comparison. Returns false if * no matching header name is found in the message. */ public function hasHeader($header) : bool { return isset($this->headerNames[strtolower($header)]); } /** * Retrieves a message header value by the given case-insensitive name. * * This method returns an array of all the header values of the given * case-insensitive header name. * * If the header does not appear in the message, this method MUST return an * empty array. * * @param string $header Case-insensitive header field name. * @return string[] An array of string values as provided for the given * header. If the header does not appear in the message, this method MUST * return an empty array. */ public function getHeader($header) : array { if (! $this->hasHeader($header)) { return []; } $header = $this->headerNames[strtolower($header)]; return $this->headers[$header]; } /** * Retrieves a comma-separated string of the values for a single header. * * This method returns all of the header values of the given * case-insensitive header name as a string concatenated together using * a comma. * * NOTE: Not all header values may be appropriately represented using * comma concatenation. For such headers, use getHeader() instead * and supply your own delimiter when concatenating. * * If the header does not appear in the message, this method MUST return * an empty string. * * @param string $name Case-insensitive header field name. * @return string A string of values as provided for the given header * concatenated together using a comma. If the header does not appear in * the message, this method MUST return an empty string. */ public function getHeaderLine($name) : string { $value = $this->getHeader($name); if (empty($value)) { return ''; } return implode(',', $value); } /** * Return an instance with the provided header, replacing any existing * values of any headers with the same case-insensitive name. * * While header names are case-insensitive, the casing of the header will * be preserved by this function, and returned from getHeaders(). * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * new and/or updated header and value. * * @param string $header Case-insensitive header field name. * @param string|string[] $value Header value(s). * @return static * @throws Exception\InvalidArgumentException for invalid header names or values. */ public function withHeader($header, $value) : MessageInterface { $this->assertHeader($header); $normalized = strtolower($header); $new = clone $this; if ($new->hasHeader($header)) { unset($new->headers[$new->headerNames[$normalized]]); } $value = $this->filterHeaderValue($value); $new->headerNames[$normalized] = $header; $new->headers[$header] = $value; return $new; } /** * Return an instance with the specified header appended with the * given value. * * Existing values for the specified header will be maintained. The new * value(s) will be appended to the existing list. If the header did not * exist previously, it will be added. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * new header and/or value. * * @param string $header Case-insensitive header field name to add. * @param string|string[] $value Header value(s). * @return static * @throws Exception\InvalidArgumentException for invalid header names or values. */ public function withAddedHeader($header, $value) : MessageInterface { $this->assertHeader($header); if (! $this->hasHeader($header)) { return $this->withHeader($header, $value); } $header = $this->headerNames[strtolower($header)]; $new = clone $this; $value = $this->filterHeaderValue($value); $new->headers[$header] = array_merge($this->headers[$header], $value); return $new; } /** * Return an instance without the specified header. * * Header resolution MUST be done without case-sensitivity. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that removes * the named header. * * @param string $header Case-insensitive header field name to remove. * @return static */ public function withoutHeader($header) : MessageInterface { if (! $this->hasHeader($header)) { return clone $this; } $normalized = strtolower($header); $original = $this->headerNames[$normalized]; $new = clone $this; unset($new->headers[$original], $new->headerNames[$normalized]); return $new; } /** * Gets the body of the message. * * @return StreamInterface Returns the body as a stream. */ public function getBody() : StreamInterface { return $this->stream; } /** * Return an instance with the specified message body. * * The body MUST be a StreamInterface object. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return a new instance that has the * new body stream. * * @param StreamInterface $body Body. * @return static * @throws Exception\InvalidArgumentException When the body is not valid. */ public function withBody(StreamInterface $body) : MessageInterface { $new = clone $this; $new->stream = $body; return $new; } private function getStream($stream, string $modeIfNotInstance) : StreamInterface { if ($stream instanceof StreamInterface) { return $stream; } if (! is_string($stream) && ! is_resource($stream)) { throw new Exception\InvalidArgumentException( 'Stream must be a string stream resource identifier, ' . 'an actual stream resource, ' . 'or a Psr\Http\Message\StreamInterface implementation' ); } return new Stream($stream, $modeIfNotInstance); } /** * Filter a set of headers to ensure they are in the correct internal format. * * Used by message constructors to allow setting all initial headers at once. * * @param array $originalHeaders Headers to filter. */ private function setHeaders(array $originalHeaders) : void { $headerNames = $headers = []; foreach ($originalHeaders as $header => $value) { $value = $this->filterHeaderValue($value); $this->assertHeader($header); $headerNames[strtolower($header)] = $header; $headers[$header] = $value; } $this->headerNames = $headerNames; $this->headers = $headers; } /** * Validate the HTTP protocol version * * @param string $version * @throws Exception\InvalidArgumentException on invalid HTTP protocol version */ private function validateProtocolVersion($version) : void { if (empty($version)) { throw new Exception\InvalidArgumentException( 'HTTP protocol version can not be empty' ); } if (! is_string($version)) { throw new Exception\InvalidArgumentException(sprintf( 'Unsupported HTTP protocol version; must be a string, received %s', (is_object($version) ? get_class($version) : gettype($version)) )); } // HTTP/1 uses a "<major>.<minor>" numbering scheme to indicate // versions of the protocol, while HTTP/2 does not. if (! preg_match('#^(1\.[01]|2)$#', $version)) { throw new Exception\InvalidArgumentException(sprintf( 'Unsupported HTTP protocol version "%s" provided', $version )); } } /** * @param mixed $values * @return string[] */ private function filterHeaderValue($values) : array { if (! is_array($values)) { $values = [$values]; } if ([] === $values) { throw new Exception\InvalidArgumentException( 'Invalid header value: must be a string or array of strings; ' . 'cannot be an empty array' ); } return array_map(function ($value) { HeaderSecurity::assertValid($value); return (string) $value; }, array_values($values)); } /** * Ensure header name and values are valid. * * @param string $name * * @throws Exception\InvalidArgumentException */ private function assertHeader($name) : void { HeaderSecurity::assertValidName($name); } } src/Response/Serializer.php 0000644 00000006446 15173734626 0012003 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros\Response; use Laminas\Diactoros\AbstractSerializer; use Laminas\Diactoros\Exception; use Laminas\Diactoros\Response; use Laminas\Diactoros\Stream; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\StreamInterface; use function preg_match; use function sprintf; final class Serializer extends AbstractSerializer { /** * Deserialize a response string to a response instance. * * @throws Exception\SerializationException when errors occur parsing the message. */ public static function fromString(string $message) : Response { $stream = new Stream('php://temp', 'wb+'); $stream->write($message); return static::fromStream($stream); } /** * Parse a response from a stream. * * @throws Exception\InvalidArgumentException when the stream is not readable. * @throws Exception\SerializationException when errors occur parsing the message. */ public static function fromStream(StreamInterface $stream) : Response { if (! $stream->isReadable() || ! $stream->isSeekable()) { throw new Exception\InvalidArgumentException('Message stream must be both readable and seekable'); } $stream->rewind(); [$version, $status, $reasonPhrase] = self::getStatusLine($stream); [$headers, $body] = self::splitStream($stream); return (new Response($body, $status, $headers)) ->withProtocolVersion($version) ->withStatus((int) $status, $reasonPhrase); } /** * Create a string representation of a response. */ public static function toString(ResponseInterface $response) : string { $reasonPhrase = $response->getReasonPhrase(); $headers = self::serializeHeaders($response->getHeaders()); $body = (string) $response->getBody(); $format = 'HTTP/%s %d%s%s%s'; if (! empty($headers)) { $headers = "\r\n" . $headers; } $headers .= "\r\n\r\n"; return sprintf( $format, $response->getProtocolVersion(), $response->getStatusCode(), ($reasonPhrase ? ' ' . $reasonPhrase : ''), $headers, $body ); } /** * Retrieve the status line for the message. * * @return array Array with three elements: 0 => version, 1 => status, 2 => reason * @throws Exception\SerializationException if line is malformed */ private static function getStatusLine(StreamInterface $stream) : array { $line = self::getLine($stream); if (! preg_match( '#^HTTP/(?P<version>[1-9]\d*\.\d) (?P<status>[1-5]\d{2})(\s+(?P<reason>.+))?$#', $line, $matches )) { throw Exception\SerializationException::forInvalidStatusLine(); } return [$matches['version'], (int) $matches['status'], isset($matches['reason']) ? $matches['reason'] : '']; } } src/Response/ArraySerializer.php 0000644 00000005616 15173734626 0013000 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros\Response; use Laminas\Diactoros\Exception; use Laminas\Diactoros\Response; use Laminas\Diactoros\Stream; use Psr\Http\Message\ResponseInterface; use Throwable; use function sprintf; /** * Serialize or deserialize response messages to/from arrays. * * This class provides functionality for serializing a ResponseInterface instance * to an array, as well as the reverse operation of creating a Response instance * from an array representing a message. */ final class ArraySerializer { /** * Serialize a response message to an array. */ public static function toArray(ResponseInterface $response) : array { return [ 'status_code' => $response->getStatusCode(), 'reason_phrase' => $response->getReasonPhrase(), 'protocol_version' => $response->getProtocolVersion(), 'headers' => $response->getHeaders(), 'body' => (string) $response->getBody(), ]; } /** * Deserialize a response array to a response instance. * * @throws Exception\DeserializationException when cannot deserialize response */ public static function fromArray(array $serializedResponse) : Response { try { $body = new Stream('php://memory', 'wb+'); $body->write(self::getValueFromKey($serializedResponse, 'body')); $statusCode = self::getValueFromKey($serializedResponse, 'status_code'); $headers = self::getValueFromKey($serializedResponse, 'headers'); $protocolVersion = self::getValueFromKey($serializedResponse, 'protocol_version'); $reasonPhrase = self::getValueFromKey($serializedResponse, 'reason_phrase'); return (new Response($body, $statusCode, $headers)) ->withProtocolVersion($protocolVersion) ->withStatus($statusCode, $reasonPhrase); } catch (Throwable $exception) { throw Exception\DeserializationException::forResponseFromArray($exception); } } /** * @param array $data * @param string $key * @param string $message * @return mixed * @throws Exception\DeserializationException */ private static function getValueFromKey(array $data, string $key, string $message = null) { if (isset($data[$key])) { return $data[$key]; } if ($message === null) { $message = sprintf('Missing "%s" key in serialized response', $key); } throw new Exception\DeserializationException($message); } } src/Response/TextResponse.php 0000644 00000004522 15173734626 0012326 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros\Response; use Laminas\Diactoros\Exception; use Laminas\Diactoros\Response; use Laminas\Diactoros\Stream; use Psr\Http\Message\StreamInterface; use function get_class; use function gettype; use function is_object; use function is_string; use function sprintf; /** * Plain text response. * * Allows creating a response by passing a string to the constructor; * by default, sets a status code of 200 and sets the Content-Type header to * text/plain. */ class TextResponse extends Response { use InjectContentTypeTrait; /** * Create a plain text response. * * Produces a text response with a Content-Type of text/plain and a default * status of 200. * * @param string|StreamInterface $text String or stream for the message body. * @param int $status Integer status code for the response; 200 by default. * @param array $headers Array of headers to use at initialization. * @throws Exception\InvalidArgumentException if $text is neither a string or stream. */ public function __construct($text, int $status = 200, array $headers = []) { parent::__construct( $this->createBody($text), $status, $this->injectContentType('text/plain; charset=utf-8', $headers) ); } /** * Create the message body. * * @param string|StreamInterface $text * @throws Exception\InvalidArgumentException if $text is neither a string or stream. */ private function createBody($text) : StreamInterface { if ($text instanceof StreamInterface) { return $text; } if (! is_string($text)) { throw new Exception\InvalidArgumentException(sprintf( 'Invalid content (%s) provided to %s', (is_object($text) ? get_class($text) : gettype($text)), __CLASS__ )); } $body = new Stream('php://temp', 'wb+'); $body->write($text); $body->rewind(); return $body; } } src/Response/XmlResponse.php 0000644 00000004542 15173734626 0012144 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros\Response; use Laminas\Diactoros\Exception; use Laminas\Diactoros\Response; use Laminas\Diactoros\Stream; use Psr\Http\Message\StreamInterface; use function get_class; use function gettype; use function is_object; use function is_string; use function sprintf; /** * XML response. * * Allows creating a response by passing an XML string to the constructor; by default, * sets a status code of 200 and sets the Content-Type header to application/xml. */ class XmlResponse extends Response { use InjectContentTypeTrait; /** * Create an XML response. * * Produces an XML response with a Content-Type of application/xml and a default * status of 200. * * @param string|StreamInterface $xml String or stream for the message body. * @param int $status Integer status code for the response; 200 by default. * @param array $headers Array of headers to use at initialization. * @throws Exception\InvalidArgumentException if $text is neither a string or stream. */ public function __construct( $xml, int $status = 200, array $headers = [] ) { parent::__construct( $this->createBody($xml), $status, $this->injectContentType('application/xml; charset=utf-8', $headers) ); } /** * Create the message body. * * @param string|StreamInterface $xml * @throws Exception\InvalidArgumentException if $xml is neither a string or stream. */ private function createBody($xml) : StreamInterface { if ($xml instanceof StreamInterface) { return $xml; } if (! is_string($xml)) { throw new Exception\InvalidArgumentException(sprintf( 'Invalid content (%s) provided to %s', (is_object($xml) ? get_class($xml) : gettype($xml)), __CLASS__ )); } $body = new Stream('php://temp', 'wb+'); $body->write($xml); $body->rewind(); return $body; } } src/Response/InjectContentTypeTrait.php 0000644 00000001766 15173734626 0014307 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros\Response; use function array_keys; use function array_reduce; use function strtolower; trait InjectContentTypeTrait { /** * Inject the provided Content-Type, if none is already present. * * @return array Headers with injected Content-Type */ private function injectContentType(string $contentType, array $headers) : array { $hasContentType = array_reduce(array_keys($headers), function ($carry, $item) { return $carry ?: (strtolower($item) === 'content-type'); }, false); if (! $hasContentType) { $headers['content-type'] = [$contentType]; } return $headers; } } src/Response/EmptyResponse.php 0000644 00000002261 15173734626 0012476 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros\Response; use Laminas\Diactoros\Response; use Laminas\Diactoros\Stream; /** * A class representing empty HTTP responses. */ class EmptyResponse extends Response { /** * Create an empty response with the given status code. * * @param int $status Status code for the response, if any. * @param array $headers Headers for the response, if any. */ public function __construct(int $status = 204, array $headers = []) { $body = new Stream('php://temp', 'r'); parent::__construct($body, $status, $headers); } /** * Create an empty response with the given headers. * * @param array $headers Headers for the response. * @return EmptyResponse */ public static function withHeaders(array $headers) : EmptyResponse { return new static(204, $headers); } } src/Response/RedirectResponse.php 0000644 00000003233 15173734626 0013141 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros\Response; use Laminas\Diactoros\Exception; use Laminas\Diactoros\Response; use Psr\Http\Message\UriInterface; use function get_class; use function gettype; use function is_object; use function is_string; use function sprintf; /** * Produce a redirect response. */ class RedirectResponse extends Response { /** * Create a redirect response. * * Produces a redirect response with a Location header and the given status * (302 by default). * * Note: this method overwrites the `location` $headers value. * * @param string|UriInterface $uri URI for the Location header. * @param int $status Integer status code for the redirect; 302 by default. * @param array $headers Array of headers to use at initialization. */ public function __construct($uri, int $status = 302, array $headers = []) { if (! is_string($uri) && ! $uri instanceof UriInterface) { throw new Exception\InvalidArgumentException(sprintf( 'Uri provided to %s MUST be a string or Psr\Http\Message\UriInterface instance; received "%s"', __CLASS__, (is_object($uri) ? get_class($uri) : gettype($uri)) )); } $headers['location'] = [(string) $uri]; parent::__construct('php://temp', $status, $headers); } } src/Response/HtmlResponse.php 0000644 00000004511 15173734626 0012304 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros\Response; use Laminas\Diactoros\Exception; use Laminas\Diactoros\Response; use Laminas\Diactoros\Stream; use Psr\Http\Message\StreamInterface; use function get_class; use function gettype; use function is_object; use function is_string; use function sprintf; /** * HTML response. * * Allows creating a response by passing an HTML string to the constructor; * by default, sets a status code of 200 and sets the Content-Type header to * text/html. */ class HtmlResponse extends Response { use InjectContentTypeTrait; /** * Create an HTML response. * * Produces an HTML response with a Content-Type of text/html and a default * status of 200. * * @param string|StreamInterface $html HTML or stream for the message body. * @param int $status Integer status code for the response; 200 by default. * @param array $headers Array of headers to use at initialization. * @throws Exception\InvalidArgumentException if $html is neither a string or stream. */ public function __construct($html, int $status = 200, array $headers = []) { parent::__construct( $this->createBody($html), $status, $this->injectContentType('text/html; charset=utf-8', $headers) ); } /** * Create the message body. * * @param string|StreamInterface $html * @throws Exception\InvalidArgumentException if $html is neither a string or stream. */ private function createBody($html) : StreamInterface { if ($html instanceof StreamInterface) { return $html; } if (! is_string($html)) { throw new Exception\InvalidArgumentException(sprintf( 'Invalid content (%s) provided to %s', (is_object($html) ? get_class($html) : gettype($html)), __CLASS__ )); } $body = new Stream('php://temp', 'wb+'); $body->write($html); $body->rewind(); return $body; } } src/Response/JsonResponse.php 0000644 00000011367 15173734626 0012320 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros\Response; use Laminas\Diactoros\Exception; use Laminas\Diactoros\Response; use Laminas\Diactoros\Stream; use function is_object; use function is_resource; use function json_encode; use function json_last_error; use function json_last_error_msg; use function sprintf; use const JSON_ERROR_NONE; /** * JSON response. * * Allows creating a response by passing data to the constructor; by default, * serializes the data to JSON, sets a status code of 200 and sets the * Content-Type header to application/json. */ class JsonResponse extends Response { use InjectContentTypeTrait; /** * Default flags for json_encode; value of: * * <code> * JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT | JSON_UNESCAPED_SLASHES * </code> * * @const int */ const DEFAULT_JSON_FLAGS = 79; /** * @var mixed */ private $payload; /** * @var int */ private $encodingOptions; /** * Create a JSON response with the given data. * * Default JSON encoding is performed with the following options, which * produces RFC4627-compliant JSON, capable of embedding into HTML. * * - JSON_HEX_TAG * - JSON_HEX_APOS * - JSON_HEX_AMP * - JSON_HEX_QUOT * - JSON_UNESCAPED_SLASHES * * @param mixed $data Data to convert to JSON. * @param int $status Integer status code for the response; 200 by default. * @param array $headers Array of headers to use at initialization. * @param int $encodingOptions JSON encoding options to use. * @throws Exception\InvalidArgumentException if unable to encode the $data to JSON. */ public function __construct( $data, int $status = 200, array $headers = [], int $encodingOptions = self::DEFAULT_JSON_FLAGS ) { $this->setPayload($data); $this->encodingOptions = $encodingOptions; $json = $this->jsonEncode($data, $this->encodingOptions); $body = $this->createBodyFromJson($json); $headers = $this->injectContentType('application/json', $headers); parent::__construct($body, $status, $headers); } /** * @return mixed */ public function getPayload() { return $this->payload; } /** * @param mixed $data */ public function withPayload($data) : JsonResponse { $new = clone $this; $new->setPayload($data); return $this->updateBodyFor($new); } public function getEncodingOptions() : int { return $this->encodingOptions; } public function withEncodingOptions(int $encodingOptions) : JsonResponse { $new = clone $this; $new->encodingOptions = $encodingOptions; return $this->updateBodyFor($new); } private function createBodyFromJson(string $json) : Stream { $body = new Stream('php://temp', 'wb+'); $body->write($json); $body->rewind(); return $body; } /** * Encode the provided data to JSON. * * @param mixed $data * @throws Exception\InvalidArgumentException if unable to encode the $data to JSON. */ private function jsonEncode($data, int $encodingOptions) : string { if (is_resource($data)) { throw new Exception\InvalidArgumentException('Cannot JSON encode resources'); } // Clear json_last_error() json_encode(null); $json = json_encode($data, $encodingOptions); if (JSON_ERROR_NONE !== json_last_error()) { throw new Exception\InvalidArgumentException(sprintf( 'Unable to encode data to JSON in %s: %s', __CLASS__, json_last_error_msg() )); } return $json; } /** * @param mixed $data */ private function setPayload($data) : void { if (is_object($data)) { $data = clone $data; } $this->payload = $data; } /** * Update the response body for the given instance. * * @param self $toUpdate Instance to update. * @return JsonResponse Returns a new instance with an updated body. */ private function updateBodyFor(JsonResponse $toUpdate) : JsonResponse { $json = $this->jsonEncode($toUpdate->payload, $toUpdate->encodingOptions); $body = $this->createBodyFromJson($json); return $toUpdate->withBody($body); } } src/RequestFactory.php 0000644 00000001226 15173734626 0011043 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use Psr\Http\Message\RequestFactoryInterface; use Psr\Http\Message\RequestInterface; class RequestFactory implements RequestFactoryInterface { /** * {@inheritDoc} */ public function createRequest(string $method, $uri) : RequestInterface { return new Request($uri, $method); } } src/HeaderSecurity.php 0000644 00000012477 15173734626 0011015 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use function get_class; use function gettype; use function in_array; use function is_numeric; use function is_object; use function is_string; use function ord; use function preg_match; use function sprintf; use function strlen; /** * Provide security tools around HTTP headers to prevent common injection vectors. * * Code is largely lifted from the Laminas\Http\Header\HeaderValue implementation in * Laminas, released with the copyright and license below. * * @copyright Copyright (c) 2005-2015 Laminas (https://www.zend.com) * @license https://getlaminas.org/license/new-bsd New BSD License */ final class HeaderSecurity { /** * Private constructor; non-instantiable. * @codeCoverageIgnore */ private function __construct() { } /** * Filter a header value * * Ensures CRLF header injection vectors are filtered. * * Per RFC 7230, only VISIBLE ASCII characters, spaces, and horizontal * tabs are allowed in values; header continuations MUST consist of * a single CRLF sequence followed by a space or horizontal tab. * * This method filters any values not allowed from the string, and is * lossy. * * @see http://en.wikipedia.org/wiki/HTTP_response_splitting */ public static function filter(string $value) : string { $value = (string) $value; $length = strlen($value); $string = ''; for ($i = 0; $i < $length; $i += 1) { $ascii = ord($value[$i]); // Detect continuation sequences if ($ascii === 13) { $lf = ord($value[$i + 1]); $ws = ord($value[$i + 2]); if ($lf === 10 && in_array($ws, [9, 32], true)) { $string .= $value[$i] . $value[$i + 1]; $i += 1; } continue; } // Non-visible, non-whitespace characters // 9 === horizontal tab // 32-126, 128-254 === visible // 127 === DEL // 255 === null byte if (($ascii < 32 && $ascii !== 9) || $ascii === 127 || $ascii > 254 ) { continue; } $string .= $value[$i]; } return $string; } /** * Validate a header value. * * Per RFC 7230, only VISIBLE ASCII characters, spaces, and horizontal * tabs are allowed in values; header continuations MUST consist of * a single CRLF sequence followed by a space or horizontal tab. * * @param string|int|float $value * @see http://en.wikipedia.org/wiki/HTTP_response_splitting */ public static function isValid($value) : bool { $value = (string) $value; // Look for: // \n not preceded by \r, OR // \r not followed by \n, OR // \r\n not followed by space or horizontal tab; these are all CRLF attacks if (preg_match("#(?:(?:(?<!\r)\n)|(?:\r(?!\n))|(?:\r\n(?![ \t])))#", $value)) { return false; } // Non-visible, non-whitespace characters // 9 === horizontal tab // 10 === line feed // 13 === carriage return // 32-126, 128-254 === visible // 127 === DEL (disallowed) // 255 === null byte (disallowed) if (preg_match('/[^\x09\x0a\x0d\x20-\x7E\x80-\xFE]/', $value)) { return false; } return true; } /** * Assert a header value is valid. * * @param mixed $value Value to be tested. This method asserts it is a string or number. * @throws Exception\InvalidArgumentException for invalid values */ public static function assertValid($value) { if (! is_string($value) && ! is_numeric($value)) { throw new Exception\InvalidArgumentException(sprintf( 'Invalid header value type; must be a string or numeric; received %s', (is_object($value) ? get_class($value) : gettype($value)) )); } if (! self::isValid($value)) { throw new Exception\InvalidArgumentException(sprintf( '"%s" is not valid header value', $value )); } } /** * Assert whether or not a header name is valid. * * @see http://tools.ietf.org/html/rfc7230#section-3.2 * @param mixed $name * @throws Exception\InvalidArgumentException */ public static function assertValidName($name) { if (! is_string($name)) { throw new Exception\InvalidArgumentException(sprintf( 'Invalid header name type; expected string; received %s', (is_object($name) ? get_class($name) : gettype($name)) )); } if (! preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/D', $name)) { throw new Exception\InvalidArgumentException(sprintf( '"%s" is not valid header name', $name )); } } } src/Exception/UnreadableStreamException.php 0000644 00000001700 15173734626 0015113 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros\Exception; use RuntimeException; class UnreadableStreamException extends RuntimeException implements ExceptionInterface { public static function dueToConfiguration() : self { return new self('Stream is not readable'); } public static function dueToMissingResource() : self { return new self('No resource available; cannot read'); } public static function dueToPhpError() : self { return new self('Error reading stream'); } public static function forCallbackStream() : self { return new self('Callback streams cannot read'); } } src/Exception/UploadedFileErrorException.php 0000644 00000002303 15173734626 0015244 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros\Exception; use RuntimeException; use function sprintf; class UploadedFileErrorException extends RuntimeException implements ExceptionInterface { public static function forUnmovableFile() : self { return new self('Error occurred while moving uploaded file'); } public static function dueToStreamUploadError(string $error) : self { return new self(sprintf( 'Cannot retrieve stream due to upload error: %s', $error )); } public static function dueToUnwritablePath() : self { return new self('Unable to write to designated path'); } public static function dueToUnwritableTarget(string $targetDirectory) : self { return new self(sprintf( 'The target directory `%s` does not exists or is not writable', $targetDirectory )); } } src/Exception/InvalidStreamPointerPositionException.php 0000644 00000001310 15173734626 0017522 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros\Exception; use RuntimeException; use Throwable; class InvalidStreamPointerPositionException extends RuntimeException implements ExceptionInterface { public function __construct( string $message = 'Invalid pointer position', $code = 0, Throwable $previous = null ) { parent::__construct($message, $code, $previous); } } src/Exception/UnseekableStreamException.php 0000644 00000001731 15173734626 0015133 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros\Exception; use RuntimeException; class UnseekableStreamException extends RuntimeException implements ExceptionInterface { public static function dueToConfiguration() : self { return new self('Stream is not seekable'); } public static function dueToMissingResource() : self { return new self('No resource available; cannot seek position'); } public static function dueToPhpError() : self { return new self('Error seeking within stream'); } public static function forCallbackStream() : self { return new self('Callback streams cannot seek position'); } } src/Exception/UnwritableStreamException.php 0000644 00000001705 15173734626 0015172 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros\Exception; use RuntimeException; class UnwritableStreamException extends RuntimeException implements ExceptionInterface { public static function dueToConfiguration() : self { return new self('Stream is not writable'); } public static function dueToMissingResource() : self { return new self('No resource available; cannot write'); } public static function dueToPhpError() : self { return new self('Error writing to stream'); } public static function forCallbackStream() : self { return new self('Callback streams cannot write'); } } src/Exception/ExceptionInterface.php 0000644 00000000747 15173734626 0013607 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros\Exception; use Throwable; /** * Marker interface for package-specific exceptions. */ interface ExceptionInterface extends Throwable { } src/Exception/UnrewindableStreamException.php 0000644 00000001141 15173734626 0015467 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros\Exception; use RuntimeException; class UnrewindableStreamException extends RuntimeException implements ExceptionInterface { public static function forCallbackStream() : self { return new self('Callback streams cannot rewind position'); } } src/Exception/UploadedFileAlreadyMovedException.php 0000644 00000001335 15173734626 0016533 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros\Exception; use RuntimeException; use Throwable; class UploadedFileAlreadyMovedException extends RuntimeException implements ExceptionInterface { public function __construct( string $message = 'Cannot retrieve stream after it has already moved', $code = 0, Throwable $previous = null ) { parent::__construct($message, $code, $previous); } } src/Exception/InvalidArgumentException.php 0000644 00000000712 15173734626 0014770 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros\Exception; class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface { } src/Exception/DeserializationException.php 0000644 00000002736 15173734626 0015035 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros\Exception; use Throwable; use UnexpectedValueException; class DeserializationException extends UnexpectedValueException implements ExceptionInterface { public static function forInvalidHeader() : self { throw new self('Invalid header detected'); } public static function forInvalidHeaderContinuation() : self { throw new self('Invalid header continuation'); } public static function forRequestFromArray(Throwable $previous) : self { return new self('Cannot deserialize request', $previous->getCode(), $previous); } public static function forResponseFromArray(Throwable $previous) : self { return new self('Cannot deserialize response', $previous->getCode(), $previous); } public static function forUnexpectedCarriageReturn() : self { throw new self('Unexpected carriage return detected'); } public static function forUnexpectedEndOfHeaders() : self { throw new self('Unexpected end of headers'); } public static function forUnexpectedLineFeed() : self { throw new self('Unexpected line feed detected'); } } src/Exception/UnrecognizedProtocolVersionException.php 0000644 00000001247 15173734626 0017427 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros\Exception; use UnexpectedValueException; use function sprintf; class UnrecognizedProtocolVersionException extends UnexpectedValueException implements ExceptionInterface { public static function forVersion(string $version) : self { return new self(sprintf('Unrecognized protocol version (%s)', $version)); } } src/Exception/UntellableStreamException.php 0000644 00000001553 15173734626 0015146 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros\Exception; use RuntimeException; class UntellableStreamException extends RuntimeException implements ExceptionInterface { public static function dueToMissingResource() : self { return new self('No resource available; cannot tell position'); } public static function dueToPhpError() : self { return new self('Error occurred during tell operation'); } public static function forCallbackStream() : self { return new self('Callback streams cannot tell position'); } } src/Exception/SerializationException.php 0000644 00000001340 15173734626 0014512 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros\Exception; use UnexpectedValueException; class SerializationException extends UnexpectedValueException implements ExceptionInterface { public static function forInvalidRequestLine() : self { return new self('Invalid request line detected'); } public static function forInvalidStatusLine() : self { return new self('No status line detected'); } } src/Module.php 0000644 00000001011 15173734626 0007300 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; class Module { public function getConfig(): array { return [ 'service_manager' => (new ConfigProvider())->getDependencies(), ]; } } src/UriFactory.php 0000644 00000001155 15173734626 0010153 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use Psr\Http\Message\UriFactoryInterface; use Psr\Http\Message\UriInterface; class UriFactory implements UriFactoryInterface { /** * {@inheritDoc} */ public function createUri(string $uri = '') : UriInterface { return new Uri($uri); } } src/UploadedFile.php 0000644 00000016763 15173734626 0010434 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use Psr\Http\Message\StreamInterface; use Psr\Http\Message\UploadedFileInterface; use function dirname; use function fclose; use function fopen; use function fwrite; use function is_dir; use function is_int; use function is_resource; use function is_string; use function is_writable; use function move_uploaded_file; use function sprintf; use function strpos; use const PHP_SAPI; use const UPLOAD_ERR_CANT_WRITE; use const UPLOAD_ERR_EXTENSION; use const UPLOAD_ERR_FORM_SIZE; use const UPLOAD_ERR_INI_SIZE; use const UPLOAD_ERR_NO_FILE; use const UPLOAD_ERR_NO_TMP_DIR; use const UPLOAD_ERR_OK; use const UPLOAD_ERR_PARTIAL; class UploadedFile implements UploadedFileInterface { const ERROR_MESSAGES = [ UPLOAD_ERR_OK => 'There is no error, the file uploaded with success', UPLOAD_ERR_INI_SIZE => 'The uploaded file exceeds the upload_max_filesize directive in php.ini', UPLOAD_ERR_FORM_SIZE => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was ' . 'specified in the HTML form', UPLOAD_ERR_PARTIAL => 'The uploaded file was only partially uploaded', UPLOAD_ERR_NO_FILE => 'No file was uploaded', UPLOAD_ERR_NO_TMP_DIR => 'Missing a temporary folder', UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk', UPLOAD_ERR_EXTENSION => 'A PHP extension stopped the file upload.', ]; /** * @var string|null */ private $clientFilename; /** * @var string|null */ private $clientMediaType; /** * @var int */ private $error; /** * @var null|string */ private $file; /** * @var bool */ private $moved = false; /** * @var int */ private $size; /** * @var null|StreamInterface */ private $stream; /** * @param string|resource $streamOrFile * @param int $size * @param int $errorStatus * @param string|null $clientFilename * @param string|null $clientMediaType * @throws Exception\InvalidArgumentException */ public function __construct( $streamOrFile, int $size, int $errorStatus, string $clientFilename = null, string $clientMediaType = null ) { if ($errorStatus === UPLOAD_ERR_OK) { if (is_string($streamOrFile)) { $this->file = $streamOrFile; } if (is_resource($streamOrFile)) { $this->stream = new Stream($streamOrFile); } if (! $this->file && ! $this->stream) { if (! $streamOrFile instanceof StreamInterface) { throw new Exception\InvalidArgumentException('Invalid stream or file provided for UploadedFile'); } $this->stream = $streamOrFile; } } $this->size = $size; if (0 > $errorStatus || 8 < $errorStatus) { throw new Exception\InvalidArgumentException( 'Invalid error status for UploadedFile; must be an UPLOAD_ERR_* constant' ); } $this->error = $errorStatus; $this->clientFilename = $clientFilename; $this->clientMediaType = $clientMediaType; } /** * {@inheritdoc} * @throws Exception\UploadedFileAlreadyMovedException if the upload was * not successful. */ public function getStream() : StreamInterface { if ($this->error !== UPLOAD_ERR_OK) { throw Exception\UploadedFileErrorException::dueToStreamUploadError( self::ERROR_MESSAGES[$this->error] ); } if ($this->moved) { throw new Exception\UploadedFileAlreadyMovedException(); } if ($this->stream instanceof StreamInterface) { return $this->stream; } $this->stream = new Stream($this->file); return $this->stream; } /** * {@inheritdoc} * * @see http://php.net/is_uploaded_file * @see http://php.net/move_uploaded_file * @param string $targetPath Path to which to move the uploaded file. * @throws Exception\UploadedFileErrorException if the upload was not successful. * @throws Exception\InvalidArgumentException if the $path specified is invalid. * @throws Exception\UploadedFileErrorException on any error during the * move operation, or on the second or subsequent call to the method. */ public function moveTo($targetPath) : void { if ($this->moved) { throw new Exception\UploadedFileAlreadyMovedException('Cannot move file; already moved!'); } if ($this->error !== UPLOAD_ERR_OK) { throw Exception\UploadedFileErrorException::dueToStreamUploadError( self::ERROR_MESSAGES[$this->error] ); } if (! is_string($targetPath) || empty($targetPath)) { throw new Exception\InvalidArgumentException( 'Invalid path provided for move operation; must be a non-empty string' ); } $targetDirectory = dirname($targetPath); if (! is_dir($targetDirectory) || ! is_writable($targetDirectory)) { throw Exception\UploadedFileErrorException::dueToUnwritableTarget($targetDirectory); } $sapi = PHP_SAPI; switch (true) { case (empty($sapi) || 0 === strpos($sapi, 'cli') || 0 === strpos($sapi, 'phpdbg') || ! $this->file): // Non-SAPI environment, or no filename present $this->writeFile($targetPath); break; default: // SAPI environment, with file present if (false === move_uploaded_file($this->file, $targetPath)) { throw Exception\UploadedFileErrorException::forUnmovableFile(); } break; } $this->moved = true; } /** * {@inheritdoc} * * @return int|null The file size in bytes or null if unknown. */ public function getSize() : ?int { return $this->size; } /** * {@inheritdoc} * * @see http://php.net/manual/en/features.file-upload.errors.php * @return int One of PHP's UPLOAD_ERR_XXX constants. */ public function getError() : int { return $this->error; } /** * {@inheritdoc} * * @return string|null The filename sent by the client or null if none * was provided. */ public function getClientFilename() : ?string { return $this->clientFilename; } /** * {@inheritdoc} */ public function getClientMediaType() : ?string { return $this->clientMediaType; } /** * Write internal stream to given path * * @param string $path */ private function writeFile(string $path) : void { $handle = fopen($path, 'wb+'); if (false === $handle) { throw Exception\UploadedFileErrorException::dueToUnwritablePath(); } $stream = $this->getStream(); $stream->rewind(); while (! $stream->eof()) { fwrite($handle, $stream->read(4096)); } fclose($handle); } } src/ResponseFactory.php 0000644 00000001327 15173734626 0011213 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; class ResponseFactory implements ResponseFactoryInterface { /** * {@inheritDoc} */ public function createResponse(int $code = 200, string $reasonPhrase = '') : ResponseInterface { return (new Response()) ->withStatus($code, $reasonPhrase); } } src/RequestTrait.php 0000644 00000023724 15173734626 0010526 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\StreamInterface; use Psr\Http\Message\UriInterface; use function array_keys; use function get_class; use function gettype; use function is_object; use function is_string; use function preg_match; use function sprintf; use function strtolower; /** * Trait with common request behaviors. * * Server and client-side requests differ slightly in how the Host header is * handled; on client-side, it should be calculated on-the-fly from the * composed URI (if present), while on server-side, it will be calculated from * the environment. As such, this trait exists to provide the common code * between both client-side and server-side requests, and each can then * use the headers functionality required by their implementations. */ trait RequestTrait { use MessageTrait; /** * @var string */ private $method = 'GET'; /** * The request-target, if it has been provided or calculated. * * @var null|string */ private $requestTarget; /** * @var UriInterface */ private $uri; /** * Initialize request state. * * Used by constructors. * * @param null|string|UriInterface $uri URI for the request, if any. * @param null|string $method HTTP method for the request, if any. * @param string|resource|StreamInterface $body Message body, if any. * @param array $headers Headers for the message, if any. * @throws Exception\InvalidArgumentException for any invalid value. */ private function initialize( $uri = null, string $method = null, $body = 'php://memory', array $headers = [] ) : void { if ($method !== null) { $this->setMethod($method); } $this->uri = $this->createUri($uri); $this->stream = $this->getStream($body, 'wb+'); $this->setHeaders($headers); // per PSR-7: attempt to set the Host header from a provided URI if no // Host header is provided if (! $this->hasHeader('Host') && $this->uri->getHost()) { $this->headerNames['host'] = 'Host'; $this->headers['Host'] = [$this->getHostFromUri()]; } } /** * Create and return a URI instance. * * If `$uri` is a already a `UriInterface` instance, returns it. * * If `$uri` is a string, passes it to the `Uri` constructor to return an * instance. * * If `$uri is null, creates and returns an empty `Uri` instance. * * Otherwise, it raises an exception. * * @param null|string|UriInterface $uri * @throws Exception\InvalidArgumentException */ private function createUri($uri) : UriInterface { if ($uri instanceof UriInterface) { return $uri; } if (is_string($uri)) { return new Uri($uri); } if ($uri === null) { return new Uri(); } throw new Exception\InvalidArgumentException( 'Invalid URI provided; must be null, a string, or a Psr\Http\Message\UriInterface instance' ); } /** * Retrieves the message's request target. * * Retrieves the message's request-target either as it will appear (for * clients), as it appeared at request (for servers), or as it was * specified for the instance (see withRequestTarget()). * * In most cases, this will be the origin-form of the composed URI, * unless a value was provided to the concrete implementation (see * withRequestTarget() below). * * If no URI is available, and no request-target has been specifically * provided, this method MUST return the string "/". */ public function getRequestTarget() : string { if (null !== $this->requestTarget) { return $this->requestTarget; } $target = $this->uri->getPath(); if ($this->uri->getQuery()) { $target .= '?' . $this->uri->getQuery(); } if (empty($target)) { $target = '/'; } return $target; } /** * Create a new instance with a specific request-target. * * If the request needs a non-origin-form request-target — e.g., for * specifying an absolute-form, authority-form, or asterisk-form — * this method may be used to create an instance with the specified * request-target, verbatim. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return a new instance that has the * changed request target. * * @link http://tools.ietf.org/html/rfc7230#section-2.7 (for the various * request-target forms allowed in request messages) * @param string $requestTarget * @throws Exception\InvalidArgumentException if the request target is invalid. */ public function withRequestTarget($requestTarget) : RequestInterface { if (preg_match('#\s#', $requestTarget)) { throw new Exception\InvalidArgumentException( 'Invalid request target provided; cannot contain whitespace' ); } $new = clone $this; $new->requestTarget = $requestTarget; return $new; } /** * Retrieves the HTTP method of the request. * * @return string Returns the request method. */ public function getMethod() : string { return $this->method; } /** * Return an instance with the provided HTTP method. * * While HTTP method names are typically all uppercase characters, HTTP * method names are case-sensitive and thus implementations SHOULD NOT * modify the given string. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * changed request method. * * @param string $method Case-insensitive method. * @throws Exception\InvalidArgumentException for invalid HTTP methods. */ public function withMethod($method) : RequestInterface { $new = clone $this; $new->setMethod($method); return $new; } /** * Retrieves the URI instance. * * This method MUST return a UriInterface instance. * * @link http://tools.ietf.org/html/rfc3986#section-4.3 * @return UriInterface Returns a UriInterface instance * representing the URI of the request, if any. */ public function getUri() : UriInterface { return $this->uri; } /** * Returns an instance with the provided URI. * * This method will update the Host header of the returned request by * default if the URI contains a host component. If the URI does not * contain a host component, any pre-existing Host header will be carried * over to the returned request. * * You can opt-in to preserving the original state of the Host header by * setting `$preserveHost` to `true`. When `$preserveHost` is set to * `true`, the returned request will not update the Host header of the * returned message -- even if the message contains no Host header. This * means that a call to `getHeader('Host')` on the original request MUST * equal the return value of a call to `getHeader('Host')` on the returned * request. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * new UriInterface instance. * * @link http://tools.ietf.org/html/rfc3986#section-4.3 * @param UriInterface $uri New request URI to use. * @param bool $preserveHost Preserve the original state of the Host header. */ public function withUri(UriInterface $uri, $preserveHost = false) : RequestInterface { $new = clone $this; $new->uri = $uri; if ($preserveHost && $this->hasHeader('Host')) { return $new; } if (! $uri->getHost()) { return $new; } $host = $uri->getHost(); if ($uri->getPort()) { $host .= ':' . $uri->getPort(); } $new->headerNames['host'] = 'Host'; // Remove an existing host header if present, regardless of current // de-normalization of the header name. // @see https://github.com/zendframework/zend-diactoros/issues/91 foreach (array_keys($new->headers) as $header) { if (strtolower($header) === 'host') { unset($new->headers[$header]); } } $new->headers['Host'] = [$host]; return $new; } /** * Set and validate the HTTP method * * @param string $method * @throws Exception\InvalidArgumentException on invalid HTTP method. */ private function setMethod($method) : void { if (! is_string($method)) { throw new Exception\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)) { throw new Exception\InvalidArgumentException(sprintf( 'Unsupported HTTP method "%s" provided', $method )); } $this->method = $method; } /** * Retrieve the host from the URI instance */ private function getHostFromUri() : string { $host = $this->uri->getHost(); $host .= $this->uri->getPort() ? ':' . $this->uri->getPort() : ''; return $host; } } src/RelativeStream.php 0000644 00000007406 15173734626 0011020 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use Psr\Http\Message\StreamInterface; use const SEEK_SET; /** * Class RelativeStream * * Wrapper for default Stream class, representing subpart (starting from given offset) of initial stream. * It can be used to avoid copying full stream, conserving memory. * @example see Laminas\Diactoros\AbstractSerializer::splitStream() */ final class RelativeStream implements StreamInterface { /** * @var StreamInterface */ private $decoratedStream; /** * @var int */ private $offset; /** * Class constructor * * @param StreamInterface $decoratedStream * @param int $offset */ public function __construct(StreamInterface $decoratedStream, ?int $offset) { $this->decoratedStream = $decoratedStream; $this->offset = (int) $offset; } /** * {@inheritdoc} */ public function __toString() : string { if ($this->isSeekable()) { $this->seek(0); } return $this->getContents(); } /** * {@inheritdoc} */ public function close() : void { $this->decoratedStream->close(); } /** * {@inheritdoc} */ public function detach() { return $this->decoratedStream->detach(); } /** * {@inheritdoc} */ public function getSize() : int { return $this->decoratedStream->getSize() - $this->offset; } /** * {@inheritdoc} */ public function tell() : int { return $this->decoratedStream->tell() - $this->offset; } /** * {@inheritdoc} */ public function eof() : bool { return $this->decoratedStream->eof(); } /** * {@inheritdoc} */ public function isSeekable() : bool { return $this->decoratedStream->isSeekable(); } /** * {@inheritdoc} */ public function seek($offset, $whence = SEEK_SET) : void { if ($whence == SEEK_SET) { $this->decoratedStream->seek($offset + $this->offset, $whence); return; } $this->decoratedStream->seek($offset, $whence); } /** * {@inheritdoc} */ public function rewind() : void { $this->seek(0); } /** * {@inheritdoc} */ public function isWritable() : bool { return $this->decoratedStream->isWritable(); } /** * {@inheritdoc} */ public function write($string) : int { if ($this->tell() < 0) { throw new Exception\InvalidStreamPointerPositionException(); } return $this->decoratedStream->write($string); } /** * {@inheritdoc} */ public function isReadable() : bool { return $this->decoratedStream->isReadable(); } /** * {@inheritdoc} */ public function read($length) : string { if ($this->tell() < 0) { throw new Exception\InvalidStreamPointerPositionException(); } return $this->decoratedStream->read($length); } /** * {@inheritdoc} */ public function getContents() : string { if ($this->tell() < 0) { throw new Exception\InvalidStreamPointerPositionException(); } return $this->decoratedStream->getContents(); } /** * {@inheritdoc} */ public function getMetadata($key = null) { return $this->decoratedStream->getMetadata($key); } } src/UploadedFileFactory.php 0000644 00000002020 15173734626 0011741 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use Psr\Http\Message\StreamInterface; use Psr\Http\Message\UploadedFileFactoryInterface; use Psr\Http\Message\UploadedFileInterface; 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 { if ($size === null) { $size = $stream->getSize(); } return new UploadedFile($stream, $size, $error, $clientFilename, $clientMediaType); } } src/Response.php 0000644 00000013612 15173734626 0007663 0 ustar 00 <?php /** * @see https://github.com/laminas/laminas-diactoros for the canonical source repository * @copyright https://github.com/laminas/laminas-diactoros/blob/master/COPYRIGHT.md * @license https://github.com/laminas/laminas-diactoros/blob/master/LICENSE.md New BSD License */ declare(strict_types=1); namespace Laminas\Diactoros; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\StreamInterface; use function gettype; use function is_float; use function is_numeric; use function is_scalar; use function sprintf; /** * HTTP response encapsulation. * * Responses 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 Response implements ResponseInterface { use MessageTrait; const MIN_STATUS_CODE_VALUE = 100; const MAX_STATUS_CODE_VALUE = 599; /** * Map of standard HTTP status code/reason phrases * * @var array */ private $phrases = [ // INFORMATIONAL CODES 100 => 'Continue', 101 => 'Switching Protocols', 102 => 'Processing', 103 => 'Early Hints', // SUCCESS CODES 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 207 => 'Multi-Status', 208 => 'Already Reported', 226 => 'IM Used', // REDIRECTION CODES 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 306 => 'Switch Proxy', // Deprecated to 306 => '(Unused)' 307 => 'Temporary Redirect', 308 => 'Permanent Redirect', // CLIENT ERROR 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Timeout', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Payload Too Large', 414 => 'URI Too Long', 415 => 'Unsupported Media Type', 416 => 'Range Not Satisfiable', 417 => 'Expectation Failed', 418 => 'I\'m a teapot', 421 => 'Misdirected Request', 422 => 'Unprocessable Entity', 423 => 'Locked', 424 => 'Failed Dependency', 425 => 'Too Early', 426 => 'Upgrade Required', 428 => 'Precondition Required', 429 => 'Too Many Requests', 431 => 'Request Header Fields Too Large', 444 => 'Connection Closed Without Response', 451 => 'Unavailable For Legal Reasons', // SERVER ERROR 499 => 'Client Closed Request', 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Timeout', 505 => 'HTTP Version Not Supported', 506 => 'Variant Also Negotiates', 507 => 'Insufficient Storage', 508 => 'Loop Detected', 510 => 'Not Extended', 511 => 'Network Authentication Required', 599 => 'Network Connect Timeout Error', ]; /** * @var string */ private $reasonPhrase; /** * @var int */ private $statusCode; /** * @param string|resource|StreamInterface $body Stream identifier and/or actual stream resource * @param int $status Status code for the response, if any. * @param array $headers Headers for the response, if any. * @throws Exception\InvalidArgumentException on any invalid element. */ public function __construct($body = 'php://memory', int $status = 200, array $headers = []) { $this->setStatusCode($status); $this->stream = $this->getStream($body, 'wb+'); $this->setHeaders($headers); } /** * {@inheritdoc} */ public function getStatusCode() : int { return $this->statusCode; } /** * {@inheritdoc} */ public function getReasonPhrase() : string { return $this->reasonPhrase; } /** * {@inheritdoc} */ public function withStatus($code, $reasonPhrase = '') : Response { $new = clone $this; $new->setStatusCode($code, $reasonPhrase); return $new; } /** * Set a valid status code. * * @param int $code * @param string $reasonPhrase * @throws Exception\InvalidArgumentException on an invalid status code. */ private function setStatusCode($code, $reasonPhrase = '') : void { if (! is_numeric($code) || is_float($code) || $code < static::MIN_STATUS_CODE_VALUE || $code > static::MAX_STATUS_CODE_VALUE ) { throw new Exception\InvalidArgumentException(sprintf( 'Invalid status code "%s"; must be an integer between %d and %d, inclusive', is_scalar($code) ? $code : gettype($code), static::MIN_STATUS_CODE_VALUE, static::MAX_STATUS_CODE_VALUE )); } if (! is_string($reasonPhrase)) { throw new Exception\InvalidArgumentException(sprintf( 'Unsupported response reason phrase; must be a string, received %s', is_object($reasonPhrase) ? get_class($reasonPhrase) : gettype($reasonPhrase) )); } if ($reasonPhrase === '' && isset($this->phrases[$code])) { $reasonPhrase = $this->phrases[$code]; } $this->reasonPhrase = $reasonPhrase; $this->statusCode = (int) $code; } }
| ver. 1.4 |
Github
|
.
| PHP 8.3.23 | Generation time: 0 |
proxy
|
phpinfo
|
Settings