forked from GNUsocial/gnu-social
		
	
		
			
				
	
	
		
			943 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			943 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * Net_URL2, a class representing a URL as per RFC 3986.
 | |
|  *
 | |
|  * PHP version 5
 | |
|  *
 | |
|  * LICENSE:
 | |
|  *
 | |
|  * Copyright (c) 2007-2009, 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:
 | |
|  *
 | |
|  *   * Redistributions of source code must retain the above copyright
 | |
|  *     notice, this list of conditions and the following disclaimer.
 | |
|  *   * 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.
 | |
|  *   * Neither the name of the Net_URL2 nor the names of its contributors may
 | |
|  *     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.
 | |
|  *
 | |
|  * @category  Networking
 | |
|  * @package   Net_URL2
 | |
|  * @author    Christian Schmidt <schmidt@php.net>
 | |
|  * @copyright 2007-2009 Peytz & Co. A/S
 | |
|  * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
 | |
|  * @version   CVS: $Id: URL2.php 309223 2011-03-14 14:26:32Z till $
 | |
|  * @link      http://www.rfc-editor.org/rfc/rfc3986.txt
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * Represents a URL as per RFC 3986.
 | |
|  *
 | |
|  * @category  Networking
 | |
|  * @package   Net_URL2
 | |
|  * @author    Christian Schmidt <schmidt@php.net>
 | |
|  * @copyright 2007-2009 Peytz & Co. A/S
 | |
|  * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
 | |
|  * @version   Release: @package_version@
 | |
|  * @link      http://pear.php.net/package/Net_URL2
 | |
|  */
 | |
| 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 "&".
 | |
|      */
 | |
|     const OPTION_SEPARATOR_INPUT = 'input_separator';
 | |
| 
 | |
|     /**
 | |
|      * Query variable separator used when generating the query string. Default
 | |
|      * is "&".
 | |
|      */
 | |
|     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  => '&',
 | |
|         self::OPTION_SEPARATOR_OUTPUT => '&',
 | |
|         );
 | |
| 
 | |
|     /**
 | |
|      * @var  string|bool
 | |
|      */
 | |
|     private $_scheme = false;
 | |
| 
 | |
|     /**
 | |
|      * @var  string|bool
 | |
|      */
 | |
|     private $_userinfo = false;
 | |
| 
 | |
|     /**
 | |
|      * @var  string|bool
 | |
|      */
 | |
|     private $_host = false;
 | |
| 
 | |
|     /**
 | |
|      * @var  string|bool
 | |
|      */
 | |
|     private $_port = false;
 | |
| 
 | |
|     /**
 | |
|      * @var  string
 | |
|      */
 | |
|     private $_path = '';
 | |
| 
 | |
|     /**
 | |
|      * @var  string|bool
 | |
|      */
 | |
|     private $_query = false;
 | |
| 
 | |
|     /**
 | |
|      * @var  string|bool
 | |
|      */
 | |
|     private $_fragment = false;
 | |
| 
 | |
|     /**
 | |
|      * Constructor.
 | |
|      *
 | |
|      * @param string $url     an absolute or relative URL
 | |
|      * @param array  $options an array of OPTION_xxx constants
 | |
|      *
 | |
|      * @return $this
 | |
|      * @uses   self::parseUrl()
 | |
|      */
 | |
|     public function __construct($url, array $options = array())
 | |
|     {
 | |
|         foreach ($options as $optionName => $value) {
 | |
|             if (array_key_exists($optionName, $this->_options)) {
 | |
|                 $this->_options[$optionName] = $value;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         $this->parseUrl($url);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Magic Setter.
 | |
|      *
 | |
|      * This method will magically set the value of a private variable ($var)
 | |
|      * with the value passed as the args
 | |
|      *
 | |
|      * @param  string $var      The private variable to set.
 | |
|      * @param  mixed  $arg      An argument of any type.
 | |
|      * @return void
 | |
|      */
 | |
|     public function __set($var, $arg)
 | |
|     {
 | |
|         $method = 'set' . $var;
 | |
|         if (method_exists($this, $method)) {
 | |
|             $this->$method($arg);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Magic Getter.
 | |
|      *
 | |
|      * This is the magic get method to retrieve the private variable
 | |
|      * that was set by either __set() or it's setter...
 | |
|      *
 | |
|      * @param  string $var         The property name to retrieve.
 | |
|      * @return mixed  $this->$var  Either a boolean false if the
 | |
|      *                             property is not set or the value
 | |
|      *                             of the private property.
 | |
|      */
 | |
|     public function __get($var)
 | |
|     {
 | |
|         $method = 'get' . $var;
 | |
|         if (method_exists($this, $method)) {
 | |
|             return $this->$method();
 | |
|         }
 | |
| 
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * 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;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Sets the scheme, e.g. "http" or "urn". Specify false if there is no
 | |
|      * scheme specified, i.e. if this is a relative URL.
 | |
|      *
 | |
|      * @param string|bool $scheme e.g. "http" or "urn", or false if there is no
 | |
|      *                            scheme specified, i.e. if this is a relative
 | |
|      *                            URL
 | |
|      *
 | |
|      * @return $this
 | |
|      * @see    getScheme()
 | |
|      */
 | |
|     public function setScheme($scheme)
 | |
|     {
 | |
|         $this->_scheme = $scheme;
 | |
|         return $this;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * 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 optional password, or false
 | |
|      *
 | |
|      * @return $this
 | |
|      */
 | |
|     public function setUserinfo($userinfo, $password = false)
 | |
|     {
 | |
|         $this->_userinfo = $userinfo;
 | |
|         if ($password !== false) {
 | |
|             $this->_userinfo .= ':' . $password;
 | |
|         }
 | |
|         return $this;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Returns the host part, or false if there is no authority part, e.g.
 | |
|      * relative URLs.
 | |
|      *
 | |
|      * @return  string|bool a hostname, an IP address, or false
 | |
|      */
 | |
|     public function getHost()
 | |
|     {
 | |
|         return $this->_host;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Sets the host part. Specify false if there is no authority part, e.g.
 | |
|      * relative URLs.
 | |
|      *
 | |
|      * @param string|bool $host a hostname, an IP address, or false
 | |
|      *
 | |
|      * @return $this
 | |
|      */
 | |
|     public function setHost($host)
 | |
|     {
 | |
|         $this->_host = $host;
 | |
|         return $this;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Returns the port number, or false if there is no port number specified,
 | |
|      * i.e. if the default port is to be used.
 | |
|      *
 | |
|      * @return  string|bool
 | |
|      */
 | |
|     public function getPort()
 | |
|     {
 | |
|         return $this->_port;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Sets the port number. Specify false if there is no port number specified,
 | |
|      * i.e. if the default port is to be used.
 | |
|      *
 | |
|      * @param string|bool $port a port number, or false
 | |
|      *
 | |
|      * @return $this
 | |
|      */
 | |
|     public function setPort($port)
 | |
|     {
 | |
|         $this->_port = $port;
 | |
|         return $this;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Returns the authority part, i.e. [ userinfo "@" ] host [ ":" port ], or
 | |
|      * false if there is no authority.
 | |
|      *
 | |
|      * @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;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Sets the authority part, i.e. [ userinfo "@" ] host [ ":" port ]. Specify
 | |
|      * false if there is no authority.
 | |
|      *
 | |
|      * @param string|false $authority a hostname or an IP addresse, possibly
 | |
|      *                                with userinfo prefixed and port number
 | |
|      *                                appended, e.g. "foo:bar@example.org:81".
 | |
|      *
 | |
|      * @return $this
 | |
|      */
 | |
|     public function setAuthority($authority)
 | |
|     {
 | |
|         $this->_userinfo = 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 = $reg[5];
 | |
|             }
 | |
|         }
 | |
|         return $this;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Returns the path part (possibly an empty string).
 | |
|      *
 | |
|      * @return string
 | |
|      */
 | |
|     public function getPath()
 | |
|     {
 | |
|         return $this->_path;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Sets the path part (possibly an empty string).
 | |
|      *
 | |
|      * @param string $path a path
 | |
|      *
 | |
|      * @return $this
 | |
|      */
 | |
|     public function setPath($path)
 | |
|     {
 | |
|         $this->_path = $path;
 | |
|         return $this;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Returns the query string (excluding the leading "?"), or false if "?"
 | |
|      * is not present in the URL.
 | |
|      *
 | |
|      * @return  string|bool
 | |
|      * @see     self::getQueryVariables()
 | |
|      */
 | |
|     public function getQuery()
 | |
|     {
 | |
|         return $this->_query;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Sets the query string (excluding the leading "?"). Specify false if "?"
 | |
|      * is not present in the URL.
 | |
|      *
 | |
|      * @param string|bool $query a query string, e.g. "foo=1&bar=2"
 | |
|      *
 | |
|      * @return $this
 | |
|      * @see    self::setQueryVariables()
 | |
|      */
 | |
|     public function setQuery($query)
 | |
|     {
 | |
|         $this->_query = $query;
 | |
|         return $this;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Returns the fragment name, or false if "#" is not present in the URL.
 | |
|      *
 | |
|      * @return  string|bool
 | |
|      */
 | |
|     public function getFragment()
 | |
|     {
 | |
|         return $this->_fragment;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Sets the fragment name. Specify false if "#" is not present in the URL.
 | |
|      *
 | |
|      * @param string|bool $fragment a fragment excluding the leading "#", or
 | |
|      *                              false
 | |
|      *
 | |
|      * @return $this
 | |
|      */
 | |
|     public function setFragment($fragment)
 | |
|     {
 | |
|         $this->_fragment = $fragment;
 | |
|         return $this;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Returns the query string like an array as the variables would appear in
 | |
|      * $_GET in a PHP script. If the URL does not contain a "?", an empty array
 | |
|      * is returned.
 | |
|      *
 | |
|      * @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;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Sets the query string to the specified variable in the query string.
 | |
|      *
 | |
|      * @param array $array (name => value) array
 | |
|      *
 | |
|      * @return $this
 | |
|      */
 | |
|     public function setQueryVariables(array $array)
 | |
|     {
 | |
|         if (!$array) {
 | |
|             $this->_query = false;
 | |
|         } else {
 | |
|             $this->_query = $this->buildQuery(
 | |
|                 $array,
 | |
|                 $this->getOption(self::OPTION_SEPARATOR_OUTPUT)
 | |
|             );
 | |
|         }
 | |
|         return $this;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Sets the specified variable in the query string.
 | |
|      *
 | |
|      * @param string $name  variable name
 | |
|      * @param mixed  $value variable value
 | |
|      *
 | |
|      * @return $this
 | |
|      */
 | |
|     public function setQueryVariable($name, $value)
 | |
|     {
 | |
|         $array = $this->getQueryVariables();
 | |
|         $array[$name] = $value;
 | |
|         $this->setQueryVariables($array);
 | |
|         return $this;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Removes the specifed variable from the query string.
 | |
|      *
 | |
|      * @param string $name a query string variable, e.g. "foo" in "?foo=1"
 | |
|      *
 | |
|      * @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 string representation of this URL.
 | |
|      *
 | |
|      * @return  string
 | |
|      * @see toString()
 | |
|      */
 | |
|     public function __toString()
 | |
|     {
 | |
|         return $this->getURL();
 | |
|     }
 | |
| 
 | |
|     /** 
 | |
|      * 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 (!$reference instanceof Net_URL2) {
 | |
|             $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
 | |
|      */
 | |
|     public 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) {
 | |
|             if (substr($path, 0, 2) == './') {
 | |
|                 // Step 2.A
 | |
|                 $path = substr($path, 2);
 | |
|             } elseif (substr($path, 0, 3) == '../') {
 | |
|                 // Step 2.A
 | |
|                 $path = substr($path, 3);
 | |
|             } elseif (substr($path, 0, 3) == '/./' || $path == '/.') {
 | |
|                 // Step 2.B
 | |
|                 $path = '/' . substr($path, 3);
 | |
|             } elseif (substr($path, 0, 4) == '/../' || $path == '/..') {
 | |
|                 // Step 2.C
 | |
|                 $path   = '/' . substr($path, 4);
 | |
|                 $i      = strrpos($output, '/');
 | |
|                 $output = $i === false ? '' : substr($output, 0, $i);
 | |
|             } elseif ($path == '.' || $path == '..') {
 | |
|                 // Step 2.D
 | |
|                 $path = '';
 | |
|             } else {
 | |
|                 // Step 2.E
 | |
|                 $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;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Percent-encodes all non-alphanumeric characters except these: _ . - ~
 | |
|      * Similar to PHP's rawurlencode(), except that it also encodes ~ in PHP
 | |
|      * 5.2.x and earlier.
 | |
|      *
 | |
|      * @param  $raw the string to encode
 | |
|      * @return string
 | |
|      */
 | |
|     public static function urlencode($string)
 | |
|     {
 | |
|     	$encoded = rawurlencode($string);
 | |
| 
 | |
|         // This is only necessary in PHP < 5.3.
 | |
|         $encoded = str_replace('%7E', '~', $encoded);
 | |
|         return $encoded;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * 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 = $_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;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Returns the value of the specified option.
 | |
|      *
 | |
|      * @param string $optionName The name of the option to retrieve
 | |
|      *
 | |
|      * @return  mixed
 | |
|      */
 | |
|     public function getOption($optionName)
 | |
|     {
 | |
|         return isset($this->_options[$optionName])
 | |
|             ? $this->_options[$optionName] : false;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * A simple version of http_build_query in userland. The encoded string is
 | |
|      * percentage encoded according to RFC 3986.
 | |
|      *
 | |
|      * @param array  $data      An array, which has to be converted into
 | |
|      *                          QUERY_STRING. Anything is possible.
 | |
|      * @param string $seperator See {@link self::OPTION_SEPARATOR_OUTPUT}
 | |
|      * @param string $key       For stacked values (arrays in an array).
 | |
|      *
 | |
|      * @return string
 | |
|      */
 | |
|     protected function buildQuery(array $data, $separator, $key = null)
 | |
|     {
 | |
|         $query = array();
 | |
|         foreach ($data as $name => $value) {
 | |
|             if ($this->getOption(self::OPTION_ENCODE_KEYS) === true) {
 | |
|                 $name = rawurlencode($name);
 | |
|             }
 | |
|             if ($key !== null) {
 | |
|                 if ($this->getOption(self::OPTION_USE_BRACKETS) === true) {
 | |
|                     $name = $key . '[' . $name . ']';
 | |
|                 } else {
 | |
|                     $name = $key;
 | |
|                 }
 | |
|             }
 | |
|             if (is_array($value)) {
 | |
|                 $query[] = $this->buildQuery($value, $separator, $name);
 | |
|             } else {
 | |
|                 $query[] = $name . '=' . rawurlencode($value);
 | |
|             }
 | |
|         }
 | |
|         return implode($separator, $query);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * This method uses a funky regex to parse the url into the designated parts.
 | |
|      *
 | |
|      * @param string $url
 | |
|      *
 | |
|      * @return void
 | |
|      * @uses   self::$_scheme, self::setAuthority(), self::$_path, self::$_query,
 | |
|      *         self::$_fragment
 | |
|      * @see    self::__construct()
 | |
|      */
 | |
|     protected function parseUrl($url)
 | |
|     {
 | |
|         // The regular expression is copied verbatim from RFC 3986, appendix B.
 | |
|         // The expression does not validate the URL but matches any string.
 | |
|         preg_match('!^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?!',
 | |
|                    $url,
 | |
|                    $matches);
 | |
| 
 | |
|         // "path" is always present (possibly as an empty string); the rest
 | |
|         // are optional.
 | |
|         $this->_scheme   = !empty($matches[1]) ? $matches[2] : false;
 | |
|         $this->setAuthority(!empty($matches[3]) ? $matches[4] : false);
 | |
|         $this->_path     = $matches[5];
 | |
|         $this->_query    = !empty($matches[6]) ? $matches[7] : false;
 | |
|         $this->_fragment = !empty($matches[8]) ? $matches[9] : false;
 | |
|     }
 | |
| }
 |