PluginProbe ʕ •ᴥ•ʔ
Independent Analytics – WordPress Analytics Plugin / 2.2.0
Independent Analytics – WordPress Analytics Plugin v2.2.0
2.15.5 2.15.4 2.15.3 2.15.2 2.15.1 2.15.0 2.14.10 trunk 1.1 1.10 1.10.1 1.11 1.12 1.13 1.14 1.15 1.16 1.17 1.17.1 1.17.2 1.17.3 1.17.4 1.18 1.18.1 1.19.0 1.19.1 1.2 1.20.0 1.21.0 1.22.0 1.22.1 1.23.0 1.23.1 1.24.0 1.24.1 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.28.1 1.28.2 1.28.3 1.29.0 1.3 1.30.0 1.30.1 1.4 1.5 1.6 1.7 1.8 1.9 2.0.0 2.0.1 2.1.4 2.1.5 2.1.6 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.10 2.11.2 2.11.3 2.11.4 2.11.5 2.11.6 2.11.7 2.11.8 2.11.9 2.12.0 2.12.1 2.12.2 2.13.1 2.13.2 2.13.5 2.13.6 2.14.0 2.14.1 2.14.2 2.14.4 2.14.6 2.14.7 2.14.8 2.14.9 2.2.0 2.2.1 2.3.1 2.3.2 2.4.2 2.4.3 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.7.1 2.7.2 2.7.3 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7
independent-analytics / vendor / league / uri / src / UriString.php
independent-analytics / vendor / league / uri / src Last commit date
Exceptions 2 years ago UriTemplate 2 years ago Http.php 2 years ago HttpFactory.php 2 years ago Uri.php 2 years ago UriInfo.php 2 years ago UriResolver.php 2 years ago UriString.php 2 years ago UriTemplate.php 2 years ago
UriString.php
386 lines
1 <?php
2
3 /**
4 * League.Uri (https://uri.thephpleague.com)
5 *
6 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11 declare (strict_types=1);
12 namespace IAWP_SCOPED\League\Uri;
13
14 use IAWP_SCOPED\League\Uri\Exceptions\IdnaConversionFailed;
15 use IAWP_SCOPED\League\Uri\Exceptions\IdnSupportMissing;
16 use IAWP_SCOPED\League\Uri\Exceptions\SyntaxError;
17 use IAWP_SCOPED\League\Uri\Idna\Idna;
18 use function array_merge;
19 use function explode;
20 use function filter_var;
21 use function gettype;
22 use function inet_pton;
23 use function is_object;
24 use function is_scalar;
25 use function method_exists;
26 use function preg_match;
27 use function rawurldecode;
28 use function sprintf;
29 use function strpos;
30 use function substr;
31 use const FILTER_FLAG_IPV6;
32 use const FILTER_VALIDATE_IP;
33 /**
34 * A class to parse a URI string according to RFC3986.
35 *
36 * @link https://tools.ietf.org/html/rfc3986
37 * @package League\Uri
38 * @author Ignace Nyamagana Butera <nyamsprod@gmail.com>
39 * @since 6.0.0
40 * @internal
41 */
42 final class UriString
43 {
44 /**
45 * Default URI component values.
46 */
47 private const URI_COMPONENTS = ['scheme' => null, 'user' => null, 'pass' => null, 'host' => null, 'port' => null, 'path' => '', 'query' => null, 'fragment' => null];
48 /**
49 * Simple URI which do not need any parsing.
50 */
51 private const URI_SCHORTCUTS = ['' => [], '#' => ['fragment' => ''], '?' => ['query' => ''], '?#' => ['query' => '', 'fragment' => ''], '/' => ['path' => '/'], '//' => ['host' => '']];
52 /**
53 * Range of invalid characters in URI string.
54 */
55 private const REGEXP_INVALID_URI_CHARS = '/[\\x00-\\x1f\\x7f]/';
56 /**
57 * RFC3986 regular expression URI splitter.
58 *
59 * @link https://tools.ietf.org/html/rfc3986#appendix-B
60 */
61 private const REGEXP_URI_PARTS = ',^
62 (?<scheme>(?<scontent>[^:/?\\#]+):)? # URI scheme component
63 (?<authority>//(?<acontent>[^/?\\#]*))? # URI authority part
64 (?<path>[^?\\#]*) # URI path component
65 (?<query>\\?(?<qcontent>[^\\#]*))? # URI query component
66 (?<fragment>\\#(?<fcontent>.*))? # URI fragment component
67 ,x';
68 /**
69 * URI scheme regular expresssion.
70 *
71 * @link https://tools.ietf.org/html/rfc3986#section-3.1
72 */
73 private const REGEXP_URI_SCHEME = '/^([a-z][a-z\\d\\+\\.\\-]*)?$/i';
74 /**
75 * IPvFuture regular expression.
76 *
77 * @link https://tools.ietf.org/html/rfc3986#section-3.2.2
78 */
79 private const REGEXP_IP_FUTURE = '/^
80 v(?<version>[A-F0-9])+\\.
81 (?:
82 (?<unreserved>[a-z0-9_~\\-\\.])|
83 (?<sub_delims>[!$&\'()*+,;=:]) # also include the : character
84 )+
85 $/ix';
86 /**
87 * General registered name regular expression.
88 *
89 * @link https://tools.ietf.org/html/rfc3986#section-3.2.2
90 */
91 private const REGEXP_REGISTERED_NAME = '/(?(DEFINE)
92 (?<unreserved>[a-z0-9_~\\-]) # . is missing as it is used to separate labels
93 (?<sub_delims>[!$&\'()*+,;=])
94 (?<encoded>%[A-F0-9]{2})
95 (?<reg_name>(?:(?&unreserved)|(?&sub_delims)|(?&encoded))*)
96 )
97 ^(?:(?&reg_name)\\.)*(?&reg_name)\\.?$/ix';
98 /**
99 * Invalid characters in host regular expression.
100 *
101 * @link https://tools.ietf.org/html/rfc3986#section-3.2.2
102 */
103 private const REGEXP_INVALID_HOST_CHARS = '/
104 [:\\/?#\\[\\]@ ] # gen-delims characters as well as the space character
105 /ix';
106 /**
107 * Invalid path for URI without scheme and authority regular expression.
108 *
109 * @link https://tools.ietf.org/html/rfc3986#section-3.3
110 */
111 private const REGEXP_INVALID_PATH = ',^(([^/]*):)(.*)?/,';
112 /**
113 * Host and Port splitter regular expression.
114 */
115 private const REGEXP_HOST_PORT = ',^(?<host>\\[.*\\]|[^:]*)(:(?<port>.*))?$,';
116 /**
117 * IDN Host detector regular expression.
118 */
119 private const REGEXP_IDN_PATTERN = '/[^\\x20-\\x7f]/';
120 /**
121 * Only the address block fe80::/10 can have a Zone ID attach to
122 * let's detect the link local significant 10 bits.
123 */
124 private const ZONE_ID_ADDRESS_BLOCK = "\xfe\x80";
125 /**
126 * Generate an URI string representation from its parsed representation
127 * returned by League\Uri\parse() or PHP's parse_url.
128 *
129 * If you supply your own array, you are responsible for providing
130 * valid components without their URI delimiters.
131 *
132 * @link https://tools.ietf.org/html/rfc3986#section-5.3
133 * @link https://tools.ietf.org/html/rfc3986#section-7.5
134 *
135 * @param array{
136 * scheme:?string,
137 * user:?string,
138 * pass:?string,
139 * host:?string,
140 * port:?int,
141 * path:string,
142 * query:?string,
143 * fragment:?string
144 * } $components
145 */
146 public static function build(array $components) : string
147 {
148 $result = $components['path'] ?? '';
149 if (isset($components['query'])) {
150 $result .= '?' . $components['query'];
151 }
152 if (isset($components['fragment'])) {
153 $result .= '#' . $components['fragment'];
154 }
155 $scheme = null;
156 if (isset($components['scheme'])) {
157 $scheme = $components['scheme'] . ':';
158 }
159 if (!isset($components['host'])) {
160 return $scheme . $result;
161 }
162 $scheme .= '//';
163 $authority = $components['host'];
164 if (isset($components['port'])) {
165 $authority .= ':' . $components['port'];
166 }
167 if (!isset($components['user'])) {
168 return $scheme . $authority . $result;
169 }
170 $authority = '@' . $authority;
171 if (!isset($components['pass'])) {
172 return $scheme . $components['user'] . $authority . $result;
173 }
174 return $scheme . $components['user'] . ':' . $components['pass'] . $authority . $result;
175 }
176 /**
177 * Parse an URI string into its components.
178 *
179 * This method parses a URI and returns an associative array containing any
180 * of the various components of the URI that are present.
181 *
182 * <code>
183 * $components = (new Parser())->parse('http://foo@test.example.com:42?query#');
184 * var_export($components);
185 * //will display
186 * array(
187 * 'scheme' => 'http', // the URI scheme component
188 * 'user' => 'foo', // the URI user component
189 * 'pass' => null, // the URI pass component
190 * 'host' => 'test.example.com', // the URI host component
191 * 'port' => 42, // the URI port component
192 * 'path' => '', // the URI path component
193 * 'query' => 'query', // the URI query component
194 * 'fragment' => '', // the URI fragment component
195 * );
196 * </code>
197 *
198 * The returned array is similar to PHP's parse_url return value with the following
199 * differences:
200 *
201 * <ul>
202 * <li>All components are always present in the returned array</li>
203 * <li>Empty and undefined component are treated differently. And empty component is
204 * set to the empty string while an undefined component is set to the `null` value.</li>
205 * <li>The path component is never undefined</li>
206 * <li>The method parses the URI following the RFC3986 rules but you are still
207 * required to validate the returned components against its related scheme specific rules.</li>
208 * </ul>
209 *
210 * @link https://tools.ietf.org/html/rfc3986
211 *
212 * @param mixed $uri any scalar or stringable object
213 *
214 * @throws SyntaxError if the URI contains invalid characters
215 * @throws SyntaxError if the URI contains an invalid scheme
216 * @throws SyntaxError if the URI contains an invalid path
217 *
218 * @return array{
219 * scheme:?string,
220 * user:?string,
221 * pass:?string,
222 * host:?string,
223 * port:?int,
224 * path:string,
225 * query:?string,
226 * fragment:?string
227 * }
228 */
229 public static function parse($uri) : array
230 {
231 if (is_object($uri) && method_exists($uri, '__toString')) {
232 $uri = (string) $uri;
233 }
234 if (!is_scalar($uri)) {
235 throw new \TypeError(sprintf('The uri must be a scalar or a stringable object `%s` given', gettype($uri)));
236 }
237 $uri = (string) $uri;
238 if (isset(self::URI_SCHORTCUTS[$uri])) {
239 /** @var array{scheme:?string, user:?string, pass:?string, host:?string, port:?int, path:string, query:?string, fragment:?string} $components */
240 $components = array_merge(self::URI_COMPONENTS, self::URI_SCHORTCUTS[$uri]);
241 return $components;
242 }
243 if (1 === preg_match(self::REGEXP_INVALID_URI_CHARS, $uri)) {
244 throw new SyntaxError(sprintf('The uri `%s` contains invalid characters', $uri));
245 }
246 //if the first character is a known URI delimiter parsing can be simplified
247 $first_char = $uri[0];
248 //The URI is made of the fragment only
249 if ('#' === $first_char) {
250 [, $fragment] = explode('#', $uri, 2);
251 $components = self::URI_COMPONENTS;
252 $components['fragment'] = $fragment;
253 return $components;
254 }
255 //The URI is made of the query and fragment
256 if ('?' === $first_char) {
257 [, $partial] = explode('?', $uri, 2);
258 [$query, $fragment] = explode('#', $partial, 2) + [1 => null];
259 $components = self::URI_COMPONENTS;
260 $components['query'] = $query;
261 $components['fragment'] = $fragment;
262 return $components;
263 }
264 //use RFC3986 URI regexp to split the URI
265 preg_match(self::REGEXP_URI_PARTS, $uri, $parts);
266 $parts += ['query' => '', 'fragment' => ''];
267 if (':' === $parts['scheme'] || 1 !== preg_match(self::REGEXP_URI_SCHEME, $parts['scontent'])) {
268 throw new SyntaxError(sprintf('The uri `%s` contains an invalid scheme', $uri));
269 }
270 if ('' === $parts['scheme'] . $parts['authority'] && 1 === preg_match(self::REGEXP_INVALID_PATH, $parts['path'])) {
271 throw new SyntaxError(sprintf('The uri `%s` contains an invalid path.', $uri));
272 }
273 /** @var array{scheme:?string, user:?string, pass:?string, host:?string, port:?int, path:string, query:?string, fragment:?string} $components */
274 $components = array_merge(self::URI_COMPONENTS, '' === $parts['authority'] ? [] : self::parseAuthority($parts['acontent']), ['path' => $parts['path'], 'scheme' => '' === $parts['scheme'] ? null : $parts['scontent'], 'query' => '' === $parts['query'] ? null : $parts['qcontent'], 'fragment' => '' === $parts['fragment'] ? null : $parts['fcontent']]);
275 return $components;
276 }
277 /**
278 * Parses the URI authority part.
279 *
280 * @link https://tools.ietf.org/html/rfc3986#section-3.2
281 *
282 * @throws SyntaxError If the port component is invalid
283 *
284 * @return array{user:?string, pass:?string, host:?string, port:?int}
285 */
286 private static function parseAuthority(string $authority) : array
287 {
288 $components = ['user' => null, 'pass' => null, 'host' => '', 'port' => null];
289 if ('' === $authority) {
290 return $components;
291 }
292 $parts = explode('@', $authority, 2);
293 if (isset($parts[1])) {
294 [$components['user'], $components['pass']] = explode(':', $parts[0], 2) + [1 => null];
295 }
296 preg_match(self::REGEXP_HOST_PORT, $parts[1] ?? $parts[0], $matches);
297 $matches += ['port' => ''];
298 $components['port'] = self::filterPort($matches['port']);
299 $components['host'] = self::filterHost($matches['host']);
300 return $components;
301 }
302 /**
303 * Filter and format the port component.
304 *
305 * @link https://tools.ietf.org/html/rfc3986#section-3.2.2
306 *
307 * @throws SyntaxError if the registered name is invalid
308 */
309 private static function filterPort(string $port) : ?int
310 {
311 if ('' === $port) {
312 return null;
313 }
314 if (1 === preg_match('/^\\d*$/', $port)) {
315 return (int) $port;
316 }
317 throw new SyntaxError(sprintf('The port `%s` is invalid', $port));
318 }
319 /**
320 * Returns whether a hostname is valid.
321 *
322 * @link https://tools.ietf.org/html/rfc3986#section-3.2.2
323 *
324 * @throws SyntaxError if the registered name is invalid
325 */
326 private static function filterHost(string $host) : string
327 {
328 if ('' === $host) {
329 return $host;
330 }
331 if ('[' !== $host[0] || ']' !== substr($host, -1)) {
332 return self::filterRegisteredName($host);
333 }
334 if (!self::isIpHost(substr($host, 1, -1))) {
335 throw new SyntaxError(sprintf('Host `%s` is invalid : the IP host is malformed', $host));
336 }
337 return $host;
338 }
339 /**
340 * Returns whether the host is an IPv4 or a registered named.
341 *
342 * @link https://tools.ietf.org/html/rfc3986#section-3.2.2
343 *
344 * @throws SyntaxError if the registered name is invalid
345 * @throws IdnSupportMissing if IDN support or ICU requirement are not available or met.
346 */
347 private static function filterRegisteredName(string $host) : string
348 {
349 $formatted_host = rawurldecode($host);
350 if (1 === preg_match(self::REGEXP_REGISTERED_NAME, $formatted_host)) {
351 return $host;
352 }
353 //to test IDN host non-ascii characters must be present in the host
354 if (1 !== preg_match(self::REGEXP_IDN_PATTERN, $formatted_host)) {
355 throw new SyntaxError(sprintf('Host `%s` is invalid : the host is not a valid registered name', $host));
356 }
357 $info = Idna::toAscii($host, Idna::IDNA2008_ASCII);
358 if (0 !== $info->errors()) {
359 throw IdnaConversionFailed::dueToIDNAError($host, $info);
360 }
361 return $host;
362 }
363 /**
364 * Validates a IPv6/IPvfuture host.
365 *
366 * @link https://tools.ietf.org/html/rfc3986#section-3.2.2
367 * @link https://tools.ietf.org/html/rfc6874#section-2
368 * @link https://tools.ietf.org/html/rfc6874#section-4
369 */
370 private static function isIpHost(string $ip_host) : bool
371 {
372 if (\false !== filter_var($ip_host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
373 return \true;
374 }
375 if (1 === preg_match(self::REGEXP_IP_FUTURE, $ip_host, $matches)) {
376 return !\in_array($matches['version'], ['4', '6'], \true);
377 }
378 $pos = strpos($ip_host, '%');
379 if (\false === $pos || 1 === preg_match(self::REGEXP_INVALID_HOST_CHARS, rawurldecode(substr($ip_host, $pos)))) {
380 return \false;
381 }
382 $ip_host = substr($ip_host, 0, $pos);
383 return \false !== filter_var($ip_host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) && 0 === strpos((string) inet_pton($ip_host), self::ZONE_ID_ADDRESS_BLOCK);
384 }
385 }
386