96 private $_options = array(
97 self::OPTION_STRICT =>
true,
98 self::OPTION_USE_BRACKETS =>
true,
99 self::OPTION_DROP_SEQUENCE =>
true,
100 self::OPTION_ENCODE_KEYS =>
true,
101 self::OPTION_SEPARATOR_INPUT =>
'&',
102 self::OPTION_SEPARATOR_OUTPUT =>
'&',
108 private $_scheme =
false;
113 private $_userinfo =
false;
118 private $_host =
false;
123 private $_port =
false;
133 private $_query =
false;
138 private $_fragment =
false;
150 foreach ($options as $optionName => $value) {
151 if (array_key_exists($optionName, $this->_options)) {
152 $this->_options[$optionName] = $value;
172 $method =
'set' . $var;
173 if (method_exists($this, $method)) {
174 $this->$method($arg);
192 $method =
'get' . $var;
193 if (method_exists($this, $method)) {
194 return $this->$method();
208 return $this->_scheme;
224 $this->_scheme = $scheme;
236 return $this->_userinfo !==
false
237 ? preg_replace(
'(:.*$)',
'', $this->_userinfo)
251 return $this->_userinfo !==
false
252 ? substr(strstr($this->_userinfo,
':'), 1)
264 return $this->_userinfo;
278 if ($password !==
false) {
279 $userinfo .=
':' . $password;
282 if ($userinfo !==
false) {
283 $userinfo = $this->_encodeData($userinfo);
286 $this->_userinfo = $userinfo;
311 $this->_host = $host;
336 $this->_port = $port;
348 if (
false === $this->_host) {
354 if (strlen($this->_userinfo)) {
355 $authority .= $this->_userinfo .
'@';
358 $authority .= $this->_host;
360 if ($this->_port !==
false) {
361 $authority .=
':' . $this->_port;
379 $this->_userinfo =
false;
380 $this->_host =
false;
381 $this->_port =
false;
383 if (
'' === $authority) {
384 $this->_host = $authority;
388 if (!preg_match(
'(^(([^\@]*)\@)?(.+?)(:(\d*))?$)', $authority, $matches)) {
393 $this->_userinfo = $this->_encodeData($matches[2]);
396 $this->_host = $matches[3];
398 if (isset($matches[5]) && strlen($matches[5])) {
399 $this->_port = $matches[5];
423 $this->_path = $path;
436 return $this->_query;
450 $this->_query = $query;
461 return $this->_fragment;
474 $this->_fragment = $fragment;
487 $separator = $this->
getOption(self::OPTION_SEPARATOR_INPUT);
488 $encodeKeys = $this->
getOption(self::OPTION_ENCODE_KEYS);
489 $useBrackets = $this->
getOption(self::OPTION_USE_BRACKETS);
493 for ($part = strtok($this->_query, $separator);
495 $part = strtok($separator)
497 list($key, $value) = explode(
'=', $part, 2) + array(1 =>
'');
500 $key = rawurldecode($key);
502 $value = rawurldecode($value);
505 $return = $this->_queryArrayByKey($key, $value, $return);
507 if (isset($return[$key])) {
508 $return[$key] = (array) $return[$key];
509 $return[$key][] = $value;
511 $return[$key] = $value;
528 private function _queryArrayByKey($key, $value, array $array = array())
534 $offset = $this->_queryKeyBracketOffset($key);
535 if ($offset ===
false) {
538 $name = substr($key, 0, $offset);
541 if (!strlen($name)) {
547 $array[$name] = $value;
550 $brackets = substr($key, $offset);
551 if (!isset($array[$name])) {
552 $array[$name] = null;
554 $array[$name] = $this->_queryArrayByBrackets(
555 $brackets, $value, $array[$name]
572 private function _queryArrayByBrackets($buffer, $value, array $array = null)
576 for ($iteration = 0; strlen($buffer); $iteration++) {
577 $open = $this->_queryKeyBracketOffset($buffer);
588 'Net_URL2 Internal Error: '. __METHOD__ .
'(): ' .
589 'Opening bracket [ must exist at offset 0'
594 $close = strpos($buffer,
']', 1);
601 'Net_URL2 Internal Error: '. __METHOD__ .
'(): ' .
602 'Closing bracket ] must exist, not found'
607 $index = substr($buffer, 1, $close - 1);
608 if (strlen($index)) {
609 $entry = &$entry[$index];
611 if (!is_array($entry)) {
618 $buffer = substr($buffer, $close + 1);
633 private function _queryKeyBracketOffset($key)
635 if (
false !== $open = strpos($key,
'[')
636 and
false === strpos($key,
']', $open + 1)
654 $this->_query =
false;
658 $this->
getOption(self::OPTION_SEPARATOR_OUTPUT)
675 $array[$name] = $value;
690 unset($array[$name]);
704 if ($this->_scheme !==
false) {
705 $url .= $this->_scheme .
':';
709 if ($authority ===
false && strtolower($this->_scheme) ===
'file') {
713 $url .= $this->_buildAuthorityAndPath($authority, $this->_path);
715 if ($this->_query !==
false) {
716 $url .=
'?' . $this->_query;
719 if ($this->_fragment !==
false) {
720 $url .=
'#' . $this->_fragment;
735 private function _buildAuthorityAndPath($authority, $path)
737 if ($authority ===
false) {
741 $terminator = ($path !==
'' && $path[0] !==
'/') ?
'/' :
'';
743 return '//' . $authority . $terminator . $path;
767 return $url->getUrl();
784 if ($this->_scheme) {
785 $this->_scheme = strtolower($this->_scheme);
790 $this->_host = strtolower($this->_host);
794 if (
'' === $this->_port
797 && $this->_port == getservbyname($this->_scheme,
'tcp')
799 $this->_port =
false;
804 $fields = array(&$this->_userinfo, &$this->_host, &$this->_path,
805 &$this->_query, &$this->_fragment);
806 foreach ($fields as &$field) {
807 if ($field !==
false) {
808 $field = $this->_normalize(
"$field");
814 $this->_path = self::removeDotSegments($this->_path);
817 if (
false !== $this->_host &&
'' === $this->_path) {
823 && strlen($this->_path)
824 && $this->_path[0] !==
'/'
826 $this->_path =
'/' . $this->_path;
840 private function _normalize($mixed)
842 return preg_replace_callback(
843 '((?:%[0-9a-fA-Z]{2})+)', array($this,
'_normalizeCallback'),
858 private function _normalizeCallback($matches)
860 return self::urlencode(urldecode($matches[0]));
870 return (
bool) $this->_scheme;
884 if (!$reference instanceof
Net_URL2) {
885 $reference =
new self($reference);
887 if (!$reference->_isFragmentOnly() && !$this->
isAbsolute()) {
889 'Base-URL must be absolute if reference is not fragment-only'
895 if (!$this->
getOption(self::OPTION_STRICT)
896 && $reference->_scheme == $this->_scheme
898 $reference->_scheme =
false;
901 $target =
new self(
'');
902 if ($reference->_scheme !==
false) {
903 $target->_scheme = $reference->_scheme;
904 $target->setAuthority($reference->getAuthority());
905 $target->_path = self::removeDotSegments($reference->_path);
906 $target->_query = $reference->_query;
908 $authority = $reference->getAuthority();
909 if ($authority !==
false) {
910 $target->setAuthority($authority);
911 $target->_path = self::removeDotSegments($reference->_path);
912 $target->_query = $reference->_query;
914 if ($reference->_path ==
'') {
915 $target->_path = $this->_path;
916 if ($reference->_query !==
false) {
917 $target->_query = $reference->_query;
919 $target->_query = $this->_query;
922 if (substr($reference->_path, 0, 1) ==
'/') {
923 $target->_path = self::removeDotSegments($reference->_path);
926 if ($this->_host !==
false && $this->_path ==
'') {
927 $target->_path =
'/' . $reference->_path;
929 $i = strrpos($this->_path,
'/');
931 $target->_path = substr($this->_path, 0, $i + 1);
933 $target->_path .= $reference->_path;
935 $target->_path = self::removeDotSegments($target->_path);
937 $target->_query = $reference->_query;
941 $target->_scheme = $this->_scheme;
944 $target->_fragment = $reference->_fragment;
955 private function _isFragmentOnly()
958 $this->_fragment !==
false
959 && $this->_query ===
false
960 && $this->_path ===
''
961 && $this->_port ===
false
962 && $this->_host ===
false
963 && $this->_userinfo ===
false
964 && $this->_scheme ===
false
978 $path = (string) $path;
985 while (
'' !== $path && $j++ < $loopLimit) {
986 if (substr($path, 0, 2) ===
'./') {
988 $path = substr($path, 2);
989 } elseif (substr($path, 0, 3) ===
'../') {
991 $path = substr($path, 3);
992 } elseif (substr($path, 0, 3) ===
'/./' || $path ===
'/.') {
994 $path =
'/' . substr($path, 3);
995 } elseif (substr($path, 0, 4) ===
'/../' || $path ===
'/..') {
997 $path =
'/' . substr($path, 4);
1000 } elseif ($path ===
'.' || $path ===
'..') {
1005 $i = strpos($path,
'/', $path[0] ===
'/');
1011 $output .= substr($path, 0, $i);
1012 $path = substr($path, $i);
1018 'Unable to remove dot segments; hit loop limit %d (left: %s)',
1019 $j, var_export($path,
true)
1021 trigger_error($message, E_USER_WARNING);
1054 if (!isset($_SERVER[
'REQUEST_METHOD'])) {
1056 throw new Exception(
'Script was not called through a webserver');
1060 $url =
new self($_SERVER[
'PHP_SELF']);
1061 $url->_scheme = isset($_SERVER[
'HTTPS']) ?
'https' :
'http';
1062 $url->_host = $_SERVER[
'SERVER_NAME'];
1063 $port = $_SERVER[
'SERVER_PORT'];
1064 if ($url->_scheme ==
'http' && $port != 80
1065 || $url->_scheme ==
'https' && $port != 443
1067 $url->_port = $port;
1079 return self::getRequested()->getUrl();
1091 if (!isset($_SERVER[
'REQUEST_METHOD'])) {
1093 throw new Exception(
'Script was not called through a webserver');
1097 $url =
new self($_SERVER[
'REQUEST_URI']);
1098 $url->_scheme = isset($_SERVER[
'HTTPS']) ?
'https' :
'http';
1100 $url->setAuthority($_SERVER[
'HTTP_HOST']);
1113 return isset($this->_options[$optionName])
1114 ? $this->_options[$optionName] :
false;
1128 protected function buildQuery(array $data, $separator, $key = null)
1132 $this->_options[self::OPTION_DROP_SEQUENCE] ===
true
1133 && array_keys($data) === array_keys(array_values($data))
1135 foreach ($data as $name => $value) {
1136 if ($this->
getOption(self::OPTION_ENCODE_KEYS) ===
true) {
1137 $name = rawurlencode($name);
1139 if ($key !== null) {
1140 if ($this->
getOption(self::OPTION_USE_BRACKETS) ===
true) {
1141 $drop_names && $name =
'';
1142 $name = $key .
'[' . $name .
']';
1147 if (is_array($value)) {
1148 $query[] = $this->
buildQuery($value, $separator, $name);
1150 $query[] = $name .
'=' . rawurlencode($value);
1153 return implode($separator, $query);
1171 '(^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?)',
1177 $this->_scheme = !empty($matches[1]) ? $matches[2] :
false;
1178 $this->
setAuthority(!empty($matches[3]) ? $matches[4] :
false);
1179 $this->_path = $this->_encodeData($matches[5]);
1180 $this->_query = !empty($matches[6])
1181 ? $this->_encodeData($matches[7])
1184 $this->_fragment = !empty($matches[8]) ? $matches[9] :
false;
1198 private function _encodeData($url)
1200 return preg_replace_callback(
1201 '([\x-\x20\x22\x3C\x3E\x7F-\xFF]+)',
1202 array($this,
'_encodeCallback'), $url
1215 private function _encodeCallback(array $matches)
1217 return rawurlencode($matches[0]);
static urlencode($string)
const OPTION_DROP_SEQUENCE
setQueryVariables(array $array)
const OPTION_SEPARATOR_OUTPUT
__construct($url, array $options=array())
static removeDotSegments($path)
setQueryVariable($name, $value)
buildQuery(array $data, $separator, $key=null)
const OPTION_SEPARATOR_INPUT
unsetQueryVariable($name)
const OPTION_USE_BRACKETS
setUserinfo($userinfo, $password=false)