@@ -12,38 +12,38 @@ |
||
12 | 12 | */ |
13 | 13 | final class DroppingStream implements StreamInterface |
14 | 14 | { |
15 | - use StreamDecoratorTrait; |
|
16 | - |
|
17 | - /** @var int */ |
|
18 | - private $maxLength; |
|
19 | - |
|
20 | - /** @var StreamInterface */ |
|
21 | - private $stream; |
|
22 | - |
|
23 | - /** |
|
24 | - * @param StreamInterface $stream Underlying stream to decorate. |
|
25 | - * @param int $maxLength Maximum size before dropping data. |
|
26 | - */ |
|
27 | - public function __construct(StreamInterface $stream, int $maxLength) |
|
28 | - { |
|
29 | - $this->stream = $stream; |
|
30 | - $this->maxLength = $maxLength; |
|
31 | - } |
|
32 | - |
|
33 | - public function write($string): int |
|
34 | - { |
|
35 | - $diff = $this->maxLength - $this->stream->getSize(); |
|
36 | - |
|
37 | - // Begin returning 0 when the underlying stream is too large. |
|
38 | - if ($diff <= 0) { |
|
39 | - return 0; |
|
40 | - } |
|
41 | - |
|
42 | - // Write the stream or a subset of the stream if needed. |
|
43 | - if (strlen($string) < $diff) { |
|
44 | - return $this->stream->write($string); |
|
45 | - } |
|
46 | - |
|
47 | - return $this->stream->write(substr($string, 0, $diff)); |
|
48 | - } |
|
15 | + use StreamDecoratorTrait; |
|
16 | + |
|
17 | + /** @var int */ |
|
18 | + private $maxLength; |
|
19 | + |
|
20 | + /** @var StreamInterface */ |
|
21 | + private $stream; |
|
22 | + |
|
23 | + /** |
|
24 | + * @param StreamInterface $stream Underlying stream to decorate. |
|
25 | + * @param int $maxLength Maximum size before dropping data. |
|
26 | + */ |
|
27 | + public function __construct(StreamInterface $stream, int $maxLength) |
|
28 | + { |
|
29 | + $this->stream = $stream; |
|
30 | + $this->maxLength = $maxLength; |
|
31 | + } |
|
32 | + |
|
33 | + public function write($string): int |
|
34 | + { |
|
35 | + $diff = $this->maxLength - $this->stream->getSize(); |
|
36 | + |
|
37 | + // Begin returning 0 when the underlying stream is too large. |
|
38 | + if ($diff <= 0) { |
|
39 | + return 0; |
|
40 | + } |
|
41 | + |
|
42 | + // Write the stream or a subset of the stream if needed. |
|
43 | + if (strlen($string) < $diff) { |
|
44 | + return $this->stream->write($string); |
|
45 | + } |
|
46 | + |
|
47 | + return $this->stream->write(substr($string, 0, $diff)); |
|
48 | + } |
|
49 | 49 | } |
@@ -10,8 +10,7 @@ |
||
10 | 10 | * Stream decorator that begins dropping data once the size of the underlying |
11 | 11 | * stream becomes too full. |
12 | 12 | */ |
13 | -final class DroppingStream implements StreamInterface |
|
14 | -{ |
|
13 | +final class DroppingStream implements StreamInterface { |
|
15 | 14 | use StreamDecoratorTrait; |
16 | 15 | |
17 | 16 | /** @var int */ |
@@ -6,129 +6,129 @@ |
||
6 | 6 | |
7 | 7 | final class Header |
8 | 8 | { |
9 | - /** |
|
10 | - * Parse an array of header values containing ";" separated data into an |
|
11 | - * array of associative arrays representing the header key value pair data |
|
12 | - * of the header. When a parameter does not contain a value, but just |
|
13 | - * contains a key, this function will inject a key with a '' string value. |
|
14 | - * |
|
15 | - * @param string|array $header Header to parse into components. |
|
16 | - */ |
|
17 | - public static function parse($header): array |
|
18 | - { |
|
19 | - static $trimmed = "\"' \n\t\r"; |
|
20 | - $params = $matches = []; |
|
21 | - |
|
22 | - foreach ((array) $header as $value) { |
|
23 | - foreach (self::splitList($value) as $val) { |
|
24 | - $part = []; |
|
25 | - foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) ?: [] as $kvp) { |
|
26 | - if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) { |
|
27 | - $m = $matches[0]; |
|
28 | - if (isset($m[1])) { |
|
29 | - $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed); |
|
30 | - } else { |
|
31 | - $part[] = trim($m[0], $trimmed); |
|
32 | - } |
|
33 | - } |
|
34 | - } |
|
35 | - if ($part) { |
|
36 | - $params[] = $part; |
|
37 | - } |
|
38 | - } |
|
39 | - } |
|
40 | - |
|
41 | - return $params; |
|
42 | - } |
|
43 | - |
|
44 | - /** |
|
45 | - * Converts an array of header values that may contain comma separated |
|
46 | - * headers into an array of headers with no comma separated values. |
|
47 | - * |
|
48 | - * @param string|array $header Header to normalize. |
|
49 | - * |
|
50 | - * @deprecated Use self::splitList() instead. |
|
51 | - */ |
|
52 | - public static function normalize($header): array |
|
53 | - { |
|
54 | - $result = []; |
|
55 | - foreach ((array) $header as $value) { |
|
56 | - foreach (self::splitList($value) as $parsed) { |
|
57 | - $result[] = $parsed; |
|
58 | - } |
|
59 | - } |
|
60 | - |
|
61 | - return $result; |
|
62 | - } |
|
63 | - |
|
64 | - /** |
|
65 | - * Splits a HTTP header defined to contain a comma-separated list into |
|
66 | - * each individual value. Empty values will be removed. |
|
67 | - * |
|
68 | - * Example headers include 'accept', 'cache-control' and 'if-none-match'. |
|
69 | - * |
|
70 | - * This method must not be used to parse headers that are not defined as |
|
71 | - * a list, such as 'user-agent' or 'set-cookie'. |
|
72 | - * |
|
73 | - * @param string|string[] $values Header value as returned by MessageInterface::getHeader() |
|
74 | - * |
|
75 | - * @return string[] |
|
76 | - */ |
|
77 | - public static function splitList($values): array |
|
78 | - { |
|
79 | - if (!\is_array($values)) { |
|
80 | - $values = [$values]; |
|
81 | - } |
|
82 | - |
|
83 | - $result = []; |
|
84 | - foreach ($values as $value) { |
|
85 | - if (!\is_string($value)) { |
|
86 | - throw new \TypeError('$header must either be a string or an array containing strings.'); |
|
87 | - } |
|
88 | - |
|
89 | - $v = ''; |
|
90 | - $isQuoted = false; |
|
91 | - $isEscaped = false; |
|
92 | - for ($i = 0, $max = \strlen($value); $i < $max; ++$i) { |
|
93 | - if ($isEscaped) { |
|
94 | - $v .= $value[$i]; |
|
95 | - $isEscaped = false; |
|
96 | - |
|
97 | - continue; |
|
98 | - } |
|
99 | - |
|
100 | - if (!$isQuoted && $value[$i] === ',') { |
|
101 | - $v = \trim($v); |
|
102 | - if ($v !== '') { |
|
103 | - $result[] = $v; |
|
104 | - } |
|
105 | - |
|
106 | - $v = ''; |
|
107 | - continue; |
|
108 | - } |
|
109 | - |
|
110 | - if ($isQuoted && $value[$i] === '\\') { |
|
111 | - $isEscaped = true; |
|
112 | - $v .= $value[$i]; |
|
113 | - |
|
114 | - continue; |
|
115 | - } |
|
116 | - if ($value[$i] === '"') { |
|
117 | - $isQuoted = !$isQuoted; |
|
118 | - $v .= $value[$i]; |
|
119 | - |
|
120 | - continue; |
|
121 | - } |
|
122 | - |
|
123 | - $v .= $value[$i]; |
|
124 | - } |
|
125 | - |
|
126 | - $v = \trim($v); |
|
127 | - if ($v !== '') { |
|
128 | - $result[] = $v; |
|
129 | - } |
|
130 | - } |
|
131 | - |
|
132 | - return $result; |
|
133 | - } |
|
9 | + /** |
|
10 | + * Parse an array of header values containing ";" separated data into an |
|
11 | + * array of associative arrays representing the header key value pair data |
|
12 | + * of the header. When a parameter does not contain a value, but just |
|
13 | + * contains a key, this function will inject a key with a '' string value. |
|
14 | + * |
|
15 | + * @param string|array $header Header to parse into components. |
|
16 | + */ |
|
17 | + public static function parse($header): array |
|
18 | + { |
|
19 | + static $trimmed = "\"' \n\t\r"; |
|
20 | + $params = $matches = []; |
|
21 | + |
|
22 | + foreach ((array) $header as $value) { |
|
23 | + foreach (self::splitList($value) as $val) { |
|
24 | + $part = []; |
|
25 | + foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) ?: [] as $kvp) { |
|
26 | + if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) { |
|
27 | + $m = $matches[0]; |
|
28 | + if (isset($m[1])) { |
|
29 | + $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed); |
|
30 | + } else { |
|
31 | + $part[] = trim($m[0], $trimmed); |
|
32 | + } |
|
33 | + } |
|
34 | + } |
|
35 | + if ($part) { |
|
36 | + $params[] = $part; |
|
37 | + } |
|
38 | + } |
|
39 | + } |
|
40 | + |
|
41 | + return $params; |
|
42 | + } |
|
43 | + |
|
44 | + /** |
|
45 | + * Converts an array of header values that may contain comma separated |
|
46 | + * headers into an array of headers with no comma separated values. |
|
47 | + * |
|
48 | + * @param string|array $header Header to normalize. |
|
49 | + * |
|
50 | + * @deprecated Use self::splitList() instead. |
|
51 | + */ |
|
52 | + public static function normalize($header): array |
|
53 | + { |
|
54 | + $result = []; |
|
55 | + foreach ((array) $header as $value) { |
|
56 | + foreach (self::splitList($value) as $parsed) { |
|
57 | + $result[] = $parsed; |
|
58 | + } |
|
59 | + } |
|
60 | + |
|
61 | + return $result; |
|
62 | + } |
|
63 | + |
|
64 | + /** |
|
65 | + * Splits a HTTP header defined to contain a comma-separated list into |
|
66 | + * each individual value. Empty values will be removed. |
|
67 | + * |
|
68 | + * Example headers include 'accept', 'cache-control' and 'if-none-match'. |
|
69 | + * |
|
70 | + * This method must not be used to parse headers that are not defined as |
|
71 | + * a list, such as 'user-agent' or 'set-cookie'. |
|
72 | + * |
|
73 | + * @param string|string[] $values Header value as returned by MessageInterface::getHeader() |
|
74 | + * |
|
75 | + * @return string[] |
|
76 | + */ |
|
77 | + public static function splitList($values): array |
|
78 | + { |
|
79 | + if (!\is_array($values)) { |
|
80 | + $values = [$values]; |
|
81 | + } |
|
82 | + |
|
83 | + $result = []; |
|
84 | + foreach ($values as $value) { |
|
85 | + if (!\is_string($value)) { |
|
86 | + throw new \TypeError('$header must either be a string or an array containing strings.'); |
|
87 | + } |
|
88 | + |
|
89 | + $v = ''; |
|
90 | + $isQuoted = false; |
|
91 | + $isEscaped = false; |
|
92 | + for ($i = 0, $max = \strlen($value); $i < $max; ++$i) { |
|
93 | + if ($isEscaped) { |
|
94 | + $v .= $value[$i]; |
|
95 | + $isEscaped = false; |
|
96 | + |
|
97 | + continue; |
|
98 | + } |
|
99 | + |
|
100 | + if (!$isQuoted && $value[$i] === ',') { |
|
101 | + $v = \trim($v); |
|
102 | + if ($v !== '') { |
|
103 | + $result[] = $v; |
|
104 | + } |
|
105 | + |
|
106 | + $v = ''; |
|
107 | + continue; |
|
108 | + } |
|
109 | + |
|
110 | + if ($isQuoted && $value[$i] === '\\') { |
|
111 | + $isEscaped = true; |
|
112 | + $v .= $value[$i]; |
|
113 | + |
|
114 | + continue; |
|
115 | + } |
|
116 | + if ($value[$i] === '"') { |
|
117 | + $isQuoted = !$isQuoted; |
|
118 | + $v .= $value[$i]; |
|
119 | + |
|
120 | + continue; |
|
121 | + } |
|
122 | + |
|
123 | + $v .= $value[$i]; |
|
124 | + } |
|
125 | + |
|
126 | + $v = \trim($v); |
|
127 | + if ($v !== '') { |
|
128 | + $result[] = $v; |
|
129 | + } |
|
130 | + } |
|
131 | + |
|
132 | + return $result; |
|
133 | + } |
|
134 | 134 | } |
@@ -19,7 +19,7 @@ discard block |
||
19 | 19 | static $trimmed = "\"' \n\t\r"; |
20 | 20 | $params = $matches = []; |
21 | 21 | |
22 | - foreach ((array) $header as $value) { |
|
22 | + foreach ((array)$header as $value) { |
|
23 | 23 | foreach (self::splitList($value) as $val) { |
24 | 24 | $part = []; |
25 | 25 | foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) ?: [] as $kvp) { |
@@ -52,7 +52,7 @@ discard block |
||
52 | 52 | public static function normalize($header): array |
53 | 53 | { |
54 | 54 | $result = []; |
55 | - foreach ((array) $header as $value) { |
|
55 | + foreach ((array)$header as $value) { |
|
56 | 56 | foreach (self::splitList($value) as $parsed) { |
57 | 57 | $result[] = $parsed; |
58 | 58 | } |
@@ -4,8 +4,7 @@ |
||
4 | 4 | |
5 | 5 | namespace OCA\FullTextSearch_Elasticsearch\Vendor\GuzzleHttp\Psr7; |
6 | 6 | |
7 | -final class Header |
|
8 | -{ |
|
7 | +final class Header { |
|
9 | 8 | /** |
10 | 9 | * Parse an array of header values containing ";" separated data into an |
11 | 10 | * array of associative arrays representing the header key value pair data |
@@ -9,6 +9,5 @@ |
||
9 | 9 | /** |
10 | 10 | * Exception thrown if a URI cannot be parsed because it's malformed. |
11 | 11 | */ |
12 | -class MalformedUriException extends InvalidArgumentException |
|
13 | -{ |
|
12 | +class MalformedUriException extends InvalidArgumentException { |
|
14 | 13 | } |
@@ -13,191 +13,191 @@ |
||
13 | 13 | */ |
14 | 14 | final class StreamWrapper |
15 | 15 | { |
16 | - /** @var resource */ |
|
17 | - public $context; |
|
18 | - |
|
19 | - /** @var StreamInterface */ |
|
20 | - private $stream; |
|
21 | - |
|
22 | - /** @var string r, r+, or w */ |
|
23 | - private $mode; |
|
24 | - |
|
25 | - /** |
|
26 | - * Returns a resource representing the stream. |
|
27 | - * |
|
28 | - * @param StreamInterface $stream The stream to get a resource for |
|
29 | - * |
|
30 | - * @return resource |
|
31 | - * |
|
32 | - * @throws \InvalidArgumentException if stream is not readable or writable |
|
33 | - */ |
|
34 | - public static function getResource(StreamInterface $stream) |
|
35 | - { |
|
36 | - self::register(); |
|
37 | - |
|
38 | - if ($stream->isReadable()) { |
|
39 | - $mode = $stream->isWritable() ? 'r+' : 'r'; |
|
40 | - } elseif ($stream->isWritable()) { |
|
41 | - $mode = 'w'; |
|
42 | - } else { |
|
43 | - throw new \InvalidArgumentException('The stream must be readable, ' |
|
44 | - .'writable, or both.'); |
|
45 | - } |
|
46 | - |
|
47 | - return fopen('guzzle://stream', $mode, false, self::createStreamContext($stream)); |
|
48 | - } |
|
49 | - |
|
50 | - /** |
|
51 | - * Creates a stream context that can be used to open a stream as a php stream resource. |
|
52 | - * |
|
53 | - * @return resource |
|
54 | - */ |
|
55 | - public static function createStreamContext(StreamInterface $stream) |
|
56 | - { |
|
57 | - return stream_context_create([ |
|
58 | - 'guzzle' => ['stream' => $stream], |
|
59 | - ]); |
|
60 | - } |
|
61 | - |
|
62 | - /** |
|
63 | - * Registers the stream wrapper if needed |
|
64 | - */ |
|
65 | - public static function register(): void |
|
66 | - { |
|
67 | - if (!in_array('guzzle', stream_get_wrappers())) { |
|
68 | - stream_wrapper_register('guzzle', __CLASS__); |
|
69 | - } |
|
70 | - } |
|
71 | - |
|
72 | - public function stream_open(string $path, string $mode, int $options, string &$opened_path = null): bool |
|
73 | - { |
|
74 | - $options = stream_context_get_options($this->context); |
|
75 | - |
|
76 | - if (!isset($options['guzzle']['stream'])) { |
|
77 | - return false; |
|
78 | - } |
|
79 | - |
|
80 | - $this->mode = $mode; |
|
81 | - $this->stream = $options['guzzle']['stream']; |
|
82 | - |
|
83 | - return true; |
|
84 | - } |
|
85 | - |
|
86 | - public function stream_read(int $count): string |
|
87 | - { |
|
88 | - return $this->stream->read($count); |
|
89 | - } |
|
90 | - |
|
91 | - public function stream_write(string $data): int |
|
92 | - { |
|
93 | - return $this->stream->write($data); |
|
94 | - } |
|
95 | - |
|
96 | - public function stream_tell(): int |
|
97 | - { |
|
98 | - return $this->stream->tell(); |
|
99 | - } |
|
100 | - |
|
101 | - public function stream_eof(): bool |
|
102 | - { |
|
103 | - return $this->stream->eof(); |
|
104 | - } |
|
105 | - |
|
106 | - public function stream_seek(int $offset, int $whence): bool |
|
107 | - { |
|
108 | - $this->stream->seek($offset, $whence); |
|
109 | - |
|
110 | - return true; |
|
111 | - } |
|
112 | - |
|
113 | - /** |
|
114 | - * @return resource|false |
|
115 | - */ |
|
116 | - public function stream_cast(int $cast_as) |
|
117 | - { |
|
118 | - $stream = clone $this->stream; |
|
119 | - $resource = $stream->detach(); |
|
120 | - |
|
121 | - return $resource ?? false; |
|
122 | - } |
|
123 | - |
|
124 | - /** |
|
125 | - * @return array{ |
|
126 | - * dev: int, |
|
127 | - * ino: int, |
|
128 | - * mode: int, |
|
129 | - * nlink: int, |
|
130 | - * uid: int, |
|
131 | - * gid: int, |
|
132 | - * rdev: int, |
|
133 | - * size: int, |
|
134 | - * atime: int, |
|
135 | - * mtime: int, |
|
136 | - * ctime: int, |
|
137 | - * blksize: int, |
|
138 | - * blocks: int |
|
139 | - * } |
|
140 | - */ |
|
141 | - public function stream_stat(): array |
|
142 | - { |
|
143 | - static $modeMap = [ |
|
144 | - 'r' => 33060, |
|
145 | - 'rb' => 33060, |
|
146 | - 'r+' => 33206, |
|
147 | - 'w' => 33188, |
|
148 | - 'wb' => 33188, |
|
149 | - ]; |
|
150 | - |
|
151 | - return [ |
|
152 | - 'dev' => 0, |
|
153 | - 'ino' => 0, |
|
154 | - 'mode' => $modeMap[$this->mode], |
|
155 | - 'nlink' => 0, |
|
156 | - 'uid' => 0, |
|
157 | - 'gid' => 0, |
|
158 | - 'rdev' => 0, |
|
159 | - 'size' => $this->stream->getSize() ?: 0, |
|
160 | - 'atime' => 0, |
|
161 | - 'mtime' => 0, |
|
162 | - 'ctime' => 0, |
|
163 | - 'blksize' => 0, |
|
164 | - 'blocks' => 0, |
|
165 | - ]; |
|
166 | - } |
|
167 | - |
|
168 | - /** |
|
169 | - * @return array{ |
|
170 | - * dev: int, |
|
171 | - * ino: int, |
|
172 | - * mode: int, |
|
173 | - * nlink: int, |
|
174 | - * uid: int, |
|
175 | - * gid: int, |
|
176 | - * rdev: int, |
|
177 | - * size: int, |
|
178 | - * atime: int, |
|
179 | - * mtime: int, |
|
180 | - * ctime: int, |
|
181 | - * blksize: int, |
|
182 | - * blocks: int |
|
183 | - * } |
|
184 | - */ |
|
185 | - public function url_stat(string $path, int $flags): array |
|
186 | - { |
|
187 | - return [ |
|
188 | - 'dev' => 0, |
|
189 | - 'ino' => 0, |
|
190 | - 'mode' => 0, |
|
191 | - 'nlink' => 0, |
|
192 | - 'uid' => 0, |
|
193 | - 'gid' => 0, |
|
194 | - 'rdev' => 0, |
|
195 | - 'size' => 0, |
|
196 | - 'atime' => 0, |
|
197 | - 'mtime' => 0, |
|
198 | - 'ctime' => 0, |
|
199 | - 'blksize' => 0, |
|
200 | - 'blocks' => 0, |
|
201 | - ]; |
|
202 | - } |
|
16 | + /** @var resource */ |
|
17 | + public $context; |
|
18 | + |
|
19 | + /** @var StreamInterface */ |
|
20 | + private $stream; |
|
21 | + |
|
22 | + /** @var string r, r+, or w */ |
|
23 | + private $mode; |
|
24 | + |
|
25 | + /** |
|
26 | + * Returns a resource representing the stream. |
|
27 | + * |
|
28 | + * @param StreamInterface $stream The stream to get a resource for |
|
29 | + * |
|
30 | + * @return resource |
|
31 | + * |
|
32 | + * @throws \InvalidArgumentException if stream is not readable or writable |
|
33 | + */ |
|
34 | + public static function getResource(StreamInterface $stream) |
|
35 | + { |
|
36 | + self::register(); |
|
37 | + |
|
38 | + if ($stream->isReadable()) { |
|
39 | + $mode = $stream->isWritable() ? 'r+' : 'r'; |
|
40 | + } elseif ($stream->isWritable()) { |
|
41 | + $mode = 'w'; |
|
42 | + } else { |
|
43 | + throw new \InvalidArgumentException('The stream must be readable, ' |
|
44 | + .'writable, or both.'); |
|
45 | + } |
|
46 | + |
|
47 | + return fopen('guzzle://stream', $mode, false, self::createStreamContext($stream)); |
|
48 | + } |
|
49 | + |
|
50 | + /** |
|
51 | + * Creates a stream context that can be used to open a stream as a php stream resource. |
|
52 | + * |
|
53 | + * @return resource |
|
54 | + */ |
|
55 | + public static function createStreamContext(StreamInterface $stream) |
|
56 | + { |
|
57 | + return stream_context_create([ |
|
58 | + 'guzzle' => ['stream' => $stream], |
|
59 | + ]); |
|
60 | + } |
|
61 | + |
|
62 | + /** |
|
63 | + * Registers the stream wrapper if needed |
|
64 | + */ |
|
65 | + public static function register(): void |
|
66 | + { |
|
67 | + if (!in_array('guzzle', stream_get_wrappers())) { |
|
68 | + stream_wrapper_register('guzzle', __CLASS__); |
|
69 | + } |
|
70 | + } |
|
71 | + |
|
72 | + public function stream_open(string $path, string $mode, int $options, string &$opened_path = null): bool |
|
73 | + { |
|
74 | + $options = stream_context_get_options($this->context); |
|
75 | + |
|
76 | + if (!isset($options['guzzle']['stream'])) { |
|
77 | + return false; |
|
78 | + } |
|
79 | + |
|
80 | + $this->mode = $mode; |
|
81 | + $this->stream = $options['guzzle']['stream']; |
|
82 | + |
|
83 | + return true; |
|
84 | + } |
|
85 | + |
|
86 | + public function stream_read(int $count): string |
|
87 | + { |
|
88 | + return $this->stream->read($count); |
|
89 | + } |
|
90 | + |
|
91 | + public function stream_write(string $data): int |
|
92 | + { |
|
93 | + return $this->stream->write($data); |
|
94 | + } |
|
95 | + |
|
96 | + public function stream_tell(): int |
|
97 | + { |
|
98 | + return $this->stream->tell(); |
|
99 | + } |
|
100 | + |
|
101 | + public function stream_eof(): bool |
|
102 | + { |
|
103 | + return $this->stream->eof(); |
|
104 | + } |
|
105 | + |
|
106 | + public function stream_seek(int $offset, int $whence): bool |
|
107 | + { |
|
108 | + $this->stream->seek($offset, $whence); |
|
109 | + |
|
110 | + return true; |
|
111 | + } |
|
112 | + |
|
113 | + /** |
|
114 | + * @return resource|false |
|
115 | + */ |
|
116 | + public function stream_cast(int $cast_as) |
|
117 | + { |
|
118 | + $stream = clone $this->stream; |
|
119 | + $resource = $stream->detach(); |
|
120 | + |
|
121 | + return $resource ?? false; |
|
122 | + } |
|
123 | + |
|
124 | + /** |
|
125 | + * @return array{ |
|
126 | + * dev: int, |
|
127 | + * ino: int, |
|
128 | + * mode: int, |
|
129 | + * nlink: int, |
|
130 | + * uid: int, |
|
131 | + * gid: int, |
|
132 | + * rdev: int, |
|
133 | + * size: int, |
|
134 | + * atime: int, |
|
135 | + * mtime: int, |
|
136 | + * ctime: int, |
|
137 | + * blksize: int, |
|
138 | + * blocks: int |
|
139 | + * } |
|
140 | + */ |
|
141 | + public function stream_stat(): array |
|
142 | + { |
|
143 | + static $modeMap = [ |
|
144 | + 'r' => 33060, |
|
145 | + 'rb' => 33060, |
|
146 | + 'r+' => 33206, |
|
147 | + 'w' => 33188, |
|
148 | + 'wb' => 33188, |
|
149 | + ]; |
|
150 | + |
|
151 | + return [ |
|
152 | + 'dev' => 0, |
|
153 | + 'ino' => 0, |
|
154 | + 'mode' => $modeMap[$this->mode], |
|
155 | + 'nlink' => 0, |
|
156 | + 'uid' => 0, |
|
157 | + 'gid' => 0, |
|
158 | + 'rdev' => 0, |
|
159 | + 'size' => $this->stream->getSize() ?: 0, |
|
160 | + 'atime' => 0, |
|
161 | + 'mtime' => 0, |
|
162 | + 'ctime' => 0, |
|
163 | + 'blksize' => 0, |
|
164 | + 'blocks' => 0, |
|
165 | + ]; |
|
166 | + } |
|
167 | + |
|
168 | + /** |
|
169 | + * @return array{ |
|
170 | + * dev: int, |
|
171 | + * ino: int, |
|
172 | + * mode: int, |
|
173 | + * nlink: int, |
|
174 | + * uid: int, |
|
175 | + * gid: int, |
|
176 | + * rdev: int, |
|
177 | + * size: int, |
|
178 | + * atime: int, |
|
179 | + * mtime: int, |
|
180 | + * ctime: int, |
|
181 | + * blksize: int, |
|
182 | + * blocks: int |
|
183 | + * } |
|
184 | + */ |
|
185 | + public function url_stat(string $path, int $flags): array |
|
186 | + { |
|
187 | + return [ |
|
188 | + 'dev' => 0, |
|
189 | + 'ino' => 0, |
|
190 | + 'mode' => 0, |
|
191 | + 'nlink' => 0, |
|
192 | + 'uid' => 0, |
|
193 | + 'gid' => 0, |
|
194 | + 'rdev' => 0, |
|
195 | + 'size' => 0, |
|
196 | + 'atime' => 0, |
|
197 | + 'mtime' => 0, |
|
198 | + 'ctime' => 0, |
|
199 | + 'blksize' => 0, |
|
200 | + 'blocks' => 0, |
|
201 | + ]; |
|
202 | + } |
|
203 | 203 | } |
@@ -11,8 +11,7 @@ |
||
11 | 11 | * |
12 | 12 | * @see https://www.php.net/streamwrapper |
13 | 13 | */ |
14 | -final class StreamWrapper |
|
15 | -{ |
|
14 | +final class StreamWrapper { |
|
16 | 15 | /** @var resource */ |
17 | 16 | public $context; |
18 | 17 |
@@ -13,236 +13,236 @@ |
||
13 | 13 | */ |
14 | 14 | final class AppendStream implements StreamInterface |
15 | 15 | { |
16 | - /** @var StreamInterface[] Streams being decorated */ |
|
17 | - private $streams = []; |
|
18 | - |
|
19 | - /** @var bool */ |
|
20 | - private $seekable = true; |
|
21 | - |
|
22 | - /** @var int */ |
|
23 | - private $current = 0; |
|
24 | - |
|
25 | - /** @var int */ |
|
26 | - private $pos = 0; |
|
27 | - |
|
28 | - /** |
|
29 | - * @param StreamInterface[] $streams Streams to decorate. Each stream must |
|
30 | - * be readable. |
|
31 | - */ |
|
32 | - public function __construct(array $streams = []) |
|
33 | - { |
|
34 | - foreach ($streams as $stream) { |
|
35 | - $this->addStream($stream); |
|
36 | - } |
|
37 | - } |
|
38 | - |
|
39 | - public function __toString(): string |
|
40 | - { |
|
41 | - try { |
|
42 | - $this->rewind(); |
|
43 | - |
|
44 | - return $this->getContents(); |
|
45 | - } catch (\Throwable $e) { |
|
46 | - if (\PHP_VERSION_ID >= 70400) { |
|
47 | - throw $e; |
|
48 | - } |
|
49 | - trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR); |
|
50 | - |
|
51 | - return ''; |
|
52 | - } |
|
53 | - } |
|
54 | - |
|
55 | - /** |
|
56 | - * Add a stream to the AppendStream |
|
57 | - * |
|
58 | - * @param StreamInterface $stream Stream to append. Must be readable. |
|
59 | - * |
|
60 | - * @throws \InvalidArgumentException if the stream is not readable |
|
61 | - */ |
|
62 | - public function addStream(StreamInterface $stream): void |
|
63 | - { |
|
64 | - if (!$stream->isReadable()) { |
|
65 | - throw new \InvalidArgumentException('Each stream must be readable'); |
|
66 | - } |
|
67 | - |
|
68 | - // The stream is only seekable if all streams are seekable |
|
69 | - if (!$stream->isSeekable()) { |
|
70 | - $this->seekable = false; |
|
71 | - } |
|
72 | - |
|
73 | - $this->streams[] = $stream; |
|
74 | - } |
|
75 | - |
|
76 | - public function getContents(): string |
|
77 | - { |
|
78 | - return Utils::copyToString($this); |
|
79 | - } |
|
80 | - |
|
81 | - /** |
|
82 | - * Closes each attached stream. |
|
83 | - */ |
|
84 | - public function close(): void |
|
85 | - { |
|
86 | - $this->pos = $this->current = 0; |
|
87 | - $this->seekable = true; |
|
88 | - |
|
89 | - foreach ($this->streams as $stream) { |
|
90 | - $stream->close(); |
|
91 | - } |
|
92 | - |
|
93 | - $this->streams = []; |
|
94 | - } |
|
95 | - |
|
96 | - /** |
|
97 | - * Detaches each attached stream. |
|
98 | - * |
|
99 | - * Returns null as it's not clear which underlying stream resource to return. |
|
100 | - */ |
|
101 | - public function detach() |
|
102 | - { |
|
103 | - $this->pos = $this->current = 0; |
|
104 | - $this->seekable = true; |
|
105 | - |
|
106 | - foreach ($this->streams as $stream) { |
|
107 | - $stream->detach(); |
|
108 | - } |
|
109 | - |
|
110 | - $this->streams = []; |
|
111 | - |
|
112 | - return null; |
|
113 | - } |
|
114 | - |
|
115 | - public function tell(): int |
|
116 | - { |
|
117 | - return $this->pos; |
|
118 | - } |
|
119 | - |
|
120 | - /** |
|
121 | - * Tries to calculate the size by adding the size of each stream. |
|
122 | - * |
|
123 | - * If any of the streams do not return a valid number, then the size of the |
|
124 | - * append stream cannot be determined and null is returned. |
|
125 | - */ |
|
126 | - public function getSize(): ?int |
|
127 | - { |
|
128 | - $size = 0; |
|
129 | - |
|
130 | - foreach ($this->streams as $stream) { |
|
131 | - $s = $stream->getSize(); |
|
132 | - if ($s === null) { |
|
133 | - return null; |
|
134 | - } |
|
135 | - $size += $s; |
|
136 | - } |
|
137 | - |
|
138 | - return $size; |
|
139 | - } |
|
140 | - |
|
141 | - public function eof(): bool |
|
142 | - { |
|
143 | - return !$this->streams |
|
144 | - || ($this->current >= count($this->streams) - 1 |
|
145 | - && $this->streams[$this->current]->eof()); |
|
146 | - } |
|
147 | - |
|
148 | - public function rewind(): void |
|
149 | - { |
|
150 | - $this->seek(0); |
|
151 | - } |
|
152 | - |
|
153 | - /** |
|
154 | - * Attempts to seek to the given position. Only supports SEEK_SET. |
|
155 | - */ |
|
156 | - public function seek($offset, $whence = SEEK_SET): void |
|
157 | - { |
|
158 | - if (!$this->seekable) { |
|
159 | - throw new \RuntimeException('This AppendStream is not seekable'); |
|
160 | - } elseif ($whence !== SEEK_SET) { |
|
161 | - throw new \RuntimeException('The AppendStream can only seek with SEEK_SET'); |
|
162 | - } |
|
163 | - |
|
164 | - $this->pos = $this->current = 0; |
|
165 | - |
|
166 | - // Rewind each stream |
|
167 | - foreach ($this->streams as $i => $stream) { |
|
168 | - try { |
|
169 | - $stream->rewind(); |
|
170 | - } catch (\Exception $e) { |
|
171 | - throw new \RuntimeException('Unable to seek stream ' |
|
172 | - .$i.' of the AppendStream', 0, $e); |
|
173 | - } |
|
174 | - } |
|
175 | - |
|
176 | - // Seek to the actual position by reading from each stream |
|
177 | - while ($this->pos < $offset && !$this->eof()) { |
|
178 | - $result = $this->read(min(8096, $offset - $this->pos)); |
|
179 | - if ($result === '') { |
|
180 | - break; |
|
181 | - } |
|
182 | - } |
|
183 | - } |
|
184 | - |
|
185 | - /** |
|
186 | - * Reads from all of the appended streams until the length is met or EOF. |
|
187 | - */ |
|
188 | - public function read($length): string |
|
189 | - { |
|
190 | - $buffer = ''; |
|
191 | - $total = count($this->streams) - 1; |
|
192 | - $remaining = $length; |
|
193 | - $progressToNext = false; |
|
194 | - |
|
195 | - while ($remaining > 0) { |
|
196 | - // Progress to the next stream if needed. |
|
197 | - if ($progressToNext || $this->streams[$this->current]->eof()) { |
|
198 | - $progressToNext = false; |
|
199 | - if ($this->current === $total) { |
|
200 | - break; |
|
201 | - } |
|
202 | - ++$this->current; |
|
203 | - } |
|
204 | - |
|
205 | - $result = $this->streams[$this->current]->read($remaining); |
|
206 | - |
|
207 | - if ($result === '') { |
|
208 | - $progressToNext = true; |
|
209 | - continue; |
|
210 | - } |
|
211 | - |
|
212 | - $buffer .= $result; |
|
213 | - $remaining = $length - strlen($buffer); |
|
214 | - } |
|
215 | - |
|
216 | - $this->pos += strlen($buffer); |
|
217 | - |
|
218 | - return $buffer; |
|
219 | - } |
|
220 | - |
|
221 | - public function isReadable(): bool |
|
222 | - { |
|
223 | - return true; |
|
224 | - } |
|
225 | - |
|
226 | - public function isWritable(): bool |
|
227 | - { |
|
228 | - return false; |
|
229 | - } |
|
230 | - |
|
231 | - public function isSeekable(): bool |
|
232 | - { |
|
233 | - return $this->seekable; |
|
234 | - } |
|
235 | - |
|
236 | - public function write($string): int |
|
237 | - { |
|
238 | - throw new \RuntimeException('Cannot write to an AppendStream'); |
|
239 | - } |
|
240 | - |
|
241 | - /** |
|
242 | - * @return mixed |
|
243 | - */ |
|
244 | - public function getMetadata($key = null) |
|
245 | - { |
|
246 | - return $key ? null : []; |
|
247 | - } |
|
16 | + /** @var StreamInterface[] Streams being decorated */ |
|
17 | + private $streams = []; |
|
18 | + |
|
19 | + /** @var bool */ |
|
20 | + private $seekable = true; |
|
21 | + |
|
22 | + /** @var int */ |
|
23 | + private $current = 0; |
|
24 | + |
|
25 | + /** @var int */ |
|
26 | + private $pos = 0; |
|
27 | + |
|
28 | + /** |
|
29 | + * @param StreamInterface[] $streams Streams to decorate. Each stream must |
|
30 | + * be readable. |
|
31 | + */ |
|
32 | + public function __construct(array $streams = []) |
|
33 | + { |
|
34 | + foreach ($streams as $stream) { |
|
35 | + $this->addStream($stream); |
|
36 | + } |
|
37 | + } |
|
38 | + |
|
39 | + public function __toString(): string |
|
40 | + { |
|
41 | + try { |
|
42 | + $this->rewind(); |
|
43 | + |
|
44 | + return $this->getContents(); |
|
45 | + } catch (\Throwable $e) { |
|
46 | + if (\PHP_VERSION_ID >= 70400) { |
|
47 | + throw $e; |
|
48 | + } |
|
49 | + trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR); |
|
50 | + |
|
51 | + return ''; |
|
52 | + } |
|
53 | + } |
|
54 | + |
|
55 | + /** |
|
56 | + * Add a stream to the AppendStream |
|
57 | + * |
|
58 | + * @param StreamInterface $stream Stream to append. Must be readable. |
|
59 | + * |
|
60 | + * @throws \InvalidArgumentException if the stream is not readable |
|
61 | + */ |
|
62 | + public function addStream(StreamInterface $stream): void |
|
63 | + { |
|
64 | + if (!$stream->isReadable()) { |
|
65 | + throw new \InvalidArgumentException('Each stream must be readable'); |
|
66 | + } |
|
67 | + |
|
68 | + // The stream is only seekable if all streams are seekable |
|
69 | + if (!$stream->isSeekable()) { |
|
70 | + $this->seekable = false; |
|
71 | + } |
|
72 | + |
|
73 | + $this->streams[] = $stream; |
|
74 | + } |
|
75 | + |
|
76 | + public function getContents(): string |
|
77 | + { |
|
78 | + return Utils::copyToString($this); |
|
79 | + } |
|
80 | + |
|
81 | + /** |
|
82 | + * Closes each attached stream. |
|
83 | + */ |
|
84 | + public function close(): void |
|
85 | + { |
|
86 | + $this->pos = $this->current = 0; |
|
87 | + $this->seekable = true; |
|
88 | + |
|
89 | + foreach ($this->streams as $stream) { |
|
90 | + $stream->close(); |
|
91 | + } |
|
92 | + |
|
93 | + $this->streams = []; |
|
94 | + } |
|
95 | + |
|
96 | + /** |
|
97 | + * Detaches each attached stream. |
|
98 | + * |
|
99 | + * Returns null as it's not clear which underlying stream resource to return. |
|
100 | + */ |
|
101 | + public function detach() |
|
102 | + { |
|
103 | + $this->pos = $this->current = 0; |
|
104 | + $this->seekable = true; |
|
105 | + |
|
106 | + foreach ($this->streams as $stream) { |
|
107 | + $stream->detach(); |
|
108 | + } |
|
109 | + |
|
110 | + $this->streams = []; |
|
111 | + |
|
112 | + return null; |
|
113 | + } |
|
114 | + |
|
115 | + public function tell(): int |
|
116 | + { |
|
117 | + return $this->pos; |
|
118 | + } |
|
119 | + |
|
120 | + /** |
|
121 | + * Tries to calculate the size by adding the size of each stream. |
|
122 | + * |
|
123 | + * If any of the streams do not return a valid number, then the size of the |
|
124 | + * append stream cannot be determined and null is returned. |
|
125 | + */ |
|
126 | + public function getSize(): ?int |
|
127 | + { |
|
128 | + $size = 0; |
|
129 | + |
|
130 | + foreach ($this->streams as $stream) { |
|
131 | + $s = $stream->getSize(); |
|
132 | + if ($s === null) { |
|
133 | + return null; |
|
134 | + } |
|
135 | + $size += $s; |
|
136 | + } |
|
137 | + |
|
138 | + return $size; |
|
139 | + } |
|
140 | + |
|
141 | + public function eof(): bool |
|
142 | + { |
|
143 | + return !$this->streams |
|
144 | + || ($this->current >= count($this->streams) - 1 |
|
145 | + && $this->streams[$this->current]->eof()); |
|
146 | + } |
|
147 | + |
|
148 | + public function rewind(): void |
|
149 | + { |
|
150 | + $this->seek(0); |
|
151 | + } |
|
152 | + |
|
153 | + /** |
|
154 | + * Attempts to seek to the given position. Only supports SEEK_SET. |
|
155 | + */ |
|
156 | + public function seek($offset, $whence = SEEK_SET): void |
|
157 | + { |
|
158 | + if (!$this->seekable) { |
|
159 | + throw new \RuntimeException('This AppendStream is not seekable'); |
|
160 | + } elseif ($whence !== SEEK_SET) { |
|
161 | + throw new \RuntimeException('The AppendStream can only seek with SEEK_SET'); |
|
162 | + } |
|
163 | + |
|
164 | + $this->pos = $this->current = 0; |
|
165 | + |
|
166 | + // Rewind each stream |
|
167 | + foreach ($this->streams as $i => $stream) { |
|
168 | + try { |
|
169 | + $stream->rewind(); |
|
170 | + } catch (\Exception $e) { |
|
171 | + throw new \RuntimeException('Unable to seek stream ' |
|
172 | + .$i.' of the AppendStream', 0, $e); |
|
173 | + } |
|
174 | + } |
|
175 | + |
|
176 | + // Seek to the actual position by reading from each stream |
|
177 | + while ($this->pos < $offset && !$this->eof()) { |
|
178 | + $result = $this->read(min(8096, $offset - $this->pos)); |
|
179 | + if ($result === '') { |
|
180 | + break; |
|
181 | + } |
|
182 | + } |
|
183 | + } |
|
184 | + |
|
185 | + /** |
|
186 | + * Reads from all of the appended streams until the length is met or EOF. |
|
187 | + */ |
|
188 | + public function read($length): string |
|
189 | + { |
|
190 | + $buffer = ''; |
|
191 | + $total = count($this->streams) - 1; |
|
192 | + $remaining = $length; |
|
193 | + $progressToNext = false; |
|
194 | + |
|
195 | + while ($remaining > 0) { |
|
196 | + // Progress to the next stream if needed. |
|
197 | + if ($progressToNext || $this->streams[$this->current]->eof()) { |
|
198 | + $progressToNext = false; |
|
199 | + if ($this->current === $total) { |
|
200 | + break; |
|
201 | + } |
|
202 | + ++$this->current; |
|
203 | + } |
|
204 | + |
|
205 | + $result = $this->streams[$this->current]->read($remaining); |
|
206 | + |
|
207 | + if ($result === '') { |
|
208 | + $progressToNext = true; |
|
209 | + continue; |
|
210 | + } |
|
211 | + |
|
212 | + $buffer .= $result; |
|
213 | + $remaining = $length - strlen($buffer); |
|
214 | + } |
|
215 | + |
|
216 | + $this->pos += strlen($buffer); |
|
217 | + |
|
218 | + return $buffer; |
|
219 | + } |
|
220 | + |
|
221 | + public function isReadable(): bool |
|
222 | + { |
|
223 | + return true; |
|
224 | + } |
|
225 | + |
|
226 | + public function isWritable(): bool |
|
227 | + { |
|
228 | + return false; |
|
229 | + } |
|
230 | + |
|
231 | + public function isSeekable(): bool |
|
232 | + { |
|
233 | + return $this->seekable; |
|
234 | + } |
|
235 | + |
|
236 | + public function write($string): int |
|
237 | + { |
|
238 | + throw new \RuntimeException('Cannot write to an AppendStream'); |
|
239 | + } |
|
240 | + |
|
241 | + /** |
|
242 | + * @return mixed |
|
243 | + */ |
|
244 | + public function getMetadata($key = null) |
|
245 | + { |
|
246 | + return $key ? null : []; |
|
247 | + } |
|
248 | 248 | } |
@@ -46,7 +46,7 @@ |
||
46 | 46 | if (\PHP_VERSION_ID >= 70400) { |
47 | 47 | throw $e; |
48 | 48 | } |
49 | - trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR); |
|
49 | + trigger_error(sprintf('%s::__toString exception: %s', self::class, (string)$e), E_USER_ERROR); |
|
50 | 50 | |
51 | 51 | return ''; |
52 | 52 | } |
@@ -11,8 +11,7 @@ |
||
11 | 11 | * |
12 | 12 | * This is a read-only stream decorator. |
13 | 13 | */ |
14 | -final class AppendStream implements StreamInterface |
|
15 | -{ |
|
14 | +final class AppendStream implements StreamInterface { |
|
16 | 15 | /** @var StreamInterface[] Streams being decorated */ |
17 | 16 | private $streams = []; |
18 | 17 |
@@ -15,197 +15,197 @@ |
||
15 | 15 | */ |
16 | 16 | final class UriResolver |
17 | 17 | { |
18 | - /** |
|
19 | - * Removes dot segments from a path and returns the new path. |
|
20 | - * |
|
21 | - * @see https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4 |
|
22 | - */ |
|
23 | - public static function removeDotSegments(string $path): string |
|
24 | - { |
|
25 | - if ($path === '' || $path === '/') { |
|
26 | - return $path; |
|
27 | - } |
|
28 | - |
|
29 | - $results = []; |
|
30 | - $segments = explode('/', $path); |
|
31 | - foreach ($segments as $segment) { |
|
32 | - if ($segment === '..') { |
|
33 | - array_pop($results); |
|
34 | - } elseif ($segment !== '.') { |
|
35 | - $results[] = $segment; |
|
36 | - } |
|
37 | - } |
|
38 | - |
|
39 | - $newPath = implode('/', $results); |
|
40 | - |
|
41 | - if ($path[0] === '/' && (!isset($newPath[0]) || $newPath[0] !== '/')) { |
|
42 | - // Re-add the leading slash if necessary for cases like "/.." |
|
43 | - $newPath = '/'.$newPath; |
|
44 | - } elseif ($newPath !== '' && ($segment === '.' || $segment === '..')) { |
|
45 | - // Add the trailing slash if necessary |
|
46 | - // If newPath is not empty, then $segment must be set and is the last segment from the foreach |
|
47 | - $newPath .= '/'; |
|
48 | - } |
|
49 | - |
|
50 | - return $newPath; |
|
51 | - } |
|
52 | - |
|
53 | - /** |
|
54 | - * Converts the relative URI into a new URI that is resolved against the base URI. |
|
55 | - * |
|
56 | - * @see https://datatracker.ietf.org/doc/html/rfc3986#section-5.2 |
|
57 | - */ |
|
58 | - public static function resolve(UriInterface $base, UriInterface $rel): UriInterface |
|
59 | - { |
|
60 | - if ((string) $rel === '') { |
|
61 | - // we can simply return the same base URI instance for this same-document reference |
|
62 | - return $base; |
|
63 | - } |
|
64 | - |
|
65 | - if ($rel->getScheme() != '') { |
|
66 | - return $rel->withPath(self::removeDotSegments($rel->getPath())); |
|
67 | - } |
|
68 | - |
|
69 | - if ($rel->getAuthority() != '') { |
|
70 | - $targetAuthority = $rel->getAuthority(); |
|
71 | - $targetPath = self::removeDotSegments($rel->getPath()); |
|
72 | - $targetQuery = $rel->getQuery(); |
|
73 | - } else { |
|
74 | - $targetAuthority = $base->getAuthority(); |
|
75 | - if ($rel->getPath() === '') { |
|
76 | - $targetPath = $base->getPath(); |
|
77 | - $targetQuery = $rel->getQuery() != '' ? $rel->getQuery() : $base->getQuery(); |
|
78 | - } else { |
|
79 | - if ($rel->getPath()[0] === '/') { |
|
80 | - $targetPath = $rel->getPath(); |
|
81 | - } else { |
|
82 | - if ($targetAuthority != '' && $base->getPath() === '') { |
|
83 | - $targetPath = '/'.$rel->getPath(); |
|
84 | - } else { |
|
85 | - $lastSlashPos = strrpos($base->getPath(), '/'); |
|
86 | - if ($lastSlashPos === false) { |
|
87 | - $targetPath = $rel->getPath(); |
|
88 | - } else { |
|
89 | - $targetPath = substr($base->getPath(), 0, $lastSlashPos + 1).$rel->getPath(); |
|
90 | - } |
|
91 | - } |
|
92 | - } |
|
93 | - $targetPath = self::removeDotSegments($targetPath); |
|
94 | - $targetQuery = $rel->getQuery(); |
|
95 | - } |
|
96 | - } |
|
97 | - |
|
98 | - return new Uri(Uri::composeComponents( |
|
99 | - $base->getScheme(), |
|
100 | - $targetAuthority, |
|
101 | - $targetPath, |
|
102 | - $targetQuery, |
|
103 | - $rel->getFragment() |
|
104 | - )); |
|
105 | - } |
|
106 | - |
|
107 | - /** |
|
108 | - * Returns the target URI as a relative reference from the base URI. |
|
109 | - * |
|
110 | - * This method is the counterpart to resolve(): |
|
111 | - * |
|
112 | - * (string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target)) |
|
113 | - * |
|
114 | - * One use-case is to use the current request URI as base URI and then generate relative links in your documents |
|
115 | - * to reduce the document size or offer self-contained downloadable document archives. |
|
116 | - * |
|
117 | - * $base = new Uri('http://example.com/a/b/'); |
|
118 | - * echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c')); // prints 'c'. |
|
119 | - * echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y')); // prints '../x/y'. |
|
120 | - * echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'. |
|
121 | - * echo UriResolver::relativize($base, new Uri('http://example.org/a/b/')); // prints '//example.org/a/b/'. |
|
122 | - * |
|
123 | - * This method also accepts a target that is already relative and will try to relativize it further. Only a |
|
124 | - * relative-path reference will be returned as-is. |
|
125 | - * |
|
126 | - * echo UriResolver::relativize($base, new Uri('/a/b/c')); // prints 'c' as well |
|
127 | - */ |
|
128 | - public static function relativize(UriInterface $base, UriInterface $target): UriInterface |
|
129 | - { |
|
130 | - if ($target->getScheme() !== '' |
|
131 | - && ($base->getScheme() !== $target->getScheme() || $target->getAuthority() === '' && $base->getAuthority() !== '') |
|
132 | - ) { |
|
133 | - return $target; |
|
134 | - } |
|
135 | - |
|
136 | - if (Uri::isRelativePathReference($target)) { |
|
137 | - // As the target is already highly relative we return it as-is. It would be possible to resolve |
|
138 | - // the target with `$target = self::resolve($base, $target);` and then try make it more relative |
|
139 | - // by removing a duplicate query. But let's not do that automatically. |
|
140 | - return $target; |
|
141 | - } |
|
142 | - |
|
143 | - if ($target->getAuthority() !== '' && $base->getAuthority() !== $target->getAuthority()) { |
|
144 | - return $target->withScheme(''); |
|
145 | - } |
|
146 | - |
|
147 | - // We must remove the path before removing the authority because if the path starts with two slashes, the URI |
|
148 | - // would turn invalid. And we also cannot set a relative path before removing the authority, as that is also |
|
149 | - // invalid. |
|
150 | - $emptyPathUri = $target->withScheme('')->withPath('')->withUserInfo('')->withPort(null)->withHost(''); |
|
151 | - |
|
152 | - if ($base->getPath() !== $target->getPath()) { |
|
153 | - return $emptyPathUri->withPath(self::getRelativePath($base, $target)); |
|
154 | - } |
|
155 | - |
|
156 | - if ($base->getQuery() === $target->getQuery()) { |
|
157 | - // Only the target fragment is left. And it must be returned even if base and target fragment are the same. |
|
158 | - return $emptyPathUri->withQuery(''); |
|
159 | - } |
|
160 | - |
|
161 | - // If the base URI has a query but the target has none, we cannot return an empty path reference as it would |
|
162 | - // inherit the base query component when resolving. |
|
163 | - if ($target->getQuery() === '') { |
|
164 | - $segments = explode('/', $target->getPath()); |
|
165 | - /** @var string $lastSegment */ |
|
166 | - $lastSegment = end($segments); |
|
167 | - |
|
168 | - return $emptyPathUri->withPath($lastSegment === '' ? './' : $lastSegment); |
|
169 | - } |
|
170 | - |
|
171 | - return $emptyPathUri; |
|
172 | - } |
|
173 | - |
|
174 | - private static function getRelativePath(UriInterface $base, UriInterface $target): string |
|
175 | - { |
|
176 | - $sourceSegments = explode('/', $base->getPath()); |
|
177 | - $targetSegments = explode('/', $target->getPath()); |
|
178 | - array_pop($sourceSegments); |
|
179 | - $targetLastSegment = array_pop($targetSegments); |
|
180 | - foreach ($sourceSegments as $i => $segment) { |
|
181 | - if (isset($targetSegments[$i]) && $segment === $targetSegments[$i]) { |
|
182 | - unset($sourceSegments[$i], $targetSegments[$i]); |
|
183 | - } else { |
|
184 | - break; |
|
185 | - } |
|
186 | - } |
|
187 | - $targetSegments[] = $targetLastSegment; |
|
188 | - $relativePath = str_repeat('../', count($sourceSegments)).implode('/', $targetSegments); |
|
189 | - |
|
190 | - // A reference to am empty last segment or an empty first sub-segment must be prefixed with "./". |
|
191 | - // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used |
|
192 | - // as the first segment of a relative-path reference, as it would be mistaken for a scheme name. |
|
193 | - if ('' === $relativePath || false !== strpos(explode('/', $relativePath, 2)[0], ':')) { |
|
194 | - $relativePath = "./$relativePath"; |
|
195 | - } elseif ('/' === $relativePath[0]) { |
|
196 | - if ($base->getAuthority() != '' && $base->getPath() === '') { |
|
197 | - // In this case an extra slash is added by resolve() automatically. So we must not add one here. |
|
198 | - $relativePath = ".$relativePath"; |
|
199 | - } else { |
|
200 | - $relativePath = "./$relativePath"; |
|
201 | - } |
|
202 | - } |
|
203 | - |
|
204 | - return $relativePath; |
|
205 | - } |
|
206 | - |
|
207 | - private function __construct() |
|
208 | - { |
|
209 | - // cannot be instantiated |
|
210 | - } |
|
18 | + /** |
|
19 | + * Removes dot segments from a path and returns the new path. |
|
20 | + * |
|
21 | + * @see https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4 |
|
22 | + */ |
|
23 | + public static function removeDotSegments(string $path): string |
|
24 | + { |
|
25 | + if ($path === '' || $path === '/') { |
|
26 | + return $path; |
|
27 | + } |
|
28 | + |
|
29 | + $results = []; |
|
30 | + $segments = explode('/', $path); |
|
31 | + foreach ($segments as $segment) { |
|
32 | + if ($segment === '..') { |
|
33 | + array_pop($results); |
|
34 | + } elseif ($segment !== '.') { |
|
35 | + $results[] = $segment; |
|
36 | + } |
|
37 | + } |
|
38 | + |
|
39 | + $newPath = implode('/', $results); |
|
40 | + |
|
41 | + if ($path[0] === '/' && (!isset($newPath[0]) || $newPath[0] !== '/')) { |
|
42 | + // Re-add the leading slash if necessary for cases like "/.." |
|
43 | + $newPath = '/'.$newPath; |
|
44 | + } elseif ($newPath !== '' && ($segment === '.' || $segment === '..')) { |
|
45 | + // Add the trailing slash if necessary |
|
46 | + // If newPath is not empty, then $segment must be set and is the last segment from the foreach |
|
47 | + $newPath .= '/'; |
|
48 | + } |
|
49 | + |
|
50 | + return $newPath; |
|
51 | + } |
|
52 | + |
|
53 | + /** |
|
54 | + * Converts the relative URI into a new URI that is resolved against the base URI. |
|
55 | + * |
|
56 | + * @see https://datatracker.ietf.org/doc/html/rfc3986#section-5.2 |
|
57 | + */ |
|
58 | + public static function resolve(UriInterface $base, UriInterface $rel): UriInterface |
|
59 | + { |
|
60 | + if ((string) $rel === '') { |
|
61 | + // we can simply return the same base URI instance for this same-document reference |
|
62 | + return $base; |
|
63 | + } |
|
64 | + |
|
65 | + if ($rel->getScheme() != '') { |
|
66 | + return $rel->withPath(self::removeDotSegments($rel->getPath())); |
|
67 | + } |
|
68 | + |
|
69 | + if ($rel->getAuthority() != '') { |
|
70 | + $targetAuthority = $rel->getAuthority(); |
|
71 | + $targetPath = self::removeDotSegments($rel->getPath()); |
|
72 | + $targetQuery = $rel->getQuery(); |
|
73 | + } else { |
|
74 | + $targetAuthority = $base->getAuthority(); |
|
75 | + if ($rel->getPath() === '') { |
|
76 | + $targetPath = $base->getPath(); |
|
77 | + $targetQuery = $rel->getQuery() != '' ? $rel->getQuery() : $base->getQuery(); |
|
78 | + } else { |
|
79 | + if ($rel->getPath()[0] === '/') { |
|
80 | + $targetPath = $rel->getPath(); |
|
81 | + } else { |
|
82 | + if ($targetAuthority != '' && $base->getPath() === '') { |
|
83 | + $targetPath = '/'.$rel->getPath(); |
|
84 | + } else { |
|
85 | + $lastSlashPos = strrpos($base->getPath(), '/'); |
|
86 | + if ($lastSlashPos === false) { |
|
87 | + $targetPath = $rel->getPath(); |
|
88 | + } else { |
|
89 | + $targetPath = substr($base->getPath(), 0, $lastSlashPos + 1).$rel->getPath(); |
|
90 | + } |
|
91 | + } |
|
92 | + } |
|
93 | + $targetPath = self::removeDotSegments($targetPath); |
|
94 | + $targetQuery = $rel->getQuery(); |
|
95 | + } |
|
96 | + } |
|
97 | + |
|
98 | + return new Uri(Uri::composeComponents( |
|
99 | + $base->getScheme(), |
|
100 | + $targetAuthority, |
|
101 | + $targetPath, |
|
102 | + $targetQuery, |
|
103 | + $rel->getFragment() |
|
104 | + )); |
|
105 | + } |
|
106 | + |
|
107 | + /** |
|
108 | + * Returns the target URI as a relative reference from the base URI. |
|
109 | + * |
|
110 | + * This method is the counterpart to resolve(): |
|
111 | + * |
|
112 | + * (string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target)) |
|
113 | + * |
|
114 | + * One use-case is to use the current request URI as base URI and then generate relative links in your documents |
|
115 | + * to reduce the document size or offer self-contained downloadable document archives. |
|
116 | + * |
|
117 | + * $base = new Uri('http://example.com/a/b/'); |
|
118 | + * echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c')); // prints 'c'. |
|
119 | + * echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y')); // prints '../x/y'. |
|
120 | + * echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'. |
|
121 | + * echo UriResolver::relativize($base, new Uri('http://example.org/a/b/')); // prints '//example.org/a/b/'. |
|
122 | + * |
|
123 | + * This method also accepts a target that is already relative and will try to relativize it further. Only a |
|
124 | + * relative-path reference will be returned as-is. |
|
125 | + * |
|
126 | + * echo UriResolver::relativize($base, new Uri('/a/b/c')); // prints 'c' as well |
|
127 | + */ |
|
128 | + public static function relativize(UriInterface $base, UriInterface $target): UriInterface |
|
129 | + { |
|
130 | + if ($target->getScheme() !== '' |
|
131 | + && ($base->getScheme() !== $target->getScheme() || $target->getAuthority() === '' && $base->getAuthority() !== '') |
|
132 | + ) { |
|
133 | + return $target; |
|
134 | + } |
|
135 | + |
|
136 | + if (Uri::isRelativePathReference($target)) { |
|
137 | + // As the target is already highly relative we return it as-is. It would be possible to resolve |
|
138 | + // the target with `$target = self::resolve($base, $target);` and then try make it more relative |
|
139 | + // by removing a duplicate query. But let's not do that automatically. |
|
140 | + return $target; |
|
141 | + } |
|
142 | + |
|
143 | + if ($target->getAuthority() !== '' && $base->getAuthority() !== $target->getAuthority()) { |
|
144 | + return $target->withScheme(''); |
|
145 | + } |
|
146 | + |
|
147 | + // We must remove the path before removing the authority because if the path starts with two slashes, the URI |
|
148 | + // would turn invalid. And we also cannot set a relative path before removing the authority, as that is also |
|
149 | + // invalid. |
|
150 | + $emptyPathUri = $target->withScheme('')->withPath('')->withUserInfo('')->withPort(null)->withHost(''); |
|
151 | + |
|
152 | + if ($base->getPath() !== $target->getPath()) { |
|
153 | + return $emptyPathUri->withPath(self::getRelativePath($base, $target)); |
|
154 | + } |
|
155 | + |
|
156 | + if ($base->getQuery() === $target->getQuery()) { |
|
157 | + // Only the target fragment is left. And it must be returned even if base and target fragment are the same. |
|
158 | + return $emptyPathUri->withQuery(''); |
|
159 | + } |
|
160 | + |
|
161 | + // If the base URI has a query but the target has none, we cannot return an empty path reference as it would |
|
162 | + // inherit the base query component when resolving. |
|
163 | + if ($target->getQuery() === '') { |
|
164 | + $segments = explode('/', $target->getPath()); |
|
165 | + /** @var string $lastSegment */ |
|
166 | + $lastSegment = end($segments); |
|
167 | + |
|
168 | + return $emptyPathUri->withPath($lastSegment === '' ? './' : $lastSegment); |
|
169 | + } |
|
170 | + |
|
171 | + return $emptyPathUri; |
|
172 | + } |
|
173 | + |
|
174 | + private static function getRelativePath(UriInterface $base, UriInterface $target): string |
|
175 | + { |
|
176 | + $sourceSegments = explode('/', $base->getPath()); |
|
177 | + $targetSegments = explode('/', $target->getPath()); |
|
178 | + array_pop($sourceSegments); |
|
179 | + $targetLastSegment = array_pop($targetSegments); |
|
180 | + foreach ($sourceSegments as $i => $segment) { |
|
181 | + if (isset($targetSegments[$i]) && $segment === $targetSegments[$i]) { |
|
182 | + unset($sourceSegments[$i], $targetSegments[$i]); |
|
183 | + } else { |
|
184 | + break; |
|
185 | + } |
|
186 | + } |
|
187 | + $targetSegments[] = $targetLastSegment; |
|
188 | + $relativePath = str_repeat('../', count($sourceSegments)).implode('/', $targetSegments); |
|
189 | + |
|
190 | + // A reference to am empty last segment or an empty first sub-segment must be prefixed with "./". |
|
191 | + // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used |
|
192 | + // as the first segment of a relative-path reference, as it would be mistaken for a scheme name. |
|
193 | + if ('' === $relativePath || false !== strpos(explode('/', $relativePath, 2)[0], ':')) { |
|
194 | + $relativePath = "./$relativePath"; |
|
195 | + } elseif ('/' === $relativePath[0]) { |
|
196 | + if ($base->getAuthority() != '' && $base->getPath() === '') { |
|
197 | + // In this case an extra slash is added by resolve() automatically. So we must not add one here. |
|
198 | + $relativePath = ".$relativePath"; |
|
199 | + } else { |
|
200 | + $relativePath = "./$relativePath"; |
|
201 | + } |
|
202 | + } |
|
203 | + |
|
204 | + return $relativePath; |
|
205 | + } |
|
206 | + |
|
207 | + private function __construct() |
|
208 | + { |
|
209 | + // cannot be instantiated |
|
210 | + } |
|
211 | 211 | } |
@@ -57,7 +57,7 @@ |
||
57 | 57 | */ |
58 | 58 | public static function resolve(UriInterface $base, UriInterface $rel): UriInterface |
59 | 59 | { |
60 | - if ((string) $rel === '') { |
|
60 | + if ((string)$rel === '') { |
|
61 | 61 | // we can simply return the same base URI instance for this same-document reference |
62 | 62 | return $base; |
63 | 63 | } |
@@ -13,8 +13,7 @@ |
||
13 | 13 | * |
14 | 14 | * @see https://datatracker.ietf.org/doc/html/rfc3986#section-5 |
15 | 15 | */ |
16 | -final class UriResolver |
|
17 | -{ |
|
16 | +final class UriResolver { |
|
18 | 17 | /** |
19 | 18 | * Removes dot segments from a path and returns the new path. |
20 | 19 | * |
@@ -6,108 +6,108 @@ |
||
6 | 6 | |
7 | 7 | final class Query |
8 | 8 | { |
9 | - /** |
|
10 | - * Parse a query string into an associative array. |
|
11 | - * |
|
12 | - * If multiple values are found for the same key, the value of that key |
|
13 | - * value pair will become an array. This function does not parse nested |
|
14 | - * PHP style arrays into an associative array (e.g., `foo[a]=1&foo[b]=2` |
|
15 | - * will be parsed into `['foo[a]' => '1', 'foo[b]' => '2'])`. |
|
16 | - * |
|
17 | - * @param string $str Query string to parse |
|
18 | - * @param int|bool $urlEncoding How the query string is encoded |
|
19 | - */ |
|
20 | - public static function parse(string $str, $urlEncoding = true): array |
|
21 | - { |
|
22 | - $result = []; |
|
9 | + /** |
|
10 | + * Parse a query string into an associative array. |
|
11 | + * |
|
12 | + * If multiple values are found for the same key, the value of that key |
|
13 | + * value pair will become an array. This function does not parse nested |
|
14 | + * PHP style arrays into an associative array (e.g., `foo[a]=1&foo[b]=2` |
|
15 | + * will be parsed into `['foo[a]' => '1', 'foo[b]' => '2'])`. |
|
16 | + * |
|
17 | + * @param string $str Query string to parse |
|
18 | + * @param int|bool $urlEncoding How the query string is encoded |
|
19 | + */ |
|
20 | + public static function parse(string $str, $urlEncoding = true): array |
|
21 | + { |
|
22 | + $result = []; |
|
23 | 23 | |
24 | - if ($str === '') { |
|
25 | - return $result; |
|
26 | - } |
|
24 | + if ($str === '') { |
|
25 | + return $result; |
|
26 | + } |
|
27 | 27 | |
28 | - if ($urlEncoding === true) { |
|
29 | - $decoder = function ($value) { |
|
30 | - return rawurldecode(str_replace('+', ' ', (string) $value)); |
|
31 | - }; |
|
32 | - } elseif ($urlEncoding === PHP_QUERY_RFC3986) { |
|
33 | - $decoder = 'rawurldecode'; |
|
34 | - } elseif ($urlEncoding === PHP_QUERY_RFC1738) { |
|
35 | - $decoder = 'urldecode'; |
|
36 | - } else { |
|
37 | - $decoder = function ($str) { |
|
38 | - return $str; |
|
39 | - }; |
|
40 | - } |
|
28 | + if ($urlEncoding === true) { |
|
29 | + $decoder = function ($value) { |
|
30 | + return rawurldecode(str_replace('+', ' ', (string) $value)); |
|
31 | + }; |
|
32 | + } elseif ($urlEncoding === PHP_QUERY_RFC3986) { |
|
33 | + $decoder = 'rawurldecode'; |
|
34 | + } elseif ($urlEncoding === PHP_QUERY_RFC1738) { |
|
35 | + $decoder = 'urldecode'; |
|
36 | + } else { |
|
37 | + $decoder = function ($str) { |
|
38 | + return $str; |
|
39 | + }; |
|
40 | + } |
|
41 | 41 | |
42 | - foreach (explode('&', $str) as $kvp) { |
|
43 | - $parts = explode('=', $kvp, 2); |
|
44 | - $key = $decoder($parts[0]); |
|
45 | - $value = isset($parts[1]) ? $decoder($parts[1]) : null; |
|
46 | - if (!array_key_exists($key, $result)) { |
|
47 | - $result[$key] = $value; |
|
48 | - } else { |
|
49 | - if (!is_array($result[$key])) { |
|
50 | - $result[$key] = [$result[$key]]; |
|
51 | - } |
|
52 | - $result[$key][] = $value; |
|
53 | - } |
|
54 | - } |
|
42 | + foreach (explode('&', $str) as $kvp) { |
|
43 | + $parts = explode('=', $kvp, 2); |
|
44 | + $key = $decoder($parts[0]); |
|
45 | + $value = isset($parts[1]) ? $decoder($parts[1]) : null; |
|
46 | + if (!array_key_exists($key, $result)) { |
|
47 | + $result[$key] = $value; |
|
48 | + } else { |
|
49 | + if (!is_array($result[$key])) { |
|
50 | + $result[$key] = [$result[$key]]; |
|
51 | + } |
|
52 | + $result[$key][] = $value; |
|
53 | + } |
|
54 | + } |
|
55 | 55 | |
56 | - return $result; |
|
57 | - } |
|
56 | + return $result; |
|
57 | + } |
|
58 | 58 | |
59 | - /** |
|
60 | - * Build a query string from an array of key value pairs. |
|
61 | - * |
|
62 | - * This function can use the return value of `parse()` to build a query |
|
63 | - * string. This function does not modify the provided keys when an array is |
|
64 | - * encountered (like `http_build_query()` would). |
|
65 | - * |
|
66 | - * @param array $params Query string parameters. |
|
67 | - * @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986 |
|
68 | - * to encode using RFC3986, or PHP_QUERY_RFC1738 |
|
69 | - * to encode using RFC1738. |
|
70 | - */ |
|
71 | - public static function build(array $params, $encoding = PHP_QUERY_RFC3986): string |
|
72 | - { |
|
73 | - if (!$params) { |
|
74 | - return ''; |
|
75 | - } |
|
59 | + /** |
|
60 | + * Build a query string from an array of key value pairs. |
|
61 | + * |
|
62 | + * This function can use the return value of `parse()` to build a query |
|
63 | + * string. This function does not modify the provided keys when an array is |
|
64 | + * encountered (like `http_build_query()` would). |
|
65 | + * |
|
66 | + * @param array $params Query string parameters. |
|
67 | + * @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986 |
|
68 | + * to encode using RFC3986, or PHP_QUERY_RFC1738 |
|
69 | + * to encode using RFC1738. |
|
70 | + */ |
|
71 | + public static function build(array $params, $encoding = PHP_QUERY_RFC3986): string |
|
72 | + { |
|
73 | + if (!$params) { |
|
74 | + return ''; |
|
75 | + } |
|
76 | 76 | |
77 | - if ($encoding === false) { |
|
78 | - $encoder = function (string $str): string { |
|
79 | - return $str; |
|
80 | - }; |
|
81 | - } elseif ($encoding === PHP_QUERY_RFC3986) { |
|
82 | - $encoder = 'rawurlencode'; |
|
83 | - } elseif ($encoding === PHP_QUERY_RFC1738) { |
|
84 | - $encoder = 'urlencode'; |
|
85 | - } else { |
|
86 | - throw new \InvalidArgumentException('Invalid type'); |
|
87 | - } |
|
77 | + if ($encoding === false) { |
|
78 | + $encoder = function (string $str): string { |
|
79 | + return $str; |
|
80 | + }; |
|
81 | + } elseif ($encoding === PHP_QUERY_RFC3986) { |
|
82 | + $encoder = 'rawurlencode'; |
|
83 | + } elseif ($encoding === PHP_QUERY_RFC1738) { |
|
84 | + $encoder = 'urlencode'; |
|
85 | + } else { |
|
86 | + throw new \InvalidArgumentException('Invalid type'); |
|
87 | + } |
|
88 | 88 | |
89 | - $qs = ''; |
|
90 | - foreach ($params as $k => $v) { |
|
91 | - $k = $encoder((string) $k); |
|
92 | - if (!is_array($v)) { |
|
93 | - $qs .= $k; |
|
94 | - $v = is_bool($v) ? (int) $v : $v; |
|
95 | - if ($v !== null) { |
|
96 | - $qs .= '='.$encoder((string) $v); |
|
97 | - } |
|
98 | - $qs .= '&'; |
|
99 | - } else { |
|
100 | - foreach ($v as $vv) { |
|
101 | - $qs .= $k; |
|
102 | - $vv = is_bool($vv) ? (int) $vv : $vv; |
|
103 | - if ($vv !== null) { |
|
104 | - $qs .= '='.$encoder((string) $vv); |
|
105 | - } |
|
106 | - $qs .= '&'; |
|
107 | - } |
|
108 | - } |
|
109 | - } |
|
89 | + $qs = ''; |
|
90 | + foreach ($params as $k => $v) { |
|
91 | + $k = $encoder((string) $k); |
|
92 | + if (!is_array($v)) { |
|
93 | + $qs .= $k; |
|
94 | + $v = is_bool($v) ? (int) $v : $v; |
|
95 | + if ($v !== null) { |
|
96 | + $qs .= '='.$encoder((string) $v); |
|
97 | + } |
|
98 | + $qs .= '&'; |
|
99 | + } else { |
|
100 | + foreach ($v as $vv) { |
|
101 | + $qs .= $k; |
|
102 | + $vv = is_bool($vv) ? (int) $vv : $vv; |
|
103 | + if ($vv !== null) { |
|
104 | + $qs .= '='.$encoder((string) $vv); |
|
105 | + } |
|
106 | + $qs .= '&'; |
|
107 | + } |
|
108 | + } |
|
109 | + } |
|
110 | 110 | |
111 | - return $qs ? (string) substr($qs, 0, -1) : ''; |
|
112 | - } |
|
111 | + return $qs ? (string) substr($qs, 0, -1) : ''; |
|
112 | + } |
|
113 | 113 | } |
@@ -26,15 +26,15 @@ discard block |
||
26 | 26 | } |
27 | 27 | |
28 | 28 | if ($urlEncoding === true) { |
29 | - $decoder = function ($value) { |
|
30 | - return rawurldecode(str_replace('+', ' ', (string) $value)); |
|
29 | + $decoder = function($value) { |
|
30 | + return rawurldecode(str_replace('+', ' ', (string)$value)); |
|
31 | 31 | }; |
32 | 32 | } elseif ($urlEncoding === PHP_QUERY_RFC3986) { |
33 | 33 | $decoder = 'rawurldecode'; |
34 | 34 | } elseif ($urlEncoding === PHP_QUERY_RFC1738) { |
35 | 35 | $decoder = 'urldecode'; |
36 | 36 | } else { |
37 | - $decoder = function ($str) { |
|
37 | + $decoder = function($str) { |
|
38 | 38 | return $str; |
39 | 39 | }; |
40 | 40 | } |
@@ -75,7 +75,7 @@ discard block |
||
75 | 75 | } |
76 | 76 | |
77 | 77 | if ($encoding === false) { |
78 | - $encoder = function (string $str): string { |
|
78 | + $encoder = function(string $str): string { |
|
79 | 79 | return $str; |
80 | 80 | }; |
81 | 81 | } elseif ($encoding === PHP_QUERY_RFC3986) { |
@@ -88,26 +88,26 @@ discard block |
||
88 | 88 | |
89 | 89 | $qs = ''; |
90 | 90 | foreach ($params as $k => $v) { |
91 | - $k = $encoder((string) $k); |
|
91 | + $k = $encoder((string)$k); |
|
92 | 92 | if (!is_array($v)) { |
93 | 93 | $qs .= $k; |
94 | - $v = is_bool($v) ? (int) $v : $v; |
|
94 | + $v = is_bool($v) ? (int)$v : $v; |
|
95 | 95 | if ($v !== null) { |
96 | - $qs .= '='.$encoder((string) $v); |
|
96 | + $qs .= '='.$encoder((string)$v); |
|
97 | 97 | } |
98 | 98 | $qs .= '&'; |
99 | 99 | } else { |
100 | 100 | foreach ($v as $vv) { |
101 | 101 | $qs .= $k; |
102 | - $vv = is_bool($vv) ? (int) $vv : $vv; |
|
102 | + $vv = is_bool($vv) ? (int)$vv : $vv; |
|
103 | 103 | if ($vv !== null) { |
104 | - $qs .= '='.$encoder((string) $vv); |
|
104 | + $qs .= '='.$encoder((string)$vv); |
|
105 | 105 | } |
106 | 106 | $qs .= '&'; |
107 | 107 | } |
108 | 108 | } |
109 | 109 | } |
110 | 110 | |
111 | - return $qs ? (string) substr($qs, 0, -1) : ''; |
|
111 | + return $qs ? (string)substr($qs, 0, -1) : ''; |
|
112 | 112 | } |
113 | 113 | } |
@@ -4,8 +4,7 @@ |
||
4 | 4 | |
5 | 5 | namespace OCA\FullTextSearch_Elasticsearch\Vendor\GuzzleHttp\Psr7; |
6 | 6 | |
7 | -final class Query |
|
8 | -{ |
|
7 | +final class Query { |
|
9 | 8 | /** |
10 | 9 | * Parse a query string into an associative array. |
11 | 10 | * |
@@ -26,315 +26,315 @@ |
||
26 | 26 | */ |
27 | 27 | class ServerRequest extends Request implements ServerRequestInterface |
28 | 28 | { |
29 | - /** |
|
30 | - * @var array |
|
31 | - */ |
|
32 | - private $attributes = []; |
|
33 | - |
|
34 | - /** |
|
35 | - * @var array |
|
36 | - */ |
|
37 | - private $cookieParams = []; |
|
38 | - |
|
39 | - /** |
|
40 | - * @var array|object|null |
|
41 | - */ |
|
42 | - private $parsedBody; |
|
43 | - |
|
44 | - /** |
|
45 | - * @var array |
|
46 | - */ |
|
47 | - private $queryParams = []; |
|
48 | - |
|
49 | - /** |
|
50 | - * @var array |
|
51 | - */ |
|
52 | - private $serverParams; |
|
53 | - |
|
54 | - /** |
|
55 | - * @var array |
|
56 | - */ |
|
57 | - private $uploadedFiles = []; |
|
58 | - |
|
59 | - /** |
|
60 | - * @param string $method HTTP method |
|
61 | - * @param string|UriInterface $uri URI |
|
62 | - * @param (string|string[])[] $headers Request headers |
|
63 | - * @param string|resource|StreamInterface|null $body Request body |
|
64 | - * @param string $version Protocol version |
|
65 | - * @param array $serverParams Typically the $_SERVER superglobal |
|
66 | - */ |
|
67 | - public function __construct( |
|
68 | - string $method, |
|
69 | - $uri, |
|
70 | - array $headers = [], |
|
71 | - $body = null, |
|
72 | - string $version = '1.1', |
|
73 | - array $serverParams = [] |
|
74 | - ) { |
|
75 | - $this->serverParams = $serverParams; |
|
76 | - |
|
77 | - parent::__construct($method, $uri, $headers, $body, $version); |
|
78 | - } |
|
79 | - |
|
80 | - /** |
|
81 | - * Return an UploadedFile instance array. |
|
82 | - * |
|
83 | - * @param array $files An array which respect $_FILES structure |
|
84 | - * |
|
85 | - * @throws InvalidArgumentException for unrecognized values |
|
86 | - */ |
|
87 | - public static function normalizeFiles(array $files): array |
|
88 | - { |
|
89 | - $normalized = []; |
|
90 | - |
|
91 | - foreach ($files as $key => $value) { |
|
92 | - if ($value instanceof UploadedFileInterface) { |
|
93 | - $normalized[$key] = $value; |
|
94 | - } elseif (is_array($value) && isset($value['tmp_name'])) { |
|
95 | - $normalized[$key] = self::createUploadedFileFromSpec($value); |
|
96 | - } elseif (is_array($value)) { |
|
97 | - $normalized[$key] = self::normalizeFiles($value); |
|
98 | - continue; |
|
99 | - } else { |
|
100 | - throw new InvalidArgumentException('Invalid value in files specification'); |
|
101 | - } |
|
102 | - } |
|
103 | - |
|
104 | - return $normalized; |
|
105 | - } |
|
106 | - |
|
107 | - /** |
|
108 | - * Create and return an UploadedFile instance from a $_FILES specification. |
|
109 | - * |
|
110 | - * If the specification represents an array of values, this method will |
|
111 | - * delegate to normalizeNestedFileSpec() and return that return value. |
|
112 | - * |
|
113 | - * @param array $value $_FILES struct |
|
114 | - * |
|
115 | - * @return UploadedFileInterface|UploadedFileInterface[] |
|
116 | - */ |
|
117 | - private static function createUploadedFileFromSpec(array $value) |
|
118 | - { |
|
119 | - if (is_array($value['tmp_name'])) { |
|
120 | - return self::normalizeNestedFileSpec($value); |
|
121 | - } |
|
122 | - |
|
123 | - return new UploadedFile( |
|
124 | - $value['tmp_name'], |
|
125 | - (int) $value['size'], |
|
126 | - (int) $value['error'], |
|
127 | - $value['name'], |
|
128 | - $value['type'] |
|
129 | - ); |
|
130 | - } |
|
131 | - |
|
132 | - /** |
|
133 | - * Normalize an array of file specifications. |
|
134 | - * |
|
135 | - * Loops through all nested files and returns a normalized array of |
|
136 | - * UploadedFileInterface instances. |
|
137 | - * |
|
138 | - * @return UploadedFileInterface[] |
|
139 | - */ |
|
140 | - private static function normalizeNestedFileSpec(array $files = []): array |
|
141 | - { |
|
142 | - $normalizedFiles = []; |
|
143 | - |
|
144 | - foreach (array_keys($files['tmp_name']) as $key) { |
|
145 | - $spec = [ |
|
146 | - 'tmp_name' => $files['tmp_name'][$key], |
|
147 | - 'size' => $files['size'][$key] ?? null, |
|
148 | - 'error' => $files['error'][$key] ?? null, |
|
149 | - 'name' => $files['name'][$key] ?? null, |
|
150 | - 'type' => $files['type'][$key] ?? null, |
|
151 | - ]; |
|
152 | - $normalizedFiles[$key] = self::createUploadedFileFromSpec($spec); |
|
153 | - } |
|
154 | - |
|
155 | - return $normalizedFiles; |
|
156 | - } |
|
157 | - |
|
158 | - /** |
|
159 | - * Return a ServerRequest populated with superglobals: |
|
160 | - * $_GET |
|
161 | - * $_POST |
|
162 | - * $_COOKIE |
|
163 | - * $_FILES |
|
164 | - * $_SERVER |
|
165 | - */ |
|
166 | - public static function fromGlobals(): ServerRequestInterface |
|
167 | - { |
|
168 | - $method = $_SERVER['REQUEST_METHOD'] ?? 'GET'; |
|
169 | - $headers = getallheaders(); |
|
170 | - $uri = self::getUriFromGlobals(); |
|
171 | - $body = new CachingStream(new LazyOpenStream('php://input', 'r+')); |
|
172 | - $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']) : '1.1'; |
|
173 | - |
|
174 | - $serverRequest = new ServerRequest($method, $uri, $headers, $body, $protocol, $_SERVER); |
|
175 | - |
|
176 | - return $serverRequest |
|
177 | - ->withCookieParams($_COOKIE) |
|
178 | - ->withQueryParams($_GET) |
|
179 | - ->withParsedBody($_POST) |
|
180 | - ->withUploadedFiles(self::normalizeFiles($_FILES)); |
|
181 | - } |
|
182 | - |
|
183 | - private static function extractHostAndPortFromAuthority(string $authority): array |
|
184 | - { |
|
185 | - $uri = 'http://'.$authority; |
|
186 | - $parts = parse_url($uri); |
|
187 | - if (false === $parts) { |
|
188 | - return [null, null]; |
|
189 | - } |
|
190 | - |
|
191 | - $host = $parts['host'] ?? null; |
|
192 | - $port = $parts['port'] ?? null; |
|
193 | - |
|
194 | - return [$host, $port]; |
|
195 | - } |
|
196 | - |
|
197 | - /** |
|
198 | - * Get a Uri populated with values from $_SERVER. |
|
199 | - */ |
|
200 | - public static function getUriFromGlobals(): UriInterface |
|
201 | - { |
|
202 | - $uri = new Uri(''); |
|
203 | - |
|
204 | - $uri = $uri->withScheme(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http'); |
|
205 | - |
|
206 | - $hasPort = false; |
|
207 | - if (isset($_SERVER['HTTP_HOST'])) { |
|
208 | - [$host, $port] = self::extractHostAndPortFromAuthority($_SERVER['HTTP_HOST']); |
|
209 | - if ($host !== null) { |
|
210 | - $uri = $uri->withHost($host); |
|
211 | - } |
|
212 | - |
|
213 | - if ($port !== null) { |
|
214 | - $hasPort = true; |
|
215 | - $uri = $uri->withPort($port); |
|
216 | - } |
|
217 | - } elseif (isset($_SERVER['SERVER_NAME'])) { |
|
218 | - $uri = $uri->withHost($_SERVER['SERVER_NAME']); |
|
219 | - } elseif (isset($_SERVER['SERVER_ADDR'])) { |
|
220 | - $uri = $uri->withHost($_SERVER['SERVER_ADDR']); |
|
221 | - } |
|
222 | - |
|
223 | - if (!$hasPort && isset($_SERVER['SERVER_PORT'])) { |
|
224 | - $uri = $uri->withPort($_SERVER['SERVER_PORT']); |
|
225 | - } |
|
226 | - |
|
227 | - $hasQuery = false; |
|
228 | - if (isset($_SERVER['REQUEST_URI'])) { |
|
229 | - $requestUriParts = explode('?', $_SERVER['REQUEST_URI'], 2); |
|
230 | - $uri = $uri->withPath($requestUriParts[0]); |
|
231 | - if (isset($requestUriParts[1])) { |
|
232 | - $hasQuery = true; |
|
233 | - $uri = $uri->withQuery($requestUriParts[1]); |
|
234 | - } |
|
235 | - } |
|
236 | - |
|
237 | - if (!$hasQuery && isset($_SERVER['QUERY_STRING'])) { |
|
238 | - $uri = $uri->withQuery($_SERVER['QUERY_STRING']); |
|
239 | - } |
|
240 | - |
|
241 | - return $uri; |
|
242 | - } |
|
243 | - |
|
244 | - public function getServerParams(): array |
|
245 | - { |
|
246 | - return $this->serverParams; |
|
247 | - } |
|
248 | - |
|
249 | - public function getUploadedFiles(): array |
|
250 | - { |
|
251 | - return $this->uploadedFiles; |
|
252 | - } |
|
253 | - |
|
254 | - public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface |
|
255 | - { |
|
256 | - $new = clone $this; |
|
257 | - $new->uploadedFiles = $uploadedFiles; |
|
258 | - |
|
259 | - return $new; |
|
260 | - } |
|
261 | - |
|
262 | - public function getCookieParams(): array |
|
263 | - { |
|
264 | - return $this->cookieParams; |
|
265 | - } |
|
266 | - |
|
267 | - public function withCookieParams(array $cookies): ServerRequestInterface |
|
268 | - { |
|
269 | - $new = clone $this; |
|
270 | - $new->cookieParams = $cookies; |
|
271 | - |
|
272 | - return $new; |
|
273 | - } |
|
274 | - |
|
275 | - public function getQueryParams(): array |
|
276 | - { |
|
277 | - return $this->queryParams; |
|
278 | - } |
|
279 | - |
|
280 | - public function withQueryParams(array $query): ServerRequestInterface |
|
281 | - { |
|
282 | - $new = clone $this; |
|
283 | - $new->queryParams = $query; |
|
284 | - |
|
285 | - return $new; |
|
286 | - } |
|
287 | - |
|
288 | - /** |
|
289 | - * @return array|object|null |
|
290 | - */ |
|
291 | - public function getParsedBody() |
|
292 | - { |
|
293 | - return $this->parsedBody; |
|
294 | - } |
|
295 | - |
|
296 | - public function withParsedBody($data): ServerRequestInterface |
|
297 | - { |
|
298 | - $new = clone $this; |
|
299 | - $new->parsedBody = $data; |
|
300 | - |
|
301 | - return $new; |
|
302 | - } |
|
303 | - |
|
304 | - public function getAttributes(): array |
|
305 | - { |
|
306 | - return $this->attributes; |
|
307 | - } |
|
308 | - |
|
309 | - /** |
|
310 | - * @return mixed |
|
311 | - */ |
|
312 | - public function getAttribute($attribute, $default = null) |
|
313 | - { |
|
314 | - if (false === array_key_exists($attribute, $this->attributes)) { |
|
315 | - return $default; |
|
316 | - } |
|
317 | - |
|
318 | - return $this->attributes[$attribute]; |
|
319 | - } |
|
320 | - |
|
321 | - public function withAttribute($attribute, $value): ServerRequestInterface |
|
322 | - { |
|
323 | - $new = clone $this; |
|
324 | - $new->attributes[$attribute] = $value; |
|
325 | - |
|
326 | - return $new; |
|
327 | - } |
|
328 | - |
|
329 | - public function withoutAttribute($attribute): ServerRequestInterface |
|
330 | - { |
|
331 | - if (false === array_key_exists($attribute, $this->attributes)) { |
|
332 | - return $this; |
|
333 | - } |
|
334 | - |
|
335 | - $new = clone $this; |
|
336 | - unset($new->attributes[$attribute]); |
|
337 | - |
|
338 | - return $new; |
|
339 | - } |
|
29 | + /** |
|
30 | + * @var array |
|
31 | + */ |
|
32 | + private $attributes = []; |
|
33 | + |
|
34 | + /** |
|
35 | + * @var array |
|
36 | + */ |
|
37 | + private $cookieParams = []; |
|
38 | + |
|
39 | + /** |
|
40 | + * @var array|object|null |
|
41 | + */ |
|
42 | + private $parsedBody; |
|
43 | + |
|
44 | + /** |
|
45 | + * @var array |
|
46 | + */ |
|
47 | + private $queryParams = []; |
|
48 | + |
|
49 | + /** |
|
50 | + * @var array |
|
51 | + */ |
|
52 | + private $serverParams; |
|
53 | + |
|
54 | + /** |
|
55 | + * @var array |
|
56 | + */ |
|
57 | + private $uploadedFiles = []; |
|
58 | + |
|
59 | + /** |
|
60 | + * @param string $method HTTP method |
|
61 | + * @param string|UriInterface $uri URI |
|
62 | + * @param (string|string[])[] $headers Request headers |
|
63 | + * @param string|resource|StreamInterface|null $body Request body |
|
64 | + * @param string $version Protocol version |
|
65 | + * @param array $serverParams Typically the $_SERVER superglobal |
|
66 | + */ |
|
67 | + public function __construct( |
|
68 | + string $method, |
|
69 | + $uri, |
|
70 | + array $headers = [], |
|
71 | + $body = null, |
|
72 | + string $version = '1.1', |
|
73 | + array $serverParams = [] |
|
74 | + ) { |
|
75 | + $this->serverParams = $serverParams; |
|
76 | + |
|
77 | + parent::__construct($method, $uri, $headers, $body, $version); |
|
78 | + } |
|
79 | + |
|
80 | + /** |
|
81 | + * Return an UploadedFile instance array. |
|
82 | + * |
|
83 | + * @param array $files An array which respect $_FILES structure |
|
84 | + * |
|
85 | + * @throws InvalidArgumentException for unrecognized values |
|
86 | + */ |
|
87 | + public static function normalizeFiles(array $files): array |
|
88 | + { |
|
89 | + $normalized = []; |
|
90 | + |
|
91 | + foreach ($files as $key => $value) { |
|
92 | + if ($value instanceof UploadedFileInterface) { |
|
93 | + $normalized[$key] = $value; |
|
94 | + } elseif (is_array($value) && isset($value['tmp_name'])) { |
|
95 | + $normalized[$key] = self::createUploadedFileFromSpec($value); |
|
96 | + } elseif (is_array($value)) { |
|
97 | + $normalized[$key] = self::normalizeFiles($value); |
|
98 | + continue; |
|
99 | + } else { |
|
100 | + throw new InvalidArgumentException('Invalid value in files specification'); |
|
101 | + } |
|
102 | + } |
|
103 | + |
|
104 | + return $normalized; |
|
105 | + } |
|
106 | + |
|
107 | + /** |
|
108 | + * Create and return an UploadedFile instance from a $_FILES specification. |
|
109 | + * |
|
110 | + * If the specification represents an array of values, this method will |
|
111 | + * delegate to normalizeNestedFileSpec() and return that return value. |
|
112 | + * |
|
113 | + * @param array $value $_FILES struct |
|
114 | + * |
|
115 | + * @return UploadedFileInterface|UploadedFileInterface[] |
|
116 | + */ |
|
117 | + private static function createUploadedFileFromSpec(array $value) |
|
118 | + { |
|
119 | + if (is_array($value['tmp_name'])) { |
|
120 | + return self::normalizeNestedFileSpec($value); |
|
121 | + } |
|
122 | + |
|
123 | + return new UploadedFile( |
|
124 | + $value['tmp_name'], |
|
125 | + (int) $value['size'], |
|
126 | + (int) $value['error'], |
|
127 | + $value['name'], |
|
128 | + $value['type'] |
|
129 | + ); |
|
130 | + } |
|
131 | + |
|
132 | + /** |
|
133 | + * Normalize an array of file specifications. |
|
134 | + * |
|
135 | + * Loops through all nested files and returns a normalized array of |
|
136 | + * UploadedFileInterface instances. |
|
137 | + * |
|
138 | + * @return UploadedFileInterface[] |
|
139 | + */ |
|
140 | + private static function normalizeNestedFileSpec(array $files = []): array |
|
141 | + { |
|
142 | + $normalizedFiles = []; |
|
143 | + |
|
144 | + foreach (array_keys($files['tmp_name']) as $key) { |
|
145 | + $spec = [ |
|
146 | + 'tmp_name' => $files['tmp_name'][$key], |
|
147 | + 'size' => $files['size'][$key] ?? null, |
|
148 | + 'error' => $files['error'][$key] ?? null, |
|
149 | + 'name' => $files['name'][$key] ?? null, |
|
150 | + 'type' => $files['type'][$key] ?? null, |
|
151 | + ]; |
|
152 | + $normalizedFiles[$key] = self::createUploadedFileFromSpec($spec); |
|
153 | + } |
|
154 | + |
|
155 | + return $normalizedFiles; |
|
156 | + } |
|
157 | + |
|
158 | + /** |
|
159 | + * Return a ServerRequest populated with superglobals: |
|
160 | + * $_GET |
|
161 | + * $_POST |
|
162 | + * $_COOKIE |
|
163 | + * $_FILES |
|
164 | + * $_SERVER |
|
165 | + */ |
|
166 | + public static function fromGlobals(): ServerRequestInterface |
|
167 | + { |
|
168 | + $method = $_SERVER['REQUEST_METHOD'] ?? 'GET'; |
|
169 | + $headers = getallheaders(); |
|
170 | + $uri = self::getUriFromGlobals(); |
|
171 | + $body = new CachingStream(new LazyOpenStream('php://input', 'r+')); |
|
172 | + $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']) : '1.1'; |
|
173 | + |
|
174 | + $serverRequest = new ServerRequest($method, $uri, $headers, $body, $protocol, $_SERVER); |
|
175 | + |
|
176 | + return $serverRequest |
|
177 | + ->withCookieParams($_COOKIE) |
|
178 | + ->withQueryParams($_GET) |
|
179 | + ->withParsedBody($_POST) |
|
180 | + ->withUploadedFiles(self::normalizeFiles($_FILES)); |
|
181 | + } |
|
182 | + |
|
183 | + private static function extractHostAndPortFromAuthority(string $authority): array |
|
184 | + { |
|
185 | + $uri = 'http://'.$authority; |
|
186 | + $parts = parse_url($uri); |
|
187 | + if (false === $parts) { |
|
188 | + return [null, null]; |
|
189 | + } |
|
190 | + |
|
191 | + $host = $parts['host'] ?? null; |
|
192 | + $port = $parts['port'] ?? null; |
|
193 | + |
|
194 | + return [$host, $port]; |
|
195 | + } |
|
196 | + |
|
197 | + /** |
|
198 | + * Get a Uri populated with values from $_SERVER. |
|
199 | + */ |
|
200 | + public static function getUriFromGlobals(): UriInterface |
|
201 | + { |
|
202 | + $uri = new Uri(''); |
|
203 | + |
|
204 | + $uri = $uri->withScheme(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http'); |
|
205 | + |
|
206 | + $hasPort = false; |
|
207 | + if (isset($_SERVER['HTTP_HOST'])) { |
|
208 | + [$host, $port] = self::extractHostAndPortFromAuthority($_SERVER['HTTP_HOST']); |
|
209 | + if ($host !== null) { |
|
210 | + $uri = $uri->withHost($host); |
|
211 | + } |
|
212 | + |
|
213 | + if ($port !== null) { |
|
214 | + $hasPort = true; |
|
215 | + $uri = $uri->withPort($port); |
|
216 | + } |
|
217 | + } elseif (isset($_SERVER['SERVER_NAME'])) { |
|
218 | + $uri = $uri->withHost($_SERVER['SERVER_NAME']); |
|
219 | + } elseif (isset($_SERVER['SERVER_ADDR'])) { |
|
220 | + $uri = $uri->withHost($_SERVER['SERVER_ADDR']); |
|
221 | + } |
|
222 | + |
|
223 | + if (!$hasPort && isset($_SERVER['SERVER_PORT'])) { |
|
224 | + $uri = $uri->withPort($_SERVER['SERVER_PORT']); |
|
225 | + } |
|
226 | + |
|
227 | + $hasQuery = false; |
|
228 | + if (isset($_SERVER['REQUEST_URI'])) { |
|
229 | + $requestUriParts = explode('?', $_SERVER['REQUEST_URI'], 2); |
|
230 | + $uri = $uri->withPath($requestUriParts[0]); |
|
231 | + if (isset($requestUriParts[1])) { |
|
232 | + $hasQuery = true; |
|
233 | + $uri = $uri->withQuery($requestUriParts[1]); |
|
234 | + } |
|
235 | + } |
|
236 | + |
|
237 | + if (!$hasQuery && isset($_SERVER['QUERY_STRING'])) { |
|
238 | + $uri = $uri->withQuery($_SERVER['QUERY_STRING']); |
|
239 | + } |
|
240 | + |
|
241 | + return $uri; |
|
242 | + } |
|
243 | + |
|
244 | + public function getServerParams(): array |
|
245 | + { |
|
246 | + return $this->serverParams; |
|
247 | + } |
|
248 | + |
|
249 | + public function getUploadedFiles(): array |
|
250 | + { |
|
251 | + return $this->uploadedFiles; |
|
252 | + } |
|
253 | + |
|
254 | + public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface |
|
255 | + { |
|
256 | + $new = clone $this; |
|
257 | + $new->uploadedFiles = $uploadedFiles; |
|
258 | + |
|
259 | + return $new; |
|
260 | + } |
|
261 | + |
|
262 | + public function getCookieParams(): array |
|
263 | + { |
|
264 | + return $this->cookieParams; |
|
265 | + } |
|
266 | + |
|
267 | + public function withCookieParams(array $cookies): ServerRequestInterface |
|
268 | + { |
|
269 | + $new = clone $this; |
|
270 | + $new->cookieParams = $cookies; |
|
271 | + |
|
272 | + return $new; |
|
273 | + } |
|
274 | + |
|
275 | + public function getQueryParams(): array |
|
276 | + { |
|
277 | + return $this->queryParams; |
|
278 | + } |
|
279 | + |
|
280 | + public function withQueryParams(array $query): ServerRequestInterface |
|
281 | + { |
|
282 | + $new = clone $this; |
|
283 | + $new->queryParams = $query; |
|
284 | + |
|
285 | + return $new; |
|
286 | + } |
|
287 | + |
|
288 | + /** |
|
289 | + * @return array|object|null |
|
290 | + */ |
|
291 | + public function getParsedBody() |
|
292 | + { |
|
293 | + return $this->parsedBody; |
|
294 | + } |
|
295 | + |
|
296 | + public function withParsedBody($data): ServerRequestInterface |
|
297 | + { |
|
298 | + $new = clone $this; |
|
299 | + $new->parsedBody = $data; |
|
300 | + |
|
301 | + return $new; |
|
302 | + } |
|
303 | + |
|
304 | + public function getAttributes(): array |
|
305 | + { |
|
306 | + return $this->attributes; |
|
307 | + } |
|
308 | + |
|
309 | + /** |
|
310 | + * @return mixed |
|
311 | + */ |
|
312 | + public function getAttribute($attribute, $default = null) |
|
313 | + { |
|
314 | + if (false === array_key_exists($attribute, $this->attributes)) { |
|
315 | + return $default; |
|
316 | + } |
|
317 | + |
|
318 | + return $this->attributes[$attribute]; |
|
319 | + } |
|
320 | + |
|
321 | + public function withAttribute($attribute, $value): ServerRequestInterface |
|
322 | + { |
|
323 | + $new = clone $this; |
|
324 | + $new->attributes[$attribute] = $value; |
|
325 | + |
|
326 | + return $new; |
|
327 | + } |
|
328 | + |
|
329 | + public function withoutAttribute($attribute): ServerRequestInterface |
|
330 | + { |
|
331 | + if (false === array_key_exists($attribute, $this->attributes)) { |
|
332 | + return $this; |
|
333 | + } |
|
334 | + |
|
335 | + $new = clone $this; |
|
336 | + unset($new->attributes[$attribute]); |
|
337 | + |
|
338 | + return $new; |
|
339 | + } |
|
340 | 340 | } |
@@ -122,8 +122,8 @@ |
||
122 | 122 | |
123 | 123 | return new UploadedFile( |
124 | 124 | $value['tmp_name'], |
125 | - (int) $value['size'], |
|
126 | - (int) $value['error'], |
|
125 | + (int)$value['size'], |
|
126 | + (int)$value['error'], |
|
127 | 127 | $value['name'], |
128 | 128 | $value['type'] |
129 | 129 | ); |
@@ -24,8 +24,7 @@ |
||
24 | 24 | * implemented such that they retain the internal state of the current |
25 | 25 | * message and return a new instance that contains the changed state. |
26 | 26 | */ |
27 | -class ServerRequest extends Request implements ServerRequestInterface |
|
28 | -{ |
|
27 | +class ServerRequest extends Request implements ServerRequestInterface { |
|
29 | 28 | /** |
30 | 29 | * @var array |
31 | 30 | */ |
@@ -12,154 +12,154 @@ |
||
12 | 12 | */ |
13 | 13 | final class MultipartStream implements StreamInterface |
14 | 14 | { |
15 | - use StreamDecoratorTrait; |
|
16 | - |
|
17 | - /** @var string */ |
|
18 | - private $boundary; |
|
19 | - |
|
20 | - /** @var StreamInterface */ |
|
21 | - private $stream; |
|
22 | - |
|
23 | - /** |
|
24 | - * @param array $elements Array of associative arrays, each containing a |
|
25 | - * required "name" key mapping to the form field, |
|
26 | - * name, a required "contents" key mapping to a |
|
27 | - * StreamInterface/resource/string, an optional |
|
28 | - * "headers" associative array of custom headers, |
|
29 | - * and an optional "filename" key mapping to a |
|
30 | - * string to send as the filename in the part. |
|
31 | - * @param string $boundary You can optionally provide a specific boundary |
|
32 | - * |
|
33 | - * @throws \InvalidArgumentException |
|
34 | - */ |
|
35 | - public function __construct(array $elements = [], string $boundary = null) |
|
36 | - { |
|
37 | - $this->boundary = $boundary ?: bin2hex(random_bytes(20)); |
|
38 | - $this->stream = $this->createStream($elements); |
|
39 | - } |
|
40 | - |
|
41 | - public function getBoundary(): string |
|
42 | - { |
|
43 | - return $this->boundary; |
|
44 | - } |
|
45 | - |
|
46 | - public function isWritable(): bool |
|
47 | - { |
|
48 | - return false; |
|
49 | - } |
|
50 | - |
|
51 | - /** |
|
52 | - * Get the headers needed before transferring the content of a POST file |
|
53 | - * |
|
54 | - * @param string[] $headers |
|
55 | - */ |
|
56 | - private function getHeaders(array $headers): string |
|
57 | - { |
|
58 | - $str = ''; |
|
59 | - foreach ($headers as $key => $value) { |
|
60 | - $str .= "{$key}: {$value}\r\n"; |
|
61 | - } |
|
62 | - |
|
63 | - return "--{$this->boundary}\r\n".trim($str)."\r\n\r\n"; |
|
64 | - } |
|
65 | - |
|
66 | - /** |
|
67 | - * Create the aggregate stream that will be used to upload the POST data |
|
68 | - */ |
|
69 | - protected function createStream(array $elements = []): StreamInterface |
|
70 | - { |
|
71 | - $stream = new AppendStream(); |
|
72 | - |
|
73 | - foreach ($elements as $element) { |
|
74 | - if (!is_array($element)) { |
|
75 | - throw new \UnexpectedValueException('An array is expected'); |
|
76 | - } |
|
77 | - $this->addElement($stream, $element); |
|
78 | - } |
|
79 | - |
|
80 | - // Add the trailing boundary with CRLF |
|
81 | - $stream->addStream(Utils::streamFor("--{$this->boundary}--\r\n")); |
|
82 | - |
|
83 | - return $stream; |
|
84 | - } |
|
85 | - |
|
86 | - private function addElement(AppendStream $stream, array $element): void |
|
87 | - { |
|
88 | - foreach (['contents', 'name'] as $key) { |
|
89 | - if (!array_key_exists($key, $element)) { |
|
90 | - throw new \InvalidArgumentException("A '{$key}' key is required"); |
|
91 | - } |
|
92 | - } |
|
93 | - |
|
94 | - $element['contents'] = Utils::streamFor($element['contents']); |
|
95 | - |
|
96 | - if (empty($element['filename'])) { |
|
97 | - $uri = $element['contents']->getMetadata('uri'); |
|
98 | - if ($uri && \is_string($uri) && \substr($uri, 0, 6) !== 'php://' && \substr($uri, 0, 7) !== 'data://') { |
|
99 | - $element['filename'] = $uri; |
|
100 | - } |
|
101 | - } |
|
102 | - |
|
103 | - [$body, $headers] = $this->createElement( |
|
104 | - $element['name'], |
|
105 | - $element['contents'], |
|
106 | - $element['filename'] ?? null, |
|
107 | - $element['headers'] ?? [] |
|
108 | - ); |
|
109 | - |
|
110 | - $stream->addStream(Utils::streamFor($this->getHeaders($headers))); |
|
111 | - $stream->addStream($body); |
|
112 | - $stream->addStream(Utils::streamFor("\r\n")); |
|
113 | - } |
|
114 | - |
|
115 | - /** |
|
116 | - * @param string[] $headers |
|
117 | - * |
|
118 | - * @return array{0: StreamInterface, 1: string[]} |
|
119 | - */ |
|
120 | - private function createElement(string $name, StreamInterface $stream, ?string $filename, array $headers): array |
|
121 | - { |
|
122 | - // Set a default content-disposition header if one was no provided |
|
123 | - $disposition = self::getHeader($headers, 'content-disposition'); |
|
124 | - if (!$disposition) { |
|
125 | - $headers['Content-Disposition'] = ($filename === '0' || $filename) |
|
126 | - ? sprintf( |
|
127 | - 'form-data; name="%s"; filename="%s"', |
|
128 | - $name, |
|
129 | - basename($filename) |
|
130 | - ) |
|
131 | - : "form-data; name=\"{$name}\""; |
|
132 | - } |
|
133 | - |
|
134 | - // Set a default content-length header if one was no provided |
|
135 | - $length = self::getHeader($headers, 'content-length'); |
|
136 | - if (!$length) { |
|
137 | - if ($length = $stream->getSize()) { |
|
138 | - $headers['Content-Length'] = (string) $length; |
|
139 | - } |
|
140 | - } |
|
141 | - |
|
142 | - // Set a default Content-Type if one was not supplied |
|
143 | - $type = self::getHeader($headers, 'content-type'); |
|
144 | - if (!$type && ($filename === '0' || $filename)) { |
|
145 | - $headers['Content-Type'] = MimeType::fromFilename($filename) ?? 'application/octet-stream'; |
|
146 | - } |
|
147 | - |
|
148 | - return [$stream, $headers]; |
|
149 | - } |
|
150 | - |
|
151 | - /** |
|
152 | - * @param string[] $headers |
|
153 | - */ |
|
154 | - private static function getHeader(array $headers, string $key): ?string |
|
155 | - { |
|
156 | - $lowercaseHeader = strtolower($key); |
|
157 | - foreach ($headers as $k => $v) { |
|
158 | - if (strtolower((string) $k) === $lowercaseHeader) { |
|
159 | - return $v; |
|
160 | - } |
|
161 | - } |
|
162 | - |
|
163 | - return null; |
|
164 | - } |
|
15 | + use StreamDecoratorTrait; |
|
16 | + |
|
17 | + /** @var string */ |
|
18 | + private $boundary; |
|
19 | + |
|
20 | + /** @var StreamInterface */ |
|
21 | + private $stream; |
|
22 | + |
|
23 | + /** |
|
24 | + * @param array $elements Array of associative arrays, each containing a |
|
25 | + * required "name" key mapping to the form field, |
|
26 | + * name, a required "contents" key mapping to a |
|
27 | + * StreamInterface/resource/string, an optional |
|
28 | + * "headers" associative array of custom headers, |
|
29 | + * and an optional "filename" key mapping to a |
|
30 | + * string to send as the filename in the part. |
|
31 | + * @param string $boundary You can optionally provide a specific boundary |
|
32 | + * |
|
33 | + * @throws \InvalidArgumentException |
|
34 | + */ |
|
35 | + public function __construct(array $elements = [], string $boundary = null) |
|
36 | + { |
|
37 | + $this->boundary = $boundary ?: bin2hex(random_bytes(20)); |
|
38 | + $this->stream = $this->createStream($elements); |
|
39 | + } |
|
40 | + |
|
41 | + public function getBoundary(): string |
|
42 | + { |
|
43 | + return $this->boundary; |
|
44 | + } |
|
45 | + |
|
46 | + public function isWritable(): bool |
|
47 | + { |
|
48 | + return false; |
|
49 | + } |
|
50 | + |
|
51 | + /** |
|
52 | + * Get the headers needed before transferring the content of a POST file |
|
53 | + * |
|
54 | + * @param string[] $headers |
|
55 | + */ |
|
56 | + private function getHeaders(array $headers): string |
|
57 | + { |
|
58 | + $str = ''; |
|
59 | + foreach ($headers as $key => $value) { |
|
60 | + $str .= "{$key}: {$value}\r\n"; |
|
61 | + } |
|
62 | + |
|
63 | + return "--{$this->boundary}\r\n".trim($str)."\r\n\r\n"; |
|
64 | + } |
|
65 | + |
|
66 | + /** |
|
67 | + * Create the aggregate stream that will be used to upload the POST data |
|
68 | + */ |
|
69 | + protected function createStream(array $elements = []): StreamInterface |
|
70 | + { |
|
71 | + $stream = new AppendStream(); |
|
72 | + |
|
73 | + foreach ($elements as $element) { |
|
74 | + if (!is_array($element)) { |
|
75 | + throw new \UnexpectedValueException('An array is expected'); |
|
76 | + } |
|
77 | + $this->addElement($stream, $element); |
|
78 | + } |
|
79 | + |
|
80 | + // Add the trailing boundary with CRLF |
|
81 | + $stream->addStream(Utils::streamFor("--{$this->boundary}--\r\n")); |
|
82 | + |
|
83 | + return $stream; |
|
84 | + } |
|
85 | + |
|
86 | + private function addElement(AppendStream $stream, array $element): void |
|
87 | + { |
|
88 | + foreach (['contents', 'name'] as $key) { |
|
89 | + if (!array_key_exists($key, $element)) { |
|
90 | + throw new \InvalidArgumentException("A '{$key}' key is required"); |
|
91 | + } |
|
92 | + } |
|
93 | + |
|
94 | + $element['contents'] = Utils::streamFor($element['contents']); |
|
95 | + |
|
96 | + if (empty($element['filename'])) { |
|
97 | + $uri = $element['contents']->getMetadata('uri'); |
|
98 | + if ($uri && \is_string($uri) && \substr($uri, 0, 6) !== 'php://' && \substr($uri, 0, 7) !== 'data://') { |
|
99 | + $element['filename'] = $uri; |
|
100 | + } |
|
101 | + } |
|
102 | + |
|
103 | + [$body, $headers] = $this->createElement( |
|
104 | + $element['name'], |
|
105 | + $element['contents'], |
|
106 | + $element['filename'] ?? null, |
|
107 | + $element['headers'] ?? [] |
|
108 | + ); |
|
109 | + |
|
110 | + $stream->addStream(Utils::streamFor($this->getHeaders($headers))); |
|
111 | + $stream->addStream($body); |
|
112 | + $stream->addStream(Utils::streamFor("\r\n")); |
|
113 | + } |
|
114 | + |
|
115 | + /** |
|
116 | + * @param string[] $headers |
|
117 | + * |
|
118 | + * @return array{0: StreamInterface, 1: string[]} |
|
119 | + */ |
|
120 | + private function createElement(string $name, StreamInterface $stream, ?string $filename, array $headers): array |
|
121 | + { |
|
122 | + // Set a default content-disposition header if one was no provided |
|
123 | + $disposition = self::getHeader($headers, 'content-disposition'); |
|
124 | + if (!$disposition) { |
|
125 | + $headers['Content-Disposition'] = ($filename === '0' || $filename) |
|
126 | + ? sprintf( |
|
127 | + 'form-data; name="%s"; filename="%s"', |
|
128 | + $name, |
|
129 | + basename($filename) |
|
130 | + ) |
|
131 | + : "form-data; name=\"{$name}\""; |
|
132 | + } |
|
133 | + |
|
134 | + // Set a default content-length header if one was no provided |
|
135 | + $length = self::getHeader($headers, 'content-length'); |
|
136 | + if (!$length) { |
|
137 | + if ($length = $stream->getSize()) { |
|
138 | + $headers['Content-Length'] = (string) $length; |
|
139 | + } |
|
140 | + } |
|
141 | + |
|
142 | + // Set a default Content-Type if one was not supplied |
|
143 | + $type = self::getHeader($headers, 'content-type'); |
|
144 | + if (!$type && ($filename === '0' || $filename)) { |
|
145 | + $headers['Content-Type'] = MimeType::fromFilename($filename) ?? 'application/octet-stream'; |
|
146 | + } |
|
147 | + |
|
148 | + return [$stream, $headers]; |
|
149 | + } |
|
150 | + |
|
151 | + /** |
|
152 | + * @param string[] $headers |
|
153 | + */ |
|
154 | + private static function getHeader(array $headers, string $key): ?string |
|
155 | + { |
|
156 | + $lowercaseHeader = strtolower($key); |
|
157 | + foreach ($headers as $k => $v) { |
|
158 | + if (strtolower((string) $k) === $lowercaseHeader) { |
|
159 | + return $v; |
|
160 | + } |
|
161 | + } |
|
162 | + |
|
163 | + return null; |
|
164 | + } |
|
165 | 165 | } |
@@ -135,7 +135,7 @@ discard block |
||
135 | 135 | $length = self::getHeader($headers, 'content-length'); |
136 | 136 | if (!$length) { |
137 | 137 | if ($length = $stream->getSize()) { |
138 | - $headers['Content-Length'] = (string) $length; |
|
138 | + $headers['Content-Length'] = (string)$length; |
|
139 | 139 | } |
140 | 140 | } |
141 | 141 | |
@@ -155,7 +155,7 @@ discard block |
||
155 | 155 | { |
156 | 156 | $lowercaseHeader = strtolower($key); |
157 | 157 | foreach ($headers as $k => $v) { |
158 | - if (strtolower((string) $k) === $lowercaseHeader) { |
|
158 | + if (strtolower((string)$k) === $lowercaseHeader) { |
|
159 | 159 | return $v; |
160 | 160 | } |
161 | 161 | } |
@@ -10,8 +10,7 @@ |
||
10 | 10 | * Stream that when read returns bytes for a streaming multipart or |
11 | 11 | * multipart/form-data stream. |
12 | 12 | */ |
13 | -final class MultipartStream implements StreamInterface |
|
14 | -{ |
|
13 | +final class MultipartStream implements StreamInterface { |
|
15 | 14 | use StreamDecoratorTrait; |
16 | 15 | |
17 | 16 | /** @var string */ |