814 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
		
		
			
		
	
	
			814 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
|   | <?php | ||
|  | // +-----------------------------------------------------------------------+
 | ||
|  | // | Copyright (c) 2007-2008, Christian Schmidt, Peytz & Co. A/S           |
 | ||
|  | // | All rights reserved.                                                  |
 | ||
|  | // |                                                                       |
 | ||
|  | // | Redistribution and use in source and binary forms, with or without    |
 | ||
|  | // | modification, are permitted provided that the following conditions    |
 | ||
|  | // | are met:                                                              |
 | ||
|  | // |                                                                       |
 | ||
|  | // | o Redistributions of source code must retain the above copyright      |
 | ||
|  | // |   notice, this list of conditions and the following disclaimer.       |
 | ||
|  | // | o Redistributions in binary form must reproduce the above copyright   |
 | ||
|  | // |   notice, this list of conditions and the following disclaimer in the |
 | ||
|  | // |   documentation and/or other materials provided with the distribution.|
 | ||
|  | // | o The names of the authors may not be used to endorse or promote      |
 | ||
|  | // |   products derived from this software without specific prior written  |
 | ||
|  | // |   permission.                                                         |
 | ||
|  | // |                                                                       |
 | ||
|  | // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |
 | ||
|  | // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |
 | ||
|  | // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
 | ||
|  | // | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |
 | ||
|  | // | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
 | ||
|  | // | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |
 | ||
|  | // | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
 | ||
|  | // | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
 | ||
|  | // | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |
 | ||
|  | // | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
 | ||
|  | // | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |
 | ||
|  | // |                                                                       |
 | ||
|  | // +-----------------------------------------------------------------------+
 | ||
|  | // | Author: Christian Schmidt <schmidt at php dot net>                    |
 | ||
|  | // +-----------------------------------------------------------------------+
 | ||
|  | //
 | ||
|  | // $Id: URL2.php,v 1.10 2008/04/26 21:57:08 schmidt Exp $
 | ||
|  | //
 | ||
|  | // Net_URL2 Class (PHP5 Only)
 | ||
|  | 
 | ||
|  | // This code is released under the BSD License - http://www.opensource.org/licenses/bsd-license.php
 | ||
|  | /** | ||
|  |  * @license BSD License | ||
|  |  */ | ||
|  | class Net_URL2 | ||
|  | { | ||
|  |     /** | ||
|  |      * Do strict parsing in resolve() (see RFC 3986, section 5.2.2). Default | ||
|  |      * is true. | ||
|  |      */ | ||
|  |     const OPTION_STRICT           = 'strict'; | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Represent arrays in query using PHP's [] notation. Default is true. | ||
|  |      */ | ||
|  |     const OPTION_USE_BRACKETS     = 'use_brackets'; | ||
|  | 
 | ||
|  |     /** | ||
|  |      * URL-encode query variable keys. Default is true. | ||
|  |      */ | ||
|  |     const OPTION_ENCODE_KEYS      = 'encode_keys'; | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Query variable separators when parsing the query string. Every character | ||
|  |      * is considered a separator. Default is specified by the | ||
|  |      * arg_separator.input php.ini setting (this defaults to "&"). | ||
|  |      */ | ||
|  |     const OPTION_SEPARATOR_INPUT  = 'input_separator'; | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Query variable separator used when generating the query string. Default | ||
|  |      * is specified by the arg_separator.output php.ini setting (this defaults | ||
|  |      * to "&"). | ||
|  |      */ | ||
|  |     const OPTION_SEPARATOR_OUTPUT = 'output_separator'; | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Default options corresponds to how PHP handles $_GET. | ||
|  |      */ | ||
|  |     private $options = array( | ||
|  |         self::OPTION_STRICT           => true, | ||
|  |         self::OPTION_USE_BRACKETS     => true, | ||
|  |         self::OPTION_ENCODE_KEYS      => true, | ||
|  |         self::OPTION_SEPARATOR_INPUT  => 'x&', | ||
|  |         self::OPTION_SEPARATOR_OUTPUT => 'x&', | ||
|  |         ); | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @var  string|bool | ||
|  |      */ | ||
|  |     private $scheme = false; | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @var  string|bool | ||
|  |      */ | ||
|  |     private $userinfo = false; | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @var  string|bool | ||
|  |      */ | ||
|  |     private $host = false; | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @var  int|bool | ||
|  |      */ | ||
|  |     private $port = false; | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @var  string | ||
|  |      */ | ||
|  |     private $path = ''; | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @var  string|bool | ||
|  |      */ | ||
|  |     private $query = false; | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @var  string|bool | ||
|  |      */ | ||
|  |     private $fragment = false; | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @param string $url     an absolute or relative URL | ||
|  |      * @param array  $options | ||
|  |      */ | ||
|  |     public function __construct($url, $options = null) | ||
|  |     { | ||
|  |         $this->setOption(self::OPTION_SEPARATOR_INPUT, | ||
|  |                          ini_get('arg_separator.input')); | ||
|  |         $this->setOption(self::OPTION_SEPARATOR_OUTPUT, | ||
|  |                          ini_get('arg_separator.output')); | ||
|  |         if (is_array($options)) { | ||
|  |             foreach ($options as $optionName => $value) { | ||
|  |                 $this->setOption($optionName); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         if (preg_match('@^([a-z][a-z0-9.+-]*):@i', $url, $reg)) { | ||
|  |             $this->scheme = $reg[1]; | ||
|  |             $url = substr($url, strlen($reg[0])); | ||
|  |         } | ||
|  | 
 | ||
|  |         if (preg_match('@^//([^/#?]+)@', $url, $reg)) { | ||
|  |             $this->setAuthority($reg[1]); | ||
|  |             $url = substr($url, strlen($reg[0])); | ||
|  |         } | ||
|  | 
 | ||
|  |         $i = strcspn($url, '?#'); | ||
|  |         $this->path = substr($url, 0, $i); | ||
|  |         $url = substr($url, $i); | ||
|  | 
 | ||
|  |         if (preg_match('@^\?([^#]*)@', $url, $reg)) { | ||
|  |             $this->query = $reg[1]; | ||
|  |             $url = substr($url, strlen($reg[0])); | ||
|  |         } | ||
|  | 
 | ||
|  |         if ($url) { | ||
|  |             $this->fragment = substr($url, 1); | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Returns the scheme, e.g. "http" or "urn", or false if there is no | ||
|  |      * scheme specified, i.e. if this is a relative URL. | ||
|  |      * | ||
|  |      * @return  string|bool | ||
|  |      */ | ||
|  |     public function getScheme() | ||
|  |     { | ||
|  |         return $this->scheme; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @param string|bool $scheme | ||
|  |      * | ||
|  |      * @return void | ||
|  |      * @see    getScheme() | ||
|  |      */ | ||
|  |     public function setScheme($scheme) | ||
|  |     { | ||
|  |         $this->scheme = $scheme; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Returns the user part of the userinfo part (the part preceding the first | ||
|  |      *  ":"), or false if there is no userinfo part. | ||
|  |      * | ||
|  |      * @return  string|bool | ||
|  |      */ | ||
|  |     public function getUser() | ||
|  |     { | ||
|  |         return $this->userinfo !== false ? preg_replace('@:.*$@', '', $this->userinfo) : false; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Returns the password part of the userinfo part (the part after the first | ||
|  |      *  ":"), or false if there is no userinfo part (i.e. the URL does not | ||
|  |      * contain "@" in front of the hostname) or the userinfo part does not | ||
|  |      * contain ":". | ||
|  |      * | ||
|  |      * @return  string|bool | ||
|  |      */ | ||
|  |     public function getPassword() | ||
|  |     { | ||
|  |         return $this->userinfo !== false ? substr(strstr($this->userinfo, ':'), 1) : false; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Returns the userinfo part, or false if there is none, i.e. if the | ||
|  |      * authority part does not contain "@". | ||
|  |      * | ||
|  |      * @return  string|bool | ||
|  |      */ | ||
|  |     public function getUserinfo() | ||
|  |     { | ||
|  |         return $this->userinfo; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Sets the userinfo part. If two arguments are passed, they are combined | ||
|  |      * in the userinfo part as username ":" password. | ||
|  |      * | ||
|  |      * @param string|bool $userinfo userinfo or username | ||
|  |      * @param string|bool $password | ||
|  |      * | ||
|  |      * @return void | ||
|  |      */ | ||
|  |     public function setUserinfo($userinfo, $password = false) | ||
|  |     { | ||
|  |         $this->userinfo = $userinfo; | ||
|  |         if ($password !== false) { | ||
|  |             $this->userinfo .= ':' . $password; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Returns the host part, or false if there is no authority part, e.g. | ||
|  |      * relative URLs. | ||
|  |      * | ||
|  |      * @return  string|bool | ||
|  |      */ | ||
|  |     public function getHost() | ||
|  |     { | ||
|  |         return $this->host; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @param string|bool $host | ||
|  |      * | ||
|  |      * @return void | ||
|  |      */ | ||
|  |     public function setHost($host) | ||
|  |     { | ||
|  |         $this->host = $host; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Returns the port number, or false if there is no port number specified, | ||
|  |      * i.e. if the default port is to be used. | ||
|  |      * | ||
|  |      * @return  int|bool | ||
|  |      */ | ||
|  |     public function getPort() | ||
|  |     { | ||
|  |         return $this->port; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @param int|bool $port | ||
|  |      * | ||
|  |      * @return void | ||
|  |      */ | ||
|  |     public function setPort($port) | ||
|  |     { | ||
|  |         $this->port = intval($port); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Returns the authority part, i.e. [ userinfo "@" ] host [ ":" port ], or | ||
|  |      * false if there is no authority none. | ||
|  |      * | ||
|  |      * @return string|bool | ||
|  |      */ | ||
|  |     public function getAuthority() | ||
|  |     { | ||
|  |         if (!$this->host) { | ||
|  |             return false; | ||
|  |         } | ||
|  | 
 | ||
|  |         $authority = ''; | ||
|  | 
 | ||
|  |         if ($this->userinfo !== false) { | ||
|  |             $authority .= $this->userinfo . '@'; | ||
|  |         } | ||
|  | 
 | ||
|  |         $authority .= $this->host; | ||
|  | 
 | ||
|  |         if ($this->port !== false) { | ||
|  |             $authority .= ':' . $this->port; | ||
|  |         } | ||
|  | 
 | ||
|  |         return $authority; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @param string|false $authority | ||
|  |      * | ||
|  |      * @return void | ||
|  |      */ | ||
|  |     public function setAuthority($authority) | ||
|  |     { | ||
|  |         $this->user = false; | ||
|  |         $this->pass = false; | ||
|  |         $this->host = false; | ||
|  |         $this->port = false; | ||
|  |         if (preg_match('@^(([^\@]+)\@)?([^:]+)(:(\d*))?$@', $authority, $reg)) { | ||
|  |             if ($reg[1]) { | ||
|  |                 $this->userinfo = $reg[2]; | ||
|  |             } | ||
|  | 
 | ||
|  |             $this->host = $reg[3]; | ||
|  |             if (isset($reg[5])) { | ||
|  |                 $this->port = intval($reg[5]); | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Returns the path part (possibly an empty string). | ||
|  |      * | ||
|  |      * @return string | ||
|  |      */ | ||
|  |     public function getPath() | ||
|  |     { | ||
|  |         return $this->path; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @param string $path | ||
|  |      * | ||
|  |      * @return void | ||
|  |      */ | ||
|  |     public function setPath($path) | ||
|  |     { | ||
|  |         $this->path = $path; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Returns the query string (excluding the leading "?"), or false if "?" | ||
|  |      * isn't present in the URL. | ||
|  |      * | ||
|  |      * @return  string|bool | ||
|  |      * @see     self::getQueryVariables() | ||
|  |      */ | ||
|  |     public function getQuery() | ||
|  |     { | ||
|  |         return $this->query; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @param string|bool $query | ||
|  |      * | ||
|  |      * @return void | ||
|  |      * @see   self::setQueryVariables() | ||
|  |      */ | ||
|  |     public function setQuery($query) | ||
|  |     { | ||
|  |         $this->query = $query; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Returns the fragment name, or false if "#" isn't present in the URL. | ||
|  |      * | ||
|  |      * @return  string|bool | ||
|  |      */ | ||
|  |     public function getFragment() | ||
|  |     { | ||
|  |         return $this->fragment; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @param string|bool $fragment | ||
|  |      * | ||
|  |      * @return void | ||
|  |      */ | ||
|  |     public function setFragment($fragment) | ||
|  |     { | ||
|  |         $this->fragment = $fragment; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Returns the query string like an array as the variables would appear in | ||
|  |      * $_GET in a PHP script. | ||
|  |      * | ||
|  |      * @return  array | ||
|  |      */ | ||
|  |     public function getQueryVariables() | ||
|  |     { | ||
|  |         $pattern = '/[' . | ||
|  |                    preg_quote($this->getOption(self::OPTION_SEPARATOR_INPUT), '/') . | ||
|  |                    ']/'; | ||
|  |         $parts   = preg_split($pattern, $this->query, -1, PREG_SPLIT_NO_EMPTY); | ||
|  |         $return  = array(); | ||
|  | 
 | ||
|  |         foreach ($parts as $part) { | ||
|  |             if (strpos($part, '=') !== false) { | ||
|  |                 list($key, $value) = explode('=', $part, 2); | ||
|  |             } else { | ||
|  |                 $key   = $part; | ||
|  |                 $value = null; | ||
|  |             } | ||
|  | 
 | ||
|  |             if ($this->getOption(self::OPTION_ENCODE_KEYS)) { | ||
|  |                 $key = rawurldecode($key); | ||
|  |             } | ||
|  |             $value = rawurldecode($value); | ||
|  | 
 | ||
|  |             if ($this->getOption(self::OPTION_USE_BRACKETS) && | ||
|  |                 preg_match('#^(.*)\[([0-9a-z_-]*)\]#i', $key, $matches)) { | ||
|  | 
 | ||
|  |                 $key = $matches[1]; | ||
|  |                 $idx = $matches[2]; | ||
|  | 
 | ||
|  |                 // Ensure is an array
 | ||
|  |                 if (empty($return[$key]) || !is_array($return[$key])) { | ||
|  |                     $return[$key] = array(); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 // Add data
 | ||
|  |                 if ($idx === '') { | ||
|  |                     $return[$key][] = $value; | ||
|  |                 } else { | ||
|  |                     $return[$key][$idx] = $value; | ||
|  |                 } | ||
|  |             } elseif (!$this->getOption(self::OPTION_USE_BRACKETS) | ||
|  |                       && !empty($return[$key]) | ||
|  |             ) { | ||
|  |                 $return[$key]   = (array) $return[$key]; | ||
|  |                 $return[$key][] = $value; | ||
|  |             } else { | ||
|  |                 $return[$key] = $value; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         return $return; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @param array $array (name => value) array | ||
|  |      * | ||
|  |      * @return void | ||
|  |      */ | ||
|  |     public function setQueryVariables(array $array) | ||
|  |     { | ||
|  |         if (!$array) { | ||
|  |             $this->query = false; | ||
|  |         } else { | ||
|  |             foreach ($array as $name => $value) { | ||
|  |                 if ($this->getOption(self::OPTION_ENCODE_KEYS)) { | ||
|  |                     $name = rawurlencode($name); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 if (is_array($value)) { | ||
|  |                     foreach ($value as $k => $v) { | ||
|  |                         $parts[] = $this->getOption(self::OPTION_USE_BRACKETS) | ||
|  |                             ? sprintf('%s[%s]=%s', $name, $k, $v) | ||
|  |                             : ($name . '=' . $v); | ||
|  |                     } | ||
|  |                 } elseif (!is_null($value)) { | ||
|  |                     $parts[] = $name . '=' . $value; | ||
|  |                 } else { | ||
|  |                     $parts[] = $name; | ||
|  |                 } | ||
|  |             } | ||
|  |             $this->query = implode($this->getOption(self::OPTION_SEPARATOR_OUTPUT), | ||
|  |                                    $parts); | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @param string $name | ||
|  |      * @param mixed  $value | ||
|  |      * | ||
|  |      * @return  array | ||
|  |      */ | ||
|  |     public function setQueryVariable($name, $value) | ||
|  |     { | ||
|  |         $array = $this->getQueryVariables(); | ||
|  |         $array[$name] = $value; | ||
|  |         $this->setQueryVariables($array); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @param string $name | ||
|  |      * | ||
|  |      * @return void | ||
|  |      */ | ||
|  |     public function unsetQueryVariable($name) | ||
|  |     { | ||
|  |         $array = $this->getQueryVariables(); | ||
|  |         unset($array[$name]); | ||
|  |         $this->setQueryVariables($array); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Returns a string representation of this URL. | ||
|  |      * | ||
|  |      * @return  string | ||
|  |      */ | ||
|  |     public function getURL() | ||
|  |     { | ||
|  |         // See RFC 3986, section 5.3
 | ||
|  |         $url = ""; | ||
|  | 
 | ||
|  |         if ($this->scheme !== false) { | ||
|  |             $url .= $this->scheme . ':'; | ||
|  |         } | ||
|  | 
 | ||
|  |         $authority = $this->getAuthority(); | ||
|  |         if ($authority !== false) { | ||
|  |             $url .= '//' . $authority; | ||
|  |         } | ||
|  |         $url .= $this->path; | ||
|  | 
 | ||
|  |         if ($this->query !== false) { | ||
|  |             $url .= '?' . $this->query; | ||
|  |         } | ||
|  | 
 | ||
|  |         if ($this->fragment !== false) { | ||
|  |             $url .= '#' . $this->fragment; | ||
|  |         } | ||
|  |      | ||
|  |         return $url; | ||
|  |     } | ||
|  | 
 | ||
|  |     /**  | ||
|  |      * Returns a normalized string representation of this URL. This is useful | ||
|  |      * for comparison of URLs. | ||
|  |      * | ||
|  |      * @return  string | ||
|  |      */ | ||
|  |     public function getNormalizedURL() | ||
|  |     { | ||
|  |         $url = clone $this; | ||
|  |         $url->normalize(); | ||
|  |         return $url->getUrl(); | ||
|  |     } | ||
|  | 
 | ||
|  |     /**  | ||
|  |      * Returns a normalized Net_URL2 instance. | ||
|  |      * | ||
|  |      * @return  Net_URL2 | ||
|  |      */ | ||
|  |     public function normalize() | ||
|  |     { | ||
|  |         // See RFC 3886, section 6
 | ||
|  | 
 | ||
|  |         // Schemes are case-insensitive
 | ||
|  |         if ($this->scheme) { | ||
|  |             $this->scheme = strtolower($this->scheme); | ||
|  |         } | ||
|  | 
 | ||
|  |         // Hostnames are case-insensitive
 | ||
|  |         if ($this->host) { | ||
|  |             $this->host = strtolower($this->host); | ||
|  |         } | ||
|  | 
 | ||
|  |         // Remove default port number for known schemes (RFC 3986, section 6.2.3)
 | ||
|  |         if ($this->port && | ||
|  |             $this->scheme && | ||
|  |             $this->port == getservbyname($this->scheme, 'tcp')) { | ||
|  | 
 | ||
|  |             $this->port = false; | ||
|  |         } | ||
|  | 
 | ||
|  |         // Normalize case of %XX percentage-encodings (RFC 3986, section 6.2.2.1)
 | ||
|  |         foreach (array('userinfo', 'host', 'path') as $part) { | ||
|  |             if ($this->$part) { | ||
|  |                 $this->$part  = preg_replace('/%[0-9a-f]{2}/ie', 'strtoupper("\0")', $this->$part); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         // Path segment normalization (RFC 3986, section 6.2.2.3)
 | ||
|  |         $this->path = self::removeDotSegments($this->path); | ||
|  | 
 | ||
|  |         // Scheme based normalization (RFC 3986, section 6.2.3)
 | ||
|  |         if ($this->host && !$this->path) { | ||
|  |             $this->path = '/'; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Returns whether this instance represents an absolute URL. | ||
|  |      * | ||
|  |      * @return  bool | ||
|  |      */ | ||
|  |     public function isAbsolute() | ||
|  |     { | ||
|  |         return (bool) $this->scheme; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Returns an Net_URL2 instance representing an absolute URL relative to | ||
|  |      * this URL. | ||
|  |      * | ||
|  |      * @param Net_URL2|string $reference relative URL | ||
|  |      * | ||
|  |      * @return Net_URL2 | ||
|  |      */ | ||
|  |     public function resolve($reference) | ||
|  |     { | ||
|  |         if (is_string($reference)) { | ||
|  |             $reference = new self($reference); | ||
|  |         } | ||
|  |         if (!$this->isAbsolute()) { | ||
|  |             throw new Exception('Base-URL must be absolute'); | ||
|  |         } | ||
|  | 
 | ||
|  |         // A non-strict parser may ignore a scheme in the reference if it is
 | ||
|  |         // identical to the base URI's scheme.
 | ||
|  |         if (!$this->getOption(self::OPTION_STRICT) && $reference->scheme == $this->scheme) { | ||
|  |             $reference->scheme = false; | ||
|  |         } | ||
|  | 
 | ||
|  |         $target = new self(''); | ||
|  |         if ($reference->scheme !== false) { | ||
|  |             $target->scheme = $reference->scheme; | ||
|  |             $target->setAuthority($reference->getAuthority()); | ||
|  |             $target->path  = self::removeDotSegments($reference->path); | ||
|  |             $target->query = $reference->query; | ||
|  |         } else { | ||
|  |             $authority = $reference->getAuthority(); | ||
|  |             if ($authority !== false) { | ||
|  |                 $target->setAuthority($authority); | ||
|  |                 $target->path  = self::removeDotSegments($reference->path); | ||
|  |                 $target->query = $reference->query; | ||
|  |             } else { | ||
|  |                 if ($reference->path == '') { | ||
|  |                     $target->path = $this->path; | ||
|  |                     if ($reference->query !== false) { | ||
|  |                         $target->query = $reference->query; | ||
|  |                     } else { | ||
|  |                         $target->query = $this->query; | ||
|  |                     } | ||
|  |                 } else { | ||
|  |                     if (substr($reference->path, 0, 1) == '/') { | ||
|  |                         $target->path = self::removeDotSegments($reference->path); | ||
|  |                     } else { | ||
|  |                         // Merge paths (RFC 3986, section 5.2.3)
 | ||
|  |                         if ($this->host !== false && $this->path == '') { | ||
|  |                             $target->path = '/' . $this->path; | ||
|  |                         } else { | ||
|  |                             $i = strrpos($this->path, '/'); | ||
|  |                             if ($i !== false) { | ||
|  |                                 $target->path = substr($this->path, 0, $i + 1); | ||
|  |                             } | ||
|  |                             $target->path .= $reference->path; | ||
|  |                         } | ||
|  |                         $target->path = self::removeDotSegments($target->path); | ||
|  |                     } | ||
|  |                     $target->query = $reference->query; | ||
|  |                 } | ||
|  |                 $target->setAuthority($this->getAuthority()); | ||
|  |             } | ||
|  |             $target->scheme = $this->scheme; | ||
|  |         } | ||
|  | 
 | ||
|  |         $target->fragment = $reference->fragment; | ||
|  | 
 | ||
|  |         return $target; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Removes dots as described in RFC 3986, section 5.2.4, e.g. | ||
|  |      * "/foo/../bar/baz" => "/bar/baz" | ||
|  |      * | ||
|  |      * @param string $path a path | ||
|  |      * | ||
|  |      * @return string a path | ||
|  |      */ | ||
|  |     private static function removeDotSegments($path) | ||
|  |     { | ||
|  |         $output = ''; | ||
|  | 
 | ||
|  |         // Make sure not to be trapped in an infinite loop due to a bug in this
 | ||
|  |         // method
 | ||
|  |         $j = 0;  | ||
|  |         while ($path && $j++ < 100) { | ||
|  |             // Step A
 | ||
|  |             if (substr($path, 0, 2) == './') { | ||
|  |                 $path = substr($path, 2); | ||
|  |             } elseif (substr($path, 0, 3) == '../') { | ||
|  |                 $path = substr($path, 3); | ||
|  | 
 | ||
|  |             // Step B
 | ||
|  |             } elseif (substr($path, 0, 3) == '/./' || $path == '/.') { | ||
|  |                 $path = '/' . substr($path, 3); | ||
|  | 
 | ||
|  |             // Step C
 | ||
|  |             } elseif (substr($path, 0, 4) == '/../' || $path == '/..') { | ||
|  |                 $path = '/' . substr($path, 4); | ||
|  |                 $i = strrpos($output, '/'); | ||
|  |                 $output = $i === false ? '' : substr($output, 0, $i); | ||
|  | 
 | ||
|  |             // Step D
 | ||
|  |             } elseif ($path == '.' || $path == '..') { | ||
|  |                 $path = ''; | ||
|  | 
 | ||
|  |             // Step E
 | ||
|  |             } else { | ||
|  |                 $i = strpos($path, '/'); | ||
|  |                 if ($i === 0) { | ||
|  |                     $i = strpos($path, '/', 1); | ||
|  |                 } | ||
|  |                 if ($i === false) { | ||
|  |                     $i = strlen($path); | ||
|  |                 } | ||
|  |                 $output .= substr($path, 0, $i); | ||
|  |                 $path = substr($path, $i); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         return $output; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Returns a Net_URL2 instance representing the canonical URL of the | ||
|  |      * currently executing PHP script. | ||
|  |      *  | ||
|  |      * @return  string | ||
|  |      */ | ||
|  |     public static function getCanonical() | ||
|  |     { | ||
|  |         if (!isset($_SERVER['REQUEST_METHOD'])) { | ||
|  |             // ALERT - no current URL
 | ||
|  |             throw new Exception('Script was not called through a webserver'); | ||
|  |         } | ||
|  | 
 | ||
|  |         // Begin with a relative URL
 | ||
|  |         $url = new self($_SERVER['PHP_SELF']); | ||
|  |         $url->scheme = isset($_SERVER['HTTPS']) ? 'https' : 'http'; | ||
|  |         $url->host = $_SERVER['SERVER_NAME']; | ||
|  |         $port = intval($_SERVER['SERVER_PORT']); | ||
|  |         if ($url->scheme == 'http' && $port != 80 || | ||
|  |             $url->scheme == 'https' && $port != 443) { | ||
|  | 
 | ||
|  |             $url->port = $port; | ||
|  |         } | ||
|  |         return $url; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Returns the URL used to retrieve the current request. | ||
|  |      * | ||
|  |      * @return  string | ||
|  |      */ | ||
|  |     public static function getRequestedURL() | ||
|  |     { | ||
|  |         return self::getRequested()->getUrl(); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Returns a Net_URL2 instance representing the URL used to retrieve the | ||
|  |      * current request. | ||
|  |      * | ||
|  |      * @return  Net_URL2 | ||
|  |      */ | ||
|  |     public static function getRequested() | ||
|  |     { | ||
|  |         if (!isset($_SERVER['REQUEST_METHOD'])) { | ||
|  |             // ALERT - no current URL
 | ||
|  |             throw new Exception('Script was not called through a webserver'); | ||
|  |         } | ||
|  | 
 | ||
|  |         // Begin with a relative URL
 | ||
|  |         $url = new self($_SERVER['REQUEST_URI']); | ||
|  |         $url->scheme = isset($_SERVER['HTTPS']) ? 'https' : 'http'; | ||
|  |         // Set host and possibly port
 | ||
|  |         $url->setAuthority($_SERVER['HTTP_HOST']); | ||
|  |         return $url; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Sets the specified option. | ||
|  |      * | ||
|  |      * @param string $optionName a self::OPTION_ constant | ||
|  |      * @param mixed  $value      option value   | ||
|  |      * | ||
|  |      * @return void | ||
|  |      * @see  self::OPTION_STRICT | ||
|  |      * @see  self::OPTION_USE_BRACKETS | ||
|  |      * @see  self::OPTION_ENCODE_KEYS | ||
|  |      */ | ||
|  |     function setOption($optionName, $value) | ||
|  |     { | ||
|  |         if (!array_key_exists($optionName, $this->options)) { | ||
|  |             return false; | ||
|  |         } | ||
|  |         $this->options[$optionName] = $value; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Returns the value of the specified option. | ||
|  |      * | ||
|  |      * @param string $optionName The name of the option to retrieve | ||
|  |      * | ||
|  |      * @return  mixed | ||
|  |      */ | ||
|  |     function getOption($optionName) | ||
|  |     { | ||
|  |         return isset($this->options[$optionName]) | ||
|  |             ? $this->options[$optionName] : false; | ||
|  |     } | ||
|  | } |