12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220 |
- <?php
- class Net_URL2
- {
-
- const OPTION_STRICT = 'strict';
-
- const OPTION_USE_BRACKETS = 'use_brackets';
-
- const OPTION_DROP_SEQUENCE = 'drop_sequence';
-
- const OPTION_ENCODE_KEYS = 'encode_keys';
-
- const OPTION_SEPARATOR_INPUT = 'input_separator';
-
- const OPTION_SEPARATOR_OUTPUT = 'output_separator';
-
- private $_options = array(
- self::OPTION_STRICT => true,
- self::OPTION_USE_BRACKETS => true,
- self::OPTION_DROP_SEQUENCE => true,
- self::OPTION_ENCODE_KEYS => true,
- self::OPTION_SEPARATOR_INPUT => '&',
- self::OPTION_SEPARATOR_OUTPUT => '&',
- );
-
- private $_scheme = false;
-
- private $_userinfo = false;
-
- private $_host = false;
-
- private $_port = false;
-
- private $_path = '';
-
- private $_query = false;
-
- private $_fragment = false;
-
- 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);
- }
-
- public function __set($var, $arg)
- {
- $method = 'set' . $var;
- if (method_exists($this, $method)) {
- $this->$method($arg);
- }
- }
-
- public function __get($var)
- {
- $method = 'get' . $var;
- if (method_exists($this, $method)) {
- return $this->$method();
- }
- return false;
- }
-
- public function getScheme()
- {
- return $this->_scheme;
- }
-
- public function setScheme($scheme)
- {
- $this->_scheme = $scheme;
- return $this;
- }
-
- public function getUser()
- {
- return $this->_userinfo !== false
- ? preg_replace('(:.*$)', '', $this->_userinfo)
- : false;
- }
-
- public function getPassword()
- {
- return $this->_userinfo !== false
- ? substr(strstr($this->_userinfo, ':'), 1)
- : false;
- }
-
- public function getUserinfo()
- {
- return $this->_userinfo;
- }
-
- public function setUserinfo($userinfo, $password = false)
- {
- if ($password !== false) {
- $userinfo .= ':' . $password;
- }
- if ($userinfo !== false) {
- $userinfo = $this->_encodeData($userinfo);
- }
- $this->_userinfo = $userinfo;
- return $this;
- }
-
- public function getHost()
- {
- return $this->_host;
- }
-
- public function setHost($host)
- {
- $this->_host = $host;
- return $this;
- }
-
- public function getPort()
- {
- return $this->_port;
- }
-
- public function setPort($port)
- {
- $this->_port = $port;
- return $this;
- }
-
- public function getAuthority()
- {
- if (false === $this->_host) {
- return false;
- }
- $authority = '';
- if (strlen($this->_userinfo)) {
- $authority .= $this->_userinfo . '@';
- }
- $authority .= $this->_host;
- if ($this->_port !== false) {
- $authority .= ':' . $this->_port;
- }
- return $authority;
- }
-
- public function setAuthority($authority)
- {
- $this->_userinfo = false;
- $this->_host = false;
- $this->_port = false;
- if ('' === $authority) {
- $this->_host = $authority;
- return $this;
- }
- if (!preg_match('(^(([^@]*)@)?(.+?)(:(\d*))?$)', $authority, $matches)) {
- return $this;
- }
- if ($matches[1]) {
- $this->_userinfo = $this->_encodeData($matches[2]);
- }
- $this->_host = $matches[3];
- if (isset($matches[5]) && strlen($matches[5])) {
- $this->_port = $matches[5];
- }
- return $this;
- }
-
- public function getPath()
- {
- return $this->_path;
- }
-
- public function setPath($path)
- {
- $this->_path = $path;
- return $this;
- }
-
- public function getQuery()
- {
- return $this->_query;
- }
-
- public function setQuery($query)
- {
- $this->_query = $query;
- return $this;
- }
-
- public function getFragment()
- {
- return $this->_fragment;
- }
-
- public function setFragment($fragment)
- {
- $this->_fragment = $fragment;
- return $this;
- }
-
- public function getQueryVariables()
- {
- $separator = $this->getOption(self::OPTION_SEPARATOR_INPUT);
- $encodeKeys = $this->getOption(self::OPTION_ENCODE_KEYS);
- $useBrackets = $this->getOption(self::OPTION_USE_BRACKETS);
- $return = array();
- for ($part = strtok($this->_query, $separator);
- strlen($part);
- $part = strtok($separator)
- ) {
- list($key, $value) = explode('=', $part, 2) + array(1 => '');
- if ($encodeKeys) {
- $key = rawurldecode($key);
- }
- $value = rawurldecode($value);
- if ($useBrackets) {
- $return = $this->_queryArrayByKey($key, $value, $return);
- } else {
- if (isset($return[$key])) {
- $return[$key] = (array) $return[$key];
- $return[$key][] = $value;
- } else {
- $return[$key] = $value;
- }
- }
- }
- return $return;
- }
-
- private function _queryArrayByKey($key, $value, array $array = array())
- {
- if (!strlen($key)) {
- return $array;
- }
- $offset = $this->_queryKeyBracketOffset($key);
- if ($offset === false) {
- $name = $key;
- } else {
- $name = substr($key, 0, $offset);
- }
- if (!strlen($name)) {
- return $array;
- }
- if (!$offset) {
-
- $array[$name] = $value;
- } else {
-
- $brackets = substr($key, $offset);
- if (!isset($array[$name])) {
- $array[$name] = null;
- }
- $array[$name] = $this->_queryArrayByBrackets(
- $brackets, $value, $array[$name]
- );
- }
- return $array;
- }
-
- private function _queryArrayByBrackets($buffer, $value, array $array = null)
- {
- $entry = &$array;
- for ($iteration = 0; strlen($buffer); $iteration++) {
- $open = $this->_queryKeyBracketOffset($buffer);
- if ($open !== 0) {
-
-
-
-
- if ($iteration) {
- break;
- }
-
- throw new Exception(
- 'Net_URL2 Internal Error: '. __METHOD__ .'(): ' .
- 'Opening bracket [ must exist at offset 0'
- );
-
- }
- $close = strpos($buffer, ']', 1);
- if (!$close) {
-
-
-
-
- throw new Exception(
- 'Net_URL2 Internal Error: '. __METHOD__ .'(): ' .
- 'Closing bracket ] must exist, not found'
- );
-
- }
- $index = substr($buffer, 1, $close - 1);
- if (strlen($index)) {
- $entry = &$entry[$index];
- } else {
- if (!is_array($entry)) {
- $entry = array();
- }
- $entry[] = &$new;
- $entry = &$new;
- unset($new);
- }
- $buffer = substr($buffer, $close + 1);
- }
- $entry = $value;
- return $array;
- }
-
- private function _queryKeyBracketOffset($key)
- {
- if (false !== $open = strpos($key, '[')
- and false === strpos($key, ']', $open + 1)
- ) {
- $open = false;
- }
- return $open;
- }
-
- public function setQueryVariables(array $array)
- {
- if (!$array) {
- $this->_query = false;
- } else {
- $this->_query = $this->buildQuery(
- $array,
- $this->getOption(self::OPTION_SEPARATOR_OUTPUT)
- );
- }
- return $this;
- }
-
- public function setQueryVariable($name, $value)
- {
- $array = $this->getQueryVariables();
- $array[$name] = $value;
- $this->setQueryVariables($array);
- return $this;
- }
-
- public function unsetQueryVariable($name)
- {
- $array = $this->getQueryVariables();
- unset($array[$name]);
- $this->setQueryVariables($array);
- }
-
- public function getURL()
- {
-
- $url = '';
- if ($this->_scheme !== false) {
- $url .= $this->_scheme . ':';
- }
- $authority = $this->getAuthority();
- if ($authority === false && strtolower($this->_scheme) === 'file') {
- $authority = '';
- }
- $url .= $this->_buildAuthorityAndPath($authority, $this->_path);
- if ($this->_query !== false) {
- $url .= '?' . $this->_query;
- }
- if ($this->_fragment !== false) {
- $url .= '#' . $this->_fragment;
- }
- return $url;
- }
-
- private function _buildAuthorityAndPath($authority, $path)
- {
- if ($authority === false) {
- return $path;
- }
- $terminator = ($path !== '' && $path[0] !== '/') ? '/' : '';
- return '//' . $authority . $terminator . $path;
- }
-
- public function __toString()
- {
- return $this->getURL();
- }
-
- public function getNormalizedURL()
- {
- $url = clone $this;
- $url->normalize();
- return $url->getURL();
- }
-
- public function normalize()
- {
-
-
- if ($this->_scheme) {
- $this->_scheme = strtolower($this->_scheme);
- }
-
- if ($this->_host) {
- $this->_host = strtolower($this->_host);
- }
-
- if ('' === $this->_port
- || $this->_port
- && $this->_scheme
- && $this->_port == getservbyname($this->_scheme, 'tcp')
- ) {
- $this->_port = false;
- }
-
-
- $fields = array(&$this->_userinfo, &$this->_host, &$this->_path,
- &$this->_query, &$this->_fragment);
- foreach ($fields as &$field) {
- if ($field !== false) {
- $field = $this->_normalize("$field");
- }
- }
- unset($field);
-
- $this->_path = self::removeDotSegments($this->_path);
-
- if (false !== $this->_host && '' === $this->_path) {
- $this->_path = '/';
- }
-
- if (strlen($this->getAuthority())
- && strlen($this->_path)
- && $this->_path[0] !== '/'
- ) {
- $this->_path = '/' . $this->_path;
- }
- }
-
- private function _normalize($mixed)
- {
- return preg_replace_callback(
- '((?:%[0-9a-fA-Z]{2})+)', array($this, '_normalizeCallback'),
- $mixed
- );
- }
-
- private function _normalizeCallback($matches)
- {
- return self::urlencode(urldecode($matches[0]));
- }
-
- public function isAbsolute()
- {
- return (bool) $this->_scheme;
- }
-
- public function resolve($reference)
- {
- if (!$reference instanceof Net_URL2) {
- $reference = new self($reference);
- }
- if (!$reference->_isFragmentOnly() && !$this->isAbsolute()) {
- throw new Exception(
- 'Base-URL must be absolute if reference is not fragment-only'
- );
- }
-
-
- 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 {
-
- if ($this->_host !== false && $this->_path == '') {
- $target->_path = '/' . $reference->_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;
- }
-
- private function _isFragmentOnly()
- {
- return (
- $this->_fragment !== false
- && $this->_query === false
- && $this->_path === ''
- && $this->_port === false
- && $this->_host === false
- && $this->_userinfo === false
- && $this->_scheme === false
- );
- }
-
- public static function removeDotSegments($path)
- {
- $path = (string) $path;
- $output = '';
-
-
- $loopLimit = 256;
- $j = 0;
- while ('' !== $path && $j++ < $loopLimit) {
- if (substr($path, 0, 2) === './') {
-
- $path = substr($path, 2);
- } elseif (substr($path, 0, 3) === '../') {
-
- $path = substr($path, 3);
- } elseif (substr($path, 0, 3) === '/./' || $path === '/.') {
-
- $path = '/' . substr($path, 3);
- } elseif (substr($path, 0, 4) === '/../' || $path === '/..') {
-
- $path = '/' . substr($path, 4);
- $i = strrpos($output, '/');
- $output = $i === false ? '' : substr($output, 0, $i);
- } elseif ($path === '.' || $path === '..') {
-
- $path = '';
- } else {
-
- $i = strpos($path, '/', $path[0] === '/');
- if ($i === false) {
- $output .= $path;
- $path = '';
- break;
- }
- $output .= substr($path, 0, $i);
- $path = substr($path, $i);
- }
- }
- if ($path !== '') {
- $message = sprintf(
- 'Unable to remove dot segments; hit loop limit %d (left: %s)',
- $j, var_export($path, true)
- );
- trigger_error($message, E_USER_WARNING);
- }
- return $output;
- }
-
- public static function urlencode($string)
- {
- $encoded = rawurlencode($string);
-
- $encoded = str_replace('%7E', '~', $encoded);
- return $encoded;
- }
-
- public static function getCanonical()
- {
- if (!isset($_SERVER['REQUEST_METHOD'])) {
-
- throw new Exception('Script was not called through a webserver');
- }
-
- $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;
- }
-
- public static function getRequestedURL()
- {
- return self::getRequested()->getUrl();
- }
-
- public static function getRequested()
- {
- if (!isset($_SERVER['REQUEST_METHOD'])) {
-
- throw new Exception('Script was not called through a webserver');
- }
-
- $url = new self($_SERVER['REQUEST_URI']);
- $url->_scheme = isset($_SERVER['HTTPS']) ? 'https' : 'http';
-
- $url->setAuthority($_SERVER['HTTP_HOST']);
- return $url;
- }
-
- public function getOption($optionName)
- {
- return isset($this->_options[$optionName])
- ? $this->_options[$optionName] : false;
- }
-
- protected function buildQuery(array $data, $separator, $key = null)
- {
- $query = array();
- $drop_names = (
- $this->_options[self::OPTION_DROP_SEQUENCE] === true
- && array_keys($data) === array_keys(array_values($data))
- );
- 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) {
- $drop_names && $name = '';
- $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);
- }
-
- protected function parseUrl($url)
- {
-
-
- preg_match(
- '(^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?)',
- $url, $matches
- );
-
-
- $this->_scheme = !empty($matches[1]) ? $matches[2] : false;
- $this->setAuthority(!empty($matches[3]) ? $matches[4] : false);
- $this->_path = $this->_encodeData($matches[5]);
- $this->_query = !empty($matches[6])
- ? $this->_encodeData($matches[7])
- : false
- ;
- $this->_fragment = !empty($matches[8]) ? $matches[9] : false;
- }
-
- private function _encodeData($url)
- {
- return preg_replace_callback(
- '([\x-\x20\x22\x3C\x3E\x7F-\xFF]+)',
- array($this, '_encodeCallback'), $url
- );
- }
-
- private function _encodeCallback(array $matches)
- {
- return rawurlencode($matches[0]);
- }
- }
|