PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.20
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.20
1.10.20 1.10.19 1.10.18 1.10.17 1.10.16 1.10.15 1.10.13 1.10.14 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 untagged-3d9b7ccddc54df87c672 1.10.7 1.10.6 1.10.5 1.10.3 1.10.4 1.10.2 1.10.1 1.10.0 1.9.17 1.9.15 1.9.16 All 164 releases
woocommerce-pos / vendor_prefixed / guzzlehttp / psr7 / src / UriResolver.php

UriResolver.php in WCPOS – Point of Sale (POS) plugin for WooCommerce 1.10.20, at vendor_prefixed/guzzlehttp/psr7/src/UriResolver.php

264 lines 12.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare (strict_types=1);
4 namespace WCPOS\Vendor\GuzzleHttp\Psr7;
5
6 use WCPOS\Vendor\Psr\Http\Message\UriInterface;
7 /**
8 * Resolves a URI reference in the context of a base URI and the opposite way.
9 *
10 * @author Tobias Schultze
11 *
12 * @see https://datatracker.ietf.org/doc/html/rfc3986#section-5
13 */
14 final class UriResolver
15 {
16 /**
17 * Removes dot segments from a path and returns the new path according to
18 * RFC 3986 Section 5.2.4.
19 *
20 * Excess `..` segments above the root of an absolute path are dropped
21 * without consuming the root, so the result can begin with `//` (e.g.
22 * `/..//a` becomes `//a`). Such a path is not valid for a URI without an
23 * authority (RFC 3986 Section 3.3); `resolve()` and
24 * `UriNormalizer::normalize()` serialize it with a `/.` prefix in that
25 * case, like the WHATWG URL Standard.
26 *
27 * @see https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4
28 */
29 public static function removeDotSegments(string $path) : string
30 {
31 if ($path === '' || $path === '/') {
32 return $path;
33 }
34 $results = [];
35 $segments = \explode('/', $path);
36 // The first segment of an absolute path is the empty root marker producing the
37 // leading slash. RFC 3986 Section 5.2.4 (2C) drops ".." segments in excess of
38 // the path hierarchy without consuming the root, so it must never be popped.
39 $floor = $segments[0] === '' ? 1 : 0;
40 foreach ($segments as $segment) {
41 if ($segment === '..') {
42 if (\count($results) > $floor) {
43 \array_pop($results);
44 }
45 } elseif ($segment !== '.') {
46 $results[] = $segment;
47 }
48 }
49 $newPath = \implode('/', $results);
50 if (\str_starts_with($path, '/') && !\str_starts_with($newPath, '/')) {
51 // Re-add the leading slash if necessary for cases like "/.."
52 $newPath = '/' . $newPath;
53 } elseif ($newPath !== '' && ($segment === '.' || $segment === '..')) {
54 // Add the trailing slash if necessary
55 // If newPath is not empty, then $segment must be set and is the last segment from the foreach
56 $newPath .= '/';
57 }
58 return $newPath;
59 }
60 /**
61 * Returns the path, prefixed with "/." or "./" when the URI could not
62 * otherwise hold it.
63 *
64 * A URI without an authority cannot hold a path beginning with "//" (RFC
65 * 3986 Section 3.3), but removeDotSegments() can produce one. The "/."
66 * prefix serializes such a path unambiguously, the same way the WHATWG URL
67 * Standard does, and resolves back to the same path. Hostless http and
68 * https Uri instances gain the default localhost host when the path is
69 * written, so the path cannot be mistaken for an authority and the prefix
70 * is not added.
71 *
72 * A relative-path reference cannot begin with a segment containing a colon
73 * (RFC 3986 Section 4.2), as it would be mistaken for a scheme name, but
74 * reference resolution and percent-encoding normalization can produce one.
75 * The "./" prefix the RFC prescribes resolves back to the same path.
76 *
77 * @see https://url.spec.whatwg.org/#url-serializing
78 * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.2
79 *
80 * @internal
81 */
82 public static function guardedPath(UriInterface $uri, string $path) : string
83 {
84 if ($uri->getAuthority() !== '') {
85 return $path;
86 }
87 if (\str_starts_with($path, '//')) {
88 if ($uri instanceof Uri && ($uri->getScheme() === 'http' || $uri->getScheme() === 'https')) {
89 return $path;
90 }
91 return '/.' . $path;
92 }
93 if ($uri->getScheme() === '' && \str_contains(\explode('/', $path, 2)[0], ':')) {
94 return './' . $path;
95 }
96 return $path;
97 }
98 /**
99 * Converts the relative URI into a new URI that is resolved against the
100 * base URI.
101 *
102 * When the resolved path is a relative-path reference whose first segment
103 * contains a colon, which would be mistaken for a scheme name (RFC 3986
104 * Section 4.2), it is prefixed with `./`, e.g. `./a:b`.
105 *
106 * @see https://datatracker.ietf.org/doc/html/rfc3986#section-5.2
107 * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.2
108 */
109 public static function resolve(UriInterface $base, UriInterface $rel) : UriInterface
110 {
111 if ((string) $rel === '') {
112 // we can simply return the same base URI instance for this same-document reference
113 return $base;
114 }
115 if ($rel->getScheme() != '') {
116 return $rel->withPath(self::guardedPath($rel, self::removeDotSegments(Uri::rawPath($rel))));
117 }
118 if ($rel->getAuthority() != '') {
119 return $rel->withScheme($base->getScheme())->withPath(self::removeDotSegments(Uri::rawPath($rel)));
120 }
121 $relPath = Uri::rawPath($rel);
122 if ($relPath === '') {
123 // the base path is used as-is per RFC 3986 Section 5.2.2, so it must not be
124 // rewritten through a getPath()/withPath() round-trip
125 return $base->withQuery($rel->getQuery() != '' ? $rel->getQuery() : $base->getQuery())->withFragment($rel->getFragment());
126 }
127 if (\str_starts_with($relPath, '/')) {
128 $targetPath = $relPath;
129 } else {
130 $basePath = Uri::rawPath($base);
131 if ($base->getAuthority() != '' && $basePath === '') {
132 $targetPath = '/' . $relPath;
133 } else {
134 $lastSlashPos = \strrpos($basePath, '/');
135 if ($lastSlashPos === \false) {
136 $targetPath = $relPath;
137 } else {
138 $targetPath = \substr($basePath, 0, $lastSlashPos + 1) . $relPath;
139 }
140 }
141 }
142 $targetPath = self::removeDotSegments($targetPath);
143 return $base->withPath(self::guardedPath($base, $targetPath))->withQuery($rel->getQuery())->withFragment($rel->getFragment());
144 }
145 /**
146 * Returns the target URI as a relative reference from the base URI.
147 *
148 * This method is the counterpart to `resolve()`:
149 *
150 * (string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target))
151 *
152 * One use case is to use the current request URI as the base URI and then
153 * generate relative links in your documents to reduce the document size or
154 * offer self-contained downloadable document archives.
155 *
156 * $base = new Uri('http://example.com/a/b/');
157 * echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c')); // prints 'c'.
158 * echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y')); // prints '../x/y'.
159 * echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'.
160 * echo UriResolver::relativize($base, new Uri('http://example.org/a/b/')); // prints '//example.org/a/b/'.
161 * echo UriResolver::relativize($base, new Uri('http://example.com')); // prints '//example.com'.
162 *
163 * This method also accepts a target that is already relative and will try
164 * to relativize it further. Only a relative-path reference will be returned
165 * as-is.
166 *
167 * echo UriResolver::relativize($base, new Uri('/a/b/c')); // prints 'c' as well
168 */
169 public static function relativize(UriInterface $base, UriInterface $target) : UriInterface
170 {
171 if ($target->getScheme() !== '' && ($base->getScheme() !== $target->getScheme() || $target->getAuthority() === '' && $base->getAuthority() !== '')) {
172 return $target;
173 }
174 if (Uri::isRelativePathReference($target)) {
175 // As the target is already highly relative we return it as-is. It would be possible to resolve
176 // the target with `$target = self::resolve($base, $target);` and then try make it more relative
177 // by removing a duplicate query. But let's not do that automatically.
178 return $target;
179 }
180 if ($target->getAuthority() !== '' && $base->getAuthority() !== $target->getAuthority()) {
181 return $target->withScheme('');
182 }
183 // A same-authority target with an empty path can only be expressed by a
184 // network-path reference (RFC 3986 Section 5.2.2).
185 if (self::needsNetworkPathReference($base, $target)) {
186 return $target->withScheme('');
187 }
188 // We must remove the path before removing the authority because if the path starts with two slashes, the URI
189 // would turn invalid. And we also cannot set a relative path before removing the authority, as that is also
190 // invalid.
191 $emptyPathUri = $target->withScheme('')->withPath('')->withUserInfo('')->withPort(null)->withHost('');
192 if (Uri::rawPath($base) !== Uri::rawPath($target)) {
193 return $emptyPathUri->withPath(self::getRelativePath($base, $target));
194 }
195 if ($base->getQuery() === $target->getQuery() && ($target->getFragment() !== '' || $base->getFragment() === '')) {
196 // Only the target fragment is left. And it must be returned even if base and target fragment are the same.
197 return $emptyPathUri->withQuery('');
198 }
199 // If the base URI has a query or fragment that the target lacks, we cannot return an empty path
200 // reference as it would inherit that base component when resolving.
201 if ($target->getQuery() === '') {
202 $segments = \explode('/', Uri::rawPath($target));
203 /** @var string $lastSegment */
204 $lastSegment = \end($segments);
205 // A reference to an empty last segment must be prefixed with "./". The same applies
206 // to a segment with a colon character, which would be mistaken for a scheme name.
207 if ($lastSegment === '' || \str_contains($lastSegment, ':')) {
208 $lastSegment = "./{$lastSegment}";
209 }
210 return $emptyPathUri->withPath($lastSegment);
211 }
212 return $emptyPathUri;
213 }
214 /**
215 * Whether relativizing to $target requires a network-path reference.
216 *
217 * A same-authority target with an empty path is expressible by a shorter
218 * relative reference unless resolving one would inherit a base component
219 * the target lacks: the base path (kept by any empty-path reference), or
220 * the base query or fragment (inherited by the empty reference).
221 */
222 private static function needsNetworkPathReference(UriInterface $base, UriInterface $target) : bool
223 {
224 if ($target->getAuthority() === '' || Uri::rawPath($target) !== '') {
225 return \false;
226 }
227 return Uri::rawPath($base) !== '' || $base->getQuery() !== '' && $target->getQuery() === '' || $base->getFragment() !== '' && $target->getFragment() === '' && $base->getQuery() === $target->getQuery();
228 }
229 private static function getRelativePath(UriInterface $base, UriInterface $target) : string
230 {
231 $sourceSegments = \explode('/', Uri::rawPath($base));
232 $targetSegments = \explode('/', Uri::rawPath($target));
233 \array_pop($sourceSegments);
234 $targetLastSegment = \array_pop($targetSegments);
235 foreach ($sourceSegments as $i => $segment) {
236 if (isset($targetSegments[$i]) && $segment === $targetSegments[$i]) {
237 unset($sourceSegments[$i], $targetSegments[$i]);
238 } else {
239 break;
240 }
241 }
242 $targetSegments[] = $targetLastSegment;
243 $relativePath = \str_repeat('../', \count($sourceSegments)) . \implode('/', $targetSegments);
244 // A reference to am empty last segment or an empty first sub-segment must be prefixed with "./".
245 // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used
246 // as the first segment of a relative-path reference, as it would be mistaken for a scheme name.
247 if ($relativePath === '' || \str_contains(\explode('/', $relativePath, 2)[0], ':')) {
248 $relativePath = "./{$relativePath}";
249 } elseif (\str_starts_with($relativePath, '/')) {
250 if ($base->getAuthority() != '' && Uri::rawPath($base) === '') {
251 // In this case an extra slash is added by resolve() automatically. So we must not add one here.
252 $relativePath = ".{$relativePath}";
253 } else {
254 $relativePath = "./{$relativePath}";
255 }
256 }
257 return $relativePath;
258 }
259 private function __construct()
260 {
261 // cannot be instantiated
262 }
263 }
264