Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- /**
- * File for N_Uri class.
- * @package NitrogenPHP
- * @see N_Uri
- **/
- /**
- * A convenient description of "uniform resource identifiers" or "data source names".
- *
- * It roughly follows the uri specifications linked to by the given wikipedia
- * page, but a few things are not accounted for yet.
- * - may encode more characters than nessesary in certain parts
- *
- * User info (keyed by "userinfo") is represented by a numerically keyed array
- * of segments. A zero-length array implies there is no user info section in the
- * URI.
- *
- * Paths are represented by a numerically keyed array of path segments.
- * A non-empty URI will always have at least one section (even if empty).
- * Absolute paths have a zero-length (ie '') first element (at index 0).
- *
- * URI components are access with array syntax. Note that getting the value of
- * the userinfo, path, or query paths will return an object that must either be
- * cast to a string, or accessed by array access as well.
- *
- * Examples:
- * <code>
- * $uri = new N_Uri('http://johndoe:password@example.com/path/to/example?key=value');
- *
- * echo $uri . "\n";
- * // http://johndoe:password@example.com/path/to/example?key=value
- *
- * echo $uri['userinfo'] . "\n";
- * // johndoe:password
- *
- * $uri['host'] = 'whatever.org';
- * $uri['userinfo'][0] = 'jim';
- * echo $uri . "\n";
- * // http://jim:password@whatever.org/path/to/example?key=value
- * </code>
- *
- * Non-standard parts will be assumed to be from the query, and will be
- * get or set appropriately. Note that non-existant parts will always return
- * null WITHOUT throwing an error.
- * <code>
- * $uri = 'sqlite://path/to/database';
- * $uri['table'] = 'some_table';
- * echo $uri . "\n";
- * // sqlite://path/to/database?table=some_table
- * </code>
- *
- * @see http://en.wikipedia.org/wiki/Uniform_Resource_Identifier
- * @see http://tools.ietf.org/html/rfc3986
- * @package NitrogenPHP
- **/
- class N_Uri implements ArrayAccess, Serializable, IteratorAggregate
- {
- /**
- * @var string The delimiters between major components in a URI string.
- **/
- const GEN_DELIMS = ":/?#[]@";
- /**
- * @var string The delimiters within components of a URI string.
- **/
- // I had to make this with single quotes, because the VPS choked and thought I was refering to a $& variable or something.
- const SUB_DELIMS = '!$&\'()*+,;=';
- /**
- * @var string All of the fully URI safe (never need encoding) characters.
- **/
- const SAFE = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~";
- /**
- * Encode non-safe characters.
- * @param string $string The string to encode unsafe characters in.
- * @param string $safe Other characters to consider safe in addition to N_Uri::SAFE
- * @return string
- **/
- public static function encode($string, $safe = '')
- {
- // prepare an array of the safe characters
- $safe .= self::SAFE;
- $safe = array_unique(str_split($safe));
- // prepare a character mapping with the safe characters mapping to themselves
- $char_map = Array();
- foreach ($safe as $char)
- {
- $char_map[$char] = $char;
- }
- // iterate through the string, mapping the characters
- $return = '';
- foreach (str_split($string) as $char)
- {
- // add escaped characters to the mapping if they are not found
- if (!isset($char_map[$char]))
- {
- $char_map[$char] = rawurlencode($char);
- }
- $return .= $char_map[$char];
- }
- return $return;
- }
- /**
- * Decode all URI encoded characters in a given string.
- * @param string
- * @return string
- **/
- public static function decode($string)
- {
- return rawurldecode($string);
- }
- /**
- * Parse a URI string into its major components.
- *
- * A non-existant scheme, host, port or fragment will be null, and a
- * non-existant userinfo, path, or query will be a zero-length array.
- *
- * @param string $uri
- * @return array
- **/
- public static function parseUriString($input_string)
- {
- // parse out 5 major parts
- preg_match('/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/', $input_string, $m);
- // 12 3 4 5 6 7 8 9
- $scheme = isset($m[1]) && $m[1] != '' ? $m[2] : null;
- $authority = isset($m[3]) ? $m[4] : null;
- $path = $m[5];
- $query = isset($m[6]) ? $m[7] : null;
- $fragment = isset($m[8]) ? $m[9] : null;
- // parse the authority
- // NOTE: port should only be an int, but I am matching everything
- if ($authority)
- {
- preg_match('/^(([^@]*)@)?([^:]*)(:(.*))?$/', $authority, $m);
- // 12 3 4 5
- $userinfo = isset($m[1]) && $m[1] != '' ? $m[2] : null;
- $host = $m[3];
- $port = isset($m[4]) ? $m[5] : null;
- }
- else
- {
- $userinfo = $host = $port = null;
- }
- return array(
- 'scheme' => $scheme === null ? null : rawurldecode($scheme),
- 'userinfo' => self::parseUserinfo($userinfo),
- 'host' => $host === null ? null : rawurldecode($host),
- 'port' => $port === null ? null : (ctype_digit($port) ? (int)$port : rawurldecode($port)),
- 'path' => self::parsePath($path),
- 'query' => self::parseQuery($query),
- 'fragment' => $fragment === null ? null : rawurldecode($fragment),
- );
- }
- /**
- * Parse the user info part of a URI into an array, decoding each element.
- * @param string $input_string
- * @return array
- **/
- public static function parseUserinfo($input_string)
- {
- $input_string = (string)$input_string;
- if ($input_string == '')
- {
- return array();
- }
- return array_map('rawurldecode', explode(':', $input_string));
- }
- /**
- * Build a userinfo string from an array, encoding as nessesary
- * @param array $input_array
- * @return string
- **/
- protected static function buildUserinfo($input_array)
- {
- $encoded = Array();
- foreach ($input_array as $segment)
- {
- $encoded[] = self::encode($segment, self::SUB_DELIMS);
- }
- return implode(':', $encoded);
- }
- /**
- * Parse the path part of a URI into an array.
- * A zero-length string at index 0 indicates an absolute path.
- * @param string $input_string
- * @return array
- **/
- protected static function parsePath($input_string)
- {
- $input_string = (string)$input_string;
- if ('' == $input_string)
- {
- return array();
- }
- return array_map('rawurldecode', explode('/', $input_string));
- }
- /**
- * Build a path string from an array, encoding as nessesary.
- * @param array $input_array
- * @return string
- **/
- protected static function buildPath($input_array)
- {
- $encoded = Array();
- foreach ($input_array as $segment)
- {
- $encoded[] = self::encode($segment, self::SUB_DELIMS . '@:');
- }
- return implode('/', $encoded);
- }
- /**
- * Parse the query part of a URI into an associative array.
- * @param string $input_string
- * @return array
- **/
- protected static function parseQuery($input_string)
- {
- parse_str($input_string, $query_data_array);
- return $query_data_array;
- }
- protected static function buildQuery($input_array)
- {
- return http_build_query($input_array);
- }
- /**
- * @var array All of the URI data goes in this array.
- **/
- protected $_data;
- /**
- * Construct a uri from a string or associative array.
- *
- * <code>
- * $uri1 = new N_Uri('http://mike:boers@website.com/path/to/thing');
- * $uri2 = new N_Uri(array(
- * 'scheme' => 'http',
- * 'userinfo' => array('mike', 'boers'),
- * 'host' => 'website.com',
- * 'path' => array('', 'path', 'to', 'thing'),
- * );
- * $uri3 = new N_Uri(array(
- * 'scheme' => 'http',
- * 'userinfo' => 'mike:boers',
- * 'host' => 'website.com',
- * 'path' => '/path/to/thing',
- * );
- * </code>
- *
- * @param string|array $input
- **/
- public function __construct($input = '')
- {
- if ($input instanceof self)
- {
- $this->_data = $input->_data;
- }
- elseif (is_array($input) || ($input instanceof Traversable))
- {
- $this->_data = self::parseUriString(''); // setup the defaults
- foreach ($input as $key => $value)
- {
- $this[$key] = $value; // note that this uses the ArrayAccess, and so handles types properly
- }
- }
- else
- {
- $this->_data = self::parseUriString((string)$input);
- }
- }
- /**
- * Parse a string, input an array, or return a N_Uri
- * @param string|array|N_Uri $input
- * @return N_Uri
- **/
- public static function factory($uri)
- {
- if ($uri instanceof self)
- {
- return $uri;
- }
- return new self($uri);
- }
- /**
- * Get a string representation of the URI.
- * @return string
- **/
- public function __toString()
- {
- $uri = '';
- // append the scheme
- if (null !== $this->_data['scheme'])
- {
- $uri .= self::encode($this->_data['scheme'], '+') . ':';
- }
- // append the authority
- $has_authority = $this->_data['userinfo'] || null !== $this->_data['host'] || null !== $this->_data['port'];
- if ($has_authority)
- {
- $uri .= '//';
- $uri .= $this->_data['userinfo'] ? self::buildUserinfo($this->_data['userinfo']) . '@' : '';
- $uri .= self::encode($this->_data['host'], self::SUB_DELIMS);
- $uri .= (null !== $this->_data['port'] && '' !== $this->_data['port'] ? ':' . self::encode($this->_data['port']) : '');
- }
- // append the path
- // TODO: conform this to section 3.3 on the reference
- if (Array('') != $this->_data['path'])
- {
- // encode most of it
- $path = Array();
- foreach ($this->_data['path'] as $row)
- {
- $path[] = self::encode($row, self::SUB_DELIMS . '@:');
- }
- // no scheme -> no colon in first chunk
- if (null === $this->_data['scheme'])
- {
- $path = $this->_data['path'];
- $path[0] = str_replace(':', rawurlencode(':'), $path[0]);
- }
- // no authority -> cannot have empty segments at beggining
- if (!$has_authority)
- {
- while ($path && $path[0] == '')
- {
- array_shift($path);
- }
- }
- // authority -> must either be empty or have a preceding slash
- elseif ($path && $path[0]) // there is a path and the first is not blank
- {
- array_unshift($path, '');
- }
- $uri .= implode('/', $path);
- }
- // append the query
- if ($this->_data['query'])
- {
- $uri .= '?' . http_build_query($this->_data['query']);
- }
- // append the fragment
- if (null !== $this->_data['fragment'])
- {
- $uri .= '#' . self::encode($this->_data['fragment'], '/?');
- }
- return $uri;
- }
- public function getSchemeParent()
- {
- switch ($this->_data['scheme'])
- {
- case 'mysql':
- case 'sqlite':
- return 'database';
- case 'file':
- return 'file';
- case 'udp':
- case 'tcp':
- case 'unix':
- return 'socket';
- }
- return $this->_data['scheme'];
- }
- public function offsetExists($offset)
- {
- switch ($offset)
- {
- case 'scheme':
- case 'host':
- case 'port':
- case 'fragment':
- return is_string($this->_data[$offset]);
- case 'userinfo':
- case 'path':
- case 'query':
- return (bool) count($this->_data[$offset]);
- default:
- return isset($this->_data['query'][$offset]);
- }
- }
- public function offsetUnset($offset)
- {
- switch ($offset)
- {
- case 'scheme':
- case 'host':
- case 'port':
- case 'fragment':
- $this->_data[$offset] = null;
- break;
- case 'userinfo':
- case 'path':
- case 'query':
- $this->_data[$offset] = array();
- break;
- default:
- unset($this->_data['query'][$offset]);
- break;
- }
- }
- public function offsetGet($offset)
- {
- switch ($offset)
- {
- case 'scheme':
- case 'host':
- case 'port':
- case 'fragment':
- return $this->_data[$offset];
- break;
- case 'userinfo':
- case 'path':
- return new N_Uri_SequenceAccessor($this, $offset);
- case 'query':
- return new N_Uri_QueryAccessor($this, $offset);
- default:
- return isset($this->_data['query'][$offset]) ? $this->_data['query'][$offset] : null;
- }
- }
- public function offsetSet($offset, $value)
- {
- switch ($offset)
- {
- case 'scheme':
- case 'host':
- case 'fragment':
- $this->_data[$offset] = (string)$value;
- break;
- case 'port':
- $this->_data[$offset] = is_int($value) || ctype_digit($value) ? (int)$value : $value;
- break;
- case 'userinfo':
- case 'path':
- case 'query':
- $callback = array(__CLASS__, 'parse' . ucfirst($offset));
- $this->_data[$offset] = is_array($value) ? $value : call_user_func($callback, $value);
- break;
- default:
- $this->_data['query'][$offset] = $value;
- break;
- }
- }
- public function serialize()
- {
- return $this->__toString();
- }
- public function unserialize($serialized)
- {
- $this->__construct($serialized);
- }
- public function getIterator()
- {
- return new ArrayIterator($this->_data);
- }
- }
- class N_Uri_SequenceAccessor extends N_Uri implements ArrayAccess
- {
- protected $_uri;
- protected $_type;
- public function __construct(N_Uri $uri, $type)
- {
- $this->_uri = $uri;
- $this->_type = $type;
- switch ($type)
- {
- case 'userinfo':
- case 'path':
- break;
- default:
- throw new Exception('Type must be valid.');
- }
- }
- public function __toString()
- {
- switch ($this->_type)
- {
- case 'path':
- return N_Uri::buildPath($this->_uri->_data[$this->_type]);
- case 'userinfo':
- return N_Uri::buildUserinfo($this->_uri->_data[$this->_type]);
- }
- }
- protected function _normalizeOffset($offset)
- {
- $count = count($this->_uri->_data[$this->_type]);
- $offset = is_int($offset) ? min($offset, $count) : $count;
- return $offset + ($offset < 0 ? count($this->_uri->_data[$this->_type]) : 0);
- }
- public function offsetExists($offset)
- {
- $offset = $this->_normalizeOffset($offset);
- return isset($this->_uri->_data[$this->_type][$offset]);
- }
- public function offsetUnset($offset)
- {
- $offset = $this->_normalizeOffset($offset);
- unset($this->_uri->_data[$this->_type][$offset]);
- $this->_uri->_data[$this->_type] = array_values($this->_uri->_data[$this->_type]);
- }
- public function offsetGet($offset)
- {
- $offset = $this->_normalizeOffset($offset);
- return $this->_uri->_data[$this->_type][$offset];
- }
- public function offsetSet($offset, $value)
- {
- if (!is_int($offset) && !is_null($offset))
- {
- throw new Exception('Offset must be an integer.');
- }
- else
- {
- $offset = $this->_normalizeOffset($offset);
- $this->_uri->_data[$this->_type][$offset] = (string)$value;
- }
- }
- }
- class N_Uri_QueryAccessor extends N_Uri implements ArrayAccess
- {
- protected $_uri;
- protected $_key = 'query';
- public function __construct(N_Uri $uri)
- {
- $this->_uri = $uri;
- }
- public function __toString()
- {
- return self::buildQuery($this->_uri->_data[$this->_key]);
- }
- public function offsetExists($offset)
- {
- return isset($this->_uri->_data[$this->_key][$offset]);
- }
- public function offsetUnset($offset)
- {
- unset($this->_uri->_data[$this->_key][$offset]);
- }
- public function offsetGet($offset)
- {
- return $this->_uri->_data[$this->_key][$offset];
- }
- public function offsetSet($offset, $value)
- {
- $this->_uri->_data[$this->_key][$offset] = $value;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement