source: trunk/www.guidonia.net/wp/wp-content/plugins/tubepress/classes/net/php/pear/Net/URL2.class.php@ 44

Last change on this file since 44 was 44, checked in by luciano, 14 years ago
File size: 23.0 KB
Line 
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 */
43class 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("&", "&amp;", $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}
Note: See TracBrowser for help on using the repository browser.