Перенес код
This commit is contained in:
248
vendor/guzzlehttp/psr7/src/AppendStream.php
vendored
Normal file
248
vendor/guzzlehttp/psr7/src/AppendStream.php
vendored
Normal file
@ -0,0 +1,248 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Psr7;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* Reads from multiple streams, one after the other.
|
||||
*
|
||||
* This is a read-only stream decorator.
|
||||
*/
|
||||
final class AppendStream implements StreamInterface
|
||||
{
|
||||
/** @var StreamInterface[] Streams being decorated */
|
||||
private $streams = [];
|
||||
|
||||
/** @var bool */
|
||||
private $seekable = true;
|
||||
|
||||
/** @var int */
|
||||
private $current = 0;
|
||||
|
||||
/** @var int */
|
||||
private $pos = 0;
|
||||
|
||||
/**
|
||||
* @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(): string
|
||||
{
|
||||
try {
|
||||
$this->rewind();
|
||||
|
||||
return $this->getContents();
|
||||
} catch (\Throwable $e) {
|
||||
if (\PHP_VERSION_ID >= 70400) {
|
||||
throw $e;
|
||||
}
|
||||
trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
|
||||
|
||||
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): void
|
||||
{
|
||||
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(): string
|
||||
{
|
||||
return Utils::copyToString($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes each attached stream.
|
||||
*/
|
||||
public function close(): void
|
||||
{
|
||||
$this->pos = $this->current = 0;
|
||||
$this->seekable = true;
|
||||
|
||||
foreach ($this->streams as $stream) {
|
||||
$stream->close();
|
||||
}
|
||||
|
||||
$this->streams = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches each attached stream.
|
||||
*
|
||||
* Returns null as it's not clear which underlying stream resource to return.
|
||||
*/
|
||||
public function detach()
|
||||
{
|
||||
$this->pos = $this->current = 0;
|
||||
$this->seekable = true;
|
||||
|
||||
foreach ($this->streams as $stream) {
|
||||
$stream->detach();
|
||||
}
|
||||
|
||||
$this->streams = [];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function tell(): int
|
||||
{
|
||||
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.
|
||||
*/
|
||||
public function getSize(): ?int
|
||||
{
|
||||
$size = 0;
|
||||
|
||||
foreach ($this->streams as $stream) {
|
||||
$s = $stream->getSize();
|
||||
if ($s === null) {
|
||||
return null;
|
||||
}
|
||||
$size += $s;
|
||||
}
|
||||
|
||||
return $size;
|
||||
}
|
||||
|
||||
public function eof(): bool
|
||||
{
|
||||
return !$this->streams
|
||||
|| ($this->current >= count($this->streams) - 1
|
||||
&& $this->streams[$this->current]->eof());
|
||||
}
|
||||
|
||||
public function rewind(): void
|
||||
{
|
||||
$this->seek(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to seek to the given position. Only supports SEEK_SET.
|
||||
*/
|
||||
public function seek($offset, $whence = SEEK_SET): void
|
||||
{
|
||||
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.
|
||||
*/
|
||||
public function read($length): string
|
||||
{
|
||||
$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);
|
||||
|
||||
if ($result === '') {
|
||||
$progressToNext = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
$buffer .= $result;
|
||||
$remaining = $length - strlen($buffer);
|
||||
}
|
||||
|
||||
$this->pos += strlen($buffer);
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
public function isReadable(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isWritable(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isSeekable(): bool
|
||||
{
|
||||
return $this->seekable;
|
||||
}
|
||||
|
||||
public function write($string): int
|
||||
{
|
||||
throw new \RuntimeException('Cannot write to an AppendStream');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getMetadata($key = null)
|
||||
{
|
||||
return $key ? null : [];
|
||||
}
|
||||
}
|
147
vendor/guzzlehttp/psr7/src/BufferStream.php
vendored
Normal file
147
vendor/guzzlehttp/psr7/src/BufferStream.php
vendored
Normal file
@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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.
|
||||
*/
|
||||
final class BufferStream implements StreamInterface
|
||||
{
|
||||
/** @var int */
|
||||
private $hwm;
|
||||
|
||||
/** @var string */
|
||||
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 0 to inform writers to slow down
|
||||
* until the buffer has been drained by reading from it.
|
||||
*/
|
||||
public function __construct(int $hwm = 16384)
|
||||
{
|
||||
$this->hwm = $hwm;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->getContents();
|
||||
}
|
||||
|
||||
public function getContents(): string
|
||||
{
|
||||
$buffer = $this->buffer;
|
||||
$this->buffer = '';
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
public function close(): void
|
||||
{
|
||||
$this->buffer = '';
|
||||
}
|
||||
|
||||
public function detach()
|
||||
{
|
||||
$this->close();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getSize(): ?int
|
||||
{
|
||||
return strlen($this->buffer);
|
||||
}
|
||||
|
||||
public function isReadable(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isWritable(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isSeekable(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function rewind(): void
|
||||
{
|
||||
$this->seek(0);
|
||||
}
|
||||
|
||||
public function seek($offset, $whence = SEEK_SET): void
|
||||
{
|
||||
throw new \RuntimeException('Cannot seek a BufferStream');
|
||||
}
|
||||
|
||||
public function eof(): bool
|
||||
{
|
||||
return strlen($this->buffer) === 0;
|
||||
}
|
||||
|
||||
public function tell(): int
|
||||
{
|
||||
throw new \RuntimeException('Cannot determine the position of a BufferStream');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads data from the buffer.
|
||||
*/
|
||||
public function read($length): string
|
||||
{
|
||||
$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): int
|
||||
{
|
||||
$this->buffer .= $string;
|
||||
|
||||
if (strlen($this->buffer) >= $this->hwm) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return strlen($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getMetadata($key = null)
|
||||
{
|
||||
if ($key === 'hwm') {
|
||||
return $this->hwm;
|
||||
}
|
||||
|
||||
return $key ? null : [];
|
||||
}
|
||||
}
|
153
vendor/guzzlehttp/psr7/src/CachingStream.php
vendored
Normal file
153
vendor/guzzlehttp/psr7/src/CachingStream.php
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Psr7;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* Stream decorator that can cache previously read bytes from a sequentially
|
||||
* read stream.
|
||||
*/
|
||||
final 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;
|
||||
|
||||
/**
|
||||
* @var StreamInterface
|
||||
*/
|
||||
private $stream;
|
||||
|
||||
/**
|
||||
* We will treat the buffer object as the body of the stream
|
||||
*
|
||||
* @param StreamInterface $stream Stream to cache. The cursor is assumed to be at the beginning of the stream.
|
||||
* @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(Utils::tryFopen('php://temp', 'r+'));
|
||||
}
|
||||
|
||||
public function getSize(): ?int
|
||||
{
|
||||
$remoteSize = $this->remoteStream->getSize();
|
||||
|
||||
if (null === $remoteSize) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return max($this->stream->getSize(), $remoteSize);
|
||||
}
|
||||
|
||||
public function rewind(): void
|
||||
{
|
||||
$this->seek(0);
|
||||
}
|
||||
|
||||
public function seek($offset, $whence = SEEK_SET): void
|
||||
{
|
||||
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): string
|
||||
{
|
||||
// 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): int
|
||||
{
|
||||
// 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(): bool
|
||||
{
|
||||
return $this->stream->eof() && $this->remoteStream->eof();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close both the remote stream and buffer stream
|
||||
*/
|
||||
public function close(): void
|
||||
{
|
||||
$this->remoteStream->close();
|
||||
$this->stream->close();
|
||||
}
|
||||
|
||||
private function cacheEntireStream(): int
|
||||
{
|
||||
$target = new FnStream(['write' => 'strlen']);
|
||||
Utils::copyToStream($this, $target);
|
||||
|
||||
return $this->tell();
|
||||
}
|
||||
}
|
49
vendor/guzzlehttp/psr7/src/DroppingStream.php
vendored
Normal file
49
vendor/guzzlehttp/psr7/src/DroppingStream.php
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Psr7;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* Stream decorator that begins dropping data once the size of the underlying
|
||||
* stream becomes too full.
|
||||
*/
|
||||
final class DroppingStream implements StreamInterface
|
||||
{
|
||||
use StreamDecoratorTrait;
|
||||
|
||||
/** @var int */
|
||||
private $maxLength;
|
||||
|
||||
/** @var StreamInterface */
|
||||
private $stream;
|
||||
|
||||
/**
|
||||
* @param StreamInterface $stream Underlying stream to decorate.
|
||||
* @param int $maxLength Maximum size before dropping data.
|
||||
*/
|
||||
public function __construct(StreamInterface $stream, int $maxLength)
|
||||
{
|
||||
$this->stream = $stream;
|
||||
$this->maxLength = $maxLength;
|
||||
}
|
||||
|
||||
public function write($string): int
|
||||
{
|
||||
$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));
|
||||
}
|
||||
}
|
14
vendor/guzzlehttp/psr7/src/Exception/MalformedUriException.php
vendored
Normal file
14
vendor/guzzlehttp/psr7/src/Exception/MalformedUriException.php
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Psr7\Exception;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Exception thrown if a URI cannot be parsed because it's malformed.
|
||||
*/
|
||||
class MalformedUriException extends InvalidArgumentException
|
||||
{
|
||||
}
|
180
vendor/guzzlehttp/psr7/src/FnStream.php
vendored
Normal file
180
vendor/guzzlehttp/psr7/src/FnStream.php
vendored
Normal file
@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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.
|
||||
*/
|
||||
#[\AllowDynamicProperties]
|
||||
final class FnStream implements StreamInterface
|
||||
{
|
||||
private const SLOTS = [
|
||||
'__toString', 'close', 'detach', 'rewind',
|
||||
'getSize', 'tell', 'eof', 'isSeekable', 'seek', 'isWritable', 'write',
|
||||
'isReadable', 'read', 'getContents', 'getMetadata',
|
||||
];
|
||||
|
||||
/** @var array<string, callable> */
|
||||
private $methods;
|
||||
|
||||
/**
|
||||
* @param array<string, callable> $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(string $name): void
|
||||
{
|
||||
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)) {
|
||||
($this->_fn_close)();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An unserialize would allow the __destruct to run when the unserialized value goes out of scope.
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function __wakeup(): void
|
||||
{
|
||||
throw new \LogicException('FnStream should never be unserialized');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds custom functionality to an underlying stream by intercepting
|
||||
* specific method calls.
|
||||
*
|
||||
* @param StreamInterface $stream Stream to decorate
|
||||
* @param array<string, callable> $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) {
|
||||
/** @var callable $callable */
|
||||
$callable = [$stream, $diff];
|
||||
$methods[$diff] = $callable;
|
||||
}
|
||||
|
||||
return new self($methods);
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
try {
|
||||
/** @var string */
|
||||
return ($this->_fn___toString)();
|
||||
} catch (\Throwable $e) {
|
||||
if (\PHP_VERSION_ID >= 70400) {
|
||||
throw $e;
|
||||
}
|
||||
trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
public function close(): void
|
||||
{
|
||||
($this->_fn_close)();
|
||||
}
|
||||
|
||||
public function detach()
|
||||
{
|
||||
return ($this->_fn_detach)();
|
||||
}
|
||||
|
||||
public function getSize(): ?int
|
||||
{
|
||||
return ($this->_fn_getSize)();
|
||||
}
|
||||
|
||||
public function tell(): int
|
||||
{
|
||||
return ($this->_fn_tell)();
|
||||
}
|
||||
|
||||
public function eof(): bool
|
||||
{
|
||||
return ($this->_fn_eof)();
|
||||
}
|
||||
|
||||
public function isSeekable(): bool
|
||||
{
|
||||
return ($this->_fn_isSeekable)();
|
||||
}
|
||||
|
||||
public function rewind(): void
|
||||
{
|
||||
($this->_fn_rewind)();
|
||||
}
|
||||
|
||||
public function seek($offset, $whence = SEEK_SET): void
|
||||
{
|
||||
($this->_fn_seek)($offset, $whence);
|
||||
}
|
||||
|
||||
public function isWritable(): bool
|
||||
{
|
||||
return ($this->_fn_isWritable)();
|
||||
}
|
||||
|
||||
public function write($string): int
|
||||
{
|
||||
return ($this->_fn_write)($string);
|
||||
}
|
||||
|
||||
public function isReadable(): bool
|
||||
{
|
||||
return ($this->_fn_isReadable)();
|
||||
}
|
||||
|
||||
public function read($length): string
|
||||
{
|
||||
return ($this->_fn_read)($length);
|
||||
}
|
||||
|
||||
public function getContents(): string
|
||||
{
|
||||
return ($this->_fn_getContents)();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getMetadata($key = null)
|
||||
{
|
||||
return ($this->_fn_getMetadata)($key);
|
||||
}
|
||||
}
|
134
vendor/guzzlehttp/psr7/src/Header.php
vendored
Normal file
134
vendor/guzzlehttp/psr7/src/Header.php
vendored
Normal file
@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Psr7;
|
||||
|
||||
final class Header
|
||||
{
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public static function parse($header): array
|
||||
{
|
||||
static $trimmed = "\"' \n\t\r";
|
||||
$params = $matches = [];
|
||||
|
||||
foreach ((array) $header as $value) {
|
||||
foreach (self::splitList($value) 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.
|
||||
*
|
||||
* @deprecated Use self::splitList() instead.
|
||||
*/
|
||||
public static function normalize($header): array
|
||||
{
|
||||
$result = [];
|
||||
foreach ((array) $header as $value) {
|
||||
foreach (self::splitList($value) as $parsed) {
|
||||
$result[] = $parsed;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a HTTP header defined to contain a comma-separated list into
|
||||
* each individual value. Empty values will be removed.
|
||||
*
|
||||
* Example headers include 'accept', 'cache-control' and 'if-none-match'.
|
||||
*
|
||||
* This method must not be used to parse headers that are not defined as
|
||||
* a list, such as 'user-agent' or 'set-cookie'.
|
||||
*
|
||||
* @param string|string[] $values Header value as returned by MessageInterface::getHeader()
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function splitList($values): array
|
||||
{
|
||||
if (!\is_array($values)) {
|
||||
$values = [$values];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($values as $value) {
|
||||
if (!\is_string($value)) {
|
||||
throw new \TypeError('$header must either be a string or an array containing strings.');
|
||||
}
|
||||
|
||||
$v = '';
|
||||
$isQuoted = false;
|
||||
$isEscaped = false;
|
||||
for ($i = 0, $max = \strlen($value); $i < $max; ++$i) {
|
||||
if ($isEscaped) {
|
||||
$v .= $value[$i];
|
||||
$isEscaped = false;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$isQuoted && $value[$i] === ',') {
|
||||
$v = \trim($v);
|
||||
if ($v !== '') {
|
||||
$result[] = $v;
|
||||
}
|
||||
|
||||
$v = '';
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($isQuoted && $value[$i] === '\\') {
|
||||
$isEscaped = true;
|
||||
$v .= $value[$i];
|
||||
|
||||
continue;
|
||||
}
|
||||
if ($value[$i] === '"') {
|
||||
$isQuoted = !$isQuoted;
|
||||
$v .= $value[$i];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$v .= $value[$i];
|
||||
}
|
||||
|
||||
$v = \trim($v);
|
||||
if ($v !== '') {
|
||||
$result[] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
94
vendor/guzzlehttp/psr7/src/HttpFactory.php
vendored
Normal file
94
vendor/guzzlehttp/psr7/src/HttpFactory.php
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Psr7;
|
||||
|
||||
use Psr\Http\Message\RequestFactoryInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseFactoryInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestFactoryInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Message\StreamFactoryInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Psr\Http\Message\UploadedFileFactoryInterface;
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
use Psr\Http\Message\UriFactoryInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
|
||||
/**
|
||||
* Implements all of the PSR-17 interfaces.
|
||||
*
|
||||
* Note: in consuming code it is recommended to require the implemented interfaces
|
||||
* and inject the instance of this class multiple times.
|
||||
*/
|
||||
final class HttpFactory implements RequestFactoryInterface, ResponseFactoryInterface, ServerRequestFactoryInterface, StreamFactoryInterface, UploadedFileFactoryInterface, UriFactoryInterface
|
||||
{
|
||||
public function createUploadedFile(
|
||||
StreamInterface $stream,
|
||||
?int $size = null,
|
||||
int $error = \UPLOAD_ERR_OK,
|
||||
?string $clientFilename = null,
|
||||
?string $clientMediaType = null
|
||||
): UploadedFileInterface {
|
||||
if ($size === null) {
|
||||
$size = $stream->getSize();
|
||||
}
|
||||
|
||||
return new UploadedFile($stream, $size, $error, $clientFilename, $clientMediaType);
|
||||
}
|
||||
|
||||
public function createStream(string $content = ''): StreamInterface
|
||||
{
|
||||
return Utils::streamFor($content);
|
||||
}
|
||||
|
||||
public function createStreamFromFile(string $file, string $mode = 'r'): StreamInterface
|
||||
{
|
||||
try {
|
||||
$resource = Utils::tryFopen($file, $mode);
|
||||
} catch (\RuntimeException $e) {
|
||||
if ('' === $mode || false === \in_array($mode[0], ['r', 'w', 'a', 'x', 'c'], true)) {
|
||||
throw new \InvalidArgumentException(sprintf('Invalid file opening mode "%s"', $mode), 0, $e);
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return Utils::streamFor($resource);
|
||||
}
|
||||
|
||||
public function createStreamFromResource($resource): StreamInterface
|
||||
{
|
||||
return Utils::streamFor($resource);
|
||||
}
|
||||
|
||||
public function createServerRequest(string $method, $uri, array $serverParams = []): ServerRequestInterface
|
||||
{
|
||||
if (empty($method)) {
|
||||
if (!empty($serverParams['REQUEST_METHOD'])) {
|
||||
$method = $serverParams['REQUEST_METHOD'];
|
||||
} else {
|
||||
throw new \InvalidArgumentException('Cannot determine HTTP method');
|
||||
}
|
||||
}
|
||||
|
||||
return new ServerRequest($method, $uri, [], null, '1.1', $serverParams);
|
||||
}
|
||||
|
||||
public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface
|
||||
{
|
||||
return new Response($code, [], null, '1.1', $reasonPhrase);
|
||||
}
|
||||
|
||||
public function createRequest(string $method, $uri): RequestInterface
|
||||
{
|
||||
return new Request($method, $uri);
|
||||
}
|
||||
|
||||
public function createUri(string $uri = ''): UriInterface
|
||||
{
|
||||
return new Uri($uri);
|
||||
}
|
||||
}
|
37
vendor/guzzlehttp/psr7/src/InflateStream.php
vendored
Normal file
37
vendor/guzzlehttp/psr7/src/InflateStream.php
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Psr7;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* Uses PHP's zlib.inflate filter to inflate zlib (HTTP deflate, RFC1950) or gzipped (RFC1952) content.
|
||||
*
|
||||
* This stream decorator 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.
|
||||
*
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc1950
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc1952
|
||||
* @see https://www.php.net/manual/en/filters.compression.php
|
||||
*/
|
||||
final class InflateStream implements StreamInterface
|
||||
{
|
||||
use StreamDecoratorTrait;
|
||||
|
||||
/** @var StreamInterface */
|
||||
private $stream;
|
||||
|
||||
public function __construct(StreamInterface $stream)
|
||||
{
|
||||
$resource = StreamWrapper::getResource($stream);
|
||||
// Specify window=15+32, so zlib will use header detection to both gzip (with header) and zlib data
|
||||
// See https://www.zlib.net/manual.html#Advanced definition of inflateInit2
|
||||
// "Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection"
|
||||
// Default window size is 15.
|
||||
stream_filter_append($resource, 'zlib.inflate', STREAM_FILTER_READ, ['window' => 15 + 32]);
|
||||
$this->stream = $stream->isSeekable() ? new Stream($resource) : new NoSeekStream(new Stream($resource));
|
||||
}
|
||||
}
|
49
vendor/guzzlehttp/psr7/src/LazyOpenStream.php
vendored
Normal file
49
vendor/guzzlehttp/psr7/src/LazyOpenStream.php
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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.
|
||||
*/
|
||||
final class LazyOpenStream implements StreamInterface
|
||||
{
|
||||
use StreamDecoratorTrait;
|
||||
|
||||
/** @var string */
|
||||
private $filename;
|
||||
|
||||
/** @var string */
|
||||
private $mode;
|
||||
|
||||
/**
|
||||
* @var StreamInterface
|
||||
*/
|
||||
private $stream;
|
||||
|
||||
/**
|
||||
* @param string $filename File to lazily open
|
||||
* @param string $mode fopen mode to use when opening the stream
|
||||
*/
|
||||
public function __construct(string $filename, string $mode)
|
||||
{
|
||||
$this->filename = $filename;
|
||||
$this->mode = $mode;
|
||||
|
||||
// unsetting the property forces the first access to go through
|
||||
// __get().
|
||||
unset($this->stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the underlying stream lazily when required.
|
||||
*/
|
||||
protected function createStream(): StreamInterface
|
||||
{
|
||||
return Utils::streamFor(Utils::tryFopen($this->filename, $this->mode));
|
||||
}
|
||||
}
|
157
vendor/guzzlehttp/psr7/src/LimitStream.php
vendored
Normal file
157
vendor/guzzlehttp/psr7/src/LimitStream.php
vendored
Normal file
@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Psr7;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* Decorator used to return only a subset of a stream.
|
||||
*/
|
||||
final 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;
|
||||
|
||||
/** @var StreamInterface */
|
||||
private $stream;
|
||||
|
||||
/**
|
||||
* @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,
|
||||
int $limit = -1,
|
||||
int $offset = 0
|
||||
) {
|
||||
$this->stream = $stream;
|
||||
$this->setLimit($limit);
|
||||
$this->setOffset($offset);
|
||||
}
|
||||
|
||||
public function eof(): bool
|
||||
{
|
||||
// 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
|
||||
*/
|
||||
public function getSize(): ?int
|
||||
{
|
||||
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
|
||||
*/
|
||||
public function seek($offset, $whence = SEEK_SET): void
|
||||
{
|
||||
if ($whence !== SEEK_SET || $offset < 0) {
|
||||
throw new \RuntimeException(sprintf(
|
||||
'Cannot seek to offset %s 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()
|
||||
*/
|
||||
public function tell(): int
|
||||
{
|
||||
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(int $offset): void
|
||||
{
|
||||
$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(int $limit): void
|
||||
{
|
||||
$this->limit = $limit;
|
||||
}
|
||||
|
||||
public function read($length): string
|
||||
{
|
||||
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 '';
|
||||
}
|
||||
}
|
246
vendor/guzzlehttp/psr7/src/Message.php
vendored
Normal file
246
vendor/guzzlehttp/psr7/src/Message.php
vendored
Normal file
@ -0,0 +1,246 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Psr7;
|
||||
|
||||
use Psr\Http\Message\MessageInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
final class Message
|
||||
{
|
||||
/**
|
||||
* Returns the string representation of an HTTP message.
|
||||
*
|
||||
* @param MessageInterface $message Message to convert to a string.
|
||||
*/
|
||||
public static function toString(MessageInterface $message): string
|
||||
{
|
||||
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) {
|
||||
if (is_string($name) && strtolower($name) === 'set-cookie') {
|
||||
foreach ($values as $value) {
|
||||
$msg .= "\r\n{$name}: ".$value;
|
||||
}
|
||||
} else {
|
||||
$msg .= "\r\n{$name}: ".implode(', ', $values);
|
||||
}
|
||||
}
|
||||
|
||||
return "{$msg}\r\n\r\n".$message->getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a short summary of the message body.
|
||||
*
|
||||
* Will return `null` if the response is not printable.
|
||||
*
|
||||
* @param MessageInterface $message The message to get the body summary
|
||||
* @param int $truncateAt The maximum allowed size of the summary
|
||||
*/
|
||||
public static function bodySummary(MessageInterface $message, int $truncateAt = 120): ?string
|
||||
{
|
||||
$body = $message->getBody();
|
||||
|
||||
if (!$body->isSeekable() || !$body->isReadable()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$size = $body->getSize();
|
||||
|
||||
if ($size === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$body->rewind();
|
||||
$summary = $body->read($truncateAt);
|
||||
$body->rewind();
|
||||
|
||||
if ($size > $truncateAt) {
|
||||
$summary .= ' (truncated...)';
|
||||
}
|
||||
|
||||
// Matches any printable character, including unicode characters:
|
||||
// letters, marks, numbers, punctuation, spacing, and separators.
|
||||
if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/u', $summary) !== 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public static function rewindBody(MessageInterface $message): void
|
||||
{
|
||||
$body = $message->getBody();
|
||||
|
||||
if ($body->tell()) {
|
||||
$body->rewind();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public static function parseMessage(string $message): array
|
||||
{
|
||||
if (!$message) {
|
||||
throw new \InvalidArgumentException('Invalid message');
|
||||
}
|
||||
|
||||
$message = ltrim($message, "\r\n");
|
||||
|
||||
$messageParts = preg_split("/\r?\n\r?\n/", $message, 2);
|
||||
|
||||
if ($messageParts === false || count($messageParts) !== 2) {
|
||||
throw new \InvalidArgumentException('Invalid message: Missing header delimiter');
|
||||
}
|
||||
|
||||
[$rawHeaders, $body] = $messageParts;
|
||||
$rawHeaders .= "\r\n"; // Put back the delimiter we split previously
|
||||
$headerParts = preg_split("/\r?\n/", $rawHeaders, 2);
|
||||
|
||||
if ($headerParts === false || count($headerParts) !== 2) {
|
||||
throw new \InvalidArgumentException('Invalid message: Missing status line');
|
||||
}
|
||||
|
||||
[$startLine, $rawHeaders] = $headerParts;
|
||||
|
||||
if (preg_match("/(?:^HTTP\/|^[A-Z]+ \S+ HTTP\/)(\d+(?:\.\d+)?)/i", $startLine, $matches) && $matches[1] === '1.0') {
|
||||
// Header folding is deprecated for HTTP/1.1, but allowed in HTTP/1.0
|
||||
$rawHeaders = preg_replace(Rfc7230::HEADER_FOLD_REGEX, ' ', $rawHeaders);
|
||||
}
|
||||
|
||||
/** @var array[] $headerLines */
|
||||
$count = preg_match_all(Rfc7230::HEADER_REGEX, $rawHeaders, $headerLines, PREG_SET_ORDER);
|
||||
|
||||
// If these aren't the same, then one line didn't match and there's an invalid header.
|
||||
if ($count !== substr_count($rawHeaders, "\n")) {
|
||||
// Folding is deprecated, see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.4
|
||||
if (preg_match(Rfc7230::HEADER_FOLD_REGEX, $rawHeaders)) {
|
||||
throw new \InvalidArgumentException('Invalid header syntax: Obsolete line folding');
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException('Invalid header syntax');
|
||||
}
|
||||
|
||||
$headers = [];
|
||||
|
||||
foreach ($headerLines as $headerLine) {
|
||||
$headers[$headerLine[1]][] = $headerLine[2];
|
||||
}
|
||||
|
||||
return [
|
||||
'start-line' => $startLine,
|
||||
'headers' => $headers,
|
||||
'body' => $body,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*/
|
||||
public static function parseRequestUri(string $path, array $headers): string
|
||||
{
|
||||
$hostKey = array_filter(array_keys($headers), function ($k) {
|
||||
// Numeric array keys are converted to int by PHP.
|
||||
$k = (string) $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, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a request message string into a request object.
|
||||
*
|
||||
* @param string $message Request message string.
|
||||
*/
|
||||
public static function parseRequest(string $message): RequestInterface
|
||||
{
|
||||
$data = self::parseMessage($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] === '/' ? self::parseRequestUri($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.
|
||||
*/
|
||||
public static function parseResponse(string $message): ResponseInterface
|
||||
{
|
||||
$data = self::parseMessage($message);
|
||||
// According to https://datatracker.ietf.org/doc/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: '.$data['start-line']);
|
||||
}
|
||||
$parts = explode(' ', $data['start-line'], 3);
|
||||
|
||||
return new Response(
|
||||
(int) $parts[1],
|
||||
$data['headers'],
|
||||
$data['body'],
|
||||
explode('/', $parts[0])[1],
|
||||
$parts[2] ?? null
|
||||
);
|
||||
}
|
||||
}
|
265
vendor/guzzlehttp/psr7/src/MessageTrait.php
vendored
Normal file
265
vendor/guzzlehttp/psr7/src/MessageTrait.php
vendored
Normal file
@ -0,0 +1,265 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Psr7;
|
||||
|
||||
use Psr\Http\Message\MessageInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* Trait implementing functionality common to requests and responses.
|
||||
*/
|
||||
trait MessageTrait
|
||||
{
|
||||
/** @var string[][] Map of all registered headers, as original name => array of values */
|
||||
private $headers = [];
|
||||
|
||||
/** @var string[] Map of lowercase header name => original name at registration */
|
||||
private $headerNames = [];
|
||||
|
||||
/** @var string */
|
||||
private $protocol = '1.1';
|
||||
|
||||
/** @var StreamInterface|null */
|
||||
private $stream;
|
||||
|
||||
public function getProtocolVersion(): string
|
||||
{
|
||||
return $this->protocol;
|
||||
}
|
||||
|
||||
public function withProtocolVersion($version): MessageInterface
|
||||
{
|
||||
if ($this->protocol === $version) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$new = clone $this;
|
||||
$new->protocol = $version;
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
public function getHeaders(): array
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
public function hasHeader($header): bool
|
||||
{
|
||||
return isset($this->headerNames[strtolower($header)]);
|
||||
}
|
||||
|
||||
public function getHeader($header): array
|
||||
{
|
||||
$header = strtolower($header);
|
||||
|
||||
if (!isset($this->headerNames[$header])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$header = $this->headerNames[$header];
|
||||
|
||||
return $this->headers[$header];
|
||||
}
|
||||
|
||||
public function getHeaderLine($header): string
|
||||
{
|
||||
return implode(', ', $this->getHeader($header));
|
||||
}
|
||||
|
||||
public function withHeader($header, $value): MessageInterface
|
||||
{
|
||||
$this->assertHeader($header);
|
||||
$value = $this->normalizeHeaderValue($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): MessageInterface
|
||||
{
|
||||
$this->assertHeader($header);
|
||||
$value = $this->normalizeHeaderValue($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): MessageInterface
|
||||
{
|
||||
$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(): StreamInterface
|
||||
{
|
||||
if (!$this->stream) {
|
||||
$this->stream = Utils::streamFor('');
|
||||
}
|
||||
|
||||
return $this->stream;
|
||||
}
|
||||
|
||||
public function withBody(StreamInterface $body): MessageInterface
|
||||
{
|
||||
if ($body === $this->stream) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$new = clone $this;
|
||||
$new->stream = $body;
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param (string|string[])[] $headers
|
||||
*/
|
||||
private function setHeaders(array $headers): void
|
||||
{
|
||||
$this->headerNames = $this->headers = [];
|
||||
foreach ($headers as $header => $value) {
|
||||
// Numeric array keys are converted to int by PHP.
|
||||
$header = (string) $header;
|
||||
|
||||
$this->assertHeader($header);
|
||||
$value = $this->normalizeHeaderValue($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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private function normalizeHeaderValue($value): array
|
||||
{
|
||||
if (!is_array($value)) {
|
||||
return $this->trimAndValidateHeaderValues([$value]);
|
||||
}
|
||||
|
||||
if (count($value) === 0) {
|
||||
throw new \InvalidArgumentException('Header value can not be an empty array.');
|
||||
}
|
||||
|
||||
return $this->trimAndValidateHeaderValues($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 mixed[] $values Header values
|
||||
*
|
||||
* @return string[] Trimmed header values
|
||||
*
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.4
|
||||
*/
|
||||
private function trimAndValidateHeaderValues(array $values): array
|
||||
{
|
||||
return array_map(function ($value) {
|
||||
if (!is_scalar($value) && null !== $value) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'Header value must be scalar or null but %s provided.',
|
||||
is_object($value) ? get_class($value) : gettype($value)
|
||||
));
|
||||
}
|
||||
|
||||
$trimmed = trim((string) $value, " \t");
|
||||
$this->assertValue($trimmed);
|
||||
|
||||
return $trimmed;
|
||||
}, array_values($values));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2
|
||||
*
|
||||
* @param mixed $header
|
||||
*/
|
||||
private function assertHeader($header): void
|
||||
{
|
||||
if (!is_string($header)) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'Header name must be a string but %s provided.',
|
||||
is_object($header) ? get_class($header) : gettype($header)
|
||||
));
|
||||
}
|
||||
|
||||
if (!preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/D', $header)) {
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf('"%s" is not valid header name.', $header)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2
|
||||
*
|
||||
* field-value = *( field-content / obs-fold )
|
||||
* field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
|
||||
* field-vchar = VCHAR / obs-text
|
||||
* VCHAR = %x21-7E
|
||||
* obs-text = %x80-FF
|
||||
* obs-fold = CRLF 1*( SP / HTAB )
|
||||
*/
|
||||
private function assertValue(string $value): void
|
||||
{
|
||||
// The regular expression intentionally does not support the obs-fold production, because as
|
||||
// per RFC 7230#3.2.4:
|
||||
//
|
||||
// A sender MUST NOT generate a message that includes
|
||||
// line folding (i.e., that has any field-value that contains a match to
|
||||
// the obs-fold rule) unless the message is intended for packaging
|
||||
// within the message/http media type.
|
||||
//
|
||||
// Clients must not send a request with line folding and a server sending folded headers is
|
||||
// likely very rare. Line folding is a fairly obscure feature of HTTP/1.1 and thus not accepting
|
||||
// folding is not likely to break any legitimate use case.
|
||||
if (!preg_match('/^[\x20\x09\x21-\x7E\x80-\xFF]*$/D', $value)) {
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf('"%s" is not valid header value.', $value)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
1259
vendor/guzzlehttp/psr7/src/MimeType.php
vendored
Normal file
1259
vendor/guzzlehttp/psr7/src/MimeType.php
vendored
Normal file
@ -0,0 +1,1259 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Psr7;
|
||||
|
||||
final class MimeType
|
||||
{
|
||||
private const MIME_TYPES = [
|
||||
'1km' => 'application/vnd.1000minds.decision-model+xml',
|
||||
'3dml' => 'text/vnd.in3d.3dml',
|
||||
'3ds' => 'image/x-3ds',
|
||||
'3g2' => 'video/3gpp2',
|
||||
'3gp' => 'video/3gp',
|
||||
'3gpp' => 'video/3gpp',
|
||||
'3mf' => 'model/3mf',
|
||||
'7z' => 'application/x-7z-compressed',
|
||||
'7zip' => 'application/x-7z-compressed',
|
||||
'123' => 'application/vnd.lotus-1-2-3',
|
||||
'aab' => 'application/x-authorware-bin',
|
||||
'aac' => 'audio/aac',
|
||||
'aam' => 'application/x-authorware-map',
|
||||
'aas' => 'application/x-authorware-seg',
|
||||
'abw' => 'application/x-abiword',
|
||||
'ac' => 'application/vnd.nokia.n-gage.ac+xml',
|
||||
'ac3' => 'audio/ac3',
|
||||
'acc' => 'application/vnd.americandynamics.acc',
|
||||
'ace' => 'application/x-ace-compressed',
|
||||
'acu' => 'application/vnd.acucobol',
|
||||
'acutc' => 'application/vnd.acucorp',
|
||||
'adp' => 'audio/adpcm',
|
||||
'adts' => 'audio/aac',
|
||||
'aep' => 'application/vnd.audiograph',
|
||||
'afm' => 'application/x-font-type1',
|
||||
'afp' => 'application/vnd.ibm.modcap',
|
||||
'age' => 'application/vnd.age',
|
||||
'ahead' => 'application/vnd.ahead.space',
|
||||
'ai' => 'application/pdf',
|
||||
'aif' => 'audio/x-aiff',
|
||||
'aifc' => 'audio/x-aiff',
|
||||
'aiff' => 'audio/x-aiff',
|
||||
'air' => 'application/vnd.adobe.air-application-installer-package+zip',
|
||||
'ait' => 'application/vnd.dvb.ait',
|
||||
'ami' => 'application/vnd.amiga.ami',
|
||||
'aml' => 'application/automationml-aml+xml',
|
||||
'amlx' => 'application/automationml-amlx+zip',
|
||||
'amr' => 'audio/amr',
|
||||
'apk' => 'application/vnd.android.package-archive',
|
||||
'apng' => 'image/apng',
|
||||
'appcache' => 'text/cache-manifest',
|
||||
'appinstaller' => 'application/appinstaller',
|
||||
'application' => 'application/x-ms-application',
|
||||
'appx' => 'application/appx',
|
||||
'appxbundle' => 'application/appxbundle',
|
||||
'apr' => 'application/vnd.lotus-approach',
|
||||
'arc' => 'application/x-freearc',
|
||||
'arj' => 'application/x-arj',
|
||||
'asc' => 'application/pgp-signature',
|
||||
'asf' => 'video/x-ms-asf',
|
||||
'asm' => 'text/x-asm',
|
||||
'aso' => 'application/vnd.accpac.simply.aso',
|
||||
'asx' => 'video/x-ms-asf',
|
||||
'atc' => 'application/vnd.acucorp',
|
||||
'atom' => 'application/atom+xml',
|
||||
'atomcat' => 'application/atomcat+xml',
|
||||
'atomdeleted' => 'application/atomdeleted+xml',
|
||||
'atomsvc' => 'application/atomsvc+xml',
|
||||
'atx' => 'application/vnd.antix.game-component',
|
||||
'au' => 'audio/x-au',
|
||||
'avci' => 'image/avci',
|
||||
'avcs' => 'image/avcs',
|
||||
'avi' => 'video/x-msvideo',
|
||||
'avif' => 'image/avif',
|
||||
'aw' => 'application/applixware',
|
||||
'azf' => 'application/vnd.airzip.filesecure.azf',
|
||||
'azs' => 'application/vnd.airzip.filesecure.azs',
|
||||
'azv' => 'image/vnd.airzip.accelerator.azv',
|
||||
'azw' => 'application/vnd.amazon.ebook',
|
||||
'b16' => 'image/vnd.pco.b16',
|
||||
'bat' => 'application/x-msdownload',
|
||||
'bcpio' => 'application/x-bcpio',
|
||||
'bdf' => 'application/x-font-bdf',
|
||||
'bdm' => 'application/vnd.syncml.dm+wbxml',
|
||||
'bdoc' => 'application/x-bdoc',
|
||||
'bed' => 'application/vnd.realvnc.bed',
|
||||
'bh2' => 'application/vnd.fujitsu.oasysprs',
|
||||
'bin' => 'application/octet-stream',
|
||||
'blb' => 'application/x-blorb',
|
||||
'blorb' => 'application/x-blorb',
|
||||
'bmi' => 'application/vnd.bmi',
|
||||
'bmml' => 'application/vnd.balsamiq.bmml+xml',
|
||||
'bmp' => 'image/bmp',
|
||||
'book' => 'application/vnd.framemaker',
|
||||
'box' => 'application/vnd.previewsystems.box',
|
||||
'boz' => 'application/x-bzip2',
|
||||
'bpk' => 'application/octet-stream',
|
||||
'bpmn' => 'application/octet-stream',
|
||||
'bsp' => 'model/vnd.valve.source.compiled-map',
|
||||
'btf' => 'image/prs.btif',
|
||||
'btif' => 'image/prs.btif',
|
||||
'buffer' => 'application/octet-stream',
|
||||
'bz' => 'application/x-bzip',
|
||||
'bz2' => 'application/x-bzip2',
|
||||
'c' => 'text/x-c',
|
||||
'c4d' => 'application/vnd.clonk.c4group',
|
||||
'c4f' => 'application/vnd.clonk.c4group',
|
||||
'c4g' => 'application/vnd.clonk.c4group',
|
||||
'c4p' => 'application/vnd.clonk.c4group',
|
||||
'c4u' => 'application/vnd.clonk.c4group',
|
||||
'c11amc' => 'application/vnd.cluetrust.cartomobile-config',
|
||||
'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg',
|
||||
'cab' => 'application/vnd.ms-cab-compressed',
|
||||
'caf' => 'audio/x-caf',
|
||||
'cap' => 'application/vnd.tcpdump.pcap',
|
||||
'car' => 'application/vnd.curl.car',
|
||||
'cat' => 'application/vnd.ms-pki.seccat',
|
||||
'cb7' => 'application/x-cbr',
|
||||
'cba' => 'application/x-cbr',
|
||||
'cbr' => 'application/x-cbr',
|
||||
'cbt' => 'application/x-cbr',
|
||||
'cbz' => 'application/x-cbr',
|
||||
'cc' => 'text/x-c',
|
||||
'cco' => 'application/x-cocoa',
|
||||
'cct' => 'application/x-director',
|
||||
'ccxml' => 'application/ccxml+xml',
|
||||
'cdbcmsg' => 'application/vnd.contact.cmsg',
|
||||
'cdf' => 'application/x-netcdf',
|
||||
'cdfx' => 'application/cdfx+xml',
|
||||
'cdkey' => 'application/vnd.mediastation.cdkey',
|
||||
'cdmia' => 'application/cdmi-capability',
|
||||
'cdmic' => 'application/cdmi-container',
|
||||
'cdmid' => 'application/cdmi-domain',
|
||||
'cdmio' => 'application/cdmi-object',
|
||||
'cdmiq' => 'application/cdmi-queue',
|
||||
'cdr' => 'application/cdr',
|
||||
'cdx' => 'chemical/x-cdx',
|
||||
'cdxml' => 'application/vnd.chemdraw+xml',
|
||||
'cdy' => 'application/vnd.cinderella',
|
||||
'cer' => 'application/pkix-cert',
|
||||
'cfs' => 'application/x-cfs-compressed',
|
||||
'cgm' => 'image/cgm',
|
||||
'chat' => 'application/x-chat',
|
||||
'chm' => 'application/vnd.ms-htmlhelp',
|
||||
'chrt' => 'application/vnd.kde.kchart',
|
||||
'cif' => 'chemical/x-cif',
|
||||
'cii' => 'application/vnd.anser-web-certificate-issue-initiation',
|
||||
'cil' => 'application/vnd.ms-artgalry',
|
||||
'cjs' => 'application/node',
|
||||
'cla' => 'application/vnd.claymore',
|
||||
'class' => 'application/octet-stream',
|
||||
'cld' => 'model/vnd.cld',
|
||||
'clkk' => 'application/vnd.crick.clicker.keyboard',
|
||||
'clkp' => 'application/vnd.crick.clicker.palette',
|
||||
'clkt' => 'application/vnd.crick.clicker.template',
|
||||
'clkw' => 'application/vnd.crick.clicker.wordbank',
|
||||
'clkx' => 'application/vnd.crick.clicker',
|
||||
'clp' => 'application/x-msclip',
|
||||
'cmc' => 'application/vnd.cosmocaller',
|
||||
'cmdf' => 'chemical/x-cmdf',
|
||||
'cml' => 'chemical/x-cml',
|
||||
'cmp' => 'application/vnd.yellowriver-custom-menu',
|
||||
'cmx' => 'image/x-cmx',
|
||||
'cod' => 'application/vnd.rim.cod',
|
||||
'coffee' => 'text/coffeescript',
|
||||
'com' => 'application/x-msdownload',
|
||||
'conf' => 'text/plain',
|
||||
'cpio' => 'application/x-cpio',
|
||||
'cpl' => 'application/cpl+xml',
|
||||
'cpp' => 'text/x-c',
|
||||
'cpt' => 'application/mac-compactpro',
|
||||
'crd' => 'application/x-mscardfile',
|
||||
'crl' => 'application/pkix-crl',
|
||||
'crt' => 'application/x-x509-ca-cert',
|
||||
'crx' => 'application/x-chrome-extension',
|
||||
'cryptonote' => 'application/vnd.rig.cryptonote',
|
||||
'csh' => 'application/x-csh',
|
||||
'csl' => 'application/vnd.citationstyles.style+xml',
|
||||
'csml' => 'chemical/x-csml',
|
||||
'csp' => 'application/vnd.commonspace',
|
||||
'csr' => 'application/octet-stream',
|
||||
'css' => 'text/css',
|
||||
'cst' => 'application/x-director',
|
||||
'csv' => 'text/csv',
|
||||
'cu' => 'application/cu-seeme',
|
||||
'curl' => 'text/vnd.curl',
|
||||
'cwl' => 'application/cwl',
|
||||
'cww' => 'application/prs.cww',
|
||||
'cxt' => 'application/x-director',
|
||||
'cxx' => 'text/x-c',
|
||||
'dae' => 'model/vnd.collada+xml',
|
||||
'daf' => 'application/vnd.mobius.daf',
|
||||
'dart' => 'application/vnd.dart',
|
||||
'dataless' => 'application/vnd.fdsn.seed',
|
||||
'davmount' => 'application/davmount+xml',
|
||||
'dbf' => 'application/vnd.dbf',
|
||||
'dbk' => 'application/docbook+xml',
|
||||
'dcr' => 'application/x-director',
|
||||
'dcurl' => 'text/vnd.curl.dcurl',
|
||||
'dd2' => 'application/vnd.oma.dd2+xml',
|
||||
'ddd' => 'application/vnd.fujixerox.ddd',
|
||||
'ddf' => 'application/vnd.syncml.dmddf+xml',
|
||||
'dds' => 'image/vnd.ms-dds',
|
||||
'deb' => 'application/x-debian-package',
|
||||
'def' => 'text/plain',
|
||||
'deploy' => 'application/octet-stream',
|
||||
'der' => 'application/x-x509-ca-cert',
|
||||
'dfac' => 'application/vnd.dreamfactory',
|
||||
'dgc' => 'application/x-dgc-compressed',
|
||||
'dib' => 'image/bmp',
|
||||
'dic' => 'text/x-c',
|
||||
'dir' => 'application/x-director',
|
||||
'dis' => 'application/vnd.mobius.dis',
|
||||
'disposition-notification' => 'message/disposition-notification',
|
||||
'dist' => 'application/octet-stream',
|
||||
'distz' => 'application/octet-stream',
|
||||
'djv' => 'image/vnd.djvu',
|
||||
'djvu' => 'image/vnd.djvu',
|
||||
'dll' => 'application/octet-stream',
|
||||
'dmg' => 'application/x-apple-diskimage',
|
||||
'dmn' => 'application/octet-stream',
|
||||
'dmp' => 'application/vnd.tcpdump.pcap',
|
||||
'dms' => 'application/octet-stream',
|
||||
'dna' => 'application/vnd.dna',
|
||||
'doc' => 'application/msword',
|
||||
'docm' => 'application/vnd.ms-word.template.macroEnabled.12',
|
||||
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'dot' => 'application/msword',
|
||||
'dotm' => 'application/vnd.ms-word.template.macroEnabled.12',
|
||||
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
||||
'dp' => 'application/vnd.osgi.dp',
|
||||
'dpg' => 'application/vnd.dpgraph',
|
||||
'dpx' => 'image/dpx',
|
||||
'dra' => 'audio/vnd.dra',
|
||||
'drle' => 'image/dicom-rle',
|
||||
'dsc' => 'text/prs.lines.tag',
|
||||
'dssc' => 'application/dssc+der',
|
||||
'dtb' => 'application/x-dtbook+xml',
|
||||
'dtd' => 'application/xml-dtd',
|
||||
'dts' => 'audio/vnd.dts',
|
||||
'dtshd' => 'audio/vnd.dts.hd',
|
||||
'dump' => 'application/octet-stream',
|
||||
'dvb' => 'video/vnd.dvb.file',
|
||||
'dvi' => 'application/x-dvi',
|
||||
'dwd' => 'application/atsc-dwd+xml',
|
||||
'dwf' => 'model/vnd.dwf',
|
||||
'dwg' => 'image/vnd.dwg',
|
||||
'dxf' => 'image/vnd.dxf',
|
||||
'dxp' => 'application/vnd.spotfire.dxp',
|
||||
'dxr' => 'application/x-director',
|
||||
'ear' => 'application/java-archive',
|
||||
'ecelp4800' => 'audio/vnd.nuera.ecelp4800',
|
||||
'ecelp7470' => 'audio/vnd.nuera.ecelp7470',
|
||||
'ecelp9600' => 'audio/vnd.nuera.ecelp9600',
|
||||
'ecma' => 'application/ecmascript',
|
||||
'edm' => 'application/vnd.novadigm.edm',
|
||||
'edx' => 'application/vnd.novadigm.edx',
|
||||
'efif' => 'application/vnd.picsel',
|
||||
'ei6' => 'application/vnd.pg.osasli',
|
||||
'elc' => 'application/octet-stream',
|
||||
'emf' => 'image/emf',
|
||||
'eml' => 'message/rfc822',
|
||||
'emma' => 'application/emma+xml',
|
||||
'emotionml' => 'application/emotionml+xml',
|
||||
'emz' => 'application/x-msmetafile',
|
||||
'eol' => 'audio/vnd.digital-winds',
|
||||
'eot' => 'application/vnd.ms-fontobject',
|
||||
'eps' => 'application/postscript',
|
||||
'epub' => 'application/epub+zip',
|
||||
'es3' => 'application/vnd.eszigno3+xml',
|
||||
'esa' => 'application/vnd.osgi.subsystem',
|
||||
'esf' => 'application/vnd.epson.esf',
|
||||
'et3' => 'application/vnd.eszigno3+xml',
|
||||
'etx' => 'text/x-setext',
|
||||
'eva' => 'application/x-eva',
|
||||
'evy' => 'application/x-envoy',
|
||||
'exe' => 'application/octet-stream',
|
||||
'exi' => 'application/exi',
|
||||
'exp' => 'application/express',
|
||||
'exr' => 'image/aces',
|
||||
'ext' => 'application/vnd.novadigm.ext',
|
||||
'ez' => 'application/andrew-inset',
|
||||
'ez2' => 'application/vnd.ezpix-album',
|
||||
'ez3' => 'application/vnd.ezpix-package',
|
||||
'f' => 'text/x-fortran',
|
||||
'f4v' => 'video/mp4',
|
||||
'f77' => 'text/x-fortran',
|
||||
'f90' => 'text/x-fortran',
|
||||
'fbs' => 'image/vnd.fastbidsheet',
|
||||
'fcdt' => 'application/vnd.adobe.formscentral.fcdt',
|
||||
'fcs' => 'application/vnd.isac.fcs',
|
||||
'fdf' => 'application/vnd.fdf',
|
||||
'fdt' => 'application/fdt+xml',
|
||||
'fe_launch' => 'application/vnd.denovo.fcselayout-link',
|
||||
'fg5' => 'application/vnd.fujitsu.oasysgp',
|
||||
'fgd' => 'application/x-director',
|
||||
'fh' => 'image/x-freehand',
|
||||
'fh4' => 'image/x-freehand',
|
||||
'fh5' => 'image/x-freehand',
|
||||
'fh7' => 'image/x-freehand',
|
||||
'fhc' => 'image/x-freehand',
|
||||
'fig' => 'application/x-xfig',
|
||||
'fits' => 'image/fits',
|
||||
'flac' => 'audio/x-flac',
|
||||
'fli' => 'video/x-fli',
|
||||
'flo' => 'application/vnd.micrografx.flo',
|
||||
'flv' => 'video/x-flv',
|
||||
'flw' => 'application/vnd.kde.kivio',
|
||||
'flx' => 'text/vnd.fmi.flexstor',
|
||||
'fly' => 'text/vnd.fly',
|
||||
'fm' => 'application/vnd.framemaker',
|
||||
'fnc' => 'application/vnd.frogans.fnc',
|
||||
'fo' => 'application/vnd.software602.filler.form+xml',
|
||||
'for' => 'text/x-fortran',
|
||||
'fpx' => 'image/vnd.fpx',
|
||||
'frame' => 'application/vnd.framemaker',
|
||||
'fsc' => 'application/vnd.fsc.weblaunch',
|
||||
'fst' => 'image/vnd.fst',
|
||||
'ftc' => 'application/vnd.fluxtime.clip',
|
||||
'fti' => 'application/vnd.anser-web-funds-transfer-initiation',
|
||||
'fvt' => 'video/vnd.fvt',
|
||||
'fxp' => 'application/vnd.adobe.fxp',
|
||||
'fxpl' => 'application/vnd.adobe.fxp',
|
||||
'fzs' => 'application/vnd.fuzzysheet',
|
||||
'g2w' => 'application/vnd.geoplan',
|
||||
'g3' => 'image/g3fax',
|
||||
'g3w' => 'application/vnd.geospace',
|
||||
'gac' => 'application/vnd.groove-account',
|
||||
'gam' => 'application/x-tads',
|
||||
'gbr' => 'application/rpki-ghostbusters',
|
||||
'gca' => 'application/x-gca-compressed',
|
||||
'gdl' => 'model/vnd.gdl',
|
||||
'gdoc' => 'application/vnd.google-apps.document',
|
||||
'ged' => 'text/vnd.familysearch.gedcom',
|
||||
'geo' => 'application/vnd.dynageo',
|
||||
'geojson' => 'application/geo+json',
|
||||
'gex' => 'application/vnd.geometry-explorer',
|
||||
'ggb' => 'application/vnd.geogebra.file',
|
||||
'ggt' => 'application/vnd.geogebra.tool',
|
||||
'ghf' => 'application/vnd.groove-help',
|
||||
'gif' => 'image/gif',
|
||||
'gim' => 'application/vnd.groove-identity-message',
|
||||
'glb' => 'model/gltf-binary',
|
||||
'gltf' => 'model/gltf+json',
|
||||
'gml' => 'application/gml+xml',
|
||||
'gmx' => 'application/vnd.gmx',
|
||||
'gnumeric' => 'application/x-gnumeric',
|
||||
'gpg' => 'application/gpg-keys',
|
||||
'gph' => 'application/vnd.flographit',
|
||||
'gpx' => 'application/gpx+xml',
|
||||
'gqf' => 'application/vnd.grafeq',
|
||||
'gqs' => 'application/vnd.grafeq',
|
||||
'gram' => 'application/srgs',
|
||||
'gramps' => 'application/x-gramps-xml',
|
||||
'gre' => 'application/vnd.geometry-explorer',
|
||||
'grv' => 'application/vnd.groove-injector',
|
||||
'grxml' => 'application/srgs+xml',
|
||||
'gsf' => 'application/x-font-ghostscript',
|
||||
'gsheet' => 'application/vnd.google-apps.spreadsheet',
|
||||
'gslides' => 'application/vnd.google-apps.presentation',
|
||||
'gtar' => 'application/x-gtar',
|
||||
'gtm' => 'application/vnd.groove-tool-message',
|
||||
'gtw' => 'model/vnd.gtw',
|
||||
'gv' => 'text/vnd.graphviz',
|
||||
'gxf' => 'application/gxf',
|
||||
'gxt' => 'application/vnd.geonext',
|
||||
'gz' => 'application/gzip',
|
||||
'gzip' => 'application/gzip',
|
||||
'h' => 'text/x-c',
|
||||
'h261' => 'video/h261',
|
||||
'h263' => 'video/h263',
|
||||
'h264' => 'video/h264',
|
||||
'hal' => 'application/vnd.hal+xml',
|
||||
'hbci' => 'application/vnd.hbci',
|
||||
'hbs' => 'text/x-handlebars-template',
|
||||
'hdd' => 'application/x-virtualbox-hdd',
|
||||
'hdf' => 'application/x-hdf',
|
||||
'heic' => 'image/heic',
|
||||
'heics' => 'image/heic-sequence',
|
||||
'heif' => 'image/heif',
|
||||
'heifs' => 'image/heif-sequence',
|
||||
'hej2' => 'image/hej2k',
|
||||
'held' => 'application/atsc-held+xml',
|
||||
'hh' => 'text/x-c',
|
||||
'hjson' => 'application/hjson',
|
||||
'hlp' => 'application/winhlp',
|
||||
'hpgl' => 'application/vnd.hp-hpgl',
|
||||
'hpid' => 'application/vnd.hp-hpid',
|
||||
'hps' => 'application/vnd.hp-hps',
|
||||
'hqx' => 'application/mac-binhex40',
|
||||
'hsj2' => 'image/hsj2',
|
||||
'htc' => 'text/x-component',
|
||||
'htke' => 'application/vnd.kenameaapp',
|
||||
'htm' => 'text/html',
|
||||
'html' => 'text/html',
|
||||
'hvd' => 'application/vnd.yamaha.hv-dic',
|
||||
'hvp' => 'application/vnd.yamaha.hv-voice',
|
||||
'hvs' => 'application/vnd.yamaha.hv-script',
|
||||
'i2g' => 'application/vnd.intergeo',
|
||||
'icc' => 'application/vnd.iccprofile',
|
||||
'ice' => 'x-conference/x-cooltalk',
|
||||
'icm' => 'application/vnd.iccprofile',
|
||||
'ico' => 'image/x-icon',
|
||||
'ics' => 'text/calendar',
|
||||
'ief' => 'image/ief',
|
||||
'ifb' => 'text/calendar',
|
||||
'ifm' => 'application/vnd.shana.informed.formdata',
|
||||
'iges' => 'model/iges',
|
||||
'igl' => 'application/vnd.igloader',
|
||||
'igm' => 'application/vnd.insors.igm',
|
||||
'igs' => 'model/iges',
|
||||
'igx' => 'application/vnd.micrografx.igx',
|
||||
'iif' => 'application/vnd.shana.informed.interchange',
|
||||
'img' => 'application/octet-stream',
|
||||
'imp' => 'application/vnd.accpac.simply.imp',
|
||||
'ims' => 'application/vnd.ms-ims',
|
||||
'in' => 'text/plain',
|
||||
'ini' => 'text/plain',
|
||||
'ink' => 'application/inkml+xml',
|
||||
'inkml' => 'application/inkml+xml',
|
||||
'install' => 'application/x-install-instructions',
|
||||
'iota' => 'application/vnd.astraea-software.iota',
|
||||
'ipfix' => 'application/ipfix',
|
||||
'ipk' => 'application/vnd.shana.informed.package',
|
||||
'irm' => 'application/vnd.ibm.rights-management',
|
||||
'irp' => 'application/vnd.irepository.package+xml',
|
||||
'iso' => 'application/x-iso9660-image',
|
||||
'itp' => 'application/vnd.shana.informed.formtemplate',
|
||||
'its' => 'application/its+xml',
|
||||
'ivp' => 'application/vnd.immervision-ivp',
|
||||
'ivu' => 'application/vnd.immervision-ivu',
|
||||
'jad' => 'text/vnd.sun.j2me.app-descriptor',
|
||||
'jade' => 'text/jade',
|
||||
'jam' => 'application/vnd.jam',
|
||||
'jar' => 'application/java-archive',
|
||||
'jardiff' => 'application/x-java-archive-diff',
|
||||
'java' => 'text/x-java-source',
|
||||
'jhc' => 'image/jphc',
|
||||
'jisp' => 'application/vnd.jisp',
|
||||
'jls' => 'image/jls',
|
||||
'jlt' => 'application/vnd.hp-jlyt',
|
||||
'jng' => 'image/x-jng',
|
||||
'jnlp' => 'application/x-java-jnlp-file',
|
||||
'joda' => 'application/vnd.joost.joda-archive',
|
||||
'jp2' => 'image/jp2',
|
||||
'jpe' => 'image/jpeg',
|
||||
'jpeg' => 'image/jpeg',
|
||||
'jpf' => 'image/jpx',
|
||||
'jpg' => 'image/jpeg',
|
||||
'jpg2' => 'image/jp2',
|
||||
'jpgm' => 'video/jpm',
|
||||
'jpgv' => 'video/jpeg',
|
||||
'jph' => 'image/jph',
|
||||
'jpm' => 'video/jpm',
|
||||
'jpx' => 'image/jpx',
|
||||
'js' => 'application/javascript',
|
||||
'json' => 'application/json',
|
||||
'json5' => 'application/json5',
|
||||
'jsonld' => 'application/ld+json',
|
||||
'jsonml' => 'application/jsonml+json',
|
||||
'jsx' => 'text/jsx',
|
||||
'jt' => 'model/jt',
|
||||
'jxr' => 'image/jxr',
|
||||
'jxra' => 'image/jxra',
|
||||
'jxrs' => 'image/jxrs',
|
||||
'jxs' => 'image/jxs',
|
||||
'jxsc' => 'image/jxsc',
|
||||
'jxsi' => 'image/jxsi',
|
||||
'jxss' => 'image/jxss',
|
||||
'kar' => 'audio/midi',
|
||||
'karbon' => 'application/vnd.kde.karbon',
|
||||
'kdb' => 'application/octet-stream',
|
||||
'kdbx' => 'application/x-keepass2',
|
||||
'key' => 'application/x-iwork-keynote-sffkey',
|
||||
'kfo' => 'application/vnd.kde.kformula',
|
||||
'kia' => 'application/vnd.kidspiration',
|
||||
'kml' => 'application/vnd.google-earth.kml+xml',
|
||||
'kmz' => 'application/vnd.google-earth.kmz',
|
||||
'kne' => 'application/vnd.kinar',
|
||||
'knp' => 'application/vnd.kinar',
|
||||
'kon' => 'application/vnd.kde.kontour',
|
||||
'kpr' => 'application/vnd.kde.kpresenter',
|
||||
'kpt' => 'application/vnd.kde.kpresenter',
|
||||
'kpxx' => 'application/vnd.ds-keypoint',
|
||||
'ksp' => 'application/vnd.kde.kspread',
|
||||
'ktr' => 'application/vnd.kahootz',
|
||||
'ktx' => 'image/ktx',
|
||||
'ktx2' => 'image/ktx2',
|
||||
'ktz' => 'application/vnd.kahootz',
|
||||
'kwd' => 'application/vnd.kde.kword',
|
||||
'kwt' => 'application/vnd.kde.kword',
|
||||
'lasxml' => 'application/vnd.las.las+xml',
|
||||
'latex' => 'application/x-latex',
|
||||
'lbd' => 'application/vnd.llamagraphics.life-balance.desktop',
|
||||
'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml',
|
||||
'les' => 'application/vnd.hhe.lesson-player',
|
||||
'less' => 'text/less',
|
||||
'lgr' => 'application/lgr+xml',
|
||||
'lha' => 'application/octet-stream',
|
||||
'link66' => 'application/vnd.route66.link66+xml',
|
||||
'list' => 'text/plain',
|
||||
'list3820' => 'application/vnd.ibm.modcap',
|
||||
'listafp' => 'application/vnd.ibm.modcap',
|
||||
'litcoffee' => 'text/coffeescript',
|
||||
'lnk' => 'application/x-ms-shortcut',
|
||||
'log' => 'text/plain',
|
||||
'lostxml' => 'application/lost+xml',
|
||||
'lrf' => 'application/octet-stream',
|
||||
'lrm' => 'application/vnd.ms-lrm',
|
||||
'ltf' => 'application/vnd.frogans.ltf',
|
||||
'lua' => 'text/x-lua',
|
||||
'luac' => 'application/x-lua-bytecode',
|
||||
'lvp' => 'audio/vnd.lucent.voice',
|
||||
'lwp' => 'application/vnd.lotus-wordpro',
|
||||
'lzh' => 'application/octet-stream',
|
||||
'm1v' => 'video/mpeg',
|
||||
'm2a' => 'audio/mpeg',
|
||||
'm2v' => 'video/mpeg',
|
||||
'm3a' => 'audio/mpeg',
|
||||
'm3u' => 'text/plain',
|
||||
'm3u8' => 'application/vnd.apple.mpegurl',
|
||||
'm4a' => 'audio/x-m4a',
|
||||
'm4p' => 'application/mp4',
|
||||
'm4s' => 'video/iso.segment',
|
||||
'm4u' => 'application/vnd.mpegurl',
|
||||
'm4v' => 'video/x-m4v',
|
||||
'm13' => 'application/x-msmediaview',
|
||||
'm14' => 'application/x-msmediaview',
|
||||
'm21' => 'application/mp21',
|
||||
'ma' => 'application/mathematica',
|
||||
'mads' => 'application/mads+xml',
|
||||
'maei' => 'application/mmt-aei+xml',
|
||||
'mag' => 'application/vnd.ecowin.chart',
|
||||
'maker' => 'application/vnd.framemaker',
|
||||
'man' => 'text/troff',
|
||||
'manifest' => 'text/cache-manifest',
|
||||
'map' => 'application/json',
|
||||
'mar' => 'application/octet-stream',
|
||||
'markdown' => 'text/markdown',
|
||||
'mathml' => 'application/mathml+xml',
|
||||
'mb' => 'application/mathematica',
|
||||
'mbk' => 'application/vnd.mobius.mbk',
|
||||
'mbox' => 'application/mbox',
|
||||
'mc1' => 'application/vnd.medcalcdata',
|
||||
'mcd' => 'application/vnd.mcd',
|
||||
'mcurl' => 'text/vnd.curl.mcurl',
|
||||
'md' => 'text/markdown',
|
||||
'mdb' => 'application/x-msaccess',
|
||||
'mdi' => 'image/vnd.ms-modi',
|
||||
'mdx' => 'text/mdx',
|
||||
'me' => 'text/troff',
|
||||
'mesh' => 'model/mesh',
|
||||
'meta4' => 'application/metalink4+xml',
|
||||
'metalink' => 'application/metalink+xml',
|
||||
'mets' => 'application/mets+xml',
|
||||
'mfm' => 'application/vnd.mfmp',
|
||||
'mft' => 'application/rpki-manifest',
|
||||
'mgp' => 'application/vnd.osgeo.mapguide.package',
|
||||
'mgz' => 'application/vnd.proteus.magazine',
|
||||
'mid' => 'audio/midi',
|
||||
'midi' => 'audio/midi',
|
||||
'mie' => 'application/x-mie',
|
||||
'mif' => 'application/vnd.mif',
|
||||
'mime' => 'message/rfc822',
|
||||
'mj2' => 'video/mj2',
|
||||
'mjp2' => 'video/mj2',
|
||||
'mjs' => 'text/javascript',
|
||||
'mk3d' => 'video/x-matroska',
|
||||
'mka' => 'audio/x-matroska',
|
||||
'mkd' => 'text/x-markdown',
|
||||
'mks' => 'video/x-matroska',
|
||||
'mkv' => 'video/x-matroska',
|
||||
'mlp' => 'application/vnd.dolby.mlp',
|
||||
'mmd' => 'application/vnd.chipnuts.karaoke-mmd',
|
||||
'mmf' => 'application/vnd.smaf',
|
||||
'mml' => 'text/mathml',
|
||||
'mmr' => 'image/vnd.fujixerox.edmics-mmr',
|
||||
'mng' => 'video/x-mng',
|
||||
'mny' => 'application/x-msmoney',
|
||||
'mobi' => 'application/x-mobipocket-ebook',
|
||||
'mods' => 'application/mods+xml',
|
||||
'mov' => 'video/quicktime',
|
||||
'movie' => 'video/x-sgi-movie',
|
||||
'mp2' => 'audio/mpeg',
|
||||
'mp2a' => 'audio/mpeg',
|
||||
'mp3' => 'audio/mpeg',
|
||||
'mp4' => 'video/mp4',
|
||||
'mp4a' => 'audio/mp4',
|
||||
'mp4s' => 'application/mp4',
|
||||
'mp4v' => 'video/mp4',
|
||||
'mp21' => 'application/mp21',
|
||||
'mpc' => 'application/vnd.mophun.certificate',
|
||||
'mpd' => 'application/dash+xml',
|
||||
'mpe' => 'video/mpeg',
|
||||
'mpeg' => 'video/mpeg',
|
||||
'mpf' => 'application/media-policy-dataset+xml',
|
||||
'mpg' => 'video/mpeg',
|
||||
'mpg4' => 'video/mp4',
|
||||
'mpga' => 'audio/mpeg',
|
||||
'mpkg' => 'application/vnd.apple.installer+xml',
|
||||
'mpm' => 'application/vnd.blueice.multipass',
|
||||
'mpn' => 'application/vnd.mophun.application',
|
||||
'mpp' => 'application/vnd.ms-project',
|
||||
'mpt' => 'application/vnd.ms-project',
|
||||
'mpy' => 'application/vnd.ibm.minipay',
|
||||
'mqy' => 'application/vnd.mobius.mqy',
|
||||
'mrc' => 'application/marc',
|
||||
'mrcx' => 'application/marcxml+xml',
|
||||
'ms' => 'text/troff',
|
||||
'mscml' => 'application/mediaservercontrol+xml',
|
||||
'mseed' => 'application/vnd.fdsn.mseed',
|
||||
'mseq' => 'application/vnd.mseq',
|
||||
'msf' => 'application/vnd.epson.msf',
|
||||
'msg' => 'application/vnd.ms-outlook',
|
||||
'msh' => 'model/mesh',
|
||||
'msi' => 'application/x-msdownload',
|
||||
'msix' => 'application/msix',
|
||||
'msixbundle' => 'application/msixbundle',
|
||||
'msl' => 'application/vnd.mobius.msl',
|
||||
'msm' => 'application/octet-stream',
|
||||
'msp' => 'application/octet-stream',
|
||||
'msty' => 'application/vnd.muvee.style',
|
||||
'mtl' => 'model/mtl',
|
||||
'mts' => 'model/vnd.mts',
|
||||
'mus' => 'application/vnd.musician',
|
||||
'musd' => 'application/mmt-usd+xml',
|
||||
'musicxml' => 'application/vnd.recordare.musicxml+xml',
|
||||
'mvb' => 'application/x-msmediaview',
|
||||
'mvt' => 'application/vnd.mapbox-vector-tile',
|
||||
'mwf' => 'application/vnd.mfer',
|
||||
'mxf' => 'application/mxf',
|
||||
'mxl' => 'application/vnd.recordare.musicxml',
|
||||
'mxmf' => 'audio/mobile-xmf',
|
||||
'mxml' => 'application/xv+xml',
|
||||
'mxs' => 'application/vnd.triscape.mxs',
|
||||
'mxu' => 'video/vnd.mpegurl',
|
||||
'n-gage' => 'application/vnd.nokia.n-gage.symbian.install',
|
||||
'n3' => 'text/n3',
|
||||
'nb' => 'application/mathematica',
|
||||
'nbp' => 'application/vnd.wolfram.player',
|
||||
'nc' => 'application/x-netcdf',
|
||||
'ncx' => 'application/x-dtbncx+xml',
|
||||
'nfo' => 'text/x-nfo',
|
||||
'ngdat' => 'application/vnd.nokia.n-gage.data',
|
||||
'nitf' => 'application/vnd.nitf',
|
||||
'nlu' => 'application/vnd.neurolanguage.nlu',
|
||||
'nml' => 'application/vnd.enliven',
|
||||
'nnd' => 'application/vnd.noblenet-directory',
|
||||
'nns' => 'application/vnd.noblenet-sealer',
|
||||
'nnw' => 'application/vnd.noblenet-web',
|
||||
'npx' => 'image/vnd.net-fpx',
|
||||
'nq' => 'application/n-quads',
|
||||
'nsc' => 'application/x-conference',
|
||||
'nsf' => 'application/vnd.lotus-notes',
|
||||
'nt' => 'application/n-triples',
|
||||
'ntf' => 'application/vnd.nitf',
|
||||
'numbers' => 'application/x-iwork-numbers-sffnumbers',
|
||||
'nzb' => 'application/x-nzb',
|
||||
'oa2' => 'application/vnd.fujitsu.oasys2',
|
||||
'oa3' => 'application/vnd.fujitsu.oasys3',
|
||||
'oas' => 'application/vnd.fujitsu.oasys',
|
||||
'obd' => 'application/x-msbinder',
|
||||
'obgx' => 'application/vnd.openblox.game+xml',
|
||||
'obj' => 'model/obj',
|
||||
'oda' => 'application/oda',
|
||||
'odb' => 'application/vnd.oasis.opendocument.database',
|
||||
'odc' => 'application/vnd.oasis.opendocument.chart',
|
||||
'odf' => 'application/vnd.oasis.opendocument.formula',
|
||||
'odft' => 'application/vnd.oasis.opendocument.formula-template',
|
||||
'odg' => 'application/vnd.oasis.opendocument.graphics',
|
||||
'odi' => 'application/vnd.oasis.opendocument.image',
|
||||
'odm' => 'application/vnd.oasis.opendocument.text-master',
|
||||
'odp' => 'application/vnd.oasis.opendocument.presentation',
|
||||
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
|
||||
'odt' => 'application/vnd.oasis.opendocument.text',
|
||||
'oga' => 'audio/ogg',
|
||||
'ogex' => 'model/vnd.opengex',
|
||||
'ogg' => 'audio/ogg',
|
||||
'ogv' => 'video/ogg',
|
||||
'ogx' => 'application/ogg',
|
||||
'omdoc' => 'application/omdoc+xml',
|
||||
'onepkg' => 'application/onenote',
|
||||
'onetmp' => 'application/onenote',
|
||||
'onetoc' => 'application/onenote',
|
||||
'onetoc2' => 'application/onenote',
|
||||
'opf' => 'application/oebps-package+xml',
|
||||
'opml' => 'text/x-opml',
|
||||
'oprc' => 'application/vnd.palm',
|
||||
'opus' => 'audio/ogg',
|
||||
'org' => 'text/x-org',
|
||||
'osf' => 'application/vnd.yamaha.openscoreformat',
|
||||
'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml',
|
||||
'osm' => 'application/vnd.openstreetmap.data+xml',
|
||||
'otc' => 'application/vnd.oasis.opendocument.chart-template',
|
||||
'otf' => 'font/otf',
|
||||
'otg' => 'application/vnd.oasis.opendocument.graphics-template',
|
||||
'oth' => 'application/vnd.oasis.opendocument.text-web',
|
||||
'oti' => 'application/vnd.oasis.opendocument.image-template',
|
||||
'otp' => 'application/vnd.oasis.opendocument.presentation-template',
|
||||
'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template',
|
||||
'ott' => 'application/vnd.oasis.opendocument.text-template',
|
||||
'ova' => 'application/x-virtualbox-ova',
|
||||
'ovf' => 'application/x-virtualbox-ovf',
|
||||
'owl' => 'application/rdf+xml',
|
||||
'oxps' => 'application/oxps',
|
||||
'oxt' => 'application/vnd.openofficeorg.extension',
|
||||
'p' => 'text/x-pascal',
|
||||
'p7a' => 'application/x-pkcs7-signature',
|
||||
'p7b' => 'application/x-pkcs7-certificates',
|
||||
'p7c' => 'application/pkcs7-mime',
|
||||
'p7m' => 'application/pkcs7-mime',
|
||||
'p7r' => 'application/x-pkcs7-certreqresp',
|
||||
'p7s' => 'application/pkcs7-signature',
|
||||
'p8' => 'application/pkcs8',
|
||||
'p10' => 'application/x-pkcs10',
|
||||
'p12' => 'application/x-pkcs12',
|
||||
'pac' => 'application/x-ns-proxy-autoconfig',
|
||||
'pages' => 'application/x-iwork-pages-sffpages',
|
||||
'pas' => 'text/x-pascal',
|
||||
'paw' => 'application/vnd.pawaafile',
|
||||
'pbd' => 'application/vnd.powerbuilder6',
|
||||
'pbm' => 'image/x-portable-bitmap',
|
||||
'pcap' => 'application/vnd.tcpdump.pcap',
|
||||
'pcf' => 'application/x-font-pcf',
|
||||
'pcl' => 'application/vnd.hp-pcl',
|
||||
'pclxl' => 'application/vnd.hp-pclxl',
|
||||
'pct' => 'image/x-pict',
|
||||
'pcurl' => 'application/vnd.curl.pcurl',
|
||||
'pcx' => 'image/x-pcx',
|
||||
'pdb' => 'application/x-pilot',
|
||||
'pde' => 'text/x-processing',
|
||||
'pdf' => 'application/pdf',
|
||||
'pem' => 'application/x-x509-user-cert',
|
||||
'pfa' => 'application/x-font-type1',
|
||||
'pfb' => 'application/x-font-type1',
|
||||
'pfm' => 'application/x-font-type1',
|
||||
'pfr' => 'application/font-tdpfr',
|
||||
'pfx' => 'application/x-pkcs12',
|
||||
'pgm' => 'image/x-portable-graymap',
|
||||
'pgn' => 'application/x-chess-pgn',
|
||||
'pgp' => 'application/pgp',
|
||||
'phar' => 'application/octet-stream',
|
||||
'php' => 'application/x-httpd-php',
|
||||
'php3' => 'application/x-httpd-php',
|
||||
'php4' => 'application/x-httpd-php',
|
||||
'phps' => 'application/x-httpd-php-source',
|
||||
'phtml' => 'application/x-httpd-php',
|
||||
'pic' => 'image/x-pict',
|
||||
'pkg' => 'application/octet-stream',
|
||||
'pki' => 'application/pkixcmp',
|
||||
'pkipath' => 'application/pkix-pkipath',
|
||||
'pkpass' => 'application/vnd.apple.pkpass',
|
||||
'pl' => 'application/x-perl',
|
||||
'plb' => 'application/vnd.3gpp.pic-bw-large',
|
||||
'plc' => 'application/vnd.mobius.plc',
|
||||
'plf' => 'application/vnd.pocketlearn',
|
||||
'pls' => 'application/pls+xml',
|
||||
'pm' => 'application/x-perl',
|
||||
'pml' => 'application/vnd.ctc-posml',
|
||||
'png' => 'image/png',
|
||||
'pnm' => 'image/x-portable-anymap',
|
||||
'portpkg' => 'application/vnd.macports.portpkg',
|
||||
'pot' => 'application/vnd.ms-powerpoint',
|
||||
'potm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
|
||||
'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
|
||||
'ppa' => 'application/vnd.ms-powerpoint',
|
||||
'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
|
||||
'ppd' => 'application/vnd.cups-ppd',
|
||||
'ppm' => 'image/x-portable-pixmap',
|
||||
'pps' => 'application/vnd.ms-powerpoint',
|
||||
'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
|
||||
'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
|
||||
'ppt' => 'application/powerpoint',
|
||||
'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
|
||||
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
'pqa' => 'application/vnd.palm',
|
||||
'prc' => 'model/prc',
|
||||
'pre' => 'application/vnd.lotus-freelance',
|
||||
'prf' => 'application/pics-rules',
|
||||
'provx' => 'application/provenance+xml',
|
||||
'ps' => 'application/postscript',
|
||||
'psb' => 'application/vnd.3gpp.pic-bw-small',
|
||||
'psd' => 'application/x-photoshop',
|
||||
'psf' => 'application/x-font-linux-psf',
|
||||
'pskcxml' => 'application/pskc+xml',
|
||||
'pti' => 'image/prs.pti',
|
||||
'ptid' => 'application/vnd.pvi.ptid1',
|
||||
'pub' => 'application/x-mspublisher',
|
||||
'pvb' => 'application/vnd.3gpp.pic-bw-var',
|
||||
'pwn' => 'application/vnd.3m.post-it-notes',
|
||||
'pya' => 'audio/vnd.ms-playready.media.pya',
|
||||
'pyo' => 'model/vnd.pytha.pyox',
|
||||
'pyox' => 'model/vnd.pytha.pyox',
|
||||
'pyv' => 'video/vnd.ms-playready.media.pyv',
|
||||
'qam' => 'application/vnd.epson.quickanime',
|
||||
'qbo' => 'application/vnd.intu.qbo',
|
||||
'qfx' => 'application/vnd.intu.qfx',
|
||||
'qps' => 'application/vnd.publishare-delta-tree',
|
||||
'qt' => 'video/quicktime',
|
||||
'qwd' => 'application/vnd.quark.quarkxpress',
|
||||
'qwt' => 'application/vnd.quark.quarkxpress',
|
||||
'qxb' => 'application/vnd.quark.quarkxpress',
|
||||
'qxd' => 'application/vnd.quark.quarkxpress',
|
||||
'qxl' => 'application/vnd.quark.quarkxpress',
|
||||
'qxt' => 'application/vnd.quark.quarkxpress',
|
||||
'ra' => 'audio/x-realaudio',
|
||||
'ram' => 'audio/x-pn-realaudio',
|
||||
'raml' => 'application/raml+yaml',
|
||||
'rapd' => 'application/route-apd+xml',
|
||||
'rar' => 'application/x-rar',
|
||||
'ras' => 'image/x-cmu-raster',
|
||||
'rcprofile' => 'application/vnd.ipunplugged.rcprofile',
|
||||
'rdf' => 'application/rdf+xml',
|
||||
'rdz' => 'application/vnd.data-vision.rdz',
|
||||
'relo' => 'application/p2p-overlay+xml',
|
||||
'rep' => 'application/vnd.businessobjects',
|
||||
'res' => 'application/x-dtbresource+xml',
|
||||
'rgb' => 'image/x-rgb',
|
||||
'rif' => 'application/reginfo+xml',
|
||||
'rip' => 'audio/vnd.rip',
|
||||
'ris' => 'application/x-research-info-systems',
|
||||
'rl' => 'application/resource-lists+xml',
|
||||
'rlc' => 'image/vnd.fujixerox.edmics-rlc',
|
||||
'rld' => 'application/resource-lists-diff+xml',
|
||||
'rm' => 'audio/x-pn-realaudio',
|
||||
'rmi' => 'audio/midi',
|
||||
'rmp' => 'audio/x-pn-realaudio-plugin',
|
||||
'rms' => 'application/vnd.jcp.javame.midlet-rms',
|
||||
'rmvb' => 'application/vnd.rn-realmedia-vbr',
|
||||
'rnc' => 'application/relax-ng-compact-syntax',
|
||||
'rng' => 'application/xml',
|
||||
'roa' => 'application/rpki-roa',
|
||||
'roff' => 'text/troff',
|
||||
'rp9' => 'application/vnd.cloanto.rp9',
|
||||
'rpm' => 'audio/x-pn-realaudio-plugin',
|
||||
'rpss' => 'application/vnd.nokia.radio-presets',
|
||||
'rpst' => 'application/vnd.nokia.radio-preset',
|
||||
'rq' => 'application/sparql-query',
|
||||
'rs' => 'application/rls-services+xml',
|
||||
'rsa' => 'application/x-pkcs7',
|
||||
'rsat' => 'application/atsc-rsat+xml',
|
||||
'rsd' => 'application/rsd+xml',
|
||||
'rsheet' => 'application/urc-ressheet+xml',
|
||||
'rss' => 'application/rss+xml',
|
||||
'rtf' => 'text/rtf',
|
||||
'rtx' => 'text/richtext',
|
||||
'run' => 'application/x-makeself',
|
||||
'rusd' => 'application/route-usd+xml',
|
||||
'rv' => 'video/vnd.rn-realvideo',
|
||||
's' => 'text/x-asm',
|
||||
's3m' => 'audio/s3m',
|
||||
'saf' => 'application/vnd.yamaha.smaf-audio',
|
||||
'sass' => 'text/x-sass',
|
||||
'sbml' => 'application/sbml+xml',
|
||||
'sc' => 'application/vnd.ibm.secure-container',
|
||||
'scd' => 'application/x-msschedule',
|
||||
'scm' => 'application/vnd.lotus-screencam',
|
||||
'scq' => 'application/scvp-cv-request',
|
||||
'scs' => 'application/scvp-cv-response',
|
||||
'scss' => 'text/x-scss',
|
||||
'scurl' => 'text/vnd.curl.scurl',
|
||||
'sda' => 'application/vnd.stardivision.draw',
|
||||
'sdc' => 'application/vnd.stardivision.calc',
|
||||
'sdd' => 'application/vnd.stardivision.impress',
|
||||
'sdkd' => 'application/vnd.solent.sdkm+xml',
|
||||
'sdkm' => 'application/vnd.solent.sdkm+xml',
|
||||
'sdp' => 'application/sdp',
|
||||
'sdw' => 'application/vnd.stardivision.writer',
|
||||
'sea' => 'application/octet-stream',
|
||||
'see' => 'application/vnd.seemail',
|
||||
'seed' => 'application/vnd.fdsn.seed',
|
||||
'sema' => 'application/vnd.sema',
|
||||
'semd' => 'application/vnd.semd',
|
||||
'semf' => 'application/vnd.semf',
|
||||
'senmlx' => 'application/senml+xml',
|
||||
'sensmlx' => 'application/sensml+xml',
|
||||
'ser' => 'application/java-serialized-object',
|
||||
'setpay' => 'application/set-payment-initiation',
|
||||
'setreg' => 'application/set-registration-initiation',
|
||||
'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data',
|
||||
'sfs' => 'application/vnd.spotfire.sfs',
|
||||
'sfv' => 'text/x-sfv',
|
||||
'sgi' => 'image/sgi',
|
||||
'sgl' => 'application/vnd.stardivision.writer-global',
|
||||
'sgm' => 'text/sgml',
|
||||
'sgml' => 'text/sgml',
|
||||
'sh' => 'application/x-sh',
|
||||
'shar' => 'application/x-shar',
|
||||
'shex' => 'text/shex',
|
||||
'shf' => 'application/shf+xml',
|
||||
'shtml' => 'text/html',
|
||||
'sid' => 'image/x-mrsid-image',
|
||||
'sieve' => 'application/sieve',
|
||||
'sig' => 'application/pgp-signature',
|
||||
'sil' => 'audio/silk',
|
||||
'silo' => 'model/mesh',
|
||||
'sis' => 'application/vnd.symbian.install',
|
||||
'sisx' => 'application/vnd.symbian.install',
|
||||
'sit' => 'application/x-stuffit',
|
||||
'sitx' => 'application/x-stuffitx',
|
||||
'siv' => 'application/sieve',
|
||||
'skd' => 'application/vnd.koan',
|
||||
'skm' => 'application/vnd.koan',
|
||||
'skp' => 'application/vnd.koan',
|
||||
'skt' => 'application/vnd.koan',
|
||||
'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12',
|
||||
'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
|
||||
'slim' => 'text/slim',
|
||||
'slm' => 'text/slim',
|
||||
'sls' => 'application/route-s-tsid+xml',
|
||||
'slt' => 'application/vnd.epson.salt',
|
||||
'sm' => 'application/vnd.stepmania.stepchart',
|
||||
'smf' => 'application/vnd.stardivision.math',
|
||||
'smi' => 'application/smil',
|
||||
'smil' => 'application/smil',
|
||||
'smv' => 'video/x-smv',
|
||||
'smzip' => 'application/vnd.stepmania.package',
|
||||
'snd' => 'audio/basic',
|
||||
'snf' => 'application/x-font-snf',
|
||||
'so' => 'application/octet-stream',
|
||||
'spc' => 'application/x-pkcs7-certificates',
|
||||
'spdx' => 'text/spdx',
|
||||
'spf' => 'application/vnd.yamaha.smaf-phrase',
|
||||
'spl' => 'application/x-futuresplash',
|
||||
'spot' => 'text/vnd.in3d.spot',
|
||||
'spp' => 'application/scvp-vp-response',
|
||||
'spq' => 'application/scvp-vp-request',
|
||||
'spx' => 'audio/ogg',
|
||||
'sql' => 'application/x-sql',
|
||||
'src' => 'application/x-wais-source',
|
||||
'srt' => 'application/x-subrip',
|
||||
'sru' => 'application/sru+xml',
|
||||
'srx' => 'application/sparql-results+xml',
|
||||
'ssdl' => 'application/ssdl+xml',
|
||||
'sse' => 'application/vnd.kodak-descriptor',
|
||||
'ssf' => 'application/vnd.epson.ssf',
|
||||
'ssml' => 'application/ssml+xml',
|
||||
'sst' => 'application/octet-stream',
|
||||
'st' => 'application/vnd.sailingtracker.track',
|
||||
'stc' => 'application/vnd.sun.xml.calc.template',
|
||||
'std' => 'application/vnd.sun.xml.draw.template',
|
||||
'step' => 'application/STEP',
|
||||
'stf' => 'application/vnd.wt.stf',
|
||||
'sti' => 'application/vnd.sun.xml.impress.template',
|
||||
'stk' => 'application/hyperstudio',
|
||||
'stl' => 'model/stl',
|
||||
'stp' => 'application/STEP',
|
||||
'stpx' => 'model/step+xml',
|
||||
'stpxz' => 'model/step-xml+zip',
|
||||
'stpz' => 'model/step+zip',
|
||||
'str' => 'application/vnd.pg.format',
|
||||
'stw' => 'application/vnd.sun.xml.writer.template',
|
||||
'styl' => 'text/stylus',
|
||||
'stylus' => 'text/stylus',
|
||||
'sub' => 'text/vnd.dvb.subtitle',
|
||||
'sus' => 'application/vnd.sus-calendar',
|
||||
'susp' => 'application/vnd.sus-calendar',
|
||||
'sv4cpio' => 'application/x-sv4cpio',
|
||||
'sv4crc' => 'application/x-sv4crc',
|
||||
'svc' => 'application/vnd.dvb.service',
|
||||
'svd' => 'application/vnd.svd',
|
||||
'svg' => 'image/svg+xml',
|
||||
'svgz' => 'image/svg+xml',
|
||||
'swa' => 'application/x-director',
|
||||
'swf' => 'application/x-shockwave-flash',
|
||||
'swi' => 'application/vnd.aristanetworks.swi',
|
||||
'swidtag' => 'application/swid+xml',
|
||||
'sxc' => 'application/vnd.sun.xml.calc',
|
||||
'sxd' => 'application/vnd.sun.xml.draw',
|
||||
'sxg' => 'application/vnd.sun.xml.writer.global',
|
||||
'sxi' => 'application/vnd.sun.xml.impress',
|
||||
'sxm' => 'application/vnd.sun.xml.math',
|
||||
'sxw' => 'application/vnd.sun.xml.writer',
|
||||
't' => 'text/troff',
|
||||
't3' => 'application/x-t3vm-image',
|
||||
't38' => 'image/t38',
|
||||
'taglet' => 'application/vnd.mynfc',
|
||||
'tao' => 'application/vnd.tao.intent-module-archive',
|
||||
'tap' => 'image/vnd.tencent.tap',
|
||||
'tar' => 'application/x-tar',
|
||||
'tcap' => 'application/vnd.3gpp2.tcap',
|
||||
'tcl' => 'application/x-tcl',
|
||||
'td' => 'application/urc-targetdesc+xml',
|
||||
'teacher' => 'application/vnd.smart.teacher',
|
||||
'tei' => 'application/tei+xml',
|
||||
'teicorpus' => 'application/tei+xml',
|
||||
'tex' => 'application/x-tex',
|
||||
'texi' => 'application/x-texinfo',
|
||||
'texinfo' => 'application/x-texinfo',
|
||||
'text' => 'text/plain',
|
||||
'tfi' => 'application/thraud+xml',
|
||||
'tfm' => 'application/x-tex-tfm',
|
||||
'tfx' => 'image/tiff-fx',
|
||||
'tga' => 'image/x-tga',
|
||||
'tgz' => 'application/x-tar',
|
||||
'thmx' => 'application/vnd.ms-officetheme',
|
||||
'tif' => 'image/tiff',
|
||||
'tiff' => 'image/tiff',
|
||||
'tk' => 'application/x-tcl',
|
||||
'tmo' => 'application/vnd.tmobile-livetv',
|
||||
'toml' => 'application/toml',
|
||||
'torrent' => 'application/x-bittorrent',
|
||||
'tpl' => 'application/vnd.groove-tool-template',
|
||||
'tpt' => 'application/vnd.trid.tpt',
|
||||
'tr' => 'text/troff',
|
||||
'tra' => 'application/vnd.trueapp',
|
||||
'trig' => 'application/trig',
|
||||
'trm' => 'application/x-msterminal',
|
||||
'ts' => 'video/mp2t',
|
||||
'tsd' => 'application/timestamped-data',
|
||||
'tsv' => 'text/tab-separated-values',
|
||||
'ttc' => 'font/collection',
|
||||
'ttf' => 'font/ttf',
|
||||
'ttl' => 'text/turtle',
|
||||
'ttml' => 'application/ttml+xml',
|
||||
'twd' => 'application/vnd.simtech-mindmapper',
|
||||
'twds' => 'application/vnd.simtech-mindmapper',
|
||||
'txd' => 'application/vnd.genomatix.tuxedo',
|
||||
'txf' => 'application/vnd.mobius.txf',
|
||||
'txt' => 'text/plain',
|
||||
'u3d' => 'model/u3d',
|
||||
'u8dsn' => 'message/global-delivery-status',
|
||||
'u8hdr' => 'message/global-headers',
|
||||
'u8mdn' => 'message/global-disposition-notification',
|
||||
'u8msg' => 'message/global',
|
||||
'u32' => 'application/x-authorware-bin',
|
||||
'ubj' => 'application/ubjson',
|
||||
'udeb' => 'application/x-debian-package',
|
||||
'ufd' => 'application/vnd.ufdl',
|
||||
'ufdl' => 'application/vnd.ufdl',
|
||||
'ulx' => 'application/x-glulx',
|
||||
'umj' => 'application/vnd.umajin',
|
||||
'unityweb' => 'application/vnd.unity',
|
||||
'uo' => 'application/vnd.uoml+xml',
|
||||
'uoml' => 'application/vnd.uoml+xml',
|
||||
'uri' => 'text/uri-list',
|
||||
'uris' => 'text/uri-list',
|
||||
'urls' => 'text/uri-list',
|
||||
'usda' => 'model/vnd.usda',
|
||||
'usdz' => 'model/vnd.usdz+zip',
|
||||
'ustar' => 'application/x-ustar',
|
||||
'utz' => 'application/vnd.uiq.theme',
|
||||
'uu' => 'text/x-uuencode',
|
||||
'uva' => 'audio/vnd.dece.audio',
|
||||
'uvd' => 'application/vnd.dece.data',
|
||||
'uvf' => 'application/vnd.dece.data',
|
||||
'uvg' => 'image/vnd.dece.graphic',
|
||||
'uvh' => 'video/vnd.dece.hd',
|
||||
'uvi' => 'image/vnd.dece.graphic',
|
||||
'uvm' => 'video/vnd.dece.mobile',
|
||||
'uvp' => 'video/vnd.dece.pd',
|
||||
'uvs' => 'video/vnd.dece.sd',
|
||||
'uvt' => 'application/vnd.dece.ttml+xml',
|
||||
'uvu' => 'video/vnd.uvvu.mp4',
|
||||
'uvv' => 'video/vnd.dece.video',
|
||||
'uvva' => 'audio/vnd.dece.audio',
|
||||
'uvvd' => 'application/vnd.dece.data',
|
||||
'uvvf' => 'application/vnd.dece.data',
|
||||
'uvvg' => 'image/vnd.dece.graphic',
|
||||
'uvvh' => 'video/vnd.dece.hd',
|
||||
'uvvi' => 'image/vnd.dece.graphic',
|
||||
'uvvm' => 'video/vnd.dece.mobile',
|
||||
'uvvp' => 'video/vnd.dece.pd',
|
||||
'uvvs' => 'video/vnd.dece.sd',
|
||||
'uvvt' => 'application/vnd.dece.ttml+xml',
|
||||
'uvvu' => 'video/vnd.uvvu.mp4',
|
||||
'uvvv' => 'video/vnd.dece.video',
|
||||
'uvvx' => 'application/vnd.dece.unspecified',
|
||||
'uvvz' => 'application/vnd.dece.zip',
|
||||
'uvx' => 'application/vnd.dece.unspecified',
|
||||
'uvz' => 'application/vnd.dece.zip',
|
||||
'vbox' => 'application/x-virtualbox-vbox',
|
||||
'vbox-extpack' => 'application/x-virtualbox-vbox-extpack',
|
||||
'vcard' => 'text/vcard',
|
||||
'vcd' => 'application/x-cdlink',
|
||||
'vcf' => 'text/x-vcard',
|
||||
'vcg' => 'application/vnd.groove-vcard',
|
||||
'vcs' => 'text/x-vcalendar',
|
||||
'vcx' => 'application/vnd.vcx',
|
||||
'vdi' => 'application/x-virtualbox-vdi',
|
||||
'vds' => 'model/vnd.sap.vds',
|
||||
'vhd' => 'application/x-virtualbox-vhd',
|
||||
'vis' => 'application/vnd.visionary',
|
||||
'viv' => 'video/vnd.vivo',
|
||||
'vlc' => 'application/videolan',
|
||||
'vmdk' => 'application/x-virtualbox-vmdk',
|
||||
'vob' => 'video/x-ms-vob',
|
||||
'vor' => 'application/vnd.stardivision.writer',
|
||||
'vox' => 'application/x-authorware-bin',
|
||||
'vrml' => 'model/vrml',
|
||||
'vsd' => 'application/vnd.visio',
|
||||
'vsf' => 'application/vnd.vsf',
|
||||
'vss' => 'application/vnd.visio',
|
||||
'vst' => 'application/vnd.visio',
|
||||
'vsw' => 'application/vnd.visio',
|
||||
'vtf' => 'image/vnd.valve.source.texture',
|
||||
'vtt' => 'text/vtt',
|
||||
'vtu' => 'model/vnd.vtu',
|
||||
'vxml' => 'application/voicexml+xml',
|
||||
'w3d' => 'application/x-director',
|
||||
'wad' => 'application/x-doom',
|
||||
'wadl' => 'application/vnd.sun.wadl+xml',
|
||||
'war' => 'application/java-archive',
|
||||
'wasm' => 'application/wasm',
|
||||
'wav' => 'audio/x-wav',
|
||||
'wax' => 'audio/x-ms-wax',
|
||||
'wbmp' => 'image/vnd.wap.wbmp',
|
||||
'wbs' => 'application/vnd.criticaltools.wbs+xml',
|
||||
'wbxml' => 'application/wbxml',
|
||||
'wcm' => 'application/vnd.ms-works',
|
||||
'wdb' => 'application/vnd.ms-works',
|
||||
'wdp' => 'image/vnd.ms-photo',
|
||||
'weba' => 'audio/webm',
|
||||
'webapp' => 'application/x-web-app-manifest+json',
|
||||
'webm' => 'video/webm',
|
||||
'webmanifest' => 'application/manifest+json',
|
||||
'webp' => 'image/webp',
|
||||
'wg' => 'application/vnd.pmi.widget',
|
||||
'wgsl' => 'text/wgsl',
|
||||
'wgt' => 'application/widget',
|
||||
'wif' => 'application/watcherinfo+xml',
|
||||
'wks' => 'application/vnd.ms-works',
|
||||
'wm' => 'video/x-ms-wm',
|
||||
'wma' => 'audio/x-ms-wma',
|
||||
'wmd' => 'application/x-ms-wmd',
|
||||
'wmf' => 'image/wmf',
|
||||
'wml' => 'text/vnd.wap.wml',
|
||||
'wmlc' => 'application/wmlc',
|
||||
'wmls' => 'text/vnd.wap.wmlscript',
|
||||
'wmlsc' => 'application/vnd.wap.wmlscriptc',
|
||||
'wmv' => 'video/x-ms-wmv',
|
||||
'wmx' => 'video/x-ms-wmx',
|
||||
'wmz' => 'application/x-msmetafile',
|
||||
'woff' => 'font/woff',
|
||||
'woff2' => 'font/woff2',
|
||||
'word' => 'application/msword',
|
||||
'wpd' => 'application/vnd.wordperfect',
|
||||
'wpl' => 'application/vnd.ms-wpl',
|
||||
'wps' => 'application/vnd.ms-works',
|
||||
'wqd' => 'application/vnd.wqd',
|
||||
'wri' => 'application/x-mswrite',
|
||||
'wrl' => 'model/vrml',
|
||||
'wsc' => 'message/vnd.wfa.wsc',
|
||||
'wsdl' => 'application/wsdl+xml',
|
||||
'wspolicy' => 'application/wspolicy+xml',
|
||||
'wtb' => 'application/vnd.webturbo',
|
||||
'wvx' => 'video/x-ms-wvx',
|
||||
'x3d' => 'model/x3d+xml',
|
||||
'x3db' => 'model/x3d+fastinfoset',
|
||||
'x3dbz' => 'model/x3d+binary',
|
||||
'x3dv' => 'model/x3d-vrml',
|
||||
'x3dvz' => 'model/x3d+vrml',
|
||||
'x3dz' => 'model/x3d+xml',
|
||||
'x32' => 'application/x-authorware-bin',
|
||||
'x_b' => 'model/vnd.parasolid.transmit.binary',
|
||||
'x_t' => 'model/vnd.parasolid.transmit.text',
|
||||
'xaml' => 'application/xaml+xml',
|
||||
'xap' => 'application/x-silverlight-app',
|
||||
'xar' => 'application/vnd.xara',
|
||||
'xav' => 'application/xcap-att+xml',
|
||||
'xbap' => 'application/x-ms-xbap',
|
||||
'xbd' => 'application/vnd.fujixerox.docuworks.binder',
|
||||
'xbm' => 'image/x-xbitmap',
|
||||
'xca' => 'application/xcap-caps+xml',
|
||||
'xcs' => 'application/calendar+xml',
|
||||
'xdf' => 'application/xcap-diff+xml',
|
||||
'xdm' => 'application/vnd.syncml.dm+xml',
|
||||
'xdp' => 'application/vnd.adobe.xdp+xml',
|
||||
'xdssc' => 'application/dssc+xml',
|
||||
'xdw' => 'application/vnd.fujixerox.docuworks',
|
||||
'xel' => 'application/xcap-el+xml',
|
||||
'xenc' => 'application/xenc+xml',
|
||||
'xer' => 'application/patch-ops-error+xml',
|
||||
'xfdf' => 'application/xfdf',
|
||||
'xfdl' => 'application/vnd.xfdl',
|
||||
'xht' => 'application/xhtml+xml',
|
||||
'xhtm' => 'application/vnd.pwg-xhtml-print+xml',
|
||||
'xhtml' => 'application/xhtml+xml',
|
||||
'xhvml' => 'application/xv+xml',
|
||||
'xif' => 'image/vnd.xiff',
|
||||
'xl' => 'application/excel',
|
||||
'xla' => 'application/vnd.ms-excel',
|
||||
'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
|
||||
'xlc' => 'application/vnd.ms-excel',
|
||||
'xlf' => 'application/xliff+xml',
|
||||
'xlm' => 'application/vnd.ms-excel',
|
||||
'xls' => 'application/vnd.ms-excel',
|
||||
'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
|
||||
'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
|
||||
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'xlt' => 'application/vnd.ms-excel',
|
||||
'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12',
|
||||
'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
|
||||
'xlw' => 'application/vnd.ms-excel',
|
||||
'xm' => 'audio/xm',
|
||||
'xml' => 'application/xml',
|
||||
'xns' => 'application/xcap-ns+xml',
|
||||
'xo' => 'application/vnd.olpc-sugar',
|
||||
'xop' => 'application/xop+xml',
|
||||
'xpi' => 'application/x-xpinstall',
|
||||
'xpl' => 'application/xproc+xml',
|
||||
'xpm' => 'image/x-xpixmap',
|
||||
'xpr' => 'application/vnd.is-xpr',
|
||||
'xps' => 'application/vnd.ms-xpsdocument',
|
||||
'xpw' => 'application/vnd.intercon.formnet',
|
||||
'xpx' => 'application/vnd.intercon.formnet',
|
||||
'xsd' => 'application/xml',
|
||||
'xsf' => 'application/prs.xsf+xml',
|
||||
'xsl' => 'application/xml',
|
||||
'xslt' => 'application/xslt+xml',
|
||||
'xsm' => 'application/vnd.syncml+xml',
|
||||
'xspf' => 'application/xspf+xml',
|
||||
'xul' => 'application/vnd.mozilla.xul+xml',
|
||||
'xvm' => 'application/xv+xml',
|
||||
'xvml' => 'application/xv+xml',
|
||||
'xwd' => 'image/x-xwindowdump',
|
||||
'xyz' => 'chemical/x-xyz',
|
||||
'xz' => 'application/x-xz',
|
||||
'yaml' => 'text/yaml',
|
||||
'yang' => 'application/yang',
|
||||
'yin' => 'application/yin+xml',
|
||||
'yml' => 'text/yaml',
|
||||
'ymp' => 'text/x-suse-ymp',
|
||||
'z' => 'application/x-compress',
|
||||
'z1' => 'application/x-zmachine',
|
||||
'z2' => 'application/x-zmachine',
|
||||
'z3' => 'application/x-zmachine',
|
||||
'z4' => 'application/x-zmachine',
|
||||
'z5' => 'application/x-zmachine',
|
||||
'z6' => 'application/x-zmachine',
|
||||
'z7' => 'application/x-zmachine',
|
||||
'z8' => 'application/x-zmachine',
|
||||
'zaz' => 'application/vnd.zzazz.deck+xml',
|
||||
'zip' => 'application/zip',
|
||||
'zir' => 'application/vnd.zul',
|
||||
'zirz' => 'application/vnd.zul',
|
||||
'zmm' => 'application/vnd.handheld-entertainment+xml',
|
||||
'zsh' => 'text/x-scriptzsh',
|
||||
];
|
||||
|
||||
/**
|
||||
* Determines the mimetype of a file by looking at its extension.
|
||||
*
|
||||
* @see https://raw.githubusercontent.com/jshttp/mime-db/master/db.json
|
||||
*/
|
||||
public static function fromFilename(string $filename): ?string
|
||||
{
|
||||
return self::fromExtension(pathinfo($filename, PATHINFO_EXTENSION));
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a file extensions to a mimetype.
|
||||
*
|
||||
* @see https://raw.githubusercontent.com/jshttp/mime-db/master/db.json
|
||||
*/
|
||||
public static function fromExtension(string $extension): ?string
|
||||
{
|
||||
return self::MIME_TYPES[strtolower($extension)] ?? null;
|
||||
}
|
||||
}
|
165
vendor/guzzlehttp/psr7/src/MultipartStream.php
vendored
Normal file
165
vendor/guzzlehttp/psr7/src/MultipartStream.php
vendored
Normal file
@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Psr7;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* Stream that when read returns bytes for a streaming multipart or
|
||||
* multipart/form-data stream.
|
||||
*/
|
||||
final class MultipartStream implements StreamInterface
|
||||
{
|
||||
use StreamDecoratorTrait;
|
||||
|
||||
/** @var string */
|
||||
private $boundary;
|
||||
|
||||
/** @var StreamInterface */
|
||||
private $stream;
|
||||
|
||||
/**
|
||||
* @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 = [], ?string $boundary = null)
|
||||
{
|
||||
$this->boundary = $boundary ?: bin2hex(random_bytes(20));
|
||||
$this->stream = $this->createStream($elements);
|
||||
}
|
||||
|
||||
public function getBoundary(): string
|
||||
{
|
||||
return $this->boundary;
|
||||
}
|
||||
|
||||
public function isWritable(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the headers needed before transferring the content of a POST file
|
||||
*
|
||||
* @param string[] $headers
|
||||
*/
|
||||
private function getHeaders(array $headers): string
|
||||
{
|
||||
$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 = []): StreamInterface
|
||||
{
|
||||
$stream = new AppendStream();
|
||||
|
||||
foreach ($elements as $element) {
|
||||
if (!is_array($element)) {
|
||||
throw new \UnexpectedValueException('An array is expected');
|
||||
}
|
||||
$this->addElement($stream, $element);
|
||||
}
|
||||
|
||||
// Add the trailing boundary with CRLF
|
||||
$stream->addStream(Utils::streamFor("--{$this->boundary}--\r\n"));
|
||||
|
||||
return $stream;
|
||||
}
|
||||
|
||||
private function addElement(AppendStream $stream, array $element): void
|
||||
{
|
||||
foreach (['contents', 'name'] as $key) {
|
||||
if (!array_key_exists($key, $element)) {
|
||||
throw new \InvalidArgumentException("A '{$key}' key is required");
|
||||
}
|
||||
}
|
||||
|
||||
$element['contents'] = Utils::streamFor($element['contents']);
|
||||
|
||||
if (empty($element['filename'])) {
|
||||
$uri = $element['contents']->getMetadata('uri');
|
||||
if ($uri && \is_string($uri) && \substr($uri, 0, 6) !== 'php://' && \substr($uri, 0, 7) !== 'data://') {
|
||||
$element['filename'] = $uri;
|
||||
}
|
||||
}
|
||||
|
||||
[$body, $headers] = $this->createElement(
|
||||
$element['name'],
|
||||
$element['contents'],
|
||||
$element['filename'] ?? null,
|
||||
$element['headers'] ?? []
|
||||
);
|
||||
|
||||
$stream->addStream(Utils::streamFor($this->getHeaders($headers)));
|
||||
$stream->addStream($body);
|
||||
$stream->addStream(Utils::streamFor("\r\n"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $headers
|
||||
*
|
||||
* @return array{0: StreamInterface, 1: string[]}
|
||||
*/
|
||||
private function createElement(string $name, StreamInterface $stream, ?string $filename, array $headers): array
|
||||
{
|
||||
// Set a default content-disposition header if one was no provided
|
||||
$disposition = self::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 = self::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 = self::getHeader($headers, 'content-type');
|
||||
if (!$type && ($filename === '0' || $filename)) {
|
||||
$headers['Content-Type'] = MimeType::fromFilename($filename) ?? 'application/octet-stream';
|
||||
}
|
||||
|
||||
return [$stream, $headers];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $headers
|
||||
*/
|
||||
private static function getHeader(array $headers, string $key): ?string
|
||||
{
|
||||
$lowercaseHeader = strtolower($key);
|
||||
foreach ($headers as $k => $v) {
|
||||
if (strtolower((string) $k) === $lowercaseHeader) {
|
||||
return $v;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
28
vendor/guzzlehttp/psr7/src/NoSeekStream.php
vendored
Normal file
28
vendor/guzzlehttp/psr7/src/NoSeekStream.php
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Psr7;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* Stream decorator that prevents a stream from being seeked.
|
||||
*/
|
||||
final class NoSeekStream implements StreamInterface
|
||||
{
|
||||
use StreamDecoratorTrait;
|
||||
|
||||
/** @var StreamInterface */
|
||||
private $stream;
|
||||
|
||||
public function seek($offset, $whence = SEEK_SET): void
|
||||
{
|
||||
throw new \RuntimeException('Cannot seek a NoSeekStream');
|
||||
}
|
||||
|
||||
public function isSeekable(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
179
vendor/guzzlehttp/psr7/src/PumpStream.php
vendored
Normal file
179
vendor/guzzlehttp/psr7/src/PumpStream.php
vendored
Normal file
@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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.
|
||||
*/
|
||||
final class PumpStream implements StreamInterface
|
||||
{
|
||||
/** @var callable(int): (string|false|null)|null */
|
||||
private $source;
|
||||
|
||||
/** @var int|null */
|
||||
private $size;
|
||||
|
||||
/** @var int */
|
||||
private $tellPos = 0;
|
||||
|
||||
/** @var array */
|
||||
private $metadata;
|
||||
|
||||
/** @var BufferStream */
|
||||
private $buffer;
|
||||
|
||||
/**
|
||||
* @param callable(int): (string|false|null) $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|null on error
|
||||
* or EOF.
|
||||
* @param array{size?: int, metadata?: 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 = $options['size'] ?? null;
|
||||
$this->metadata = $options['metadata'] ?? [];
|
||||
$this->buffer = new BufferStream();
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
try {
|
||||
return Utils::copyToString($this);
|
||||
} catch (\Throwable $e) {
|
||||
if (\PHP_VERSION_ID >= 70400) {
|
||||
throw $e;
|
||||
}
|
||||
trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
public function close(): void
|
||||
{
|
||||
$this->detach();
|
||||
}
|
||||
|
||||
public function detach()
|
||||
{
|
||||
$this->tellPos = 0;
|
||||
$this->source = null;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getSize(): ?int
|
||||
{
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
public function tell(): int
|
||||
{
|
||||
return $this->tellPos;
|
||||
}
|
||||
|
||||
public function eof(): bool
|
||||
{
|
||||
return $this->source === null;
|
||||
}
|
||||
|
||||
public function isSeekable(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function rewind(): void
|
||||
{
|
||||
$this->seek(0);
|
||||
}
|
||||
|
||||
public function seek($offset, $whence = SEEK_SET): void
|
||||
{
|
||||
throw new \RuntimeException('Cannot seek a PumpStream');
|
||||
}
|
||||
|
||||
public function isWritable(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function write($string): int
|
||||
{
|
||||
throw new \RuntimeException('Cannot write to a PumpStream');
|
||||
}
|
||||
|
||||
public function isReadable(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function read($length): string
|
||||
{
|
||||
$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(): string
|
||||
{
|
||||
$result = '';
|
||||
while (!$this->eof()) {
|
||||
$result .= $this->read(1000000);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getMetadata($key = null)
|
||||
{
|
||||
if (!$key) {
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
return $this->metadata[$key] ?? null;
|
||||
}
|
||||
|
||||
private function pump(int $length): void
|
||||
{
|
||||
if ($this->source !== null) {
|
||||
do {
|
||||
$data = ($this->source)($length);
|
||||
if ($data === false || $data === null) {
|
||||
$this->source = null;
|
||||
|
||||
return;
|
||||
}
|
||||
$this->buffer->write($data);
|
||||
$length -= strlen($data);
|
||||
} while ($length > 0);
|
||||
}
|
||||
}
|
||||
}
|
118
vendor/guzzlehttp/psr7/src/Query.php
vendored
Normal file
118
vendor/guzzlehttp/psr7/src/Query.php
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Psr7;
|
||||
|
||||
final class Query
|
||||
{
|
||||
/**
|
||||
* 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 int|bool $urlEncoding How the query string is encoded
|
||||
*/
|
||||
public static function parse(string $str, $urlEncoding = true): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
if ($str === '') {
|
||||
return $result;
|
||||
}
|
||||
|
||||
if ($urlEncoding === true) {
|
||||
$decoder = function ($value) {
|
||||
return rawurldecode(str_replace('+', ' ', (string) $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 (!array_key_exists($key, $result)) {
|
||||
$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()` 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.
|
||||
* @param bool $treatBoolsAsInts Set to true to encode as 0/1, and
|
||||
* false as false/true.
|
||||
*/
|
||||
public static function build(array $params, $encoding = PHP_QUERY_RFC3986, bool $treatBoolsAsInts = true): string
|
||||
{
|
||||
if (!$params) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($encoding === false) {
|
||||
$encoder = function (string $str): string {
|
||||
return $str;
|
||||
};
|
||||
} elseif ($encoding === PHP_QUERY_RFC3986) {
|
||||
$encoder = 'rawurlencode';
|
||||
} elseif ($encoding === PHP_QUERY_RFC1738) {
|
||||
$encoder = 'urlencode';
|
||||
} else {
|
||||
throw new \InvalidArgumentException('Invalid type');
|
||||
}
|
||||
|
||||
$castBool = $treatBoolsAsInts ? static function ($v) { return (int) $v; } : static function ($v) { return $v ? 'true' : 'false'; };
|
||||
|
||||
$qs = '';
|
||||
foreach ($params as $k => $v) {
|
||||
$k = $encoder((string) $k);
|
||||
if (!is_array($v)) {
|
||||
$qs .= $k;
|
||||
$v = is_bool($v) ? $castBool($v) : $v;
|
||||
if ($v !== null) {
|
||||
$qs .= '='.$encoder((string) $v);
|
||||
}
|
||||
$qs .= '&';
|
||||
} else {
|
||||
foreach ($v as $vv) {
|
||||
$qs .= $k;
|
||||
$vv = is_bool($vv) ? $castBool($vv) : $vv;
|
||||
if ($vv !== null) {
|
||||
$qs .= '='.$encoder((string) $vv);
|
||||
}
|
||||
$qs .= '&';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $qs ? (string) substr($qs, 0, -1) : '';
|
||||
}
|
||||
}
|
159
vendor/guzzlehttp/psr7/src/Request.php
vendored
Normal file
159
vendor/guzzlehttp/psr7/src/Request.php
vendored
Normal file
@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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 string|null */
|
||||
private $requestTarget;
|
||||
|
||||
/** @var UriInterface */
|
||||
private $uri;
|
||||
|
||||
/**
|
||||
* @param string $method HTTP method
|
||||
* @param string|UriInterface $uri URI
|
||||
* @param (string|string[])[] $headers Request headers
|
||||
* @param string|resource|StreamInterface|null $body Request body
|
||||
* @param string $version Protocol version
|
||||
*/
|
||||
public function __construct(
|
||||
string $method,
|
||||
$uri,
|
||||
array $headers = [],
|
||||
$body = null,
|
||||
string $version = '1.1'
|
||||
) {
|
||||
$this->assertMethod($method);
|
||||
if (!($uri instanceof UriInterface)) {
|
||||
$uri = new Uri($uri);
|
||||
}
|
||||
|
||||
$this->method = strtoupper($method);
|
||||
$this->uri = $uri;
|
||||
$this->setHeaders($headers);
|
||||
$this->protocol = $version;
|
||||
|
||||
if (!isset($this->headerNames['host'])) {
|
||||
$this->updateHostFromUri();
|
||||
}
|
||||
|
||||
if ($body !== '' && $body !== null) {
|
||||
$this->stream = Utils::streamFor($body);
|
||||
}
|
||||
}
|
||||
|
||||
public function getRequestTarget(): string
|
||||
{
|
||||
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): RequestInterface
|
||||
{
|
||||
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(): string
|
||||
{
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
public function withMethod($method): RequestInterface
|
||||
{
|
||||
$this->assertMethod($method);
|
||||
$new = clone $this;
|
||||
$new->method = strtoupper($method);
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
public function getUri(): UriInterface
|
||||
{
|
||||
return $this->uri;
|
||||
}
|
||||
|
||||
public function withUri(UriInterface $uri, $preserveHost = false): RequestInterface
|
||||
{
|
||||
if ($uri === $this->uri) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$new = clone $this;
|
||||
$new->uri = $uri;
|
||||
|
||||
if (!$preserveHost || !isset($this->headerNames['host'])) {
|
||||
$new->updateHostFromUri();
|
||||
}
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
private function updateHostFromUri(): void
|
||||
{
|
||||
$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: https://datatracker.ietf.org/doc/html/rfc7230#section-5.4
|
||||
$this->headers = [$header => [$host]] + $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $method
|
||||
*/
|
||||
private function assertMethod($method): void
|
||||
{
|
||||
if (!is_string($method) || $method === '') {
|
||||
throw new InvalidArgumentException('Method must be a non-empty string.');
|
||||
}
|
||||
}
|
||||
}
|
161
vendor/guzzlehttp/psr7/src/Response.php
vendored
Normal file
161
vendor/guzzlehttp/psr7/src/Response.php
vendored
Normal file
@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Psr7;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* PSR-7 response implementation.
|
||||
*/
|
||||
class Response implements ResponseInterface
|
||||
{
|
||||
use MessageTrait;
|
||||
|
||||
/** Map of standard HTTP status code/reason phrases */
|
||||
private const 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',
|
||||
308 => 'Permanent 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',
|
||||
510 => 'Not Extended',
|
||||
511 => 'Network Authentication Required',
|
||||
];
|
||||
|
||||
/** @var string */
|
||||
private $reasonPhrase;
|
||||
|
||||
/** @var int */
|
||||
private $statusCode;
|
||||
|
||||
/**
|
||||
* @param int $status Status code
|
||||
* @param (string|string[])[] $headers Response headers
|
||||
* @param string|resource|StreamInterface|null $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(
|
||||
int $status = 200,
|
||||
array $headers = [],
|
||||
$body = null,
|
||||
string $version = '1.1',
|
||||
?string $reason = null
|
||||
) {
|
||||
$this->assertStatusCodeRange($status);
|
||||
|
||||
$this->statusCode = $status;
|
||||
|
||||
if ($body !== '' && $body !== null) {
|
||||
$this->stream = Utils::streamFor($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(): int
|
||||
{
|
||||
return $this->statusCode;
|
||||
}
|
||||
|
||||
public function getReasonPhrase(): string
|
||||
{
|
||||
return $this->reasonPhrase;
|
||||
}
|
||||
|
||||
public function withStatus($code, $reasonPhrase = ''): ResponseInterface
|
||||
{
|
||||
$this->assertStatusCodeIsInteger($code);
|
||||
$code = (int) $code;
|
||||
$this->assertStatusCodeRange($code);
|
||||
|
||||
$new = clone $this;
|
||||
$new->statusCode = $code;
|
||||
if ($reasonPhrase == '' && isset(self::PHRASES[$new->statusCode])) {
|
||||
$reasonPhrase = self::PHRASES[$new->statusCode];
|
||||
}
|
||||
$new->reasonPhrase = (string) $reasonPhrase;
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $statusCode
|
||||
*/
|
||||
private function assertStatusCodeIsInteger($statusCode): void
|
||||
{
|
||||
if (filter_var($statusCode, FILTER_VALIDATE_INT) === false) {
|
||||
throw new \InvalidArgumentException('Status code must be an integer value.');
|
||||
}
|
||||
}
|
||||
|
||||
private function assertStatusCodeRange(int $statusCode): void
|
||||
{
|
||||
if ($statusCode < 100 || $statusCode >= 600) {
|
||||
throw new \InvalidArgumentException('Status code must be an integer value between 1xx and 5xx.');
|
||||
}
|
||||
}
|
||||
}
|
23
vendor/guzzlehttp/psr7/src/Rfc7230.php
vendored
Normal file
23
vendor/guzzlehttp/psr7/src/Rfc7230.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Psr7;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Rfc7230
|
||||
{
|
||||
/**
|
||||
* Header related regular expressions (based on amphp/http package)
|
||||
*
|
||||
* Note: header delimiter (\r\n) is modified to \r?\n to accept line feed only delimiters for BC reasons.
|
||||
*
|
||||
* @see https://github.com/amphp/http/blob/v1.0.1/src/Rfc7230.php#L12-L15
|
||||
*
|
||||
* @license https://github.com/amphp/http/blob/v1.0.1/LICENSE
|
||||
*/
|
||||
public const HEADER_REGEX = "(^([^()<>@,;:\\\"/[\]?={}\x01-\x20\x7F]++):[ \t]*+((?:[ \t]*+[\x21-\x7E\x80-\xFF]++)*+)[ \t]*+\r?\n)m";
|
||||
public const HEADER_FOLD_REGEX = "(\r?\n[ \t]++)";
|
||||
}
|
340
vendor/guzzlehttp/psr7/src/ServerRequest.php
vendored
Normal file
340
vendor/guzzlehttp/psr7/src/ServerRequest.php
vendored
Normal file
@ -0,0 +1,340 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Psr7;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
|
||||
/**
|
||||
* Server-side HTTP request
|
||||
*
|
||||
* Extends the Request definition to add methods for accessing incoming data,
|
||||
* specifically server parameters, cookies, matched path parameters, query
|
||||
* string arguments, body parameters, and upload file information.
|
||||
*
|
||||
* "Attributes" are discovered via decomposing the request (and usually
|
||||
* specifically the URI path), and typically will be injected by the application.
|
||||
*
|
||||
* Requests are considered immutable; all methods that might change state are
|
||||
* implemented such that they retain the internal state of the current
|
||||
* message and return a new instance that contains the changed state.
|
||||
*/
|
||||
class ServerRequest extends Request implements ServerRequestInterface
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $attributes = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $cookieParams = [];
|
||||
|
||||
/**
|
||||
* @var array|object|null
|
||||
*/
|
||||
private $parsedBody;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $queryParams = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $serverParams;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $uploadedFiles = [];
|
||||
|
||||
/**
|
||||
* @param string $method HTTP method
|
||||
* @param string|UriInterface $uri URI
|
||||
* @param (string|string[])[] $headers Request headers
|
||||
* @param string|resource|StreamInterface|null $body Request body
|
||||
* @param string $version Protocol version
|
||||
* @param array $serverParams Typically the $_SERVER superglobal
|
||||
*/
|
||||
public function __construct(
|
||||
string $method,
|
||||
$uri,
|
||||
array $headers = [],
|
||||
$body = null,
|
||||
string $version = '1.1',
|
||||
array $serverParams = []
|
||||
) {
|
||||
$this->serverParams = $serverParams;
|
||||
|
||||
parent::__construct($method, $uri, $headers, $body, $version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an UploadedFile instance array.
|
||||
*
|
||||
* @param array $files An array which respect $_FILES structure
|
||||
*
|
||||
* @throws InvalidArgumentException for unrecognized values
|
||||
*/
|
||||
public static function normalizeFiles(array $files): array
|
||||
{
|
||||
$normalized = [];
|
||||
|
||||
foreach ($files as $key => $value) {
|
||||
if ($value instanceof UploadedFileInterface) {
|
||||
$normalized[$key] = $value;
|
||||
} elseif (is_array($value) && isset($value['tmp_name'])) {
|
||||
$normalized[$key] = self::createUploadedFileFromSpec($value);
|
||||
} elseif (is_array($value)) {
|
||||
$normalized[$key] = self::normalizeFiles($value);
|
||||
continue;
|
||||
} else {
|
||||
throw new InvalidArgumentException('Invalid value in files specification');
|
||||
}
|
||||
}
|
||||
|
||||
return $normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return an UploadedFile instance from a $_FILES specification.
|
||||
*
|
||||
* If the specification represents an array of values, this method will
|
||||
* delegate to normalizeNestedFileSpec() and return that return value.
|
||||
*
|
||||
* @param array $value $_FILES struct
|
||||
*
|
||||
* @return UploadedFileInterface|UploadedFileInterface[]
|
||||
*/
|
||||
private static function createUploadedFileFromSpec(array $value)
|
||||
{
|
||||
if (is_array($value['tmp_name'])) {
|
||||
return self::normalizeNestedFileSpec($value);
|
||||
}
|
||||
|
||||
return new UploadedFile(
|
||||
$value['tmp_name'],
|
||||
(int) $value['size'],
|
||||
(int) $value['error'],
|
||||
$value['name'],
|
||||
$value['type']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize an array of file specifications.
|
||||
*
|
||||
* Loops through all nested files and returns a normalized array of
|
||||
* UploadedFileInterface instances.
|
||||
*
|
||||
* @return UploadedFileInterface[]
|
||||
*/
|
||||
private static function normalizeNestedFileSpec(array $files = []): array
|
||||
{
|
||||
$normalizedFiles = [];
|
||||
|
||||
foreach (array_keys($files['tmp_name']) as $key) {
|
||||
$spec = [
|
||||
'tmp_name' => $files['tmp_name'][$key],
|
||||
'size' => $files['size'][$key] ?? null,
|
||||
'error' => $files['error'][$key] ?? null,
|
||||
'name' => $files['name'][$key] ?? null,
|
||||
'type' => $files['type'][$key] ?? null,
|
||||
];
|
||||
$normalizedFiles[$key] = self::createUploadedFileFromSpec($spec);
|
||||
}
|
||||
|
||||
return $normalizedFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a ServerRequest populated with superglobals:
|
||||
* $_GET
|
||||
* $_POST
|
||||
* $_COOKIE
|
||||
* $_FILES
|
||||
* $_SERVER
|
||||
*/
|
||||
public static function fromGlobals(): ServerRequestInterface
|
||||
{
|
||||
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
|
||||
$headers = getallheaders();
|
||||
$uri = self::getUriFromGlobals();
|
||||
$body = new CachingStream(new LazyOpenStream('php://input', 'r+'));
|
||||
$protocol = isset($_SERVER['SERVER_PROTOCOL']) ? str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']) : '1.1';
|
||||
|
||||
$serverRequest = new ServerRequest($method, $uri, $headers, $body, $protocol, $_SERVER);
|
||||
|
||||
return $serverRequest
|
||||
->withCookieParams($_COOKIE)
|
||||
->withQueryParams($_GET)
|
||||
->withParsedBody($_POST)
|
||||
->withUploadedFiles(self::normalizeFiles($_FILES));
|
||||
}
|
||||
|
||||
private static function extractHostAndPortFromAuthority(string $authority): array
|
||||
{
|
||||
$uri = 'http://'.$authority;
|
||||
$parts = parse_url($uri);
|
||||
if (false === $parts) {
|
||||
return [null, null];
|
||||
}
|
||||
|
||||
$host = $parts['host'] ?? null;
|
||||
$port = $parts['port'] ?? null;
|
||||
|
||||
return [$host, $port];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Uri populated with values from $_SERVER.
|
||||
*/
|
||||
public static function getUriFromGlobals(): UriInterface
|
||||
{
|
||||
$uri = new Uri('');
|
||||
|
||||
$uri = $uri->withScheme(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http');
|
||||
|
||||
$hasPort = false;
|
||||
if (isset($_SERVER['HTTP_HOST'])) {
|
||||
[$host, $port] = self::extractHostAndPortFromAuthority($_SERVER['HTTP_HOST']);
|
||||
if ($host !== null) {
|
||||
$uri = $uri->withHost($host);
|
||||
}
|
||||
|
||||
if ($port !== null) {
|
||||
$hasPort = true;
|
||||
$uri = $uri->withPort($port);
|
||||
}
|
||||
} elseif (isset($_SERVER['SERVER_NAME'])) {
|
||||
$uri = $uri->withHost($_SERVER['SERVER_NAME']);
|
||||
} elseif (isset($_SERVER['SERVER_ADDR'])) {
|
||||
$uri = $uri->withHost($_SERVER['SERVER_ADDR']);
|
||||
}
|
||||
|
||||
if (!$hasPort && isset($_SERVER['SERVER_PORT'])) {
|
||||
$uri = $uri->withPort($_SERVER['SERVER_PORT']);
|
||||
}
|
||||
|
||||
$hasQuery = false;
|
||||
if (isset($_SERVER['REQUEST_URI'])) {
|
||||
$requestUriParts = explode('?', $_SERVER['REQUEST_URI'], 2);
|
||||
$uri = $uri->withPath($requestUriParts[0]);
|
||||
if (isset($requestUriParts[1])) {
|
||||
$hasQuery = true;
|
||||
$uri = $uri->withQuery($requestUriParts[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$hasQuery && isset($_SERVER['QUERY_STRING'])) {
|
||||
$uri = $uri->withQuery($_SERVER['QUERY_STRING']);
|
||||
}
|
||||
|
||||
return $uri;
|
||||
}
|
||||
|
||||
public function getServerParams(): array
|
||||
{
|
||||
return $this->serverParams;
|
||||
}
|
||||
|
||||
public function getUploadedFiles(): array
|
||||
{
|
||||
return $this->uploadedFiles;
|
||||
}
|
||||
|
||||
public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface
|
||||
{
|
||||
$new = clone $this;
|
||||
$new->uploadedFiles = $uploadedFiles;
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
public function getCookieParams(): array
|
||||
{
|
||||
return $this->cookieParams;
|
||||
}
|
||||
|
||||
public function withCookieParams(array $cookies): ServerRequestInterface
|
||||
{
|
||||
$new = clone $this;
|
||||
$new->cookieParams = $cookies;
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
public function getQueryParams(): array
|
||||
{
|
||||
return $this->queryParams;
|
||||
}
|
||||
|
||||
public function withQueryParams(array $query): ServerRequestInterface
|
||||
{
|
||||
$new = clone $this;
|
||||
$new->queryParams = $query;
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|object|null
|
||||
*/
|
||||
public function getParsedBody()
|
||||
{
|
||||
return $this->parsedBody;
|
||||
}
|
||||
|
||||
public function withParsedBody($data): ServerRequestInterface
|
||||
{
|
||||
$new = clone $this;
|
||||
$new->parsedBody = $data;
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
public function getAttributes(): array
|
||||
{
|
||||
return $this->attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAttribute($attribute, $default = null)
|
||||
{
|
||||
if (false === array_key_exists($attribute, $this->attributes)) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return $this->attributes[$attribute];
|
||||
}
|
||||
|
||||
public function withAttribute($attribute, $value): ServerRequestInterface
|
||||
{
|
||||
$new = clone $this;
|
||||
$new->attributes[$attribute] = $value;
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
public function withoutAttribute($attribute): ServerRequestInterface
|
||||
{
|
||||
if (false === array_key_exists($attribute, $this->attributes)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$new = clone $this;
|
||||
unset($new->attributes[$attribute]);
|
||||
|
||||
return $new;
|
||||
}
|
||||
}
|
283
vendor/guzzlehttp/psr7/src/Stream.php
vendored
Normal file
283
vendor/guzzlehttp/psr7/src/Stream.php
vendored
Normal file
@ -0,0 +1,283 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Psr7;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* PHP stream implementation.
|
||||
*/
|
||||
class Stream implements StreamInterface
|
||||
{
|
||||
/**
|
||||
* @see https://www.php.net/manual/en/function.fopen.php
|
||||
* @see https://www.php.net/manual/en/function.gzopen.php
|
||||
*/
|
||||
private const READABLE_MODES = '/r|a\+|ab\+|w\+|wb\+|x\+|xb\+|c\+|cb\+/';
|
||||
private const WRITABLE_MODES = '/a|w|r\+|rb\+|rw|x|c/';
|
||||
|
||||
/** @var resource */
|
||||
private $stream;
|
||||
/** @var int|null */
|
||||
private $size;
|
||||
/** @var bool */
|
||||
private $seekable;
|
||||
/** @var bool */
|
||||
private $readable;
|
||||
/** @var bool */
|
||||
private $writable;
|
||||
/** @var string|null */
|
||||
private $uri;
|
||||
/** @var mixed[] */
|
||||
private $customMetadata;
|
||||
|
||||
/**
|
||||
* 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{size?: int, metadata?: array} $options Associative array of options.
|
||||
*
|
||||
* @throws \InvalidArgumentException if the stream is not a stream resource
|
||||
*/
|
||||
public function __construct($stream, array $options = [])
|
||||
{
|
||||
if (!is_resource($stream)) {
|
||||
throw new \InvalidArgumentException('Stream must be a resource');
|
||||
}
|
||||
|
||||
if (isset($options['size'])) {
|
||||
$this->size = $options['size'];
|
||||
}
|
||||
|
||||
$this->customMetadata = $options['metadata'] ?? [];
|
||||
$this->stream = $stream;
|
||||
$meta = stream_get_meta_data($this->stream);
|
||||
$this->seekable = $meta['seekable'];
|
||||
$this->readable = (bool) preg_match(self::READABLE_MODES, $meta['mode']);
|
||||
$this->writable = (bool) preg_match(self::WRITABLE_MODES, $meta['mode']);
|
||||
$this->uri = $this->getMetadata('uri');
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the stream when the destructed
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->close();
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
try {
|
||||
if ($this->isSeekable()) {
|
||||
$this->seek(0);
|
||||
}
|
||||
|
||||
return $this->getContents();
|
||||
} catch (\Throwable $e) {
|
||||
if (\PHP_VERSION_ID >= 70400) {
|
||||
throw $e;
|
||||
}
|
||||
trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
public function getContents(): string
|
||||
{
|
||||
if (!isset($this->stream)) {
|
||||
throw new \RuntimeException('Stream is detached');
|
||||
}
|
||||
|
||||
if (!$this->readable) {
|
||||
throw new \RuntimeException('Cannot read from non-readable stream');
|
||||
}
|
||||
|
||||
return Utils::tryGetContents($this->stream);
|
||||
}
|
||||
|
||||
public function close(): void
|
||||
{
|
||||
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(): ?int
|
||||
{
|
||||
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 (is_array($stats) && isset($stats['size'])) {
|
||||
$this->size = $stats['size'];
|
||||
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function isReadable(): bool
|
||||
{
|
||||
return $this->readable;
|
||||
}
|
||||
|
||||
public function isWritable(): bool
|
||||
{
|
||||
return $this->writable;
|
||||
}
|
||||
|
||||
public function isSeekable(): bool
|
||||
{
|
||||
return $this->seekable;
|
||||
}
|
||||
|
||||
public function eof(): bool
|
||||
{
|
||||
if (!isset($this->stream)) {
|
||||
throw new \RuntimeException('Stream is detached');
|
||||
}
|
||||
|
||||
return feof($this->stream);
|
||||
}
|
||||
|
||||
public function tell(): int
|
||||
{
|
||||
if (!isset($this->stream)) {
|
||||
throw new \RuntimeException('Stream is detached');
|
||||
}
|
||||
|
||||
$result = ftell($this->stream);
|
||||
|
||||
if ($result === false) {
|
||||
throw new \RuntimeException('Unable to determine stream position');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function rewind(): void
|
||||
{
|
||||
$this->seek(0);
|
||||
}
|
||||
|
||||
public function seek($offset, $whence = SEEK_SET): void
|
||||
{
|
||||
$whence = (int) $whence;
|
||||
|
||||
if (!isset($this->stream)) {
|
||||
throw new \RuntimeException('Stream is detached');
|
||||
}
|
||||
if (!$this->seekable) {
|
||||
throw new \RuntimeException('Stream is not seekable');
|
||||
}
|
||||
if (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): string
|
||||
{
|
||||
if (!isset($this->stream)) {
|
||||
throw new \RuntimeException('Stream is detached');
|
||||
}
|
||||
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 '';
|
||||
}
|
||||
|
||||
try {
|
||||
$string = fread($this->stream, $length);
|
||||
} catch (\Exception $e) {
|
||||
throw new \RuntimeException('Unable to read from stream', 0, $e);
|
||||
}
|
||||
|
||||
if (false === $string) {
|
||||
throw new \RuntimeException('Unable to read from stream');
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
public function write($string): int
|
||||
{
|
||||
if (!isset($this->stream)) {
|
||||
throw new \RuntimeException('Stream is detached');
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
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 $meta[$key] ?? null;
|
||||
}
|
||||
}
|
156
vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php
vendored
Normal file
156
vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php
vendored
Normal file
@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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).
|
||||
*
|
||||
* @return StreamInterface
|
||||
*/
|
||||
public function __get(string $name)
|
||||
{
|
||||
if ($name === 'stream') {
|
||||
$this->stream = $this->createStream();
|
||||
|
||||
return $this->stream;
|
||||
}
|
||||
|
||||
throw new \UnexpectedValueException("$name not found on class");
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
try {
|
||||
if ($this->isSeekable()) {
|
||||
$this->seek(0);
|
||||
}
|
||||
|
||||
return $this->getContents();
|
||||
} catch (\Throwable $e) {
|
||||
if (\PHP_VERSION_ID >= 70400) {
|
||||
throw $e;
|
||||
}
|
||||
trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
public function getContents(): string
|
||||
{
|
||||
return Utils::copyToString($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow decorators to implement custom methods
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $method, array $args)
|
||||
{
|
||||
/** @var callable $callable */
|
||||
$callable = [$this->stream, $method];
|
||||
$result = ($callable)(...$args);
|
||||
|
||||
// Always return the wrapped object if the result is a return $this
|
||||
return $result === $this->stream ? $this : $result;
|
||||
}
|
||||
|
||||
public function close(): void
|
||||
{
|
||||
$this->stream->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getMetadata($key = null)
|
||||
{
|
||||
return $this->stream->getMetadata($key);
|
||||
}
|
||||
|
||||
public function detach()
|
||||
{
|
||||
return $this->stream->detach();
|
||||
}
|
||||
|
||||
public function getSize(): ?int
|
||||
{
|
||||
return $this->stream->getSize();
|
||||
}
|
||||
|
||||
public function eof(): bool
|
||||
{
|
||||
return $this->stream->eof();
|
||||
}
|
||||
|
||||
public function tell(): int
|
||||
{
|
||||
return $this->stream->tell();
|
||||
}
|
||||
|
||||
public function isReadable(): bool
|
||||
{
|
||||
return $this->stream->isReadable();
|
||||
}
|
||||
|
||||
public function isWritable(): bool
|
||||
{
|
||||
return $this->stream->isWritable();
|
||||
}
|
||||
|
||||
public function isSeekable(): bool
|
||||
{
|
||||
return $this->stream->isSeekable();
|
||||
}
|
||||
|
||||
public function rewind(): void
|
||||
{
|
||||
$this->seek(0);
|
||||
}
|
||||
|
||||
public function seek($offset, $whence = SEEK_SET): void
|
||||
{
|
||||
$this->stream->seek($offset, $whence);
|
||||
}
|
||||
|
||||
public function read($length): string
|
||||
{
|
||||
return $this->stream->read($length);
|
||||
}
|
||||
|
||||
public function write($string): int
|
||||
{
|
||||
return $this->stream->write($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement in subclasses to dynamically create streams when requested.
|
||||
*
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
protected function createStream(): StreamInterface
|
||||
{
|
||||
throw new \BadMethodCallException('Not implemented');
|
||||
}
|
||||
}
|
207
vendor/guzzlehttp/psr7/src/StreamWrapper.php
vendored
Normal file
207
vendor/guzzlehttp/psr7/src/StreamWrapper.php
vendored
Normal file
@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Psr7;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* Converts Guzzle streams into PHP stream resources.
|
||||
*
|
||||
* @see https://www.php.net/streamwrapper
|
||||
*/
|
||||
final 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, false, self::createStreamContext($stream));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a stream context that can be used to open a stream as a php stream resource.
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
public static function createStreamContext(StreamInterface $stream)
|
||||
{
|
||||
return stream_context_create([
|
||||
'guzzle' => ['stream' => $stream],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the stream wrapper if needed
|
||||
*/
|
||||
public static function register(): void
|
||||
{
|
||||
if (!in_array('guzzle', stream_get_wrappers())) {
|
||||
stream_wrapper_register('guzzle', __CLASS__);
|
||||
}
|
||||
}
|
||||
|
||||
public function stream_open(string $path, string $mode, int $options, ?string &$opened_path = null): bool
|
||||
{
|
||||
$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(int $count): string
|
||||
{
|
||||
return $this->stream->read($count);
|
||||
}
|
||||
|
||||
public function stream_write(string $data): int
|
||||
{
|
||||
return $this->stream->write($data);
|
||||
}
|
||||
|
||||
public function stream_tell(): int
|
||||
{
|
||||
return $this->stream->tell();
|
||||
}
|
||||
|
||||
public function stream_eof(): bool
|
||||
{
|
||||
return $this->stream->eof();
|
||||
}
|
||||
|
||||
public function stream_seek(int $offset, int $whence): bool
|
||||
{
|
||||
$this->stream->seek($offset, $whence);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return resource|false
|
||||
*/
|
||||
public function stream_cast(int $cast_as)
|
||||
{
|
||||
$stream = clone $this->stream;
|
||||
$resource = $stream->detach();
|
||||
|
||||
return $resource ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{
|
||||
* dev: int,
|
||||
* ino: int,
|
||||
* mode: int,
|
||||
* nlink: int,
|
||||
* uid: int,
|
||||
* gid: int,
|
||||
* rdev: int,
|
||||
* size: int,
|
||||
* atime: int,
|
||||
* mtime: int,
|
||||
* ctime: int,
|
||||
* blksize: int,
|
||||
* blocks: int
|
||||
* }|false
|
||||
*/
|
||||
public function stream_stat()
|
||||
{
|
||||
if ($this->stream->getSize() === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static $modeMap = [
|
||||
'r' => 33060,
|
||||
'rb' => 33060,
|
||||
'r+' => 33206,
|
||||
'w' => 33188,
|
||||
'wb' => 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,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{
|
||||
* dev: int,
|
||||
* ino: int,
|
||||
* mode: int,
|
||||
* nlink: int,
|
||||
* uid: int,
|
||||
* gid: int,
|
||||
* rdev: int,
|
||||
* size: int,
|
||||
* atime: int,
|
||||
* mtime: int,
|
||||
* ctime: int,
|
||||
* blksize: int,
|
||||
* blocks: int
|
||||
* }
|
||||
*/
|
||||
public function url_stat(string $path, int $flags): array
|
||||
{
|
||||
return [
|
||||
'dev' => 0,
|
||||
'ino' => 0,
|
||||
'mode' => 0,
|
||||
'nlink' => 0,
|
||||
'uid' => 0,
|
||||
'gid' => 0,
|
||||
'rdev' => 0,
|
||||
'size' => 0,
|
||||
'atime' => 0,
|
||||
'mtime' => 0,
|
||||
'ctime' => 0,
|
||||
'blksize' => 0,
|
||||
'blocks' => 0,
|
||||
];
|
||||
}
|
||||
}
|
211
vendor/guzzlehttp/psr7/src/UploadedFile.php
vendored
Normal file
211
vendor/guzzlehttp/psr7/src/UploadedFile.php
vendored
Normal file
@ -0,0 +1,211 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Psr7;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
use RuntimeException;
|
||||
|
||||
class UploadedFile implements UploadedFileInterface
|
||||
{
|
||||
private const 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|null
|
||||
*/
|
||||
private $clientFilename;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $clientMediaType;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $error;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $file;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $moved = false;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $size;
|
||||
|
||||
/**
|
||||
* @var StreamInterface|null
|
||||
*/
|
||||
private $stream;
|
||||
|
||||
/**
|
||||
* @param StreamInterface|string|resource $streamOrFile
|
||||
*/
|
||||
public function __construct(
|
||||
$streamOrFile,
|
||||
?int $size,
|
||||
int $errorStatus,
|
||||
?string $clientFilename = null,
|
||||
?string $clientMediaType = null
|
||||
) {
|
||||
$this->setError($errorStatus);
|
||||
$this->size = $size;
|
||||
$this->clientFilename = $clientFilename;
|
||||
$this->clientMediaType = $clientMediaType;
|
||||
|
||||
if ($this->isOk()) {
|
||||
$this->setStreamOrFile($streamOrFile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Depending on the value set file or stream variable
|
||||
*
|
||||
* @param StreamInterface|string|resource $streamOrFile
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
private function setStreamOrFile($streamOrFile): void
|
||||
{
|
||||
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'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
private function setError(int $error): void
|
||||
{
|
||||
if (false === in_array($error, UploadedFile::ERRORS, true)) {
|
||||
throw new InvalidArgumentException(
|
||||
'Invalid error status for UploadedFile'
|
||||
);
|
||||
}
|
||||
|
||||
$this->error = $error;
|
||||
}
|
||||
|
||||
private static function isStringNotEmpty($param): bool
|
||||
{
|
||||
return is_string($param) && false === empty($param);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there is no upload error
|
||||
*/
|
||||
private function isOk(): bool
|
||||
{
|
||||
return $this->error === UPLOAD_ERR_OK;
|
||||
}
|
||||
|
||||
public function isMoved(): bool
|
||||
{
|
||||
return $this->moved;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException if is moved or not ok
|
||||
*/
|
||||
private function validateActive(): void
|
||||
{
|
||||
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');
|
||||
}
|
||||
}
|
||||
|
||||
public function getStream(): StreamInterface
|
||||
{
|
||||
$this->validateActive();
|
||||
|
||||
if ($this->stream instanceof StreamInterface) {
|
||||
return $this->stream;
|
||||
}
|
||||
|
||||
/** @var string $file */
|
||||
$file = $this->file;
|
||||
|
||||
return new LazyOpenStream($file, 'r+');
|
||||
}
|
||||
|
||||
public function moveTo($targetPath): void
|
||||
{
|
||||
$this->validateActive();
|
||||
|
||||
if (false === self::isStringNotEmpty($targetPath)) {
|
||||
throw new InvalidArgumentException(
|
||||
'Invalid path provided for move operation; must be a non-empty string'
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->file) {
|
||||
$this->moved = PHP_SAPI === 'cli'
|
||||
? rename($this->file, $targetPath)
|
||||
: move_uploaded_file($this->file, $targetPath);
|
||||
} else {
|
||||
Utils::copyToStream(
|
||||
$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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function getSize(): ?int
|
||||
{
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
public function getError(): int
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
|
||||
public function getClientFilename(): ?string
|
||||
{
|
||||
return $this->clientFilename;
|
||||
}
|
||||
|
||||
public function getClientMediaType(): ?string
|
||||
{
|
||||
return $this->clientMediaType;
|
||||
}
|
||||
}
|
743
vendor/guzzlehttp/psr7/src/Uri.php
vendored
Normal file
743
vendor/guzzlehttp/psr7/src/Uri.php
vendored
Normal file
@ -0,0 +1,743 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Psr7;
|
||||
|
||||
use GuzzleHttp\Psr7\Exception\MalformedUriException;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
|
||||
/**
|
||||
* PSR-7 URI implementation.
|
||||
*
|
||||
* @author Michael Dowling
|
||||
* @author Tobias Schultze
|
||||
* @author Matthew Weier O'Phinney
|
||||
*/
|
||||
class Uri implements UriInterface, \JsonSerializable
|
||||
{
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
private const HTTP_DEFAULT_HOST = 'localhost';
|
||||
|
||||
private const DEFAULT_PORTS = [
|
||||
'http' => 80,
|
||||
'https' => 443,
|
||||
'ftp' => 21,
|
||||
'gopher' => 70,
|
||||
'nntp' => 119,
|
||||
'news' => 119,
|
||||
'telnet' => 23,
|
||||
'tn3270' => 23,
|
||||
'imap' => 143,
|
||||
'pop' => 110,
|
||||
'ldap' => 389,
|
||||
];
|
||||
|
||||
/**
|
||||
* Unreserved characters for use in a regex.
|
||||
*
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc3986#section-2.3
|
||||
*/
|
||||
private const CHAR_UNRESERVED = 'a-zA-Z0-9_\-\.~';
|
||||
|
||||
/**
|
||||
* Sub-delims for use in a regex.
|
||||
*
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc3986#section-2.2
|
||||
*/
|
||||
private const CHAR_SUB_DELIMS = '!\$&\'\(\)\*\+,;=';
|
||||
private const QUERY_SEPARATORS_REPLACEMENT = ['=' => '%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 = '';
|
||||
|
||||
/** @var string|null String representation */
|
||||
private $composedComponents;
|
||||
|
||||
public function __construct(string $uri = '')
|
||||
{
|
||||
if ($uri !== '') {
|
||||
$parts = self::parse($uri);
|
||||
if ($parts === false) {
|
||||
throw new MalformedUriException("Unable to parse URI: $uri");
|
||||
}
|
||||
$this->applyParts($parts);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* UTF-8 aware \parse_url() replacement.
|
||||
*
|
||||
* The internal function produces broken output for non ASCII domain names
|
||||
* (IDN) when used with locales other than "C".
|
||||
*
|
||||
* On the other hand, cURL understands IDN correctly only when UTF-8 locale
|
||||
* is configured ("C.UTF-8", "en_US.UTF-8", etc.).
|
||||
*
|
||||
* @see https://bugs.php.net/bug.php?id=52923
|
||||
* @see https://www.php.net/manual/en/function.parse-url.php#114817
|
||||
* @see https://curl.haxx.se/libcurl/c/CURLOPT_URL.html#ENCODING
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
private static function parse(string $url)
|
||||
{
|
||||
// If IPv6
|
||||
$prefix = '';
|
||||
if (preg_match('%^(.*://\[[0-9:a-f]+\])(.*?)$%', $url, $matches)) {
|
||||
/** @var array{0:string, 1:string, 2:string} $matches */
|
||||
$prefix = $matches[1];
|
||||
$url = $matches[2];
|
||||
}
|
||||
|
||||
/** @var string */
|
||||
$encodedUrl = preg_replace_callback(
|
||||
'%[^:/@?&=#]+%usD',
|
||||
static function ($matches) {
|
||||
return urlencode($matches[0]);
|
||||
},
|
||||
$url
|
||||
);
|
||||
|
||||
$result = parse_url($prefix.$encodedUrl);
|
||||
|
||||
if ($result === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return array_map('urldecode', $result);
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
if ($this->composedComponents === null) {
|
||||
$this->composedComponents = self::composeComponents(
|
||||
$this->scheme,
|
||||
$this->getAuthority(),
|
||||
$this->path,
|
||||
$this->query,
|
||||
$this->fragment
|
||||
);
|
||||
}
|
||||
|
||||
return $this->composedComponents;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc3986#section-5.3
|
||||
*/
|
||||
public static function composeComponents(?string $scheme, ?string $authority, string $path, ?string $query, ?string $fragment): string
|
||||
{
|
||||
$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;
|
||||
}
|
||||
|
||||
if ($authority != '' && $path != '' && $path[0] != '/') {
|
||||
$path = '/'.$path;
|
||||
}
|
||||
|
||||
$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.
|
||||
*/
|
||||
public static function isDefaultPort(UriInterface $uri): bool
|
||||
{
|
||||
return $uri->getPort() === null
|
||||
|| (isset(self::DEFAULT_PORTS[$uri->getScheme()]) && $uri->getPort() === self::DEFAULT_PORTS[$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'
|
||||
*
|
||||
* @see Uri::isNetworkPathReference
|
||||
* @see Uri::isAbsolutePathReference
|
||||
* @see Uri::isRelativePathReference
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc3986#section-4
|
||||
*/
|
||||
public static function isAbsolute(UriInterface $uri): bool
|
||||
{
|
||||
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.
|
||||
*
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.2
|
||||
*/
|
||||
public static function isNetworkPathReference(UriInterface $uri): bool
|
||||
{
|
||||
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.
|
||||
*
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.2
|
||||
*/
|
||||
public static function isAbsolutePathReference(UriInterface $uri): bool
|
||||
{
|
||||
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.
|
||||
*
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.2
|
||||
*/
|
||||
public static function isRelativePathReference(UriInterface $uri): bool
|
||||
{
|
||||
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
|
||||
*
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.4
|
||||
*/
|
||||
public static function isSameDocumentReference(UriInterface $uri, ?UriInterface $base = null): bool
|
||||
{
|
||||
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() === '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public static function withoutQueryValue(UriInterface $uri, string $key): UriInterface
|
||||
{
|
||||
$result = self::getFilteredQueryString($uri, [$key]);
|
||||
|
||||
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
|
||||
*/
|
||||
public static function withQueryValue(UriInterface $uri, string $key, ?string $value): UriInterface
|
||||
{
|
||||
$result = self::getFilteredQueryString($uri, [$key]);
|
||||
|
||||
$result[] = self::generateQueryString($key, $value);
|
||||
|
||||
return $uri->withQuery(implode('&', $result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new URI with multiple specific query string values.
|
||||
*
|
||||
* It has the same behavior as withQueryValue() but for an associative array of key => value.
|
||||
*
|
||||
* @param UriInterface $uri URI to use as a base.
|
||||
* @param (string|null)[] $keyValueArray Associative array of key and values
|
||||
*/
|
||||
public static function withQueryValues(UriInterface $uri, array $keyValueArray): UriInterface
|
||||
{
|
||||
$result = self::getFilteredQueryString($uri, array_keys($keyValueArray));
|
||||
|
||||
foreach ($keyValueArray as $key => $value) {
|
||||
$result[] = self::generateQueryString((string) $key, $value !== null ? (string) $value : null);
|
||||
}
|
||||
|
||||
return $uri->withQuery(implode('&', $result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a URI from a hash of `parse_url` components.
|
||||
*
|
||||
* @see https://www.php.net/manual/en/function.parse-url.php
|
||||
*
|
||||
* @throws MalformedUriException If the components do not form a valid URI.
|
||||
*/
|
||||
public static function fromParts(array $parts): UriInterface
|
||||
{
|
||||
$uri = new self();
|
||||
$uri->applyParts($parts);
|
||||
$uri->validateState();
|
||||
|
||||
return $uri;
|
||||
}
|
||||
|
||||
public function getScheme(): string
|
||||
{
|
||||
return $this->scheme;
|
||||
}
|
||||
|
||||
public function getAuthority(): string
|
||||
{
|
||||
$authority = $this->host;
|
||||
if ($this->userInfo !== '') {
|
||||
$authority = $this->userInfo.'@'.$authority;
|
||||
}
|
||||
|
||||
if ($this->port !== null) {
|
||||
$authority .= ':'.$this->port;
|
||||
}
|
||||
|
||||
return $authority;
|
||||
}
|
||||
|
||||
public function getUserInfo(): string
|
||||
{
|
||||
return $this->userInfo;
|
||||
}
|
||||
|
||||
public function getHost(): string
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
public function getPort(): ?int
|
||||
{
|
||||
return $this->port;
|
||||
}
|
||||
|
||||
public function getPath(): string
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
public function getQuery(): string
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
public function getFragment(): string
|
||||
{
|
||||
return $this->fragment;
|
||||
}
|
||||
|
||||
public function withScheme($scheme): UriInterface
|
||||
{
|
||||
$scheme = $this->filterScheme($scheme);
|
||||
|
||||
if ($this->scheme === $scheme) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$new = clone $this;
|
||||
$new->scheme = $scheme;
|
||||
$new->composedComponents = null;
|
||||
$new->removeDefaultPort();
|
||||
$new->validateState();
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
public function withUserInfo($user, $password = null): UriInterface
|
||||
{
|
||||
$info = $this->filterUserInfoComponent($user);
|
||||
if ($password !== null) {
|
||||
$info .= ':'.$this->filterUserInfoComponent($password);
|
||||
}
|
||||
|
||||
if ($this->userInfo === $info) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$new = clone $this;
|
||||
$new->userInfo = $info;
|
||||
$new->composedComponents = null;
|
||||
$new->validateState();
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
public function withHost($host): UriInterface
|
||||
{
|
||||
$host = $this->filterHost($host);
|
||||
|
||||
if ($this->host === $host) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$new = clone $this;
|
||||
$new->host = $host;
|
||||
$new->composedComponents = null;
|
||||
$new->validateState();
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
public function withPort($port): UriInterface
|
||||
{
|
||||
$port = $this->filterPort($port);
|
||||
|
||||
if ($this->port === $port) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$new = clone $this;
|
||||
$new->port = $port;
|
||||
$new->composedComponents = null;
|
||||
$new->removeDefaultPort();
|
||||
$new->validateState();
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
public function withPath($path): UriInterface
|
||||
{
|
||||
$path = $this->filterPath($path);
|
||||
|
||||
if ($this->path === $path) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$new = clone $this;
|
||||
$new->path = $path;
|
||||
$new->composedComponents = null;
|
||||
$new->validateState();
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
public function withQuery($query): UriInterface
|
||||
{
|
||||
$query = $this->filterQueryAndFragment($query);
|
||||
|
||||
if ($this->query === $query) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$new = clone $this;
|
||||
$new->query = $query;
|
||||
$new->composedComponents = null;
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
public function withFragment($fragment): UriInterface
|
||||
{
|
||||
$fragment = $this->filterQueryAndFragment($fragment);
|
||||
|
||||
if ($this->fragment === $fragment) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$new = clone $this;
|
||||
$new->fragment = $fragment;
|
||||
$new->composedComponents = null;
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): string
|
||||
{
|
||||
return $this->__toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply parse_url parts to a URI.
|
||||
*
|
||||
* @param array $parts Array of parse_url parts to apply.
|
||||
*/
|
||||
private function applyParts(array $parts): void
|
||||
{
|
||||
$this->scheme = isset($parts['scheme'])
|
||||
? $this->filterScheme($parts['scheme'])
|
||||
: '';
|
||||
$this->userInfo = isset($parts['user'])
|
||||
? $this->filterUserInfoComponent($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 .= ':'.$this->filterUserInfoComponent($parts['pass']);
|
||||
}
|
||||
|
||||
$this->removeDefaultPort();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $scheme
|
||||
*
|
||||
* @throws \InvalidArgumentException If the scheme is invalid.
|
||||
*/
|
||||
private function filterScheme($scheme): string
|
||||
{
|
||||
if (!is_string($scheme)) {
|
||||
throw new \InvalidArgumentException('Scheme must be a string');
|
||||
}
|
||||
|
||||
return \strtr($scheme, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $component
|
||||
*
|
||||
* @throws \InvalidArgumentException If the user info is invalid.
|
||||
*/
|
||||
private function filterUserInfoComponent($component): string
|
||||
{
|
||||
if (!is_string($component)) {
|
||||
throw new \InvalidArgumentException('User info must be a string');
|
||||
}
|
||||
|
||||
return preg_replace_callback(
|
||||
'/(?:[^%'.self::CHAR_UNRESERVED.self::CHAR_SUB_DELIMS.']+|%(?![A-Fa-f0-9]{2}))/',
|
||||
[$this, 'rawurlencodeMatchZero'],
|
||||
$component
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $host
|
||||
*
|
||||
* @throws \InvalidArgumentException If the host is invalid.
|
||||
*/
|
||||
private function filterHost($host): string
|
||||
{
|
||||
if (!is_string($host)) {
|
||||
throw new \InvalidArgumentException('Host must be a string');
|
||||
}
|
||||
|
||||
return \strtr($host, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $port
|
||||
*
|
||||
* @throws \InvalidArgumentException If the port is invalid.
|
||||
*/
|
||||
private function filterPort($port): ?int
|
||||
{
|
||||
if ($port === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$port = (int) $port;
|
||||
if (0 > $port || 0xFFFF < $port) {
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf('Invalid port: %d. Must be between 0 and 65535', $port)
|
||||
);
|
||||
}
|
||||
|
||||
return $port;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param (string|int)[] $keys
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private static function getFilteredQueryString(UriInterface $uri, array $keys): array
|
||||
{
|
||||
$current = $uri->getQuery();
|
||||
|
||||
if ($current === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$decodedKeys = array_map(function ($k): string {
|
||||
return rawurldecode((string) $k);
|
||||
}, $keys);
|
||||
|
||||
return array_filter(explode('&', $current), function ($part) use ($decodedKeys) {
|
||||
return !in_array(rawurldecode(explode('=', $part)[0]), $decodedKeys, true);
|
||||
});
|
||||
}
|
||||
|
||||
private static function generateQueryString(string $key, ?string $value): string
|
||||
{
|
||||
// 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().
|
||||
$queryString = strtr($key, self::QUERY_SEPARATORS_REPLACEMENT);
|
||||
|
||||
if ($value !== null) {
|
||||
$queryString .= '='.strtr($value, self::QUERY_SEPARATORS_REPLACEMENT);
|
||||
}
|
||||
|
||||
return $queryString;
|
||||
}
|
||||
|
||||
private function removeDefaultPort(): void
|
||||
{
|
||||
if ($this->port !== null && self::isDefaultPort($this)) {
|
||||
$this->port = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the path of a URI
|
||||
*
|
||||
* @param mixed $path
|
||||
*
|
||||
* @throws \InvalidArgumentException If the path is invalid.
|
||||
*/
|
||||
private function filterPath($path): string
|
||||
{
|
||||
if (!is_string($path)) {
|
||||
throw new \InvalidArgumentException('Path must be a string');
|
||||
}
|
||||
|
||||
return preg_replace_callback(
|
||||
'/(?:[^'.self::CHAR_UNRESERVED.self::CHAR_SUB_DELIMS.'%:@\/]++|%(?![A-Fa-f0-9]{2}))/',
|
||||
[$this, 'rawurlencodeMatchZero'],
|
||||
$path
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the query string or fragment of a URI.
|
||||
*
|
||||
* @param mixed $str
|
||||
*
|
||||
* @throws \InvalidArgumentException If the query or fragment is invalid.
|
||||
*/
|
||||
private function filterQueryAndFragment($str): string
|
||||
{
|
||||
if (!is_string($str)) {
|
||||
throw new \InvalidArgumentException('Query and fragment must be a string');
|
||||
}
|
||||
|
||||
return preg_replace_callback(
|
||||
'/(?:[^'.self::CHAR_UNRESERVED.self::CHAR_SUB_DELIMS.'%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/',
|
||||
[$this, 'rawurlencodeMatchZero'],
|
||||
$str
|
||||
);
|
||||
}
|
||||
|
||||
private function rawurlencodeMatchZero(array $match): string
|
||||
{
|
||||
return rawurlencode($match[0]);
|
||||
}
|
||||
|
||||
private function validateState(): void
|
||||
{
|
||||
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 MalformedUriException('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 MalformedUriException('A relative URI must not have a path beginning with a segment containing a colon');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
52
vendor/guzzlehttp/psr7/src/UriComparator.php
vendored
Normal file
52
vendor/guzzlehttp/psr7/src/UriComparator.php
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Psr7;
|
||||
|
||||
use Psr\Http\Message\UriInterface;
|
||||
|
||||
/**
|
||||
* Provides methods to determine if a modified URL should be considered cross-origin.
|
||||
*
|
||||
* @author Graham Campbell
|
||||
*/
|
||||
final class UriComparator
|
||||
{
|
||||
/**
|
||||
* Determines if a modified URL should be considered cross-origin with
|
||||
* respect to an original URL.
|
||||
*/
|
||||
public static function isCrossOrigin(UriInterface $original, UriInterface $modified): bool
|
||||
{
|
||||
if (\strcasecmp($original->getHost(), $modified->getHost()) !== 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($original->getScheme() !== $modified->getScheme()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (self::computePort($original) !== self::computePort($modified)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static function computePort(UriInterface $uri): int
|
||||
{
|
||||
$port = $uri->getPort();
|
||||
|
||||
if (null !== $port) {
|
||||
return $port;
|
||||
}
|
||||
|
||||
return 'https' === $uri->getScheme() ? 443 : 80;
|
||||
}
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
// cannot be instantiated
|
||||
}
|
||||
}
|
220
vendor/guzzlehttp/psr7/src/UriNormalizer.php
vendored
Normal file
220
vendor/guzzlehttp/psr7/src/UriNormalizer.php
vendored
Normal file
@ -0,0 +1,220 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Psr7;
|
||||
|
||||
use Psr\Http\Message\UriInterface;
|
||||
|
||||
/**
|
||||
* Provides methods to normalize and compare URIs.
|
||||
*
|
||||
* @author Tobias Schultze
|
||||
*
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc3986#section-6
|
||||
*/
|
||||
final class UriNormalizer
|
||||
{
|
||||
/**
|
||||
* Default normalizations which only include the ones that preserve semantics.
|
||||
*/
|
||||
public const PRESERVING_NORMALIZATIONS =
|
||||
self::CAPITALIZE_PERCENT_ENCODING |
|
||||
self::DECODE_UNRESERVED_CHARACTERS |
|
||||
self::CONVERT_EMPTY_PATH |
|
||||
self::REMOVE_DEFAULT_HOST |
|
||||
self::REMOVE_DEFAULT_PORT |
|
||||
self::REMOVE_DOT_SEGMENTS;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public 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/
|
||||
*/
|
||||
public const DECODE_UNRESERVED_CHARACTERS = 2;
|
||||
|
||||
/**
|
||||
* Converts the empty path to "/" for http and https URIs.
|
||||
*
|
||||
* Example: http://example.org → http://example.org/
|
||||
*/
|
||||
public 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
|
||||
*/
|
||||
public 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/
|
||||
*/
|
||||
public 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
|
||||
*/
|
||||
public 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
|
||||
*/
|
||||
public 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.
|
||||
*/
|
||||
public 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
|
||||
*
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc3986#section-6.2
|
||||
*/
|
||||
public static function normalize(UriInterface $uri, int $flags = self::PRESERVING_NORMALIZATIONS): UriInterface
|
||||
{
|
||||
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
|
||||
*
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc3986#section-6.1
|
||||
*/
|
||||
public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, int $normalizations = self::PRESERVING_NORMALIZATIONS): bool
|
||||
{
|
||||
return (string) self::normalize($uri1, $normalizations) === (string) self::normalize($uri2, $normalizations);
|
||||
}
|
||||
|
||||
private static function capitalizePercentEncoding(UriInterface $uri): UriInterface
|
||||
{
|
||||
$regex = '/(?:%[A-Fa-f0-9]{2})++/';
|
||||
|
||||
$callback = function (array $match): string {
|
||||
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): UriInterface
|
||||
{
|
||||
$regex = '/%(?:2D|2E|5F|7E|3[0-9]|[46][1-9A-F]|[57][0-9A])/i';
|
||||
|
||||
$callback = function (array $match): string {
|
||||
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
|
||||
}
|
||||
}
|
211
vendor/guzzlehttp/psr7/src/UriResolver.php
vendored
Normal file
211
vendor/guzzlehttp/psr7/src/UriResolver.php
vendored
Normal file
@ -0,0 +1,211 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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
|
||||
*
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc3986#section-5
|
||||
*/
|
||||
final class UriResolver
|
||||
{
|
||||
/**
|
||||
* Removes dot segments from a path and returns the new path.
|
||||
*
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4
|
||||
*/
|
||||
public static function removeDotSegments(string $path): string
|
||||
{
|
||||
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.
|
||||
*
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc3986#section-5.2
|
||||
*/
|
||||
public static function resolve(UriInterface $base, UriInterface $rel): UriInterface
|
||||
{
|
||||
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
|
||||
*/
|
||||
public static function relativize(UriInterface $base, UriInterface $target): UriInterface
|
||||
{
|
||||
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());
|
||||
/** @var string $lastSegment */
|
||||
$lastSegment = end($segments);
|
||||
|
||||
return $emptyPathUri->withPath($lastSegment === '' ? './' : $lastSegment);
|
||||
}
|
||||
|
||||
return $emptyPathUri;
|
||||
}
|
||||
|
||||
private static function getRelativePath(UriInterface $base, UriInterface $target): string
|
||||
{
|
||||
$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
|
||||
}
|
||||
}
|
477
vendor/guzzlehttp/psr7/src/Utils.php
vendored
Normal file
477
vendor/guzzlehttp/psr7/src/Utils.php
vendored
Normal file
@ -0,0 +1,477 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Psr7;
|
||||
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
|
||||
final class Utils
|
||||
{
|
||||
/**
|
||||
* Remove the items given by the keys, case insensitively from the data.
|
||||
*
|
||||
* @param (string|int)[] $keys
|
||||
*/
|
||||
public static function caselessRemove(array $keys, array $data): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($keys as &$key) {
|
||||
$key = strtolower((string) $key);
|
||||
}
|
||||
|
||||
foreach ($data as $k => $v) {
|
||||
if (!in_array(strtolower((string) $k), $keys)) {
|
||||
$result[$k] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public static function copyToStream(StreamInterface $source, StreamInterface $dest, int $maxLen = -1): void
|
||||
{
|
||||
$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @throws \RuntimeException on error.
|
||||
*/
|
||||
public static function copyToString(StreamInterface $stream, int $maxLen = -1): string
|
||||
{
|
||||
$buffer = '';
|
||||
|
||||
if ($maxLen === -1) {
|
||||
while (!$stream->eof()) {
|
||||
$buf = $stream->read(1048576);
|
||||
if ($buf === '') {
|
||||
break;
|
||||
}
|
||||
$buffer .= $buf;
|
||||
}
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
$len = 0;
|
||||
while (!$stream->eof() && $len < $maxLen) {
|
||||
$buf = $stream->read($maxLen - $len);
|
||||
if ($buf === '') {
|
||||
break;
|
||||
}
|
||||
$buffer .= $buf;
|
||||
$len = strlen($buffer);
|
||||
}
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a hash of a stream.
|
||||
*
|
||||
* This method reads the entire stream to calculate a rolling hash, based
|
||||
* on PHP's `hash_init` functions.
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @throws \RuntimeException on error.
|
||||
*/
|
||||
public static function hash(StreamInterface $stream, string $algo, bool $rawOutput = false): string
|
||||
{
|
||||
$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, $rawOutput);
|
||||
$stream->seek($pos);
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone and modify a request with the given changes.
|
||||
*
|
||||
* This method is useful for reducing the number of clones needed to mutate
|
||||
* a message.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
public static function modifyRequest(RequestInterface $request, array $changes): RequestInterface
|
||||
{
|
||||
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 = self::caselessRemove($changes['remove_headers'], $headers);
|
||||
}
|
||||
|
||||
if (!empty($changes['set_headers'])) {
|
||||
$headers = self::caselessRemove(array_keys($changes['set_headers']), $headers);
|
||||
$headers = $changes['set_headers'] + $headers;
|
||||
}
|
||||
|
||||
if (isset($changes['query'])) {
|
||||
$uri = $uri->withQuery($changes['query']);
|
||||
}
|
||||
|
||||
if ($request instanceof ServerRequestInterface) {
|
||||
$new = (new ServerRequest(
|
||||
$changes['method'] ?? $request->getMethod(),
|
||||
$uri,
|
||||
$headers,
|
||||
$changes['body'] ?? $request->getBody(),
|
||||
$changes['version'] ?? $request->getProtocolVersion(),
|
||||
$request->getServerParams()
|
||||
))
|
||||
->withParsedBody($request->getParsedBody())
|
||||
->withQueryParams($request->getQueryParams())
|
||||
->withCookieParams($request->getCookieParams())
|
||||
->withUploadedFiles($request->getUploadedFiles());
|
||||
|
||||
foreach ($request->getAttributes() as $key => $value) {
|
||||
$new = $new->withAttribute($key, $value);
|
||||
}
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
return new Request(
|
||||
$changes['method'] ?? $request->getMethod(),
|
||||
$uri,
|
||||
$headers,
|
||||
$changes['body'] ?? $request->getBody(),
|
||||
$changes['version'] ?? $request->getProtocolVersion()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a line from the stream up to the maximum allowed buffer length.
|
||||
*
|
||||
* @param StreamInterface $stream Stream to read from
|
||||
* @param int|null $maxLength Maximum buffer length
|
||||
*/
|
||||
public static function readLine(StreamInterface $stream, ?int $maxLength = null): string
|
||||
{
|
||||
$buffer = '';
|
||||
$size = 0;
|
||||
|
||||
while (!$stream->eof()) {
|
||||
if ('' === ($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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redact the password in the user info part of a URI.
|
||||
*/
|
||||
public static function redactUserInfo(UriInterface $uri): UriInterface
|
||||
{
|
||||
$userInfo = $uri->getUserInfo();
|
||||
|
||||
if (false !== ($pos = \strpos($userInfo, ':'))) {
|
||||
return $uri->withUserInfo(\substr($userInfo, 0, $pos), '***');
|
||||
}
|
||||
|
||||
return $uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* This method accepts the following `$resource` types:
|
||||
* - `Psr\Http\Message\StreamInterface`: Returns the value as-is.
|
||||
* - `string`: Creates a stream object that uses the given string as the contents.
|
||||
* - `resource`: Creates a stream object that wraps the given PHP stream resource.
|
||||
* - `Iterator`: If the provided value implements `Iterator`, then a read-only
|
||||
* stream object will be created that wraps the given iterable. Each time the
|
||||
* stream is read from, data from the iterator will fill a buffer and will be
|
||||
* continuously called until the buffer is equal to the requested read size.
|
||||
* Subsequent read calls will first read from the buffer and then call `next`
|
||||
* on the underlying iterator until it is exhausted.
|
||||
* - `object` with `__toString()`: If the object has the `__toString()` method,
|
||||
* the object will be cast to a string and then a stream will be returned that
|
||||
* uses the string value.
|
||||
* - `NULL`: When `null` is passed, an empty stream object is returned.
|
||||
* - `callable` When a callable is passed, a read-only stream object will be
|
||||
* created that invokes the given callable. The callable is invoked with the
|
||||
* number of suggested bytes to read. The callable can return any number of
|
||||
* bytes, but MUST return `false` when there is no more data to return. The
|
||||
* stream object that wraps the callable will invoke the callable until the
|
||||
* number of requested bytes are available. Any additional bytes will be
|
||||
* buffered and used in subsequent reads.
|
||||
*
|
||||
* @param resource|string|int|float|bool|StreamInterface|callable|\Iterator|null $resource Entity body data
|
||||
* @param array{size?: int, metadata?: array} $options Additional options
|
||||
*
|
||||
* @throws \InvalidArgumentException if the $resource arg is not valid.
|
||||
*/
|
||||
public static function streamFor($resource = '', array $options = []): StreamInterface
|
||||
{
|
||||
if (is_scalar($resource)) {
|
||||
$stream = self::tryFopen('php://temp', 'r+');
|
||||
if ($resource !== '') {
|
||||
fwrite($stream, (string) $resource);
|
||||
fseek($stream, 0);
|
||||
}
|
||||
|
||||
return new Stream($stream, $options);
|
||||
}
|
||||
|
||||
switch (gettype($resource)) {
|
||||
case 'resource':
|
||||
/*
|
||||
* The 'php://input' is a special stream with quirks and inconsistencies.
|
||||
* We avoid using that stream by reading it into php://temp
|
||||
*/
|
||||
|
||||
/** @var resource $resource */
|
||||
if ((\stream_get_meta_data($resource)['uri'] ?? '') === 'php://input') {
|
||||
$stream = self::tryFopen('php://temp', 'w+');
|
||||
stream_copy_to_stream($resource, $stream);
|
||||
fseek($stream, 0);
|
||||
$resource = $stream;
|
||||
}
|
||||
|
||||
return new Stream($resource, $options);
|
||||
case 'object':
|
||||
/** @var object $resource */
|
||||
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 self::streamFor((string) $resource, $options);
|
||||
}
|
||||
break;
|
||||
case 'NULL':
|
||||
return new Stream(self::tryFopen('php://temp', 'r+'), $options);
|
||||
}
|
||||
|
||||
if (is_callable($resource)) {
|
||||
return new PumpStream($resource, $options);
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException('Invalid resource type: '.gettype($resource));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public static function tryFopen(string $filename, string $mode)
|
||||
{
|
||||
$ex = null;
|
||||
set_error_handler(static function (int $errno, string $errstr) use ($filename, $mode, &$ex): bool {
|
||||
$ex = new \RuntimeException(sprintf(
|
||||
'Unable to open "%s" using mode "%s": %s',
|
||||
$filename,
|
||||
$mode,
|
||||
$errstr
|
||||
));
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
try {
|
||||
/** @var resource $handle */
|
||||
$handle = fopen($filename, $mode);
|
||||
} catch (\Throwable $e) {
|
||||
$ex = new \RuntimeException(sprintf(
|
||||
'Unable to open "%s" using mode "%s": %s',
|
||||
$filename,
|
||||
$mode,
|
||||
$e->getMessage()
|
||||
), 0, $e);
|
||||
}
|
||||
|
||||
restore_error_handler();
|
||||
|
||||
if ($ex) {
|
||||
/** @var $ex \RuntimeException */
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
return $handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely gets the contents of a given stream.
|
||||
*
|
||||
* When stream_get_contents fails, PHP normally raises a warning. This
|
||||
* function adds an error handler that checks for errors and throws an
|
||||
* exception instead.
|
||||
*
|
||||
* @param resource $stream
|
||||
*
|
||||
* @throws \RuntimeException if the stream cannot be read
|
||||
*/
|
||||
public static function tryGetContents($stream): string
|
||||
{
|
||||
$ex = null;
|
||||
set_error_handler(static function (int $errno, string $errstr) use (&$ex): bool {
|
||||
$ex = new \RuntimeException(sprintf(
|
||||
'Unable to read stream contents: %s',
|
||||
$errstr
|
||||
));
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
try {
|
||||
/** @var string|false $contents */
|
||||
$contents = stream_get_contents($stream);
|
||||
|
||||
if ($contents === false) {
|
||||
$ex = new \RuntimeException('Unable to read stream contents');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$ex = new \RuntimeException(sprintf(
|
||||
'Unable to read stream contents: %s',
|
||||
$e->getMessage()
|
||||
), 0, $e);
|
||||
}
|
||||
|
||||
restore_error_handler();
|
||||
|
||||
if ($ex) {
|
||||
/** @var $ex \RuntimeException */
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
return $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a UriInterface for the given value.
|
||||
*
|
||||
* This function accepts a string or 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
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public static function uriFor($uri): UriInterface
|
||||
{
|
||||
if ($uri instanceof UriInterface) {
|
||||
return $uri;
|
||||
}
|
||||
|
||||
if (is_string($uri)) {
|
||||
return new Uri($uri);
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException('URI must be a string or UriInterface');
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user