1 | <?php
|
---|
2 | // +-----------------------------------------------------------------------+
|
---|
3 | // | Copyright (c) 2007-2008, Christian Schmidt, Peytz & Co. A/S |
|
---|
4 | // | All rights reserved. |
|
---|
5 | // | |
|
---|
6 | // | Redistribution and use in source and binary forms, with or without |
|
---|
7 | // | modification, are permitted provided that the following conditions |
|
---|
8 | // | are met: |
|
---|
9 | // | |
|
---|
10 | // | o Redistributions of source code must retain the above copyright |
|
---|
11 | // | notice, this list of conditions and the following disclaimer. |
|
---|
12 | // | o Redistributions in binary form must reproduce the above copyright |
|
---|
13 | // | notice, this list of conditions and the following disclaimer in the |
|
---|
14 | // | documentation and/or other materials provided with the distribution.|
|
---|
15 | // | o The names of the authors may not be used to endorse or promote |
|
---|
16 | // | products derived from this software without specific prior written |
|
---|
17 | // | permission. |
|
---|
18 | // | |
|
---|
19 | // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
---|
20 | // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
---|
21 | // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
---|
22 | // | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
---|
23 | // | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
---|
24 | // | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
---|
25 | // | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
---|
26 | // | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
---|
27 | // | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
---|
28 | // | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
---|
29 | // | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
---|
30 | // | |
|
---|
31 | // +-----------------------------------------------------------------------+
|
---|
32 | // | Author: Christian Schmidt <schmidt at php dot net> |
|
---|
33 | // +-----------------------------------------------------------------------+
|
---|
34 | //
|
---|
35 | // $Id: URL2.php,v 1.10 2008/04/26 21:57:08 schmidt Exp $
|
---|
36 | //
|
---|
37 | // net_php_pear_Net_URL2 Class (PHP5 Only)
|
---|
38 |
|
---|
39 | // This code is released under the BSD License - http://www.opensource.org/licenses/bsd-license.php
|
---|
40 | /**
|
---|
41 | * @license BSD License
|
---|
42 | */
|
---|
43 | class net_php_pear_Net_URL2
|
---|
44 | {
|
---|
45 | /**
|
---|
46 | * Do strict parsing in resolve() (see RFC 3986, section 5.2.2). Default
|
---|
47 | * is true.
|
---|
48 | */
|
---|
49 | const OPTION_STRICT = 'strict';
|
---|
50 |
|
---|
51 | /**
|
---|
52 | * Represent arrays in query using PHP's [] notation. Default is true.
|
---|
53 | */
|
---|
54 | const OPTION_USE_BRACKETS = 'use_brackets';
|
---|
55 |
|
---|
56 | /**
|
---|
57 | * URL-encode query variable keys. Default is true.
|
---|
58 | */
|
---|
59 | const OPTION_ENCODE_KEYS = 'encode_keys';
|
---|
60 |
|
---|
61 | /**
|
---|
62 | * Query variable separators when parsing the query string. Every character
|
---|
63 | * is considered a separator. Default is specified by the
|
---|
64 | * arg_separator.input php.ini setting (this defaults to "&").
|
---|
65 | */
|
---|
66 | const OPTION_SEPARATOR_INPUT = 'input_separator';
|
---|
67 |
|
---|
68 | /**
|
---|
69 | * Query variable separator used when generating the query string. Default
|
---|
70 | * is specified by the arg_separator.output php.ini setting (this defaults
|
---|
71 | * to "&").
|
---|
72 | */
|
---|
73 | const OPTION_SEPARATOR_OUTPUT = 'output_separator';
|
---|
74 |
|
---|
75 | /**
|
---|
76 | * Default options corresponds to how PHP handles $_GET.
|
---|
77 | */
|
---|
78 | private $options = array(
|
---|
79 | self::OPTION_STRICT => true,
|
---|
80 | self::OPTION_USE_BRACKETS => true,
|
---|
81 | self::OPTION_ENCODE_KEYS => true,
|
---|
82 | self::OPTION_SEPARATOR_INPUT => 'x&',
|
---|
83 | self::OPTION_SEPARATOR_OUTPUT => 'x&',
|
---|
84 | );
|
---|
85 |
|
---|
86 | /**
|
---|
87 | * @var string|bool
|
---|
88 | */
|
---|
89 | private $scheme = false;
|
---|
90 |
|
---|
91 | /**
|
---|
92 | * @var string|bool
|
---|
93 | */
|
---|
94 | private $userinfo = false;
|
---|
95 |
|
---|
96 | /**
|
---|
97 | * @var string|bool
|
---|
98 | */
|
---|
99 | private $host = false;
|
---|
100 |
|
---|
101 | /**
|
---|
102 | * @var int|bool
|
---|
103 | */
|
---|
104 | private $port = false;
|
---|
105 |
|
---|
106 | /**
|
---|
107 | * @var string
|
---|
108 | */
|
---|
109 | private $path = '';
|
---|
110 |
|
---|
111 | /**
|
---|
112 | * @var string|bool
|
---|
113 | */
|
---|
114 | private $query = false;
|
---|
115 |
|
---|
116 | /**
|
---|
117 | * @var string|bool
|
---|
118 | */
|
---|
119 | private $fragment = false;
|
---|
120 |
|
---|
121 | /**
|
---|
122 | * @param string $url an absolute or relative URL
|
---|
123 | * @param array $options
|
---|
124 | */
|
---|
125 | public function __construct($url, $options = null)
|
---|
126 | {
|
---|
127 | $this->setOption(self::OPTION_SEPARATOR_INPUT,
|
---|
128 | "&");
|
---|
129 | $this->setOption(self::OPTION_SEPARATOR_OUTPUT,
|
---|
130 | "&");
|
---|
131 | if (is_array($options)) {
|
---|
132 | foreach ($options as $optionName => $value) {
|
---|
133 | $this->setOption($optionName);
|
---|
134 | }
|
---|
135 | }
|
---|
136 |
|
---|
137 | if (preg_match('@^([a-z][a-z0-9.+-]*):@i', $url, $reg)) {
|
---|
138 | $this->scheme = $reg[1];
|
---|
139 | $url = substr($url, strlen($reg[0]));
|
---|
140 | }
|
---|
141 |
|
---|
142 | if (preg_match('@^//([^/#?]+)@', $url, $reg)) {
|
---|
143 | $this->setAuthority($reg[1]);
|
---|
144 | $url = substr($url, strlen($reg[0]));
|
---|
145 | }
|
---|
146 |
|
---|
147 | $i = strcspn($url, '?#');
|
---|
148 | $this->path = substr($url, 0, $i);
|
---|
149 | $url = substr($url, $i);
|
---|
150 |
|
---|
151 | if (preg_match('@^\?([^#]*)@', $url, $reg)) {
|
---|
152 | $this->query = $reg[1];
|
---|
153 | $url = substr($url, strlen($reg[0]));
|
---|
154 | }
|
---|
155 |
|
---|
156 | if ($url) {
|
---|
157 | $this->fragment = substr($url, 1);
|
---|
158 | }
|
---|
159 | }
|
---|
160 |
|
---|
161 | /**
|
---|
162 | * Returns the scheme, e.g. "http" or "urn", or false if there is no
|
---|
163 | * scheme specified, i.e. if this is a relative URL.
|
---|
164 | *
|
---|
165 | * @return string|bool
|
---|
166 | */
|
---|
167 | public function getScheme()
|
---|
168 | {
|
---|
169 | return $this->scheme;
|
---|
170 | }
|
---|
171 |
|
---|
172 | /**
|
---|
173 | * @param string|bool $scheme
|
---|
174 | *
|
---|
175 | * @return void
|
---|
176 | * @see getScheme()
|
---|
177 | */
|
---|
178 | public function setScheme($scheme)
|
---|
179 | {
|
---|
180 | $this->scheme = $scheme;
|
---|
181 | }
|
---|
182 |
|
---|
183 | /**
|
---|
184 | * Returns the user part of the userinfo part (the part preceding the first
|
---|
185 | * ":"), or false if there is no userinfo part.
|
---|
186 | *
|
---|
187 | * @return string|bool
|
---|
188 | */
|
---|
189 | public function getUser()
|
---|
190 | {
|
---|
191 | return $this->userinfo !== false ? preg_replace('@:.*$@', '', $this->userinfo) : false;
|
---|
192 | }
|
---|
193 |
|
---|
194 | /**
|
---|
195 | * Returns the password part of the userinfo part (the part after the first
|
---|
196 | * ":"), or false if there is no userinfo part (i.e. the URL does not
|
---|
197 | * contain "@" in front of the hostname) or the userinfo part does not
|
---|
198 | * contain ":".
|
---|
199 | *
|
---|
200 | * @return string|bool
|
---|
201 | */
|
---|
202 | public function getPassword()
|
---|
203 | {
|
---|
204 | return $this->userinfo !== false ? substr(strstr($this->userinfo, ':'), 1) : false;
|
---|
205 | }
|
---|
206 |
|
---|
207 | /**
|
---|
208 | * Returns the userinfo part, or false if there is none, i.e. if the
|
---|
209 | * authority part does not contain "@".
|
---|
210 | *
|
---|
211 | * @return string|bool
|
---|
212 | */
|
---|
213 | public function getUserinfo()
|
---|
214 | {
|
---|
215 | return $this->userinfo;
|
---|
216 | }
|
---|
217 |
|
---|
218 | /**
|
---|
219 | * Sets the userinfo part. If two arguments are passed, they are combined
|
---|
220 | * in the userinfo part as username ":" password.
|
---|
221 | *
|
---|
222 | * @param string|bool $userinfo userinfo or username
|
---|
223 | * @param string|bool $password
|
---|
224 | *
|
---|
225 | * @return void
|
---|
226 | */
|
---|
227 | public function setUserinfo($userinfo, $password = false)
|
---|
228 | {
|
---|
229 | $this->userinfo = $userinfo;
|
---|
230 | if ($password !== false) {
|
---|
231 | $this->userinfo .= ':' . $password;
|
---|
232 | }
|
---|
233 | }
|
---|
234 |
|
---|
235 | /**
|
---|
236 | * Returns the host part, or false if there is no authority part, e.g.
|
---|
237 | * relative URLs.
|
---|
238 | *
|
---|
239 | * @return string|bool
|
---|
240 | */
|
---|
241 | public function getHost()
|
---|
242 | {
|
---|
243 | return $this->host;
|
---|
244 | }
|
---|
245 |
|
---|
246 | /**
|
---|
247 | * @param string|bool $host
|
---|
248 | *
|
---|
249 | * @return void
|
---|
250 | */
|
---|
251 | public function setHost($host)
|
---|
252 | {
|
---|
253 | $this->host = $host;
|
---|
254 | }
|
---|
255 |
|
---|
256 | /**
|
---|
257 | * Returns the port number, or false if there is no port number specified,
|
---|
258 | * i.e. if the default port is to be used.
|
---|
259 | *
|
---|
260 | * @return int|bool
|
---|
261 | */
|
---|
262 | public function getPort()
|
---|
263 | {
|
---|
264 | return $this->port;
|
---|
265 | }
|
---|
266 |
|
---|
267 | /**
|
---|
268 | * @param int|bool $port
|
---|
269 | *
|
---|
270 | * @return void
|
---|
271 | */
|
---|
272 | public function setPort($port)
|
---|
273 | {
|
---|
274 | $this->port = intval($port);
|
---|
275 | }
|
---|
276 |
|
---|
277 | /**
|
---|
278 | * Returns the authority part, i.e. [ userinfo "@" ] host [ ":" port ], or
|
---|
279 | * false if there is no authority none.
|
---|
280 | *
|
---|
281 | * @return string|bool
|
---|
282 | */
|
---|
283 | public function getAuthority()
|
---|
284 | {
|
---|
285 | if (!$this->host) {
|
---|
286 | return false;
|
---|
287 | }
|
---|
288 |
|
---|
289 | $authority = '';
|
---|
290 |
|
---|
291 | if ($this->userinfo !== false) {
|
---|
292 | $authority .= $this->userinfo . '@';
|
---|
293 | }
|
---|
294 |
|
---|
295 | $authority .= $this->host;
|
---|
296 |
|
---|
297 | if ($this->port !== false) {
|
---|
298 | $authority .= ':' . $this->port;
|
---|
299 | }
|
---|
300 |
|
---|
301 | return $authority;
|
---|
302 | }
|
---|
303 |
|
---|
304 | /**
|
---|
305 | * @param string|false $authority
|
---|
306 | *
|
---|
307 | * @return void
|
---|
308 | */
|
---|
309 | public function setAuthority($authority)
|
---|
310 | {
|
---|
311 | $this->user = false;
|
---|
312 | $this->pass = false;
|
---|
313 | $this->host = false;
|
---|
314 | $this->port = false;
|
---|
315 | if (preg_match('@^(([^\@]+)\@)?([^:]+)(:(\d*))?$@', $authority, $reg)) {
|
---|
316 | if ($reg[1]) {
|
---|
317 | $this->userinfo = $reg[2];
|
---|
318 | }
|
---|
319 |
|
---|
320 | $this->host = $reg[3];
|
---|
321 | if (isset($reg[5])) {
|
---|
322 | $this->port = intval($reg[5]);
|
---|
323 | }
|
---|
324 | }
|
---|
325 | }
|
---|
326 |
|
---|
327 | /**
|
---|
328 | * Returns the path part (possibly an empty string).
|
---|
329 | *
|
---|
330 | * @return string
|
---|
331 | */
|
---|
332 | public function getPath()
|
---|
333 | {
|
---|
334 | return $this->path;
|
---|
335 | }
|
---|
336 |
|
---|
337 | /**
|
---|
338 | * @param string $path
|
---|
339 | *
|
---|
340 | * @return void
|
---|
341 | */
|
---|
342 | public function setPath($path)
|
---|
343 | {
|
---|
344 | $this->path = $path;
|
---|
345 | }
|
---|
346 |
|
---|
347 | /**
|
---|
348 | * Returns the query string (excluding the leading "?"), or false if "?"
|
---|
349 | * isn't present in the URL.
|
---|
350 | *
|
---|
351 | * @return string|bool
|
---|
352 | * @see self::getQueryVariables()
|
---|
353 | */
|
---|
354 | public function getQuery()
|
---|
355 | {
|
---|
356 | return $this->query;
|
---|
357 | }
|
---|
358 |
|
---|
359 | /**
|
---|
360 | * @param string|bool $query
|
---|
361 | *
|
---|
362 | * @return void
|
---|
363 | * @see self::setQueryVariables()
|
---|
364 | */
|
---|
365 | public function setQuery($query)
|
---|
366 | {
|
---|
367 | $this->query = $query;
|
---|
368 | }
|
---|
369 |
|
---|
370 | /**
|
---|
371 | * Returns the fragment name, or false if "#" isn't present in the URL.
|
---|
372 | *
|
---|
373 | * @return string|bool
|
---|
374 | */
|
---|
375 | public function getFragment()
|
---|
376 | {
|
---|
377 | return $this->fragment;
|
---|
378 | }
|
---|
379 |
|
---|
380 | /**
|
---|
381 | * @param string|bool $fragment
|
---|
382 | *
|
---|
383 | * @return void
|
---|
384 | */
|
---|
385 | public function setFragment($fragment)
|
---|
386 | {
|
---|
387 | $this->fragment = $fragment;
|
---|
388 | }
|
---|
389 |
|
---|
390 | /**
|
---|
391 | * Returns the query string like an array as the variables would appear in
|
---|
392 | * $_GET in a PHP script.
|
---|
393 | *
|
---|
394 | * @return array
|
---|
395 | */
|
---|
396 | public function getQueryVariables()
|
---|
397 | {
|
---|
398 | $pattern = '/[' .
|
---|
399 | preg_quote($this->getOption(self::OPTION_SEPARATOR_INPUT), '/') .
|
---|
400 | ']/';
|
---|
401 | $parts = preg_split($pattern, $this->query, -1, PREG_SPLIT_NO_EMPTY);
|
---|
402 | $return = array();
|
---|
403 |
|
---|
404 | foreach ($parts as $part) {
|
---|
405 | if (strpos($part, '=') !== false) {
|
---|
406 | list($key, $value) = explode('=', $part, 2);
|
---|
407 | } else {
|
---|
408 | $key = $part;
|
---|
409 | $value = null;
|
---|
410 | }
|
---|
411 |
|
---|
412 | if ($this->getOption(self::OPTION_ENCODE_KEYS)) {
|
---|
413 | $key = rawurldecode($key);
|
---|
414 | }
|
---|
415 | $value = rawurldecode($value);
|
---|
416 |
|
---|
417 | if ($this->getOption(self::OPTION_USE_BRACKETS) &&
|
---|
418 | preg_match('#^(.*)\[([0-9a-z_-]*)\]#i', $key, $matches)) {
|
---|
419 |
|
---|
420 | $key = $matches[1];
|
---|
421 | $idx = $matches[2];
|
---|
422 |
|
---|
423 | // Ensure is an array
|
---|
424 | if (empty($return[$key]) || !is_array($return[$key])) {
|
---|
425 | $return[$key] = array();
|
---|
426 | }
|
---|
427 |
|
---|
428 | // Add data
|
---|
429 | if ($idx === '') {
|
---|
430 | $return[$key][] = $value;
|
---|
431 | } else {
|
---|
432 | $return[$key][$idx] = $value;
|
---|
433 | }
|
---|
434 | } elseif (!$this->getOption(self::OPTION_USE_BRACKETS)
|
---|
435 | && !empty($return[$key])
|
---|
436 | ) {
|
---|
437 | $return[$key] = (array) $return[$key];
|
---|
438 | $return[$key][] = $value;
|
---|
439 | } else {
|
---|
440 | $return[$key] = $value;
|
---|
441 | }
|
---|
442 | }
|
---|
443 |
|
---|
444 | return $return;
|
---|
445 | }
|
---|
446 |
|
---|
447 | /**
|
---|
448 | * @param array $array (name => value) array
|
---|
449 | *
|
---|
450 | * @return void
|
---|
451 | */
|
---|
452 | public function setQueryVariables($array)
|
---|
453 | {
|
---|
454 | if (!$array) {
|
---|
455 | $this->query = false;
|
---|
456 | } else {
|
---|
457 | foreach ($array as $name => $value) {
|
---|
458 | if ($this->getOption(self::OPTION_ENCODE_KEYS)) {
|
---|
459 | $name = rawurlencode($name);
|
---|
460 | }
|
---|
461 |
|
---|
462 | if (is_array($value)) {
|
---|
463 | foreach ($value as $k => $v) {
|
---|
464 | $parts[] = $this->getOption(self::OPTION_USE_BRACKETS)
|
---|
465 | ? sprintf('%s[%s]=%s', $name, $k, $v)
|
---|
466 | : ($name . '=' . $v);
|
---|
467 | }
|
---|
468 | } elseif (!is_null($value)) {
|
---|
469 | $parts[] = $name . '=' . rawurlencode($value);
|
---|
470 | } else {
|
---|
471 | $parts[] = $name;
|
---|
472 | }
|
---|
473 | }
|
---|
474 | $this->query = implode($this->getOption(self::OPTION_SEPARATOR_OUTPUT),
|
---|
475 | $parts);
|
---|
476 | }
|
---|
477 | }
|
---|
478 |
|
---|
479 | /**
|
---|
480 | * @param string $name
|
---|
481 | * @param mixed $value
|
---|
482 | *
|
---|
483 | * @return array
|
---|
484 | */
|
---|
485 | public function setQueryVariable($name, $value)
|
---|
486 | {
|
---|
487 | $array = $this->getQueryVariables();
|
---|
488 | $array[$name] = $value;
|
---|
489 | $this->setQueryVariables($array);
|
---|
490 | }
|
---|
491 |
|
---|
492 | /**
|
---|
493 | * @param string $name
|
---|
494 | *
|
---|
495 | * @return void
|
---|
496 | */
|
---|
497 | public function unsetQueryVariable($name)
|
---|
498 | {
|
---|
499 | $array = $this->getQueryVariables();
|
---|
500 | unset($array[$name]);
|
---|
501 | $this->setQueryVariables($array);
|
---|
502 | }
|
---|
503 |
|
---|
504 | /**
|
---|
505 | * Returns a string representation of this URL.
|
---|
506 | *
|
---|
507 | * @return string
|
---|
508 | */
|
---|
509 | public function getURL($encodeAmpersands = false)
|
---|
510 | {
|
---|
511 | // See RFC 3986, section 5.3
|
---|
512 | $url = "";
|
---|
513 |
|
---|
514 | if ($this->scheme !== false) {
|
---|
515 | $url .= $this->scheme . ':';
|
---|
516 | }
|
---|
517 |
|
---|
518 | $authority = $this->getAuthority();
|
---|
519 | if ($authority !== false) {
|
---|
520 | $url .= '//' . $authority;
|
---|
521 | }
|
---|
522 | $url .= $this->path;
|
---|
523 |
|
---|
524 | if ($this->query !== false) {
|
---|
525 | $url .= '?' . $this->query;
|
---|
526 | }
|
---|
527 |
|
---|
528 | if ($this->fragment !== false) {
|
---|
529 | $url .= '#' . $this->fragment;
|
---|
530 | }
|
---|
531 |
|
---|
532 | if ($encodeAmpersands) {
|
---|
533 | return str_replace("&", "&", $url);
|
---|
534 | }
|
---|
535 | return $url;
|
---|
536 | }
|
---|
537 |
|
---|
538 | /**
|
---|
539 | * Returns a normalized string representation of this URL. This is useful
|
---|
540 | * for comparison of URLs.
|
---|
541 | *
|
---|
542 | * @return string
|
---|
543 | */
|
---|
544 | public function getNormalizedURL()
|
---|
545 | {
|
---|
546 | $url = clone $this;
|
---|
547 | $url->normalize();
|
---|
548 | return $url->getUrl();
|
---|
549 | }
|
---|
550 |
|
---|
551 | /**
|
---|
552 | * Returns a normalized net_php_pear_Net_URL2 instance.
|
---|
553 | *
|
---|
554 | * @return net_php_pear_Net_URL2
|
---|
555 | */
|
---|
556 | public function normalize()
|
---|
557 | {
|
---|
558 | // See RFC 3886, section 6
|
---|
559 |
|
---|
560 | // Schemes are case-insensitive
|
---|
561 | if ($this->scheme) {
|
---|
562 | $this->scheme = strtolower($this->scheme);
|
---|
563 | }
|
---|
564 |
|
---|
565 | // Hostnames are case-insensitive
|
---|
566 | if ($this->host) {
|
---|
567 | $this->host = strtolower($this->host);
|
---|
568 | }
|
---|
569 |
|
---|
570 | // Remove default port number for known schemes (RFC 3986, section 6.2.3)
|
---|
571 | if ($this->port &&
|
---|
572 | $this->scheme &&
|
---|
573 | $this->port == getservbyname($this->scheme, 'tcp')) {
|
---|
574 |
|
---|
575 | $this->port = false;
|
---|
576 | }
|
---|
577 |
|
---|
578 | // Normalize case of %XX percentage-encodings (RFC 3986, section 6.2.2.1)
|
---|
579 | foreach (array('userinfo', 'host', 'path') as $part) {
|
---|
580 | if ($this->$part) {
|
---|
581 | $this->$part = preg_replace('/%[0-9a-f]{2}/ie', 'strtoupper("\0")', $this->$part);
|
---|
582 | }
|
---|
583 | }
|
---|
584 |
|
---|
585 | // Path segment normalization (RFC 3986, section 6.2.2.3)
|
---|
586 | $this->path = self::removeDotSegments($this->path);
|
---|
587 |
|
---|
588 | // Scheme based normalization (RFC 3986, section 6.2.3)
|
---|
589 | if ($this->host && !$this->path) {
|
---|
590 | $this->path = '/';
|
---|
591 | }
|
---|
592 | }
|
---|
593 |
|
---|
594 | /**
|
---|
595 | * Returns whether this instance represents an absolute URL.
|
---|
596 | *
|
---|
597 | * @return bool
|
---|
598 | */
|
---|
599 | public function isAbsolute()
|
---|
600 | {
|
---|
601 | return (bool) $this->scheme;
|
---|
602 | }
|
---|
603 |
|
---|
604 | /**
|
---|
605 | * Returns an net_php_pear_Net_URL2 instance representing an absolute URL relative to
|
---|
606 | * this URL.
|
---|
607 | *
|
---|
608 | * @param net_php_pear_Net_URL2|string $reference relative URL
|
---|
609 | *
|
---|
610 | * @return net_php_pear_Net_URL2
|
---|
611 | */
|
---|
612 | public function resolve($reference)
|
---|
613 | {
|
---|
614 | if (is_string($reference)) {
|
---|
615 | $reference = new self($reference);
|
---|
616 | }
|
---|
617 | if (!$this->isAbsolute()) {
|
---|
618 | throw new Exception('Base-URL must be absolute');
|
---|
619 | }
|
---|
620 |
|
---|
621 | // A non-strict parser may ignore a scheme in the reference if it is
|
---|
622 | // identical to the base URI's scheme.
|
---|
623 | if (!$this->getOption(self::OPTION_STRICT) && $reference->scheme == $this->scheme) {
|
---|
624 | $reference->scheme = false;
|
---|
625 | }
|
---|
626 |
|
---|
627 | $target = new self('');
|
---|
628 | if ($reference->scheme !== false) {
|
---|
629 | $target->scheme = $reference->scheme;
|
---|
630 | $target->setAuthority($reference->getAuthority());
|
---|
631 | $target->path = self::removeDotSegments($reference->path);
|
---|
632 | $target->query = $reference->query;
|
---|
633 | } else {
|
---|
634 | $authority = $reference->getAuthority();
|
---|
635 | if ($authority !== false) {
|
---|
636 | $target->setAuthority($authority);
|
---|
637 | $target->path = self::removeDotSegments($reference->path);
|
---|
638 | $target->query = $reference->query;
|
---|
639 | } else {
|
---|
640 | if ($reference->path == '') {
|
---|
641 | $target->path = $this->path;
|
---|
642 | if ($reference->query !== false) {
|
---|
643 | $target->query = $reference->query;
|
---|
644 | } else {
|
---|
645 | $target->query = $this->query;
|
---|
646 | }
|
---|
647 | } else {
|
---|
648 | if (substr($reference->path, 0, 1) == '/') {
|
---|
649 | $target->path = self::removeDotSegments($reference->path);
|
---|
650 | } else {
|
---|
651 | // Merge paths (RFC 3986, section 5.2.3)
|
---|
652 | if ($this->host !== false && $this->path == '') {
|
---|
653 | $target->path = '/' . $this->path;
|
---|
654 | } else {
|
---|
655 | $i = strrpos($this->path, '/');
|
---|
656 | if ($i !== false) {
|
---|
657 | $target->path = substr($this->path, 0, $i + 1);
|
---|
658 | }
|
---|
659 | $target->path .= $reference->path;
|
---|
660 | }
|
---|
661 | $target->path = self::removeDotSegments($target->path);
|
---|
662 | }
|
---|
663 | $target->query = $reference->query;
|
---|
664 | }
|
---|
665 | $target->setAuthority($this->getAuthority());
|
---|
666 | }
|
---|
667 | $target->scheme = $this->scheme;
|
---|
668 | }
|
---|
669 |
|
---|
670 | $target->fragment = $reference->fragment;
|
---|
671 |
|
---|
672 | return $target;
|
---|
673 | }
|
---|
674 |
|
---|
675 | /**
|
---|
676 | * Removes dots as described in RFC 3986, section 5.2.4, e.g.
|
---|
677 | * "/foo/../bar/baz" => "/bar/baz"
|
---|
678 | *
|
---|
679 | * @param string $path a path
|
---|
680 | *
|
---|
681 | * @return string a path
|
---|
682 | */
|
---|
683 | private static function removeDotSegments($path)
|
---|
684 | {
|
---|
685 | $output = '';
|
---|
686 |
|
---|
687 | // Make sure not to be trapped in an infinite loop due to a bug in this
|
---|
688 | // method
|
---|
689 | $j = 0;
|
---|
690 | while ($path && $j++ < 100) {
|
---|
691 | // Step A
|
---|
692 | if (substr($path, 0, 2) == './') {
|
---|
693 | $path = substr($path, 2);
|
---|
694 | } elseif (substr($path, 0, 3) == '../') {
|
---|
695 | $path = substr($path, 3);
|
---|
696 |
|
---|
697 | // Step B
|
---|
698 | } elseif (substr($path, 0, 3) == '/./' || $path == '/.') {
|
---|
699 | $path = '/' . substr($path, 3);
|
---|
700 |
|
---|
701 | // Step C
|
---|
702 | } elseif (substr($path, 0, 4) == '/../' || $path == '/..') {
|
---|
703 | $path = '/' . substr($path, 4);
|
---|
704 | $i = strrpos($output, '/');
|
---|
705 | $output = $i === false ? '' : substr($output, 0, $i);
|
---|
706 |
|
---|
707 | // Step D
|
---|
708 | } elseif ($path == '.' || $path == '..') {
|
---|
709 | $path = '';
|
---|
710 |
|
---|
711 | // Step E
|
---|
712 | } else {
|
---|
713 | $i = strpos($path, '/');
|
---|
714 | if ($i === 0) {
|
---|
715 | $i = strpos($path, '/', 1);
|
---|
716 | }
|
---|
717 | if ($i === false) {
|
---|
718 | $i = strlen($path);
|
---|
719 | }
|
---|
720 | $output .= substr($path, 0, $i);
|
---|
721 | $path = substr($path, $i);
|
---|
722 | }
|
---|
723 | }
|
---|
724 |
|
---|
725 | return $output;
|
---|
726 | }
|
---|
727 |
|
---|
728 | /**
|
---|
729 | * Returns a net_php_pear_Net_URL2 instance representing the canonical URL of the
|
---|
730 | * currently executing PHP script.
|
---|
731 | *
|
---|
732 | * @return string
|
---|
733 | */
|
---|
734 | public static function getCanonical()
|
---|
735 | {
|
---|
736 | if (!isset($_SERVER['REQUEST_METHOD'])) {
|
---|
737 | // ALERT - no current URL
|
---|
738 | throw new Exception('Script was not called through a webserver');
|
---|
739 | }
|
---|
740 |
|
---|
741 | // Begin with a relative URL
|
---|
742 | $url = new self($_SERVER['PHP_SELF']);
|
---|
743 | $url->scheme = isset($_SERVER['HTTPS']) ? 'https' : 'http';
|
---|
744 | $url->host = $_SERVER['SERVER_NAME'];
|
---|
745 | $port = intval($_SERVER['SERVER_PORT']);
|
---|
746 | if ($url->scheme == 'http' && $port != 80 ||
|
---|
747 | $url->scheme == 'https' && $port != 443) {
|
---|
748 |
|
---|
749 | $url->port = $port;
|
---|
750 | }
|
---|
751 | return $url;
|
---|
752 | }
|
---|
753 |
|
---|
754 | /**
|
---|
755 | * Returns the URL used to retrieve the current request.
|
---|
756 | *
|
---|
757 | * @return string
|
---|
758 | */
|
---|
759 | public static function getRequestedURL()
|
---|
760 | {
|
---|
761 | return self::getRequested()->getUrl();
|
---|
762 | }
|
---|
763 |
|
---|
764 | /**
|
---|
765 | * Returns a net_php_pear_Net_URL2 instance representing the URL used to retrieve the
|
---|
766 | * current request.
|
---|
767 | *
|
---|
768 | * @return net_php_pear_Net_URL2
|
---|
769 | */
|
---|
770 | public static function getRequested()
|
---|
771 | {
|
---|
772 | if (!isset($_SERVER['REQUEST_METHOD'])) {
|
---|
773 | // ALERT - no current URL
|
---|
774 | throw new Exception('Script was not called through a webserver');
|
---|
775 | }
|
---|
776 |
|
---|
777 | // Begin with a relative URL
|
---|
778 | $url = new self($_SERVER['REQUEST_URI']);
|
---|
779 | $url->scheme = isset($_SERVER['HTTPS']) ? 'https' : 'http';
|
---|
780 | // Set host and possibly port
|
---|
781 | $url->setAuthority($_SERVER['HTTP_HOST']);
|
---|
782 | return $url;
|
---|
783 | }
|
---|
784 |
|
---|
785 | /**
|
---|
786 | * Sets the specified option.
|
---|
787 | *
|
---|
788 | * @param string $optionName a self::OPTION_ constant
|
---|
789 | * @param mixed $value option value
|
---|
790 | *
|
---|
791 | * @return void
|
---|
792 | * @see self::OPTION_STRICT
|
---|
793 | * @see self::OPTION_USE_BRACKETS
|
---|
794 | * @see self::OPTION_ENCODE_KEYS
|
---|
795 | */
|
---|
796 | function setOption($optionName, $value)
|
---|
797 | {
|
---|
798 | if (!array_key_exists($optionName, $this->options)) {
|
---|
799 | return false;
|
---|
800 | }
|
---|
801 | $this->options[$optionName] = $value;
|
---|
802 | }
|
---|
803 |
|
---|
804 | /**
|
---|
805 | * Returns the value of the specified option.
|
---|
806 | *
|
---|
807 | * @param string $optionName The name of the option to retrieve
|
---|
808 | *
|
---|
809 | * @return mixed
|
---|
810 | */
|
---|
811 | function getOption($optionName)
|
---|
812 | {
|
---|
813 | return isset($this->options[$optionName])
|
---|
814 | ? $this->options[$optionName] : false;
|
---|
815 | }
|
---|
816 | }
|
---|