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