diff options
author | marvin-borner@live.com | 2018-04-10 21:50:16 +0200 |
---|---|---|
committer | marvin-borner@live.com | 2018-04-10 21:54:48 +0200 |
commit | fc9401f04a3aca5abb22f87ebc210de8afe11d32 (patch) | |
tree | b0b310f3581764ec3955f4e496a05137a32951c3 /assets/php/vendor/guzzlehttp/psr7/src | |
parent | 286d643180672f20526f3dc3bd19d7b751e2fa97 (diff) |
Initial Commit
Diffstat (limited to 'assets/php/vendor/guzzlehttp/psr7/src')
24 files changed, 4914 insertions, 0 deletions
diff --git a/assets/php/vendor/guzzlehttp/psr7/src/AppendStream.php b/assets/php/vendor/guzzlehttp/psr7/src/AppendStream.php new file mode 100644 index 0000000..23039fd --- /dev/null +++ b/assets/php/vendor/guzzlehttp/psr7/src/AppendStream.php @@ -0,0 +1,233 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Reads from multiple streams, one after the other. + * + * This is a read-only stream decorator. + */ +class AppendStream implements StreamInterface +{ + /** @var StreamInterface[] Streams being decorated */ + private $streams = []; + + private $seekable = true; + private $current = 0; + private $pos = 0; + private $detached = false; + + /** + * @param StreamInterface[] $streams Streams to decorate. Each stream must + * be readable. + */ + public function __construct(array $streams = []) + { + foreach ($streams as $stream) { + $this->addStream($stream); + } + } + + public function __toString() + { + try { + $this->rewind(); + return $this->getContents(); + } catch (\Exception $e) { + return ''; + } + } + + /** + * Add a stream to the AppendStream + * + * @param StreamInterface $stream Stream to append. Must be readable. + * + * @throws \InvalidArgumentException if the stream is not readable + */ + public function addStream(StreamInterface $stream) + { + if (!$stream->isReadable()) { + throw new \InvalidArgumentException('Each stream must be readable'); + } + + // The stream is only seekable if all streams are seekable + if (!$stream->isSeekable()) { + $this->seekable = false; + } + + $this->streams[] = $stream; + } + + public function getContents() + { + return copy_to_string($this); + } + + /** + * Closes each attached stream. + * + * {@inheritdoc} + */ + public function close() + { + $this->pos = $this->current = 0; + + foreach ($this->streams as $stream) { + $stream->close(); + } + + $this->streams = []; + } + + /** + * Detaches each attached stream + * + * {@inheritdoc} + */ + public function detach() + { + $this->close(); + $this->detached = true; + } + + public function tell() + { + return $this->pos; + } + + /** + * Tries to calculate the size by adding the size of each stream. + * + * If any of the streams do not return a valid number, then the size of the + * append stream cannot be determined and null is returned. + * + * {@inheritdoc} + */ + public function getSize() + { + $size = 0; + + foreach ($this->streams as $stream) { + $s = $stream->getSize(); + if ($s === null) { + return null; + } + $size += $s; + } + + return $size; + } + + public function eof() + { + return !$this->streams || + ($this->current >= count($this->streams) - 1 && + $this->streams[$this->current]->eof()); + } + + public function rewind() + { + $this->seek(0); + } + + /** + * Attempts to seek to the given position. Only supports SEEK_SET. + * + * {@inheritdoc} + */ + public function seek($offset, $whence = SEEK_SET) + { + if (!$this->seekable) { + throw new \RuntimeException('This AppendStream is not seekable'); + } elseif ($whence !== SEEK_SET) { + throw new \RuntimeException('The AppendStream can only seek with SEEK_SET'); + } + + $this->pos = $this->current = 0; + + // Rewind each stream + foreach ($this->streams as $i => $stream) { + try { + $stream->rewind(); + } catch (\Exception $e) { + throw new \RuntimeException('Unable to seek stream ' + . $i . ' of the AppendStream', 0, $e); + } + } + + // Seek to the actual position by reading from each stream + while ($this->pos < $offset && !$this->eof()) { + $result = $this->read(min(8096, $offset - $this->pos)); + if ($result === '') { + break; + } + } + } + + /** + * Reads from all of the appended streams until the length is met or EOF. + * + * {@inheritdoc} + */ + public function read($length) + { + $buffer = ''; + $total = count($this->streams) - 1; + $remaining = $length; + $progressToNext = false; + + while ($remaining > 0) { + + // Progress to the next stream if needed. + if ($progressToNext || $this->streams[$this->current]->eof()) { + $progressToNext = false; + if ($this->current === $total) { + break; + } + $this->current++; + } + + $result = $this->streams[$this->current]->read($remaining); + + // Using a loose comparison here to match on '', false, and null + if ($result == null) { + $progressToNext = true; + continue; + } + + $buffer .= $result; + $remaining = $length - strlen($buffer); + } + + $this->pos += strlen($buffer); + + return $buffer; + } + + public function isReadable() + { + return true; + } + + public function isWritable() + { + return false; + } + + public function isSeekable() + { + return $this->seekable; + } + + public function write($string) + { + throw new \RuntimeException('Cannot write to an AppendStream'); + } + + public function getMetadata($key = null) + { + return $key ? null : []; + } +} diff --git a/assets/php/vendor/guzzlehttp/psr7/src/BufferStream.php b/assets/php/vendor/guzzlehttp/psr7/src/BufferStream.php new file mode 100644 index 0000000..af4d4c2 --- /dev/null +++ b/assets/php/vendor/guzzlehttp/psr7/src/BufferStream.php @@ -0,0 +1,137 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Provides a buffer stream that can be written to to fill a buffer, and read + * from to remove bytes from the buffer. + * + * This stream returns a "hwm" metadata value that tells upstream consumers + * what the configured high water mark of the stream is, or the maximum + * preferred size of the buffer. + */ +class BufferStream implements StreamInterface +{ + private $hwm; + private $buffer = ''; + + /** + * @param int $hwm High water mark, representing the preferred maximum + * buffer size. If the size of the buffer exceeds the high + * water mark, then calls to write will continue to succeed + * but will return false to inform writers to slow down + * until the buffer has been drained by reading from it. + */ + public function __construct($hwm = 16384) + { + $this->hwm = $hwm; + } + + public function __toString() + { + return $this->getContents(); + } + + public function getContents() + { + $buffer = $this->buffer; + $this->buffer = ''; + + return $buffer; + } + + public function close() + { + $this->buffer = ''; + } + + public function detach() + { + $this->close(); + } + + public function getSize() + { + return strlen($this->buffer); + } + + public function isReadable() + { + return true; + } + + public function isWritable() + { + return true; + } + + public function isSeekable() + { + return false; + } + + public function rewind() + { + $this->seek(0); + } + + public function seek($offset, $whence = SEEK_SET) + { + throw new \RuntimeException('Cannot seek a BufferStream'); + } + + public function eof() + { + return strlen($this->buffer) === 0; + } + + public function tell() + { + throw new \RuntimeException('Cannot determine the position of a BufferStream'); + } + + /** + * Reads data from the buffer. + */ + public function read($length) + { + $currentLength = strlen($this->buffer); + + if ($length >= $currentLength) { + // No need to slice the buffer because we don't have enough data. + $result = $this->buffer; + $this->buffer = ''; + } else { + // Slice up the result to provide a subset of the buffer. + $result = substr($this->buffer, 0, $length); + $this->buffer = substr($this->buffer, $length); + } + + return $result; + } + + /** + * Writes data to the buffer. + */ + public function write($string) + { + $this->buffer .= $string; + + // TODO: What should happen here? + if (strlen($this->buffer) >= $this->hwm) { + return false; + } + + return strlen($string); + } + + public function getMetadata($key = null) + { + if ($key == 'hwm') { + return $this->hwm; + } + + return $key ? null : []; + } +} diff --git a/assets/php/vendor/guzzlehttp/psr7/src/CachingStream.php b/assets/php/vendor/guzzlehttp/psr7/src/CachingStream.php new file mode 100644 index 0000000..ed68f08 --- /dev/null +++ b/assets/php/vendor/guzzlehttp/psr7/src/CachingStream.php @@ -0,0 +1,138 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Stream decorator that can cache previously read bytes from a sequentially + * read stream. + */ +class CachingStream implements StreamInterface +{ + use StreamDecoratorTrait; + + /** @var StreamInterface Stream being wrapped */ + private $remoteStream; + + /** @var int Number of bytes to skip reading due to a write on the buffer */ + private $skipReadBytes = 0; + + /** + * We will treat the buffer object as the body of the stream + * + * @param StreamInterface $stream Stream to cache + * @param StreamInterface $target Optionally specify where data is cached + */ + public function __construct( + StreamInterface $stream, + StreamInterface $target = null + ) { + $this->remoteStream = $stream; + $this->stream = $target ?: new Stream(fopen('php://temp', 'r+')); + } + + public function getSize() + { + return max($this->stream->getSize(), $this->remoteStream->getSize()); + } + + public function rewind() + { + $this->seek(0); + } + + public function seek($offset, $whence = SEEK_SET) + { + if ($whence == SEEK_SET) { + $byte = $offset; + } elseif ($whence == SEEK_CUR) { + $byte = $offset + $this->tell(); + } elseif ($whence == SEEK_END) { + $size = $this->remoteStream->getSize(); + if ($size === null) { + $size = $this->cacheEntireStream(); + } + $byte = $size + $offset; + } else { + throw new \InvalidArgumentException('Invalid whence'); + } + + $diff = $byte - $this->stream->getSize(); + + if ($diff > 0) { + // Read the remoteStream until we have read in at least the amount + // of bytes requested, or we reach the end of the file. + while ($diff > 0 && !$this->remoteStream->eof()) { + $this->read($diff); + $diff = $byte - $this->stream->getSize(); + } + } else { + // We can just do a normal seek since we've already seen this byte. + $this->stream->seek($byte); + } + } + + public function read($length) + { + // Perform a regular read on any previously read data from the buffer + $data = $this->stream->read($length); + $remaining = $length - strlen($data); + + // More data was requested so read from the remote stream + if ($remaining) { + // If data was written to the buffer in a position that would have + // been filled from the remote stream, then we must skip bytes on + // the remote stream to emulate overwriting bytes from that + // position. This mimics the behavior of other PHP stream wrappers. + $remoteData = $this->remoteStream->read( + $remaining + $this->skipReadBytes + ); + + if ($this->skipReadBytes) { + $len = strlen($remoteData); + $remoteData = substr($remoteData, $this->skipReadBytes); + $this->skipReadBytes = max(0, $this->skipReadBytes - $len); + } + + $data .= $remoteData; + $this->stream->write($remoteData); + } + + return $data; + } + + public function write($string) + { + // When appending to the end of the currently read stream, you'll want + // to skip bytes from being read from the remote stream to emulate + // other stream wrappers. Basically replacing bytes of data of a fixed + // length. + $overflow = (strlen($string) + $this->tell()) - $this->remoteStream->tell(); + if ($overflow > 0) { + $this->skipReadBytes += $overflow; + } + + return $this->stream->write($string); + } + + public function eof() + { + return $this->stream->eof() && $this->remoteStream->eof(); + } + + /** + * Close both the remote stream and buffer stream + */ + public function close() + { + $this->remoteStream->close() && $this->stream->close(); + } + + private function cacheEntireStream() + { + $target = new FnStream(['write' => 'strlen']); + copy_to_stream($this, $target); + + return $this->tell(); + } +} diff --git a/assets/php/vendor/guzzlehttp/psr7/src/DroppingStream.php b/assets/php/vendor/guzzlehttp/psr7/src/DroppingStream.php new file mode 100644 index 0000000..8935c80 --- /dev/null +++ b/assets/php/vendor/guzzlehttp/psr7/src/DroppingStream.php @@ -0,0 +1,42 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Stream decorator that begins dropping data once the size of the underlying + * stream becomes too full. + */ +class DroppingStream implements StreamInterface +{ + use StreamDecoratorTrait; + + private $maxLength; + + /** + * @param StreamInterface $stream Underlying stream to decorate. + * @param int $maxLength Maximum size before dropping data. + */ + public function __construct(StreamInterface $stream, $maxLength) + { + $this->stream = $stream; + $this->maxLength = $maxLength; + } + + public function write($string) + { + $diff = $this->maxLength - $this->stream->getSize(); + + // Begin returning 0 when the underlying stream is too large. + if ($diff <= 0) { + return 0; + } + + // Write the stream or a subset of the stream if needed. + if (strlen($string) < $diff) { + return $this->stream->write($string); + } + + return $this->stream->write(substr($string, 0, $diff)); + } +} diff --git a/assets/php/vendor/guzzlehttp/psr7/src/FnStream.php b/assets/php/vendor/guzzlehttp/psr7/src/FnStream.php new file mode 100644 index 0000000..cc9b445 --- /dev/null +++ b/assets/php/vendor/guzzlehttp/psr7/src/FnStream.php @@ -0,0 +1,149 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Compose stream implementations based on a hash of functions. + * + * Allows for easy testing and extension of a provided stream without needing + * to create a concrete class for a simple extension point. + */ +class FnStream implements StreamInterface +{ + /** @var array */ + private $methods; + + /** @var array Methods that must be implemented in the given array */ + private static $slots = ['__toString', 'close', 'detach', 'rewind', + 'getSize', 'tell', 'eof', 'isSeekable', 'seek', 'isWritable', 'write', + 'isReadable', 'read', 'getContents', 'getMetadata']; + + /** + * @param array $methods Hash of method name to a callable. + */ + public function __construct(array $methods) + { + $this->methods = $methods; + + // Create the functions on the class + foreach ($methods as $name => $fn) { + $this->{'_fn_' . $name} = $fn; + } + } + + /** + * Lazily determine which methods are not implemented. + * @throws \BadMethodCallException + */ + public function __get($name) + { + throw new \BadMethodCallException(str_replace('_fn_', '', $name) + . '() is not implemented in the FnStream'); + } + + /** + * The close method is called on the underlying stream only if possible. + */ + public function __destruct() + { + if (isset($this->_fn_close)) { + call_user_func($this->_fn_close); + } + } + + /** + * Adds custom functionality to an underlying stream by intercepting + * specific method calls. + * + * @param StreamInterface $stream Stream to decorate + * @param array $methods Hash of method name to a closure + * + * @return FnStream + */ + public static function decorate(StreamInterface $stream, array $methods) + { + // If any of the required methods were not provided, then simply + // proxy to the decorated stream. + foreach (array_diff(self::$slots, array_keys($methods)) as $diff) { + $methods[$diff] = [$stream, $diff]; + } + + return new self($methods); + } + + public function __toString() + { + return call_user_func($this->_fn___toString); + } + + public function close() + { + return call_user_func($this->_fn_close); + } + + public function detach() + { + return call_user_func($this->_fn_detach); + } + + public function getSize() + { + return call_user_func($this->_fn_getSize); + } + + public function tell() + { + return call_user_func($this->_fn_tell); + } + + public function eof() + { + return call_user_func($this->_fn_eof); + } + + public function isSeekable() + { + return call_user_func($this->_fn_isSeekable); + } + + public function rewind() + { + call_user_func($this->_fn_rewind); + } + + public function seek($offset, $whence = SEEK_SET) + { + call_user_func($this->_fn_seek, $offset, $whence); + } + + public function isWritable() + { + return call_user_func($this->_fn_isWritable); + } + + public function write($string) + { + return call_user_func($this->_fn_write, $string); + } + + public function isReadable() + { + return call_user_func($this->_fn_isReadable); + } + + public function read($length) + { + return call_user_func($this->_fn_read, $length); + } + + public function getContents() + { + return call_user_func($this->_fn_getContents); + } + + public function getMetadata($key = null) + { + return call_user_func($this->_fn_getMetadata, $key); + } +} diff --git a/assets/php/vendor/guzzlehttp/psr7/src/InflateStream.php b/assets/php/vendor/guzzlehttp/psr7/src/InflateStream.php new file mode 100644 index 0000000..0051d3f --- /dev/null +++ b/assets/php/vendor/guzzlehttp/psr7/src/InflateStream.php @@ -0,0 +1,52 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Uses PHP's zlib.inflate filter to inflate deflate or gzipped content. + * + * This stream decorator skips the first 10 bytes of the given stream to remove + * the gzip header, converts the provided stream to a PHP stream resource, + * then appends the zlib.inflate filter. The stream is then converted back + * to a Guzzle stream resource to be used as a Guzzle stream. + * + * @link http://tools.ietf.org/html/rfc1952 + * @link http://php.net/manual/en/filters.compression.php + */ +class InflateStream implements StreamInterface +{ + use StreamDecoratorTrait; + + public function __construct(StreamInterface $stream) + { + // read the first 10 bytes, ie. gzip header + $header = $stream->read(10); + $filenameHeaderLength = $this->getLengthOfPossibleFilenameHeader($stream, $header); + // Skip the header, that is 10 + length of filename + 1 (nil) bytes + $stream = new LimitStream($stream, -1, 10 + $filenameHeaderLength); + $resource = StreamWrapper::getResource($stream); + stream_filter_append($resource, 'zlib.inflate', STREAM_FILTER_READ); + $this->stream = new Stream($resource); + } + + /** + * @param StreamInterface $stream + * @param $header + * @return int + */ + private function getLengthOfPossibleFilenameHeader(StreamInterface $stream, $header) + { + $filename_header_length = 0; + + if (substr(bin2hex($header), 6, 2) === '08') { + // we have a filename, read until nil + $filename_header_length = 1; + while ($stream->read(1) !== chr(0)) { + $filename_header_length++; + } + } + + return $filename_header_length; + } +} diff --git a/assets/php/vendor/guzzlehttp/psr7/src/LazyOpenStream.php b/assets/php/vendor/guzzlehttp/psr7/src/LazyOpenStream.php new file mode 100644 index 0000000..02cec3a --- /dev/null +++ b/assets/php/vendor/guzzlehttp/psr7/src/LazyOpenStream.php @@ -0,0 +1,39 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Lazily reads or writes to a file that is opened only after an IO operation + * take place on the stream. + */ +class LazyOpenStream implements StreamInterface +{ + use StreamDecoratorTrait; + + /** @var string File to open */ + private $filename; + + /** @var string $mode */ + private $mode; + + /** + * @param string $filename File to lazily open + * @param string $mode fopen mode to use when opening the stream + */ + public function __construct($filename, $mode) + { + $this->filename = $filename; + $this->mode = $mode; + } + + /** + * Creates the underlying stream lazily when required. + * + * @return StreamInterface + */ + protected function createStream() + { + return stream_for(try_fopen($this->filename, $this->mode)); + } +} diff --git a/assets/php/vendor/guzzlehttp/psr7/src/LimitStream.php b/assets/php/vendor/guzzlehttp/psr7/src/LimitStream.php new file mode 100644 index 0000000..3c13d4f --- /dev/null +++ b/assets/php/vendor/guzzlehttp/psr7/src/LimitStream.php @@ -0,0 +1,155 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + + +/** + * Decorator used to return only a subset of a stream + */ +class LimitStream implements StreamInterface +{ + use StreamDecoratorTrait; + + /** @var int Offset to start reading from */ + private $offset; + + /** @var int Limit the number of bytes that can be read */ + private $limit; + + /** + * @param StreamInterface $stream Stream to wrap + * @param int $limit Total number of bytes to allow to be read + * from the stream. Pass -1 for no limit. + * @param int $offset Position to seek to before reading (only + * works on seekable streams). + */ + public function __construct( + StreamInterface $stream, + $limit = -1, + $offset = 0 + ) { + $this->stream = $stream; + $this->setLimit($limit); + $this->setOffset($offset); + } + + public function eof() + { + // Always return true if the underlying stream is EOF + if ($this->stream->eof()) { + return true; + } + + // No limit and the underlying stream is not at EOF + if ($this->limit == -1) { + return false; + } + + return $this->stream->tell() >= $this->offset + $this->limit; + } + + /** + * Returns the size of the limited subset of data + * {@inheritdoc} + */ + public function getSize() + { + if (null === ($length = $this->stream->getSize())) { + return null; + } elseif ($this->limit == -1) { + return $length - $this->offset; + } else { + return min($this->limit, $length - $this->offset); + } + } + + /** + * Allow for a bounded seek on the read limited stream + * {@inheritdoc} + */ + public function seek($offset, $whence = SEEK_SET) + { + if ($whence !== SEEK_SET || $offset < 0) { + throw new \RuntimeException(sprintf( + 'Cannot seek to offset % with whence %s', + $offset, + $whence + )); + } + + $offset += $this->offset; + + if ($this->limit !== -1) { + if ($offset > $this->offset + $this->limit) { + $offset = $this->offset + $this->limit; + } + } + + $this->stream->seek($offset); + } + + /** + * Give a relative tell() + * {@inheritdoc} + */ + public function tell() + { + return $this->stream->tell() - $this->offset; + } + + /** + * Set the offset to start limiting from + * + * @param int $offset Offset to seek to and begin byte limiting from + * + * @throws \RuntimeException if the stream cannot be seeked. + */ + public function setOffset($offset) + { + $current = $this->stream->tell(); + + if ($current !== $offset) { + // If the stream cannot seek to the offset position, then read to it + if ($this->stream->isSeekable()) { + $this->stream->seek($offset); + } elseif ($current > $offset) { + throw new \RuntimeException("Could not seek to stream offset $offset"); + } else { + $this->stream->read($offset - $current); + } + } + + $this->offset = $offset; + } + + /** + * Set the limit of bytes that the decorator allows to be read from the + * stream. + * + * @param int $limit Number of bytes to allow to be read from the stream. + * Use -1 for no limit. + */ + public function setLimit($limit) + { + $this->limit = $limit; + } + + public function read($length) + { + if ($this->limit == -1) { + return $this->stream->read($length); + } + + // Check if the current position is less than the total allowed + // bytes + original offset + $remaining = ($this->offset + $this->limit) - $this->stream->tell(); + if ($remaining > 0) { + // Only return the amount of requested data, ensuring that the byte + // limit is not exceeded + return $this->stream->read(min($remaining, $length)); + } + + return ''; + } +} diff --git a/assets/php/vendor/guzzlehttp/psr7/src/MessageTrait.php b/assets/php/vendor/guzzlehttp/psr7/src/MessageTrait.php new file mode 100644 index 0000000..1e4da64 --- /dev/null +++ b/assets/php/vendor/guzzlehttp/psr7/src/MessageTrait.php @@ -0,0 +1,183 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Trait implementing functionality common to requests and responses. + */ +trait MessageTrait +{ + /** @var array Map of all registered headers, as original name => array of values */ + private $headers = []; + + /** @var array Map of lowercase header name => original name at registration */ + private $headerNames = []; + + /** @var string */ + private $protocol = '1.1'; + + /** @var StreamInterface */ + private $stream; + + public function getProtocolVersion() + { + return $this->protocol; + } + + public function withProtocolVersion($version) + { + if ($this->protocol === $version) { + return $this; + } + + $new = clone $this; + $new->protocol = $version; + return $new; + } + + public function getHeaders() + { + return $this->headers; + } + + public function hasHeader($header) + { + return isset($this->headerNames[strtolower($header)]); + } + + public function getHeader($header) + { + $header = strtolower($header); + + if (!isset($this->headerNames[$header])) { + return []; + } + + $header = $this->headerNames[$header]; + + return $this->headers[$header]; + } + + public function getHeaderLine($header) + { + return implode(', ', $this->getHeader($header)); + } + + public function withHeader($header, $value) + { + if (!is_array($value)) { + $value = [$value]; + } + + $value = $this->trimHeaderValues($value); + $normalized = strtolower($header); + + $new = clone $this; + if (isset($new->headerNames[$normalized])) { + unset($new->headers[$new->headerNames[$normalized]]); + } + $new->headerNames[$normalized] = $header; + $new->headers[$header] = $value; + + return $new; + } + + public function withAddedHeader($header, $value) + { + if (!is_array($value)) { + $value = [$value]; + } + + $value = $this->trimHeaderValues($value); + $normalized = strtolower($header); + + $new = clone $this; + if (isset($new->headerNames[$normalized])) { + $header = $this->headerNames[$normalized]; + $new->headers[$header] = array_merge($this->headers[$header], $value); + } else { + $new->headerNames[$normalized] = $header; + $new->headers[$header] = $value; + } + + return $new; + } + + public function withoutHeader($header) + { + $normalized = strtolower($header); + + if (!isset($this->headerNames[$normalized])) { + return $this; + } + + $header = $this->headerNames[$normalized]; + + $new = clone $this; + unset($new->headers[$header], $new->headerNames[$normalized]); + + return $new; + } + + public function getBody() + { + if (!$this->stream) { + $this->stream = stream_for(''); + } + + return $this->stream; + } + + public function withBody(StreamInterface $body) + { + if ($body === $this->stream) { + return $this; + } + + $new = clone $this; + $new->stream = $body; + return $new; + } + + private function setHeaders(array $headers) + { + $this->headerNames = $this->headers = []; + foreach ($headers as $header => $value) { + if (!is_array($value)) { + $value = [$value]; + } + + $value = $this->trimHeaderValues($value); + $normalized = strtolower($header); + if (isset($this->headerNames[$normalized])) { + $header = $this->headerNames[$normalized]; + $this->headers[$header] = array_merge($this->headers[$header], $value); + } else { + $this->headerNames[$normalized] = $header; + $this->headers[$header] = $value; + } + } + } + + /** + * Trims whitespace from the header values. + * + * Spaces and tabs ought to be excluded by parsers when extracting the field value from a header field. + * + * header-field = field-name ":" OWS field-value OWS + * OWS = *( SP / HTAB ) + * + * @param string[] $values Header values + * + * @return string[] Trimmed header values + * + * @see https://tools.ietf.org/html/rfc7230#section-3.2.4 + */ + private function trimHeaderValues(array $values) + { + return array_map(function ($value) { + return trim($value, " \t"); + }, $values); + } +} diff --git a/assets/php/vendor/guzzlehttp/psr7/src/MultipartStream.php b/assets/php/vendor/guzzlehttp/psr7/src/MultipartStream.php new file mode 100644 index 0000000..c0fd584 --- /dev/null +++ b/assets/php/vendor/guzzlehttp/psr7/src/MultipartStream.php @@ -0,0 +1,153 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Stream that when read returns bytes for a streaming multipart or + * multipart/form-data stream. + */ +class MultipartStream implements StreamInterface +{ + use StreamDecoratorTrait; + + private $boundary; + + /** + * @param array $elements Array of associative arrays, each containing a + * required "name" key mapping to the form field, + * name, a required "contents" key mapping to a + * StreamInterface/resource/string, an optional + * "headers" associative array of custom headers, + * and an optional "filename" key mapping to a + * string to send as the filename in the part. + * @param string $boundary You can optionally provide a specific boundary + * + * @throws \InvalidArgumentException + */ + public function __construct(array $elements = [], $boundary = null) + { + $this->boundary = $boundary ?: sha1(uniqid('', true)); + $this->stream = $this->createStream($elements); + } + + /** + * Get the boundary + * + * @return string + */ + public function getBoundary() + { + return $this->boundary; + } + + public function isWritable() + { + return false; + } + + /** + * Get the headers needed before transferring the content of a POST file + */ + private function getHeaders(array $headers) + { + $str = ''; + foreach ($headers as $key => $value) { + $str .= "{$key}: {$value}\r\n"; + } + + return "--{$this->boundary}\r\n" . trim($str) . "\r\n\r\n"; + } + + /** + * Create the aggregate stream that will be used to upload the POST data + */ + protected function createStream(array $elements) + { + $stream = new AppendStream(); + + foreach ($elements as $element) { + $this->addElement($stream, $element); + } + + // Add the trailing boundary with CRLF + $stream->addStream(stream_for("--{$this->boundary}--\r\n")); + + return $stream; + } + + private function addElement(AppendStream $stream, array $element) + { + foreach (['contents', 'name'] as $key) { + if (!array_key_exists($key, $element)) { + throw new \InvalidArgumentException("A '{$key}' key is required"); + } + } + + $element['contents'] = stream_for($element['contents']); + + if (empty($element['filename'])) { + $uri = $element['contents']->getMetadata('uri'); + if (substr($uri, 0, 6) !== 'php://') { + $element['filename'] = $uri; + } + } + + list($body, $headers) = $this->createElement( + $element['name'], + $element['contents'], + isset($element['filename']) ? $element['filename'] : null, + isset($element['headers']) ? $element['headers'] : [] + ); + + $stream->addStream(stream_for($this->getHeaders($headers))); + $stream->addStream($body); + $stream->addStream(stream_for("\r\n")); + } + + /** + * @return array + */ + private function createElement($name, StreamInterface $stream, $filename, array $headers) + { + // Set a default content-disposition header if one was no provided + $disposition = $this->getHeader($headers, 'content-disposition'); + if (!$disposition) { + $headers['Content-Disposition'] = ($filename === '0' || $filename) + ? sprintf('form-data; name="%s"; filename="%s"', + $name, + basename($filename)) + : "form-data; name=\"{$name}\""; + } + + // Set a default content-length header if one was no provided + $length = $this->getHeader($headers, 'content-length'); + if (!$length) { + if ($length = $stream->getSize()) { + $headers['Content-Length'] = (string) $length; + } + } + + // Set a default Content-Type if one was not supplied + $type = $this->getHeader($headers, 'content-type'); + if (!$type && ($filename === '0' || $filename)) { + if ($type = mimetype_from_filename($filename)) { + $headers['Content-Type'] = $type; + } + } + + return [$stream, $headers]; + } + + private function getHeader(array $headers, $key) + { + $lowercaseHeader = strtolower($key); + foreach ($headers as $k => $v) { + if (strtolower($k) === $lowercaseHeader) { + return $v; + } + } + + return null; + } +} diff --git a/assets/php/vendor/guzzlehttp/psr7/src/NoSeekStream.php b/assets/php/vendor/guzzlehttp/psr7/src/NoSeekStream.php new file mode 100644 index 0000000..2332218 --- /dev/null +++ b/assets/php/vendor/guzzlehttp/psr7/src/NoSeekStream.php @@ -0,0 +1,22 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Stream decorator that prevents a stream from being seeked + */ +class NoSeekStream implements StreamInterface +{ + use StreamDecoratorTrait; + + public function seek($offset, $whence = SEEK_SET) + { + throw new \RuntimeException('Cannot seek a NoSeekStream'); + } + + public function isSeekable() + { + return false; + } +} diff --git a/assets/php/vendor/guzzlehttp/psr7/src/PumpStream.php b/assets/php/vendor/guzzlehttp/psr7/src/PumpStream.php new file mode 100644 index 0000000..ffb5440 --- /dev/null +++ b/assets/php/vendor/guzzlehttp/psr7/src/PumpStream.php @@ -0,0 +1,165 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Provides a read only stream that pumps data from a PHP callable. + * + * When invoking the provided callable, the PumpStream will pass the amount of + * data requested to read to the callable. The callable can choose to ignore + * this value and return fewer or more bytes than requested. Any extra data + * returned by the provided callable is buffered internally until drained using + * the read() function of the PumpStream. The provided callable MUST return + * false when there is no more data to read. + */ +class PumpStream implements StreamInterface +{ + /** @var callable */ + private $source; + + /** @var int */ + private $size; + + /** @var int */ + private $tellPos = 0; + + /** @var array */ + private $metadata; + + /** @var BufferStream */ + private $buffer; + + /** + * @param callable $source Source of the stream data. The callable MAY + * accept an integer argument used to control the + * amount of data to return. The callable MUST + * return a string when called, or false on error + * or EOF. + * @param array $options Stream options: + * - metadata: Hash of metadata to use with stream. + * - size: Size of the stream, if known. + */ + public function __construct(callable $source, array $options = []) + { + $this->source = $source; + $this->size = isset($options['size']) ? $options['size'] : null; + $this->metadata = isset($options['metadata']) ? $options['metadata'] : []; + $this->buffer = new BufferStream(); + } + + public function __toString() + { + try { + return copy_to_string($this); + } catch (\Exception $e) { + return ''; + } + } + + public function close() + { + $this->detach(); + } + + public function detach() + { + $this->tellPos = false; + $this->source = null; + } + + public function getSize() + { + return $this->size; + } + + public function tell() + { + return $this->tellPos; + } + + public function eof() + { + return !$this->source; + } + + public function isSeekable() + { + return false; + } + + public function rewind() + { + $this->seek(0); + } + + public function seek($offset, $whence = SEEK_SET) + { + throw new \RuntimeException('Cannot seek a PumpStream'); + } + + public function isWritable() + { + return false; + } + + public function write($string) + { + throw new \RuntimeException('Cannot write to a PumpStream'); + } + + public function isReadable() + { + return true; + } + + public function read($length) + { + $data = $this->buffer->read($length); + $readLen = strlen($data); + $this->tellPos += $readLen; + $remaining = $length - $readLen; + + if ($remaining) { + $this->pump($remaining); + $data .= $this->buffer->read($remaining); + $this->tellPos += strlen($data) - $readLen; + } + + return $data; + } + + public function getContents() + { + $result = ''; + while (!$this->eof()) { + $result .= $this->read(1000000); + } + + return $result; + } + + public function getMetadata($key = null) + { + if (!$key) { + return $this->metadata; + } + + return isset($this->metadata[$key]) ? $this->metadata[$key] : null; + } + + private function pump($length) + { + if ($this->source) { + do { + $data = call_user_func($this->source, $length); + if ($data === false || $data === null) { + $this->source = null; + return; + } + $this->buffer->write($data); + $length -= strlen($data); + } while ($length > 0); + } + } +} diff --git a/assets/php/vendor/guzzlehttp/psr7/src/Request.php b/assets/php/vendor/guzzlehttp/psr7/src/Request.php new file mode 100644 index 0000000..0828548 --- /dev/null +++ b/assets/php/vendor/guzzlehttp/psr7/src/Request.php @@ -0,0 +1,142 @@ +<?php +namespace GuzzleHttp\Psr7; + +use InvalidArgumentException; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\StreamInterface; +use Psr\Http\Message\UriInterface; + +/** + * PSR-7 request implementation. + */ +class Request implements RequestInterface +{ + use MessageTrait; + + /** @var string */ + private $method; + + /** @var null|string */ + private $requestTarget; + + /** @var UriInterface */ + private $uri; + + /** + * @param string $method HTTP method + * @param string|UriInterface $uri URI + * @param array $headers Request headers + * @param string|null|resource|StreamInterface $body Request body + * @param string $version Protocol version + */ + public function __construct( + $method, + $uri, + array $headers = [], + $body = null, + $version = '1.1' + ) { + if (!($uri instanceof UriInterface)) { + $uri = new Uri($uri); + } + + $this->method = strtoupper($method); + $this->uri = $uri; + $this->setHeaders($headers); + $this->protocol = $version; + + if (!$this->hasHeader('Host')) { + $this->updateHostFromUri(); + } + + if ($body !== '' && $body !== null) { + $this->stream = stream_for($body); + } + } + + public function getRequestTarget() + { + if ($this->requestTarget !== null) { + return $this->requestTarget; + } + + $target = $this->uri->getPath(); + if ($target == '') { + $target = '/'; + } + if ($this->uri->getQuery() != '') { + $target .= '?' . $this->uri->getQuery(); + } + + return $target; + } + + public function withRequestTarget($requestTarget) + { + if (preg_match('#\s#', $requestTarget)) { + throw new InvalidArgumentException( + 'Invalid request target provided; cannot contain whitespace' + ); + } + + $new = clone $this; + $new->requestTarget = $requestTarget; + return $new; + } + + public function getMethod() + { + return $this->method; + } + + public function withMethod($method) + { + $new = clone $this; + $new->method = strtoupper($method); + return $new; + } + + public function getUri() + { + return $this->uri; + } + + public function withUri(UriInterface $uri, $preserveHost = false) + { + if ($uri === $this->uri) { + return $this; + } + + $new = clone $this; + $new->uri = $uri; + + if (!$preserveHost) { + $new->updateHostFromUri(); + } + + return $new; + } + + private function updateHostFromUri() + { + $host = $this->uri->getHost(); + + if ($host == '') { + return; + } + + if (($port = $this->uri->getPort()) !== null) { + $host .= ':' . $port; + } + + if (isset($this->headerNames['host'])) { + $header = $this->headerNames['host']; + } else { + $header = 'Host'; + $this->headerNames['host'] = 'Host'; + } + // Ensure Host is the first header. + // See: http://tools.ietf.org/html/rfc7230#section-5.4 + $this->headers = [$header => [$host]] + $this->headers; + } +} diff --git a/assets/php/vendor/guzzlehttp/psr7/src/Response.php b/assets/php/vendor/guzzlehttp/psr7/src/Response.php new file mode 100644 index 0000000..2830c6c --- /dev/null +++ b/assets/php/vendor/guzzlehttp/psr7/src/Response.php @@ -0,0 +1,132 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\StreamInterface; + +/** + * PSR-7 response implementation. + */ +class Response implements ResponseInterface +{ + use MessageTrait; + + /** @var array Map of standard HTTP status code/reason phrases */ + private static $phrases = [ + 100 => 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', + 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', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 306 => 'Switch Proxy', + 307 => 'Temporary Redirect', + 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 Time-out', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Large', + 415 => 'Unsupported Media Type', + 416 => 'Requested range not satisfiable', + 417 => 'Expectation Failed', + 418 => 'I\'m a teapot', + 422 => 'Unprocessable Entity', + 423 => 'Locked', + 424 => 'Failed Dependency', + 425 => 'Unordered Collection', + 426 => 'Upgrade Required', + 428 => 'Precondition Required', + 429 => 'Too Many Requests', + 431 => 'Request Header Fields Too Large', + 451 => 'Unavailable For Legal Reasons', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Time-out', + 505 => 'HTTP Version not supported', + 506 => 'Variant Also Negotiates', + 507 => 'Insufficient Storage', + 508 => 'Loop Detected', + 511 => 'Network Authentication Required', + ]; + + /** @var string */ + private $reasonPhrase = ''; + + /** @var int */ + private $statusCode = 200; + + /** + * @param int $status Status code + * @param array $headers Response headers + * @param string|null|resource|StreamInterface $body Response body + * @param string $version Protocol version + * @param string|null $reason Reason phrase (when empty a default will be used based on the status code) + */ + public function __construct( + $status = 200, + array $headers = [], + $body = null, + $version = '1.1', + $reason = null + ) { + $this->statusCode = (int) $status; + + if ($body !== '' && $body !== null) { + $this->stream = stream_for($body); + } + + $this->setHeaders($headers); + if ($reason == '' && isset(self::$phrases[$this->statusCode])) { + $this->reasonPhrase = self::$phrases[$this->statusCode]; + } else { + $this->reasonPhrase = (string) $reason; + } + + $this->protocol = $version; + } + + public function getStatusCode() + { + return $this->statusCode; + } + + public function getReasonPhrase() + { + return $this->reasonPhrase; + } + + public function withStatus($code, $reasonPhrase = '') + { + $new = clone $this; + $new->statusCode = (int) $code; + if ($reasonPhrase == '' && isset(self::$phrases[$new->statusCode])) { + $reasonPhrase = self::$phrases[$new->statusCode]; + } + $new->reasonPhrase = $reasonPhrase; + return $new; + } +} diff --git a/assets/php/vendor/guzzlehttp/psr7/src/ServerRequest.php b/assets/php/vendor/guzzlehttp/psr7/src/ServerRequest.php new file mode 100644 index 0000000..575aab8 --- /dev/null +++ b/assets/php/vendor/guzzlehttp/psr7/src/ServerRequest.php @@ -0,0 +1,358 @@ +<?php + +namespace GuzzleHttp\Psr7; + +use InvalidArgumentException; +use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Message\UriInterface; +use Psr\Http\Message\StreamInterface; +use Psr\Http\Message\UploadedFileInterface; + +/** + * Server-side HTTP request + * + * Extends the Request definition to add methods for accessing incoming data, + * specifically server parameters, cookies, matched path parameters, query + * string arguments, body parameters, and upload file information. + * + * "Attributes" are discovered via decomposing the request (and usually + * specifically the URI path), and typically will be injected by the application. + * + * Requests are considered immutable; all methods that might change state are + * implemented such that they retain the internal state of the current + * message and return a new instance that contains the changed state. + */ +class ServerRequest extends Request implements ServerRequestInterface +{ + /** + * @var array + */ + private $attributes = []; + + /** + * @var array + */ + private $cookieParams = []; + + /** + * @var null|array|object + */ + private $parsedBody; + + /** + * @var array + */ + private $queryParams = []; + + /** + * @var array + */ + private $serverParams; + + /** + * @var array + */ + private $uploadedFiles = []; + + /** + * @param string $method HTTP method + * @param string|UriInterface $uri URI + * @param array $headers Request headers + * @param string|null|resource|StreamInterface $body Request body + * @param string $version Protocol version + * @param array $serverParams Typically the $_SERVER superglobal + */ + public function __construct( + $method, + $uri, + array $headers = [], + $body = null, + $version = '1.1', + array $serverParams = [] + ) { + $this->serverParams = $serverParams; + + parent::__construct($method, $uri, $headers, $body, $version); + } + + /** + * Return an UploadedFile instance array. + * + * @param array $files A array which respect $_FILES structure + * @throws InvalidArgumentException for unrecognized values + * @return array + */ + public static function normalizeFiles(array $files) + { + $normalized = []; + + foreach ($files as $key => $value) { + if ($value instanceof UploadedFileInterface) { + $normalized[$key] = $value; + } elseif (is_array($value) && isset($value['tmp_name'])) { + $normalized[$key] = self::createUploadedFileFromSpec($value); + } elseif (is_array($value)) { + $normalized[$key] = self::normalizeFiles($value); + continue; + } else { + throw new InvalidArgumentException('Invalid value in files specification'); + } + } + + return $normalized; + } + + /** + * Create and return an UploadedFile instance from a $_FILES specification. + * + * If the specification represents an array of values, this method will + * delegate to normalizeNestedFileSpec() and return that return value. + * + * @param array $value $_FILES struct + * @return array|UploadedFileInterface + */ + private static function createUploadedFileFromSpec(array $value) + { + if (is_array($value['tmp_name'])) { + return self::normalizeNestedFileSpec($value); + } + + return new UploadedFile( + $value['tmp_name'], + (int) $value['size'], + (int) $value['error'], + $value['name'], + $value['type'] + ); + } + + /** + * Normalize an array of file specifications. + * + * Loops through all nested files and returns a normalized array of + * UploadedFileInterface instances. + * + * @param array $files + * @return UploadedFileInterface[] + */ + private static function normalizeNestedFileSpec(array $files = []) + { + $normalizedFiles = []; + + foreach (array_keys($files['tmp_name']) as $key) { + $spec = [ + 'tmp_name' => $files['tmp_name'][$key], + 'size' => $files['size'][$key], + 'error' => $files['error'][$key], + 'name' => $files['name'][$key], + 'type' => $files['type'][$key], + ]; + $normalizedFiles[$key] = self::createUploadedFileFromSpec($spec); + } + + return $normalizedFiles; + } + + /** + * Return a ServerRequest populated with superglobals: + * $_GET + * $_POST + * $_COOKIE + * $_FILES + * $_SERVER + * + * @return ServerRequestInterface + */ + public static function fromGlobals() + { + $method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET'; + $headers = function_exists('getallheaders') ? getallheaders() : []; + $uri = self::getUriFromGlobals(); + $body = new LazyOpenStream('php://input', 'r+'); + $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']) : '1.1'; + + $serverRequest = new ServerRequest($method, $uri, $headers, $body, $protocol, $_SERVER); + + return $serverRequest + ->withCookieParams($_COOKIE) + ->withQueryParams($_GET) + ->withParsedBody($_POST) + ->withUploadedFiles(self::normalizeFiles($_FILES)); + } + + /** + * Get a Uri populated with values from $_SERVER. + * + * @return UriInterface + */ + public static function getUriFromGlobals() { + $uri = new Uri(''); + + $uri = $uri->withScheme(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http'); + + $hasPort = false; + if (isset($_SERVER['HTTP_HOST'])) { + $hostHeaderParts = explode(':', $_SERVER['HTTP_HOST']); + $uri = $uri->withHost($hostHeaderParts[0]); + if (isset($hostHeaderParts[1])) { + $hasPort = true; + $uri = $uri->withPort($hostHeaderParts[1]); + } + } elseif (isset($_SERVER['SERVER_NAME'])) { + $uri = $uri->withHost($_SERVER['SERVER_NAME']); + } elseif (isset($_SERVER['SERVER_ADDR'])) { + $uri = $uri->withHost($_SERVER['SERVER_ADDR']); + } + + if (!$hasPort && isset($_SERVER['SERVER_PORT'])) { + $uri = $uri->withPort($_SERVER['SERVER_PORT']); + } + + $hasQuery = false; + if (isset($_SERVER['REQUEST_URI'])) { + $requestUriParts = explode('?', $_SERVER['REQUEST_URI']); + $uri = $uri->withPath($requestUriParts[0]); + if (isset($requestUriParts[1])) { + $hasQuery = true; + $uri = $uri->withQuery($requestUriParts[1]); + } + } + + if (!$hasQuery && isset($_SERVER['QUERY_STRING'])) { + $uri = $uri->withQuery($_SERVER['QUERY_STRING']); + } + + return $uri; + } + + + /** + * {@inheritdoc} + */ + public function getServerParams() + { + return $this->serverParams; + } + + /** + * {@inheritdoc} + */ + public function getUploadedFiles() + { + return $this->uploadedFiles; + } + + /** + * {@inheritdoc} + */ + public function withUploadedFiles(array $uploadedFiles) + { + $new = clone $this; + $new->uploadedFiles = $uploadedFiles; + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getCookieParams() + { + return $this->cookieParams; + } + + /** + * {@inheritdoc} + */ + public function withCookieParams(array $cookies) + { + $new = clone $this; + $new->cookieParams = $cookies; + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getQueryParams() + { + return $this->queryParams; + } + + /** + * {@inheritdoc} + */ + public function withQueryParams(array $query) + { + $new = clone $this; + $new->queryParams = $query; + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getParsedBody() + { + return $this->parsedBody; + } + + /** + * {@inheritdoc} + */ + public function withParsedBody($data) + { + $new = clone $this; + $new->parsedBody = $data; + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getAttributes() + { + return $this->attributes; + } + + /** + * {@inheritdoc} + */ + public function getAttribute($attribute, $default = null) + { + if (false === array_key_exists($attribute, $this->attributes)) { + return $default; + } + + return $this->attributes[$attribute]; + } + + /** + * {@inheritdoc} + */ + public function withAttribute($attribute, $value) + { + $new = clone $this; + $new->attributes[$attribute] = $value; + + return $new; + } + + /** + * {@inheritdoc} + */ + public function withoutAttribute($attribute) + { + if (false === array_key_exists($attribute, $this->attributes)) { + return $this; + } + + $new = clone $this; + unset($new->attributes[$attribute]); + + return $new; + } +} diff --git a/assets/php/vendor/guzzlehttp/psr7/src/Stream.php b/assets/php/vendor/guzzlehttp/psr7/src/Stream.php new file mode 100644 index 0000000..e336628 --- /dev/null +++ b/assets/php/vendor/guzzlehttp/psr7/src/Stream.php @@ -0,0 +1,257 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * PHP stream implementation. + * + * @var $stream + */ +class Stream implements StreamInterface +{ + private $stream; + private $size; + private $seekable; + private $readable; + private $writable; + private $uri; + private $customMetadata; + + /** @var array Hash of readable and writable stream types */ + private static $readWriteHash = [ + 'read' => [ + 'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true, + 'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, + 'c+b' => true, 'rt' => true, 'w+t' => true, 'r+t' => true, + 'x+t' => true, 'c+t' => true, 'a+' => true + ], + 'write' => [ + 'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true, + 'c+' => true, 'wb' => true, 'w+b' => true, 'r+b' => true, + 'x+b' => true, 'c+b' => true, 'w+t' => true, 'r+t' => true, + 'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true + ] + ]; + + /** + * This constructor accepts an associative array of options. + * + * - size: (int) If a read stream would otherwise have an indeterminate + * size, but the size is known due to foreknowledge, then you can + * provide that size, in bytes. + * - metadata: (array) Any additional metadata to return when the metadata + * of the stream is accessed. + * + * @param resource $stream Stream resource to wrap. + * @param array $options Associative array of options. + * + * @throws \InvalidArgumentException if the stream is not a stream resource + */ + public function __construct($stream, $options = []) + { + if (!is_resource($stream)) { + throw new \InvalidArgumentException('Stream must be a resource'); + } + + if (isset($options['size'])) { + $this->size = $options['size']; + } + + $this->customMetadata = isset($options['metadata']) + ? $options['metadata'] + : []; + + $this->stream = $stream; + $meta = stream_get_meta_data($this->stream); + $this->seekable = $meta['seekable']; + $this->readable = isset(self::$readWriteHash['read'][$meta['mode']]); + $this->writable = isset(self::$readWriteHash['write'][$meta['mode']]); + $this->uri = $this->getMetadata('uri'); + } + + public function __get($name) + { + if ($name == 'stream') { + throw new \RuntimeException('The stream is detached'); + } + + throw new \BadMethodCallException('No value for ' . $name); + } + + /** + * Closes the stream when the destructed + */ + public function __destruct() + { + $this->close(); + } + + public function __toString() + { + try { + $this->seek(0); + return (string) stream_get_contents($this->stream); + } catch (\Exception $e) { + return ''; + } + } + + public function getContents() + { + $contents = stream_get_contents($this->stream); + + if ($contents === false) { + throw new \RuntimeException('Unable to read stream contents'); + } + + return $contents; + } + + public function close() + { + if (isset($this->stream)) { + if (is_resource($this->stream)) { + fclose($this->stream); + } + $this->detach(); + } + } + + public function detach() + { + if (!isset($this->stream)) { + return null; + } + + $result = $this->stream; + unset($this->stream); + $this->size = $this->uri = null; + $this->readable = $this->writable = $this->seekable = false; + + return $result; + } + + public function getSize() + { + if ($this->size !== null) { + return $this->size; + } + + if (!isset($this->stream)) { + return null; + } + + // Clear the stat cache if the stream has a URI + if ($this->uri) { + clearstatcache(true, $this->uri); + } + + $stats = fstat($this->stream); + if (isset($stats['size'])) { + $this->size = $stats['size']; + return $this->size; + } + + return null; + } + + public function isReadable() + { + return $this->readable; + } + + public function isWritable() + { + return $this->writable; + } + + public function isSeekable() + { + return $this->seekable; + } + + public function eof() + { + return !$this->stream || feof($this->stream); + } + + public function tell() + { + $result = ftell($this->stream); + + if ($result === false) { + throw new \RuntimeException('Unable to determine stream position'); + } + + return $result; + } + + public function rewind() + { + $this->seek(0); + } + + public function seek($offset, $whence = SEEK_SET) + { + if (!$this->seekable) { + throw new \RuntimeException('Stream is not seekable'); + } elseif (fseek($this->stream, $offset, $whence) === -1) { + throw new \RuntimeException('Unable to seek to stream position ' + . $offset . ' with whence ' . var_export($whence, true)); + } + } + + public function read($length) + { + if (!$this->readable) { + throw new \RuntimeException('Cannot read from non-readable stream'); + } + if ($length < 0) { + throw new \RuntimeException('Length parameter cannot be negative'); + } + + if (0 === $length) { + return ''; + } + + $string = fread($this->stream, $length); + if (false === $string) { + throw new \RuntimeException('Unable to read from stream'); + } + + return $string; + } + + public function write($string) + { + if (!$this->writable) { + throw new \RuntimeException('Cannot write to a non-writable stream'); + } + + // We can't know the size after writing anything + $this->size = null; + $result = fwrite($this->stream, $string); + + if ($result === false) { + throw new \RuntimeException('Unable to write to stream'); + } + + return $result; + } + + public function getMetadata($key = null) + { + if (!isset($this->stream)) { + return $key ? null : []; + } elseif (!$key) { + return $this->customMetadata + stream_get_meta_data($this->stream); + } elseif (isset($this->customMetadata[$key])) { + return $this->customMetadata[$key]; + } + + $meta = stream_get_meta_data($this->stream); + + return isset($meta[$key]) ? $meta[$key] : null; + } +} diff --git a/assets/php/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php b/assets/php/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php new file mode 100644 index 0000000..daec6f5 --- /dev/null +++ b/assets/php/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php @@ -0,0 +1,149 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Stream decorator trait + * @property StreamInterface stream + */ +trait StreamDecoratorTrait +{ + /** + * @param StreamInterface $stream Stream to decorate + */ + public function __construct(StreamInterface $stream) + { + $this->stream = $stream; + } + + /** + * Magic method used to create a new stream if streams are not added in + * the constructor of a decorator (e.g., LazyOpenStream). + * + * @param string $name Name of the property (allows "stream" only). + * + * @return StreamInterface + */ + public function __get($name) + { + if ($name == 'stream') { + $this->stream = $this->createStream(); + return $this->stream; + } + + throw new \UnexpectedValueException("$name not found on class"); + } + + public function __toString() + { + try { + if ($this->isSeekable()) { + $this->seek(0); + } + return $this->getContents(); + } catch (\Exception $e) { + // Really, PHP? https://bugs.php.net/bug.php?id=53648 + trigger_error('StreamDecorator::__toString exception: ' + . (string) $e, E_USER_ERROR); + return ''; + } + } + + public function getContents() + { + return copy_to_string($this); + } + + /** + * Allow decorators to implement custom methods + * + * @param string $method Missing method name + * @param array $args Method arguments + * + * @return mixed + */ + public function __call($method, array $args) + { + $result = call_user_func_array([$this->stream, $method], $args); + + // Always return the wrapped object if the result is a return $this + return $result === $this->stream ? $this : $result; + } + + public function close() + { + $this->stream->close(); + } + + public function getMetadata($key = null) + { + return $this->stream->getMetadata($key); + } + + public function detach() + { + return $this->stream->detach(); + } + + public function getSize() + { + return $this->stream->getSize(); + } + + public function eof() + { + return $this->stream->eof(); + } + + public function tell() + { + return $this->stream->tell(); + } + + public function isReadable() + { + return $this->stream->isReadable(); + } + + public function isWritable() + { + return $this->stream->isWritable(); + } + + public function isSeekable() + { + return $this->stream->isSeekable(); + } + + public function rewind() + { + $this->seek(0); + } + + public function seek($offset, $whence = SEEK_SET) + { + $this->stream->seek($offset, $whence); + } + + public function read($length) + { + return $this->stream->read($length); + } + + public function write($string) + { + return $this->stream->write($string); + } + + /** + * Implement in subclasses to dynamically create streams when requested. + * + * @return StreamInterface + * @throws \BadMethodCallException + */ + protected function createStream() + { + throw new \BadMethodCallException('Not implemented'); + } +} diff --git a/assets/php/vendor/guzzlehttp/psr7/src/StreamWrapper.php b/assets/php/vendor/guzzlehttp/psr7/src/StreamWrapper.php new file mode 100644 index 0000000..cf7b223 --- /dev/null +++ b/assets/php/vendor/guzzlehttp/psr7/src/StreamWrapper.php @@ -0,0 +1,121 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Converts Guzzle streams into PHP stream resources. + */ +class StreamWrapper +{ + /** @var resource */ + public $context; + + /** @var StreamInterface */ + private $stream; + + /** @var string r, r+, or w */ + private $mode; + + /** + * Returns a resource representing the stream. + * + * @param StreamInterface $stream The stream to get a resource for + * + * @return resource + * @throws \InvalidArgumentException if stream is not readable or writable + */ + public static function getResource(StreamInterface $stream) + { + self::register(); + + if ($stream->isReadable()) { + $mode = $stream->isWritable() ? 'r+' : 'r'; + } elseif ($stream->isWritable()) { + $mode = 'w'; + } else { + throw new \InvalidArgumentException('The stream must be readable, ' + . 'writable, or both.'); + } + + return fopen('guzzle://stream', $mode, null, stream_context_create([ + 'guzzle' => ['stream' => $stream] + ])); + } + + /** + * Registers the stream wrapper if needed + */ + public static function register() + { + if (!in_array('guzzle', stream_get_wrappers())) { + stream_wrapper_register('guzzle', __CLASS__); + } + } + + public function stream_open($path, $mode, $options, &$opened_path) + { + $options = stream_context_get_options($this->context); + + if (!isset($options['guzzle']['stream'])) { + return false; + } + + $this->mode = $mode; + $this->stream = $options['guzzle']['stream']; + + return true; + } + + public function stream_read($count) + { + return $this->stream->read($count); + } + + public function stream_write($data) + { + return (int) $this->stream->write($data); + } + + public function stream_tell() + { + return $this->stream->tell(); + } + + public function stream_eof() + { + return $this->stream->eof(); + } + + public function stream_seek($offset, $whence) + { + $this->stream->seek($offset, $whence); + + return true; + } + + public function stream_stat() + { + static $modeMap = [ + 'r' => 33060, + 'r+' => 33206, + 'w' => 33188 + ]; + + return [ + 'dev' => 0, + 'ino' => 0, + 'mode' => $modeMap[$this->mode], + 'nlink' => 0, + 'uid' => 0, + 'gid' => 0, + 'rdev' => 0, + 'size' => $this->stream->getSize() ?: 0, + 'atime' => 0, + 'mtime' => 0, + 'ctime' => 0, + 'blksize' => 0, + 'blocks' => 0 + ]; + } +} diff --git a/assets/php/vendor/guzzlehttp/psr7/src/UploadedFile.php b/assets/php/vendor/guzzlehttp/psr7/src/UploadedFile.php new file mode 100644 index 0000000..e62bd5c --- /dev/null +++ b/assets/php/vendor/guzzlehttp/psr7/src/UploadedFile.php @@ -0,0 +1,316 @@ +<?php +namespace GuzzleHttp\Psr7; + +use InvalidArgumentException; +use Psr\Http\Message\StreamInterface; +use Psr\Http\Message\UploadedFileInterface; +use RuntimeException; + +class UploadedFile implements UploadedFileInterface +{ + /** + * @var int[] + */ + private static $errors = [ + UPLOAD_ERR_OK, + UPLOAD_ERR_INI_SIZE, + UPLOAD_ERR_FORM_SIZE, + UPLOAD_ERR_PARTIAL, + UPLOAD_ERR_NO_FILE, + UPLOAD_ERR_NO_TMP_DIR, + UPLOAD_ERR_CANT_WRITE, + UPLOAD_ERR_EXTENSION, + ]; + + /** + * @var string + */ + private $clientFilename; + + /** + * @var string + */ + private $clientMediaType; + + /** + * @var int + */ + private $error; + + /** + * @var null|string + */ + private $file; + + /** + * @var bool + */ + private $moved = false; + + /** + * @var int + */ + private $size; + + /** + * @var StreamInterface|null + */ + private $stream; + + /** + * @param StreamInterface|string|resource $streamOrFile + * @param int $size + * @param int $errorStatus + * @param string|null $clientFilename + * @param string|null $clientMediaType + */ + public function __construct( + $streamOrFile, + $size, + $errorStatus, + $clientFilename = null, + $clientMediaType = null + ) { + $this->setError($errorStatus); + $this->setSize($size); + $this->setClientFilename($clientFilename); + $this->setClientMediaType($clientMediaType); + + if ($this->isOk()) { + $this->setStreamOrFile($streamOrFile); + } + } + + /** + * Depending on the value set file or stream variable + * + * @param mixed $streamOrFile + * @throws InvalidArgumentException + */ + private function setStreamOrFile($streamOrFile) + { + if (is_string($streamOrFile)) { + $this->file = $streamOrFile; + } elseif (is_resource($streamOrFile)) { + $this->stream = new Stream($streamOrFile); + } elseif ($streamOrFile instanceof StreamInterface) { + $this->stream = $streamOrFile; + } else { + throw new InvalidArgumentException( + 'Invalid stream or file provided for UploadedFile' + ); + } + } + + /** + * @param int $error + * @throws InvalidArgumentException + */ + private function setError($error) + { + if (false === is_int($error)) { + throw new InvalidArgumentException( + 'Upload file error status must be an integer' + ); + } + + if (false === in_array($error, UploadedFile::$errors)) { + throw new InvalidArgumentException( + 'Invalid error status for UploadedFile' + ); + } + + $this->error = $error; + } + + /** + * @param int $size + * @throws InvalidArgumentException + */ + private function setSize($size) + { + if (false === is_int($size)) { + throw new InvalidArgumentException( + 'Upload file size must be an integer' + ); + } + + $this->size = $size; + } + + /** + * @param mixed $param + * @return boolean + */ + private function isStringOrNull($param) + { + return in_array(gettype($param), ['string', 'NULL']); + } + + /** + * @param mixed $param + * @return boolean + */ + private function isStringNotEmpty($param) + { + return is_string($param) && false === empty($param); + } + + /** + * @param string|null $clientFilename + * @throws InvalidArgumentException + */ + private function setClientFilename($clientFilename) + { + if (false === $this->isStringOrNull($clientFilename)) { + throw new InvalidArgumentException( + 'Upload file client filename must be a string or null' + ); + } + + $this->clientFilename = $clientFilename; + } + + /** + * @param string|null $clientMediaType + * @throws InvalidArgumentException + */ + private function setClientMediaType($clientMediaType) + { + if (false === $this->isStringOrNull($clientMediaType)) { + throw new InvalidArgumentException( + 'Upload file client media type must be a string or null' + ); + } + + $this->clientMediaType = $clientMediaType; + } + + /** + * Return true if there is no upload error + * + * @return boolean + */ + private function isOk() + { + return $this->error === UPLOAD_ERR_OK; + } + + /** + * @return boolean + */ + public function isMoved() + { + return $this->moved; + } + + /** + * @throws RuntimeException if is moved or not ok + */ + private function validateActive() + { + if (false === $this->isOk()) { + throw new RuntimeException('Cannot retrieve stream due to upload error'); + } + + if ($this->isMoved()) { + throw new RuntimeException('Cannot retrieve stream after it has already been moved'); + } + } + + /** + * {@inheritdoc} + * @throws RuntimeException if the upload was not successful. + */ + public function getStream() + { + $this->validateActive(); + + if ($this->stream instanceof StreamInterface) { + return $this->stream; + } + + return new LazyOpenStream($this->file, 'r+'); + } + + /** + * {@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 RuntimeException if the upload was not successful. + * @throws InvalidArgumentException if the $path specified is invalid. + * @throws RuntimeException on any error during the move operation, or on + * the second or subsequent call to the method. + */ + public function moveTo($targetPath) + { + $this->validateActive(); + + if (false === $this->isStringNotEmpty($targetPath)) { + throw new InvalidArgumentException( + 'Invalid path provided for move operation; must be a non-empty string' + ); + } + + if ($this->file) { + $this->moved = php_sapi_name() == 'cli' + ? rename($this->file, $targetPath) + : move_uploaded_file($this->file, $targetPath); + } else { + copy_to_stream( + $this->getStream(), + new LazyOpenStream($targetPath, 'w') + ); + + $this->moved = true; + } + + if (false === $this->moved) { + throw new RuntimeException( + sprintf('Uploaded file could not be moved to %s', $targetPath) + ); + } + } + + /** + * {@inheritdoc} + * + * @return int|null The file size in bytes or null if unknown. + */ + public function getSize() + { + 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() + { + return $this->error; + } + + /** + * {@inheritdoc} + * + * @return string|null The filename sent by the client or null if none + * was provided. + */ + public function getClientFilename() + { + return $this->clientFilename; + } + + /** + * {@inheritdoc} + */ + public function getClientMediaType() + { + return $this->clientMediaType; + } +} diff --git a/assets/php/vendor/guzzlehttp/psr7/src/Uri.php b/assets/php/vendor/guzzlehttp/psr7/src/Uri.php new file mode 100644 index 0000000..f46c1db --- /dev/null +++ b/assets/php/vendor/guzzlehttp/psr7/src/Uri.php @@ -0,0 +1,702 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\UriInterface; + +/** + * PSR-7 URI implementation. + * + * @author Michael Dowling + * @author Tobias Schultze + * @author Matthew Weier O'Phinney + */ +class Uri implements UriInterface +{ + /** + * Absolute http and https URIs require a host per RFC 7230 Section 2.7 + * but in generic URIs the host can be empty. So for http(s) URIs + * we apply this default host when no host is given yet to form a + * valid URI. + */ + const HTTP_DEFAULT_HOST = 'localhost'; + + private static $defaultPorts = [ + 'http' => 80, + 'https' => 443, + 'ftp' => 21, + 'gopher' => 70, + 'nntp' => 119, + 'news' => 119, + 'telnet' => 23, + 'tn3270' => 23, + 'imap' => 143, + 'pop' => 110, + 'ldap' => 389, + ]; + + private static $charUnreserved = 'a-zA-Z0-9_\-\.~'; + private static $charSubDelims = '!\$&\'\(\)\*\+,;='; + private static $replaceQuery = ['=' => '%3D', '&' => '%26']; + + /** @var string Uri scheme. */ + private $scheme = ''; + + /** @var string Uri user info. */ + private $userInfo = ''; + + /** @var string Uri host. */ + private $host = ''; + + /** @var int|null Uri port. */ + private $port; + + /** @var string Uri path. */ + private $path = ''; + + /** @var string Uri query string. */ + private $query = ''; + + /** @var string Uri fragment. */ + private $fragment = ''; + + /** + * @param string $uri URI to parse + */ + public function __construct($uri = '') + { + // weak type check to also accept null until we can add scalar type hints + if ($uri != '') { + $parts = parse_url($uri); + if ($parts === false) { + throw new \InvalidArgumentException("Unable to parse URI: $uri"); + } + $this->applyParts($parts); + } + } + + public function __toString() + { + return self::composeComponents( + $this->scheme, + $this->getAuthority(), + $this->path, + $this->query, + $this->fragment + ); + } + + /** + * Composes a URI reference string from its various components. + * + * Usually this method does not need to be called manually but instead is used indirectly via + * `Psr\Http\Message\UriInterface::__toString`. + * + * PSR-7 UriInterface treats an empty component the same as a missing component as + * getQuery(), getFragment() etc. always return a string. This explains the slight + * difference to RFC 3986 Section 5.3. + * + * Another adjustment is that the authority separator is added even when the authority is missing/empty + * for the "file" scheme. This is because PHP stream functions like `file_get_contents` only work with + * `file:///myfile` but not with `file:/myfile` although they are equivalent according to RFC 3986. But + * `file:///` is the more common syntax for the file scheme anyway (Chrome for example redirects to + * that format). + * + * @param string $scheme + * @param string $authority + * @param string $path + * @param string $query + * @param string $fragment + * + * @return string + * + * @link https://tools.ietf.org/html/rfc3986#section-5.3 + */ + public static function composeComponents($scheme, $authority, $path, $query, $fragment) + { + $uri = ''; + + // weak type checks to also accept null until we can add scalar type hints + if ($scheme != '') { + $uri .= $scheme . ':'; + } + + if ($authority != ''|| $scheme === 'file') { + $uri .= '//' . $authority; + } + + $uri .= $path; + + if ($query != '') { + $uri .= '?' . $query; + } + + if ($fragment != '') { + $uri .= '#' . $fragment; + } + + return $uri; + } + + /** + * Whether the URI has the default port of the current scheme. + * + * `Psr\Http\Message\UriInterface::getPort` may return null or the standard port. This method can be used + * independently of the implementation. + * + * @param UriInterface $uri + * + * @return bool + */ + public static function isDefaultPort(UriInterface $uri) + { + return $uri->getPort() === null + || (isset(self::$defaultPorts[$uri->getScheme()]) && $uri->getPort() === self::$defaultPorts[$uri->getScheme()]); + } + + /** + * Whether the URI is absolute, i.e. it has a scheme. + * + * An instance of UriInterface can either be an absolute URI or a relative reference. This method returns true + * if it is the former. An absolute URI has a scheme. A relative reference is used to express a URI relative + * to another URI, the base URI. Relative references can be divided into several forms: + * - network-path references, e.g. '//example.com/path' + * - absolute-path references, e.g. '/path' + * - relative-path references, e.g. 'subpath' + * + * @param UriInterface $uri + * + * @return bool + * @see Uri::isNetworkPathReference + * @see Uri::isAbsolutePathReference + * @see Uri::isRelativePathReference + * @link https://tools.ietf.org/html/rfc3986#section-4 + */ + public static function isAbsolute(UriInterface $uri) + { + return $uri->getScheme() !== ''; + } + + /** + * Whether the URI is a network-path reference. + * + * A relative reference that begins with two slash characters is termed an network-path reference. + * + * @param UriInterface $uri + * + * @return bool + * @link https://tools.ietf.org/html/rfc3986#section-4.2 + */ + public static function isNetworkPathReference(UriInterface $uri) + { + return $uri->getScheme() === '' && $uri->getAuthority() !== ''; + } + + /** + * Whether the URI is a absolute-path reference. + * + * A relative reference that begins with a single slash character is termed an absolute-path reference. + * + * @param UriInterface $uri + * + * @return bool + * @link https://tools.ietf.org/html/rfc3986#section-4.2 + */ + public static function isAbsolutePathReference(UriInterface $uri) + { + return $uri->getScheme() === '' + && $uri->getAuthority() === '' + && isset($uri->getPath()[0]) + && $uri->getPath()[0] === '/'; + } + + /** + * Whether the URI is a relative-path reference. + * + * A relative reference that does not begin with a slash character is termed a relative-path reference. + * + * @param UriInterface $uri + * + * @return bool + * @link https://tools.ietf.org/html/rfc3986#section-4.2 + */ + public static function isRelativePathReference(UriInterface $uri) + { + return $uri->getScheme() === '' + && $uri->getAuthority() === '' + && (!isset($uri->getPath()[0]) || $uri->getPath()[0] !== '/'); + } + + /** + * Whether the URI is a same-document reference. + * + * A same-document reference refers to a URI that is, aside from its fragment + * component, identical to the base URI. When no base URI is given, only an empty + * URI reference (apart from its fragment) is considered a same-document reference. + * + * @param UriInterface $uri The URI to check + * @param UriInterface|null $base An optional base URI to compare against + * + * @return bool + * @link https://tools.ietf.org/html/rfc3986#section-4.4 + */ + public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null) + { + if ($base !== null) { + $uri = UriResolver::resolve($base, $uri); + + return ($uri->getScheme() === $base->getScheme()) + && ($uri->getAuthority() === $base->getAuthority()) + && ($uri->getPath() === $base->getPath()) + && ($uri->getQuery() === $base->getQuery()); + } + + return $uri->getScheme() === '' && $uri->getAuthority() === '' && $uri->getPath() === '' && $uri->getQuery() === ''; + } + + /** + * Removes dot segments from a path and returns the new path. + * + * @param string $path + * + * @return string + * + * @deprecated since version 1.4. Use UriResolver::removeDotSegments instead. + * @see UriResolver::removeDotSegments + */ + public static function removeDotSegments($path) + { + return UriResolver::removeDotSegments($path); + } + + /** + * Converts the relative URI into a new URI that is resolved against the base URI. + * + * @param UriInterface $base Base URI + * @param string|UriInterface $rel Relative URI + * + * @return UriInterface + * + * @deprecated since version 1.4. Use UriResolver::resolve instead. + * @see UriResolver::resolve + */ + public static function resolve(UriInterface $base, $rel) + { + if (!($rel instanceof UriInterface)) { + $rel = new self($rel); + } + + return UriResolver::resolve($base, $rel); + } + + /** + * Creates a new URI with a specific query string value removed. + * + * Any existing query string values that exactly match the provided key are + * removed. + * + * @param UriInterface $uri URI to use as a base. + * @param string $key Query string key to remove. + * + * @return UriInterface + */ + public static function withoutQueryValue(UriInterface $uri, $key) + { + $current = $uri->getQuery(); + if ($current === '') { + return $uri; + } + + $decodedKey = rawurldecode($key); + $result = array_filter(explode('&', $current), function ($part) use ($decodedKey) { + return rawurldecode(explode('=', $part)[0]) !== $decodedKey; + }); + + return $uri->withQuery(implode('&', $result)); + } + + /** + * Creates a new URI with a specific query string value. + * + * Any existing query string values that exactly match the provided key are + * removed and replaced with the given key value pair. + * + * A value of null will set the query string key without a value, e.g. "key" + * instead of "key=value". + * + * @param UriInterface $uri URI to use as a base. + * @param string $key Key to set. + * @param string|null $value Value to set + * + * @return UriInterface + */ + public static function withQueryValue(UriInterface $uri, $key, $value) + { + $current = $uri->getQuery(); + + if ($current === '') { + $result = []; + } else { + $decodedKey = rawurldecode($key); + $result = array_filter(explode('&', $current), function ($part) use ($decodedKey) { + return rawurldecode(explode('=', $part)[0]) !== $decodedKey; + }); + } + + // Query string separators ("=", "&") within the key or value need to be encoded + // (while preventing double-encoding) before setting the query string. All other + // chars that need percent-encoding will be encoded by withQuery(). + $key = strtr($key, self::$replaceQuery); + + if ($value !== null) { + $result[] = $key . '=' . strtr($value, self::$replaceQuery); + } else { + $result[] = $key; + } + + return $uri->withQuery(implode('&', $result)); + } + + /** + * Creates a URI from a hash of `parse_url` components. + * + * @param array $parts + * + * @return UriInterface + * @link http://php.net/manual/en/function.parse-url.php + * + * @throws \InvalidArgumentException If the components do not form a valid URI. + */ + public static function fromParts(array $parts) + { + $uri = new self(); + $uri->applyParts($parts); + $uri->validateState(); + + return $uri; + } + + public function getScheme() + { + return $this->scheme; + } + + public function getAuthority() + { + $authority = $this->host; + if ($this->userInfo !== '') { + $authority = $this->userInfo . '@' . $authority; + } + + if ($this->port !== null) { + $authority .= ':' . $this->port; + } + + return $authority; + } + + public function getUserInfo() + { + return $this->userInfo; + } + + public function getHost() + { + return $this->host; + } + + public function getPort() + { + return $this->port; + } + + public function getPath() + { + return $this->path; + } + + public function getQuery() + { + return $this->query; + } + + public function getFragment() + { + return $this->fragment; + } + + public function withScheme($scheme) + { + $scheme = $this->filterScheme($scheme); + + if ($this->scheme === $scheme) { + return $this; + } + + $new = clone $this; + $new->scheme = $scheme; + $new->removeDefaultPort(); + $new->validateState(); + + return $new; + } + + public function withUserInfo($user, $password = null) + { + $info = $user; + if ($password != '') { + $info .= ':' . $password; + } + + if ($this->userInfo === $info) { + return $this; + } + + $new = clone $this; + $new->userInfo = $info; + $new->validateState(); + + return $new; + } + + public function withHost($host) + { + $host = $this->filterHost($host); + + if ($this->host === $host) { + return $this; + } + + $new = clone $this; + $new->host = $host; + $new->validateState(); + + return $new; + } + + public function withPort($port) + { + $port = $this->filterPort($port); + + if ($this->port === $port) { + return $this; + } + + $new = clone $this; + $new->port = $port; + $new->removeDefaultPort(); + $new->validateState(); + + return $new; + } + + public function withPath($path) + { + $path = $this->filterPath($path); + + if ($this->path === $path) { + return $this; + } + + $new = clone $this; + $new->path = $path; + $new->validateState(); + + return $new; + } + + public function withQuery($query) + { + $query = $this->filterQueryAndFragment($query); + + if ($this->query === $query) { + return $this; + } + + $new = clone $this; + $new->query = $query; + + return $new; + } + + public function withFragment($fragment) + { + $fragment = $this->filterQueryAndFragment($fragment); + + if ($this->fragment === $fragment) { + return $this; + } + + $new = clone $this; + $new->fragment = $fragment; + + return $new; + } + + /** + * Apply parse_url parts to a URI. + * + * @param array $parts Array of parse_url parts to apply. + */ + private function applyParts(array $parts) + { + $this->scheme = isset($parts['scheme']) + ? $this->filterScheme($parts['scheme']) + : ''; + $this->userInfo = isset($parts['user']) ? $parts['user'] : ''; + $this->host = isset($parts['host']) + ? $this->filterHost($parts['host']) + : ''; + $this->port = isset($parts['port']) + ? $this->filterPort($parts['port']) + : null; + $this->path = isset($parts['path']) + ? $this->filterPath($parts['path']) + : ''; + $this->query = isset($parts['query']) + ? $this->filterQueryAndFragment($parts['query']) + : ''; + $this->fragment = isset($parts['fragment']) + ? $this->filterQueryAndFragment($parts['fragment']) + : ''; + if (isset($parts['pass'])) { + $this->userInfo .= ':' . $parts['pass']; + } + + $this->removeDefaultPort(); + } + + /** + * @param string $scheme + * + * @return string + * + * @throws \InvalidArgumentException If the scheme is invalid. + */ + private function filterScheme($scheme) + { + if (!is_string($scheme)) { + throw new \InvalidArgumentException('Scheme must be a string'); + } + + return strtolower($scheme); + } + + /** + * @param string $host + * + * @return string + * + * @throws \InvalidArgumentException If the host is invalid. + */ + private function filterHost($host) + { + if (!is_string($host)) { + throw new \InvalidArgumentException('Host must be a string'); + } + + return strtolower($host); + } + + /** + * @param int|null $port + * + * @return int|null + * + * @throws \InvalidArgumentException If the port is invalid. + */ + private function filterPort($port) + { + if ($port === null) { + return null; + } + + $port = (int) $port; + if (1 > $port || 0xffff < $port) { + throw new \InvalidArgumentException( + sprintf('Invalid port: %d. Must be between 1 and 65535', $port) + ); + } + + return $port; + } + + private function removeDefaultPort() + { + if ($this->port !== null && self::isDefaultPort($this)) { + $this->port = null; + } + } + + /** + * Filters the path of a URI + * + * @param string $path + * + * @return string + * + * @throws \InvalidArgumentException If the path is invalid. + */ + private function filterPath($path) + { + if (!is_string($path)) { + throw new \InvalidArgumentException('Path must be a string'); + } + + return preg_replace_callback( + '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/]++|%(?![A-Fa-f0-9]{2}))/', + [$this, 'rawurlencodeMatchZero'], + $path + ); + } + + /** + * Filters the query string or fragment of a URI. + * + * @param string $str + * + * @return string + * + * @throws \InvalidArgumentException If the query or fragment is invalid. + */ + private function filterQueryAndFragment($str) + { + if (!is_string($str)) { + throw new \InvalidArgumentException('Query and fragment must be a string'); + } + + return preg_replace_callback( + '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/', + [$this, 'rawurlencodeMatchZero'], + $str + ); + } + + private function rawurlencodeMatchZero(array $match) + { + return rawurlencode($match[0]); + } + + private function validateState() + { + if ($this->host === '' && ($this->scheme === 'http' || $this->scheme === 'https')) { + $this->host = self::HTTP_DEFAULT_HOST; + } + + if ($this->getAuthority() === '') { + if (0 === strpos($this->path, '//')) { + throw new \InvalidArgumentException('The path of a URI without an authority must not start with two slashes "//"'); + } + if ($this->scheme === '' && false !== strpos(explode('/', $this->path, 2)[0], ':')) { + throw new \InvalidArgumentException('A relative URI must not have a path beginning with a segment containing a colon'); + } + } elseif (isset($this->path[0]) && $this->path[0] !== '/') { + @trigger_error( + 'The path of a URI with an authority must start with a slash "/" or be empty. Automagically fixing the URI ' . + 'by adding a leading slash to the path is deprecated since version 1.4 and will throw an exception instead.', + E_USER_DEPRECATED + ); + $this->path = '/'. $this->path; + //throw new \InvalidArgumentException('The path of a URI with an authority must start with a slash "/" or be empty'); + } + } +} diff --git a/assets/php/vendor/guzzlehttp/psr7/src/UriNormalizer.php b/assets/php/vendor/guzzlehttp/psr7/src/UriNormalizer.php new file mode 100644 index 0000000..384c29e --- /dev/null +++ b/assets/php/vendor/guzzlehttp/psr7/src/UriNormalizer.php @@ -0,0 +1,216 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\UriInterface; + +/** + * Provides methods to normalize and compare URIs. + * + * @author Tobias Schultze + * + * @link https://tools.ietf.org/html/rfc3986#section-6 + */ +final class UriNormalizer +{ + /** + * Default normalizations which only include the ones that preserve semantics. + * + * self::CAPITALIZE_PERCENT_ENCODING | self::DECODE_UNRESERVED_CHARACTERS | self::CONVERT_EMPTY_PATH | + * self::REMOVE_DEFAULT_HOST | self::REMOVE_DEFAULT_PORT | self::REMOVE_DOT_SEGMENTS + */ + const PRESERVING_NORMALIZATIONS = 63; + + /** + * All letters within a percent-encoding triplet (e.g., "%3A") are case-insensitive, and should be capitalized. + * + * Example: http://example.org/a%c2%b1b → http://example.org/a%C2%B1b + */ + const CAPITALIZE_PERCENT_ENCODING = 1; + + /** + * Decodes percent-encoded octets of unreserved characters. + * + * For consistency, percent-encoded octets in the ranges of ALPHA (%41–%5A and %61–%7A), DIGIT (%30–%39), + * hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should not be created by URI producers and, + * when found in a URI, should be decoded to their corresponding unreserved characters by URI normalizers. + * + * Example: http://example.org/%7Eusern%61me/ → http://example.org/~username/ + */ + const DECODE_UNRESERVED_CHARACTERS = 2; + + /** + * Converts the empty path to "/" for http and https URIs. + * + * Example: http://example.org → http://example.org/ + */ + const CONVERT_EMPTY_PATH = 4; + + /** + * Removes the default host of the given URI scheme from the URI. + * + * Only the "file" scheme defines the default host "localhost". + * All of `file:/myfile`, `file:///myfile`, and `file://localhost/myfile` + * are equivalent according to RFC 3986. The first format is not accepted + * by PHPs stream functions and thus already normalized implicitly to the + * second format in the Uri class. See `GuzzleHttp\Psr7\Uri::composeComponents`. + * + * Example: file://localhost/myfile → file:///myfile + */ + const REMOVE_DEFAULT_HOST = 8; + + /** + * Removes the default port of the given URI scheme from the URI. + * + * Example: http://example.org:80/ → http://example.org/ + */ + const REMOVE_DEFAULT_PORT = 16; + + /** + * Removes unnecessary dot-segments. + * + * Dot-segments in relative-path references are not removed as it would + * change the semantics of the URI reference. + * + * Example: http://example.org/../a/b/../c/./d.html → http://example.org/a/c/d.html + */ + const REMOVE_DOT_SEGMENTS = 32; + + /** + * Paths which include two or more adjacent slashes are converted to one. + * + * Webservers usually ignore duplicate slashes and treat those URIs equivalent. + * But in theory those URIs do not need to be equivalent. So this normalization + * may change the semantics. Encoded slashes (%2F) are not removed. + * + * Example: http://example.org//foo///bar.html → http://example.org/foo/bar.html + */ + const REMOVE_DUPLICATE_SLASHES = 64; + + /** + * Sort query parameters with their values in alphabetical order. + * + * However, the order of parameters in a URI may be significant (this is not defined by the standard). + * So this normalization is not safe and may change the semantics of the URI. + * + * Example: ?lang=en&article=fred → ?article=fred&lang=en + * + * Note: The sorting is neither locale nor Unicode aware (the URI query does not get decoded at all) as the + * purpose is to be able to compare URIs in a reproducible way, not to have the params sorted perfectly. + */ + const SORT_QUERY_PARAMETERS = 128; + + /** + * Returns a normalized URI. + * + * The scheme and host component are already normalized to lowercase per PSR-7 UriInterface. + * This methods adds additional normalizations that can be configured with the $flags parameter. + * + * PSR-7 UriInterface cannot distinguish between an empty component and a missing component as + * getQuery(), getFragment() etc. always return a string. This means the URIs "/?#" and "/" are + * treated equivalent which is not necessarily true according to RFC 3986. But that difference + * is highly uncommon in reality. So this potential normalization is implied in PSR-7 as well. + * + * @param UriInterface $uri The URI to normalize + * @param int $flags A bitmask of normalizations to apply, see constants + * + * @return UriInterface The normalized URI + * @link https://tools.ietf.org/html/rfc3986#section-6.2 + */ + public static function normalize(UriInterface $uri, $flags = self::PRESERVING_NORMALIZATIONS) + { + if ($flags & self::CAPITALIZE_PERCENT_ENCODING) { + $uri = self::capitalizePercentEncoding($uri); + } + + if ($flags & self::DECODE_UNRESERVED_CHARACTERS) { + $uri = self::decodeUnreservedCharacters($uri); + } + + if ($flags & self::CONVERT_EMPTY_PATH && $uri->getPath() === '' && + ($uri->getScheme() === 'http' || $uri->getScheme() === 'https') + ) { + $uri = $uri->withPath('/'); + } + + if ($flags & self::REMOVE_DEFAULT_HOST && $uri->getScheme() === 'file' && $uri->getHost() === 'localhost') { + $uri = $uri->withHost(''); + } + + if ($flags & self::REMOVE_DEFAULT_PORT && $uri->getPort() !== null && Uri::isDefaultPort($uri)) { + $uri = $uri->withPort(null); + } + + if ($flags & self::REMOVE_DOT_SEGMENTS && !Uri::isRelativePathReference($uri)) { + $uri = $uri->withPath(UriResolver::removeDotSegments($uri->getPath())); + } + + if ($flags & self::REMOVE_DUPLICATE_SLASHES) { + $uri = $uri->withPath(preg_replace('#//++#', '/', $uri->getPath())); + } + + if ($flags & self::SORT_QUERY_PARAMETERS && $uri->getQuery() !== '') { + $queryKeyValues = explode('&', $uri->getQuery()); + sort($queryKeyValues); + $uri = $uri->withQuery(implode('&', $queryKeyValues)); + } + + return $uri; + } + + /** + * Whether two URIs can be considered equivalent. + * + * Both URIs are normalized automatically before comparison with the given $normalizations bitmask. The method also + * accepts relative URI references and returns true when they are equivalent. This of course assumes they will be + * resolved against the same base URI. If this is not the case, determination of equivalence or difference of + * relative references does not mean anything. + * + * @param UriInterface $uri1 An URI to compare + * @param UriInterface $uri2 An URI to compare + * @param int $normalizations A bitmask of normalizations to apply, see constants + * + * @return bool + * @link https://tools.ietf.org/html/rfc3986#section-6.1 + */ + public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, $normalizations = self::PRESERVING_NORMALIZATIONS) + { + return (string) self::normalize($uri1, $normalizations) === (string) self::normalize($uri2, $normalizations); + } + + private static function capitalizePercentEncoding(UriInterface $uri) + { + $regex = '/(?:%[A-Fa-f0-9]{2})++/'; + + $callback = function (array $match) { + return strtoupper($match[0]); + }; + + return + $uri->withPath( + preg_replace_callback($regex, $callback, $uri->getPath()) + )->withQuery( + preg_replace_callback($regex, $callback, $uri->getQuery()) + ); + } + + private static function decodeUnreservedCharacters(UriInterface $uri) + { + $regex = '/%(?:2D|2E|5F|7E|3[0-9]|[46][1-9A-F]|[57][0-9A])/i'; + + $callback = function (array $match) { + return rawurldecode($match[0]); + }; + + return + $uri->withPath( + preg_replace_callback($regex, $callback, $uri->getPath()) + )->withQuery( + preg_replace_callback($regex, $callback, $uri->getQuery()) + ); + } + + private function __construct() + { + // cannot be instantiated + } +} diff --git a/assets/php/vendor/guzzlehttp/psr7/src/UriResolver.php b/assets/php/vendor/guzzlehttp/psr7/src/UriResolver.php new file mode 100644 index 0000000..c1cb8a2 --- /dev/null +++ b/assets/php/vendor/guzzlehttp/psr7/src/UriResolver.php @@ -0,0 +1,219 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\UriInterface; + +/** + * Resolves a URI reference in the context of a base URI and the opposite way. + * + * @author Tobias Schultze + * + * @link https://tools.ietf.org/html/rfc3986#section-5 + */ +final class UriResolver +{ + /** + * Removes dot segments from a path and returns the new path. + * + * @param string $path + * + * @return string + * @link http://tools.ietf.org/html/rfc3986#section-5.2.4 + */ + public static function removeDotSegments($path) + { + if ($path === '' || $path === '/') { + return $path; + } + + $results = []; + $segments = explode('/', $path); + foreach ($segments as $segment) { + if ($segment === '..') { + array_pop($results); + } elseif ($segment !== '.') { + $results[] = $segment; + } + } + + $newPath = implode('/', $results); + + if ($path[0] === '/' && (!isset($newPath[0]) || $newPath[0] !== '/')) { + // Re-add the leading slash if necessary for cases like "/.." + $newPath = '/' . $newPath; + } elseif ($newPath !== '' && ($segment === '.' || $segment === '..')) { + // Add the trailing slash if necessary + // If newPath is not empty, then $segment must be set and is the last segment from the foreach + $newPath .= '/'; + } + + return $newPath; + } + + /** + * Converts the relative URI into a new URI that is resolved against the base URI. + * + * @param UriInterface $base Base URI + * @param UriInterface $rel Relative URI + * + * @return UriInterface + * @link http://tools.ietf.org/html/rfc3986#section-5.2 + */ + public static function resolve(UriInterface $base, UriInterface $rel) + { + if ((string) $rel === '') { + // we can simply return the same base URI instance for this same-document reference + return $base; + } + + if ($rel->getScheme() != '') { + return $rel->withPath(self::removeDotSegments($rel->getPath())); + } + + if ($rel->getAuthority() != '') { + $targetAuthority = $rel->getAuthority(); + $targetPath = self::removeDotSegments($rel->getPath()); + $targetQuery = $rel->getQuery(); + } else { + $targetAuthority = $base->getAuthority(); + if ($rel->getPath() === '') { + $targetPath = $base->getPath(); + $targetQuery = $rel->getQuery() != '' ? $rel->getQuery() : $base->getQuery(); + } else { + if ($rel->getPath()[0] === '/') { + $targetPath = $rel->getPath(); + } else { + if ($targetAuthority != '' && $base->getPath() === '') { + $targetPath = '/' . $rel->getPath(); + } else { + $lastSlashPos = strrpos($base->getPath(), '/'); + if ($lastSlashPos === false) { + $targetPath = $rel->getPath(); + } else { + $targetPath = substr($base->getPath(), 0, $lastSlashPos + 1) . $rel->getPath(); + } + } + } + $targetPath = self::removeDotSegments($targetPath); + $targetQuery = $rel->getQuery(); + } + } + + return new Uri(Uri::composeComponents( + $base->getScheme(), + $targetAuthority, + $targetPath, + $targetQuery, + $rel->getFragment() + )); + } + + /** + * Returns the target URI as a relative reference from the base URI. + * + * This method is the counterpart to resolve(): + * + * (string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target)) + * + * One use-case is to use the current request URI as base URI and then generate relative links in your documents + * to reduce the document size or offer self-contained downloadable document archives. + * + * $base = new Uri('http://example.com/a/b/'); + * echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c')); // prints 'c'. + * echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y')); // prints '../x/y'. + * echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'. + * echo UriResolver::relativize($base, new Uri('http://example.org/a/b/')); // prints '//example.org/a/b/'. + * + * This method also accepts a target that is already relative and will try to relativize it further. Only a + * relative-path reference will be returned as-is. + * + * echo UriResolver::relativize($base, new Uri('/a/b/c')); // prints 'c' as well + * + * @param UriInterface $base Base URI + * @param UriInterface $target Target URI + * + * @return UriInterface The relative URI reference + */ + public static function relativize(UriInterface $base, UriInterface $target) + { + if ($target->getScheme() !== '' && + ($base->getScheme() !== $target->getScheme() || $target->getAuthority() === '' && $base->getAuthority() !== '') + ) { + return $target; + } + + if (Uri::isRelativePathReference($target)) { + // As the target is already highly relative we return it as-is. It would be possible to resolve + // the target with `$target = self::resolve($base, $target);` and then try make it more relative + // by removing a duplicate query. But let's not do that automatically. + return $target; + } + + if ($target->getAuthority() !== '' && $base->getAuthority() !== $target->getAuthority()) { + return $target->withScheme(''); + } + + // We must remove the path before removing the authority because if the path starts with two slashes, the URI + // would turn invalid. And we also cannot set a relative path before removing the authority, as that is also + // invalid. + $emptyPathUri = $target->withScheme('')->withPath('')->withUserInfo('')->withPort(null)->withHost(''); + + if ($base->getPath() !== $target->getPath()) { + return $emptyPathUri->withPath(self::getRelativePath($base, $target)); + } + + if ($base->getQuery() === $target->getQuery()) { + // Only the target fragment is left. And it must be returned even if base and target fragment are the same. + return $emptyPathUri->withQuery(''); + } + + // If the base URI has a query but the target has none, we cannot return an empty path reference as it would + // inherit the base query component when resolving. + if ($target->getQuery() === '') { + $segments = explode('/', $target->getPath()); + $lastSegment = end($segments); + + return $emptyPathUri->withPath($lastSegment === '' ? './' : $lastSegment); + } + + return $emptyPathUri; + } + + private static function getRelativePath(UriInterface $base, UriInterface $target) + { + $sourceSegments = explode('/', $base->getPath()); + $targetSegments = explode('/', $target->getPath()); + array_pop($sourceSegments); + $targetLastSegment = array_pop($targetSegments); + foreach ($sourceSegments as $i => $segment) { + if (isset($targetSegments[$i]) && $segment === $targetSegments[$i]) { + unset($sourceSegments[$i], $targetSegments[$i]); + } else { + break; + } + } + $targetSegments[] = $targetLastSegment; + $relativePath = str_repeat('../', count($sourceSegments)) . implode('/', $targetSegments); + + // A reference to am empty last segment or an empty first sub-segment must be prefixed with "./". + // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used + // as the first segment of a relative-path reference, as it would be mistaken for a scheme name. + if ('' === $relativePath || false !== strpos(explode('/', $relativePath, 2)[0], ':')) { + $relativePath = "./$relativePath"; + } elseif ('/' === $relativePath[0]) { + if ($base->getAuthority() != '' && $base->getPath() === '') { + // In this case an extra slash is added by resolve() automatically. So we must not add one here. + $relativePath = ".$relativePath"; + } else { + $relativePath = "./$relativePath"; + } + } + + return $relativePath; + } + + private function __construct() + { + // cannot be instantiated + } +} diff --git a/assets/php/vendor/guzzlehttp/psr7/src/functions.php b/assets/php/vendor/guzzlehttp/psr7/src/functions.php new file mode 100644 index 0000000..e40348d --- /dev/null +++ b/assets/php/vendor/guzzlehttp/psr7/src/functions.php @@ -0,0 +1,828 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\MessageInterface; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Message\StreamInterface; +use Psr\Http\Message\UriInterface; + +/** + * Returns the string representation of an HTTP message. + * + * @param MessageInterface $message Message to convert to a string. + * + * @return string + */ +function str(MessageInterface $message) +{ + if ($message instanceof RequestInterface) { + $msg = trim($message->getMethod() . ' ' + . $message->getRequestTarget()) + . ' HTTP/' . $message->getProtocolVersion(); + if (!$message->hasHeader('host')) { + $msg .= "\r\nHost: " . $message->getUri()->getHost(); + } + } elseif ($message instanceof ResponseInterface) { + $msg = 'HTTP/' . $message->getProtocolVersion() . ' ' + . $message->getStatusCode() . ' ' + . $message->getReasonPhrase(); + } else { + throw new \InvalidArgumentException('Unknown message type'); + } + + foreach ($message->getHeaders() as $name => $values) { + $msg .= "\r\n{$name}: " . implode(', ', $values); + } + + return "{$msg}\r\n\r\n" . $message->getBody(); +} + +/** + * Returns a UriInterface for the given value. + * + * This function accepts a string or {@see Psr\Http\Message\UriInterface} and + * returns a UriInterface for the given value. If the value is already a + * `UriInterface`, it is returned as-is. + * + * @param string|UriInterface $uri + * + * @return UriInterface + * @throws \InvalidArgumentException + */ +function uri_for($uri) +{ + if ($uri instanceof UriInterface) { + return $uri; + } elseif (is_string($uri)) { + return new Uri($uri); + } + + throw new \InvalidArgumentException('URI must be a string or UriInterface'); +} + +/** + * Create a new stream based on the input type. + * + * Options is an associative array that can contain the following keys: + * - metadata: Array of custom metadata. + * - size: Size of the stream. + * + * @param resource|string|null|int|float|bool|StreamInterface|callable $resource Entity body data + * @param array $options Additional options + * + * @return Stream + * @throws \InvalidArgumentException if the $resource arg is not valid. + */ +function stream_for($resource = '', array $options = []) +{ + if (is_scalar($resource)) { + $stream = fopen('php://temp', 'r+'); + if ($resource !== '') { + fwrite($stream, $resource); + fseek($stream, 0); + } + return new Stream($stream, $options); + } + + switch (gettype($resource)) { + case 'resource': + return new Stream($resource, $options); + case 'object': + if ($resource instanceof StreamInterface) { + return $resource; + } elseif ($resource instanceof \Iterator) { + return new PumpStream(function () use ($resource) { + if (!$resource->valid()) { + return false; + } + $result = $resource->current(); + $resource->next(); + return $result; + }, $options); + } elseif (method_exists($resource, '__toString')) { + return stream_for((string) $resource, $options); + } + break; + case 'NULL': + return new Stream(fopen('php://temp', 'r+'), $options); + } + + if (is_callable($resource)) { + return new PumpStream($resource, $options); + } + + throw new \InvalidArgumentException('Invalid resource type: ' . gettype($resource)); +} + +/** + * Parse an array of header values containing ";" separated data into an + * array of associative arrays representing the header key value pair + * data of the header. When a parameter does not contain a value, but just + * contains a key, this function will inject a key with a '' string value. + * + * @param string|array $header Header to parse into components. + * + * @return array Returns the parsed header values. + */ +function parse_header($header) +{ + static $trimmed = "\"' \n\t\r"; + $params = $matches = []; + + foreach (normalize_header($header) as $val) { + $part = []; + foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) { + if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) { + $m = $matches[0]; + if (isset($m[1])) { + $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed); + } else { + $part[] = trim($m[0], $trimmed); + } + } + } + if ($part) { + $params[] = $part; + } + } + + return $params; +} + +/** + * Converts an array of header values that may contain comma separated + * headers into an array of headers with no comma separated values. + * + * @param string|array $header Header to normalize. + * + * @return array Returns the normalized header field values. + */ +function normalize_header($header) +{ + if (!is_array($header)) { + return array_map('trim', explode(',', $header)); + } + + $result = []; + foreach ($header as $value) { + foreach ((array) $value as $v) { + if (strpos($v, ',') === false) { + $result[] = $v; + continue; + } + foreach (preg_split('/,(?=([^"]*"[^"]*")*[^"]*$)/', $v) as $vv) { + $result[] = trim($vv); + } + } + } + + return $result; +} + +/** + * Clone and modify a request with the given changes. + * + * The changes can be one of: + * - method: (string) Changes the HTTP method. + * - set_headers: (array) Sets the given headers. + * - remove_headers: (array) Remove the given headers. + * - body: (mixed) Sets the given body. + * - uri: (UriInterface) Set the URI. + * - query: (string) Set the query string value of the URI. + * - version: (string) Set the protocol version. + * + * @param RequestInterface $request Request to clone and modify. + * @param array $changes Changes to apply. + * + * @return RequestInterface + */ +function modify_request(RequestInterface $request, array $changes) +{ + if (!$changes) { + return $request; + } + + $headers = $request->getHeaders(); + + if (!isset($changes['uri'])) { + $uri = $request->getUri(); + } else { + // Remove the host header if one is on the URI + if ($host = $changes['uri']->getHost()) { + $changes['set_headers']['Host'] = $host; + + if ($port = $changes['uri']->getPort()) { + $standardPorts = ['http' => 80, 'https' => 443]; + $scheme = $changes['uri']->getScheme(); + if (isset($standardPorts[$scheme]) && $port != $standardPorts[$scheme]) { + $changes['set_headers']['Host'] .= ':'.$port; + } + } + } + $uri = $changes['uri']; + } + + if (!empty($changes['remove_headers'])) { + $headers = _caseless_remove($changes['remove_headers'], $headers); + } + + if (!empty($changes['set_headers'])) { + $headers = _caseless_remove(array_keys($changes['set_headers']), $headers); + $headers = $changes['set_headers'] + $headers; + } + + if (isset($changes['query'])) { + $uri = $uri->withQuery($changes['query']); + } + + if ($request instanceof ServerRequestInterface) { + return new ServerRequest( + isset($changes['method']) ? $changes['method'] : $request->getMethod(), + $uri, + $headers, + isset($changes['body']) ? $changes['body'] : $request->getBody(), + isset($changes['version']) + ? $changes['version'] + : $request->getProtocolVersion(), + $request->getServerParams() + ); + } + + return new Request( + isset($changes['method']) ? $changes['method'] : $request->getMethod(), + $uri, + $headers, + isset($changes['body']) ? $changes['body'] : $request->getBody(), + isset($changes['version']) + ? $changes['version'] + : $request->getProtocolVersion() + ); +} + +/** + * Attempts to rewind a message body and throws an exception on failure. + * + * The body of the message will only be rewound if a call to `tell()` returns a + * value other than `0`. + * + * @param MessageInterface $message Message to rewind + * + * @throws \RuntimeException + */ +function rewind_body(MessageInterface $message) +{ + $body = $message->getBody(); + + if ($body->tell()) { + $body->rewind(); + } +} + +/** + * Safely opens a PHP stream resource using a filename. + * + * When fopen fails, PHP normally raises a warning. This function adds an + * error handler that checks for errors and throws an exception instead. + * + * @param string $filename File to open + * @param string $mode Mode used to open the file + * + * @return resource + * @throws \RuntimeException if the file cannot be opened + */ +function try_fopen($filename, $mode) +{ + $ex = null; + set_error_handler(function () use ($filename, $mode, &$ex) { + $ex = new \RuntimeException(sprintf( + 'Unable to open %s using mode %s: %s', + $filename, + $mode, + func_get_args()[1] + )); + }); + + $handle = fopen($filename, $mode); + restore_error_handler(); + + if ($ex) { + /** @var $ex \RuntimeException */ + throw $ex; + } + + return $handle; +} + +/** + * Copy the contents of a stream into a string until the given number of + * bytes have been read. + * + * @param StreamInterface $stream Stream to read + * @param int $maxLen Maximum number of bytes to read. Pass -1 + * to read the entire stream. + * @return string + * @throws \RuntimeException on error. + */ +function copy_to_string(StreamInterface $stream, $maxLen = -1) +{ + $buffer = ''; + + if ($maxLen === -1) { + while (!$stream->eof()) { + $buf = $stream->read(1048576); + // Using a loose equality here to match on '' and false. + if ($buf == null) { + break; + } + $buffer .= $buf; + } + return $buffer; + } + + $len = 0; + while (!$stream->eof() && $len < $maxLen) { + $buf = $stream->read($maxLen - $len); + // Using a loose equality here to match on '' and false. + if ($buf == null) { + break; + } + $buffer .= $buf; + $len = strlen($buffer); + } + + return $buffer; +} + +/** + * Copy the contents of a stream into another stream until the given number + * of bytes have been read. + * + * @param StreamInterface $source Stream to read from + * @param StreamInterface $dest Stream to write to + * @param int $maxLen Maximum number of bytes to read. Pass -1 + * to read the entire stream. + * + * @throws \RuntimeException on error. + */ +function copy_to_stream( + StreamInterface $source, + StreamInterface $dest, + $maxLen = -1 +) { + $bufferSize = 8192; + + if ($maxLen === -1) { + while (!$source->eof()) { + if (!$dest->write($source->read($bufferSize))) { + break; + } + } + } else { + $remaining = $maxLen; + while ($remaining > 0 && !$source->eof()) { + $buf = $source->read(min($bufferSize, $remaining)); + $len = strlen($buf); + if (!$len) { + break; + } + $remaining -= $len; + $dest->write($buf); + } + } +} + +/** + * Calculate a hash of a Stream + * + * @param StreamInterface $stream Stream to calculate the hash for + * @param string $algo Hash algorithm (e.g. md5, crc32, etc) + * @param bool $rawOutput Whether or not to use raw output + * + * @return string Returns the hash of the stream + * @throws \RuntimeException on error. + */ +function hash( + StreamInterface $stream, + $algo, + $rawOutput = false +) { + $pos = $stream->tell(); + + if ($pos > 0) { + $stream->rewind(); + } + + $ctx = hash_init($algo); + while (!$stream->eof()) { + hash_update($ctx, $stream->read(1048576)); + } + + $out = hash_final($ctx, (bool) $rawOutput); + $stream->seek($pos); + + return $out; +} + +/** + * Read a line from the stream up to the maximum allowed buffer length + * + * @param StreamInterface $stream Stream to read from + * @param int $maxLength Maximum buffer length + * + * @return string|bool + */ +function readline(StreamInterface $stream, $maxLength = null) +{ + $buffer = ''; + $size = 0; + + while (!$stream->eof()) { + // Using a loose equality here to match on '' and false. + if (null == ($byte = $stream->read(1))) { + return $buffer; + } + $buffer .= $byte; + // Break when a new line is found or the max length - 1 is reached + if ($byte === "\n" || ++$size === $maxLength - 1) { + break; + } + } + + return $buffer; +} + +/** + * Parses a request message string into a request object. + * + * @param string $message Request message string. + * + * @return Request + */ +function parse_request($message) +{ + $data = _parse_message($message); + $matches = []; + if (!preg_match('/^[\S]+\s+([a-zA-Z]+:\/\/|\/).*/', $data['start-line'], $matches)) { + throw new \InvalidArgumentException('Invalid request string'); + } + $parts = explode(' ', $data['start-line'], 3); + $version = isset($parts[2]) ? explode('/', $parts[2])[1] : '1.1'; + + $request = new Request( + $parts[0], + $matches[1] === '/' ? _parse_request_uri($parts[1], $data['headers']) : $parts[1], + $data['headers'], + $data['body'], + $version + ); + + return $matches[1] === '/' ? $request : $request->withRequestTarget($parts[1]); +} + +/** + * Parses a response message string into a response object. + * + * @param string $message Response message string. + * + * @return Response + */ +function parse_response($message) +{ + $data = _parse_message($message); + // According to https://tools.ietf.org/html/rfc7230#section-3.1.2 the space + // between status-code and reason-phrase is required. But browsers accept + // responses without space and reason as well. + if (!preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) { + throw new \InvalidArgumentException('Invalid response string'); + } + $parts = explode(' ', $data['start-line'], 3); + + return new Response( + $parts[1], + $data['headers'], + $data['body'], + explode('/', $parts[0])[1], + isset($parts[2]) ? $parts[2] : null + ); +} + +/** + * Parse a query string into an associative array. + * + * If multiple values are found for the same key, the value of that key + * value pair will become an array. This function does not parse nested + * PHP style arrays into an associative array (e.g., foo[a]=1&foo[b]=2 will + * be parsed into ['foo[a]' => '1', 'foo[b]' => '2']). + * + * @param string $str Query string to parse + * @param bool|string $urlEncoding How the query string is encoded + * + * @return array + */ +function parse_query($str, $urlEncoding = true) +{ + $result = []; + + if ($str === '') { + return $result; + } + + if ($urlEncoding === true) { + $decoder = function ($value) { + return rawurldecode(str_replace('+', ' ', $value)); + }; + } elseif ($urlEncoding == PHP_QUERY_RFC3986) { + $decoder = 'rawurldecode'; + } elseif ($urlEncoding == PHP_QUERY_RFC1738) { + $decoder = 'urldecode'; + } else { + $decoder = function ($str) { return $str; }; + } + + foreach (explode('&', $str) as $kvp) { + $parts = explode('=', $kvp, 2); + $key = $decoder($parts[0]); + $value = isset($parts[1]) ? $decoder($parts[1]) : null; + if (!isset($result[$key])) { + $result[$key] = $value; + } else { + if (!is_array($result[$key])) { + $result[$key] = [$result[$key]]; + } + $result[$key][] = $value; + } + } + + return $result; +} + +/** + * Build a query string from an array of key value pairs. + * + * This function can use the return value of parse_query() to build a query + * string. This function does not modify the provided keys when an array is + * encountered (like http_build_query would). + * + * @param array $params Query string parameters. + * @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986 + * to encode using RFC3986, or PHP_QUERY_RFC1738 + * to encode using RFC1738. + * @return string + */ +function build_query(array $params, $encoding = PHP_QUERY_RFC3986) +{ + if (!$params) { + return ''; + } + + if ($encoding === false) { + $encoder = function ($str) { return $str; }; + } elseif ($encoding === PHP_QUERY_RFC3986) { + $encoder = 'rawurlencode'; + } elseif ($encoding === PHP_QUERY_RFC1738) { + $encoder = 'urlencode'; + } else { + throw new \InvalidArgumentException('Invalid type'); + } + + $qs = ''; + foreach ($params as $k => $v) { + $k = $encoder($k); + if (!is_array($v)) { + $qs .= $k; + if ($v !== null) { + $qs .= '=' . $encoder($v); + } + $qs .= '&'; + } else { + foreach ($v as $vv) { + $qs .= $k; + if ($vv !== null) { + $qs .= '=' . $encoder($vv); + } + $qs .= '&'; + } + } + } + + return $qs ? (string) substr($qs, 0, -1) : ''; +} + +/** + * Determines the mimetype of a file by looking at its extension. + * + * @param $filename + * + * @return null|string + */ +function mimetype_from_filename($filename) +{ + return mimetype_from_extension(pathinfo($filename, PATHINFO_EXTENSION)); +} + +/** + * Maps a file extensions to a mimetype. + * + * @param $extension string The file extension. + * + * @return string|null + * @link http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types + */ +function mimetype_from_extension($extension) +{ + static $mimetypes = [ + '7z' => 'application/x-7z-compressed', + 'aac' => 'audio/x-aac', + 'ai' => 'application/postscript', + 'aif' => 'audio/x-aiff', + 'asc' => 'text/plain', + 'asf' => 'video/x-ms-asf', + 'atom' => 'application/atom+xml', + 'avi' => 'video/x-msvideo', + 'bmp' => 'image/bmp', + 'bz2' => 'application/x-bzip2', + 'cer' => 'application/pkix-cert', + 'crl' => 'application/pkix-crl', + 'crt' => 'application/x-x509-ca-cert', + 'css' => 'text/css', + 'csv' => 'text/csv', + 'cu' => 'application/cu-seeme', + 'deb' => 'application/x-debian-package', + 'doc' => 'application/msword', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dvi' => 'application/x-dvi', + 'eot' => 'application/vnd.ms-fontobject', + 'eps' => 'application/postscript', + 'epub' => 'application/epub+zip', + 'etx' => 'text/x-setext', + 'flac' => 'audio/flac', + 'flv' => 'video/x-flv', + 'gif' => 'image/gif', + 'gz' => 'application/gzip', + 'htm' => 'text/html', + 'html' => 'text/html', + 'ico' => 'image/x-icon', + 'ics' => 'text/calendar', + 'ini' => 'text/plain', + 'iso' => 'application/x-iso9660-image', + 'jar' => 'application/java-archive', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'js' => 'text/javascript', + 'json' => 'application/json', + 'latex' => 'application/x-latex', + 'log' => 'text/plain', + 'm4a' => 'audio/mp4', + 'm4v' => 'video/mp4', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mov' => 'video/quicktime', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'mp4a' => 'audio/mp4', + 'mp4v' => 'video/mp4', + 'mpe' => 'video/mpeg', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpg4' => 'video/mp4', + 'oga' => 'audio/ogg', + 'ogg' => 'audio/ogg', + 'ogv' => 'video/ogg', + 'ogx' => 'application/ogg', + 'pbm' => 'image/x-portable-bitmap', + 'pdf' => 'application/pdf', + 'pgm' => 'image/x-portable-graymap', + 'png' => 'image/png', + 'pnm' => 'image/x-portable-anymap', + 'ppm' => 'image/x-portable-pixmap', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'ps' => 'application/postscript', + 'qt' => 'video/quicktime', + 'rar' => 'application/x-rar-compressed', + 'ras' => 'image/x-cmu-raster', + 'rss' => 'application/rss+xml', + 'rtf' => 'application/rtf', + 'sgm' => 'text/sgml', + 'sgml' => 'text/sgml', + 'svg' => 'image/svg+xml', + 'swf' => 'application/x-shockwave-flash', + 'tar' => 'application/x-tar', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'torrent' => 'application/x-bittorrent', + 'ttf' => 'application/x-font-ttf', + 'txt' => 'text/plain', + 'wav' => 'audio/x-wav', + 'webm' => 'video/webm', + 'wma' => 'audio/x-ms-wma', + 'wmv' => 'video/x-ms-wmv', + 'woff' => 'application/x-font-woff', + 'wsdl' => 'application/wsdl+xml', + 'xbm' => 'image/x-xbitmap', + 'xls' => 'application/vnd.ms-excel', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xml' => 'application/xml', + 'xpm' => 'image/x-xpixmap', + 'xwd' => 'image/x-xwindowdump', + 'yaml' => 'text/yaml', + 'yml' => 'text/yaml', + 'zip' => 'application/zip', + ]; + + $extension = strtolower($extension); + + return isset($mimetypes[$extension]) + ? $mimetypes[$extension] + : null; +} + +/** + * Parses an HTTP message into an associative array. + * + * The array contains the "start-line" key containing the start line of + * the message, "headers" key containing an associative array of header + * array values, and a "body" key containing the body of the message. + * + * @param string $message HTTP request or response to parse. + * + * @return array + * @internal + */ +function _parse_message($message) +{ + if (!$message) { + throw new \InvalidArgumentException('Invalid message'); + } + + // Iterate over each line in the message, accounting for line endings + $lines = preg_split('/(\\r?\\n)/', $message, -1, PREG_SPLIT_DELIM_CAPTURE); + $result = ['start-line' => array_shift($lines), 'headers' => [], 'body' => '']; + array_shift($lines); + + for ($i = 0, $totalLines = count($lines); $i < $totalLines; $i += 2) { + $line = $lines[$i]; + // If two line breaks were encountered, then this is the end of body + if (empty($line)) { + if ($i < $totalLines - 1) { + $result['body'] = implode('', array_slice($lines, $i + 2)); + } + break; + } + if (strpos($line, ':')) { + $parts = explode(':', $line, 2); + $key = trim($parts[0]); + $value = isset($parts[1]) ? trim($parts[1]) : ''; + $result['headers'][$key][] = $value; + } + } + + return $result; +} + +/** + * Constructs a URI for an HTTP request message. + * + * @param string $path Path from the start-line + * @param array $headers Array of headers (each value an array). + * + * @return string + * @internal + */ +function _parse_request_uri($path, array $headers) +{ + $hostKey = array_filter(array_keys($headers), function ($k) { + return strtolower($k) === 'host'; + }); + + // If no host is found, then a full URI cannot be constructed. + if (!$hostKey) { + return $path; + } + + $host = $headers[reset($hostKey)][0]; + $scheme = substr($host, -4) === ':443' ? 'https' : 'http'; + + return $scheme . '://' . $host . '/' . ltrim($path, '/'); +} + +/** @internal */ +function _caseless_remove($keys, array $data) +{ + $result = []; + + foreach ($keys as &$key) { + $key = strtolower($key); + } + + foreach ($data as $k => $v) { + if (!in_array(strtolower($k), $keys)) { + $result[$k] = $v; + } + } + + return $result; +} diff --git a/assets/php/vendor/guzzlehttp/psr7/src/functions_include.php b/assets/php/vendor/guzzlehttp/psr7/src/functions_include.php new file mode 100644 index 0000000..96a4a83 --- /dev/null +++ b/assets/php/vendor/guzzlehttp/psr7/src/functions_include.php @@ -0,0 +1,6 @@ +<?php + +// Don't redefine the functions if included multiple times. +if (!function_exists('GuzzleHttp\Psr7\str')) { + require __DIR__ . '/functions.php'; +} |