Source for file Uri.php

Documentation is available at Uri.php

  1. 1: <?php
  2. 2:  
  3. 3: /**
  4. 4:  * Nette Framework
  5. 5:  *
  6. 6:  * Copyright (c) 2004, 2009 David Grudl (http://davidgrudl.com)
  7. 7:  *
  8. 8:  * This source file is subject to the "Nette license" that is bundled
  9. 9:  * with this package in the file license.txt.
  10. 10:  *
  11. 11:  * For more information please see https://nette.org
  12. 12:  *
  13. 13:  * @copyright  Copyright (c) 2004, 2009 David Grudl
  14. 14:  * @license    https://nette.org/license  Nette license
  15. 15:  * @link       https://nette.org
  16. 16:  * @category   Nette
  17. 17:  * @package    Nette\Web
  18. 18:  * @version    $Id$
  19. 19:  */
  20. 20:  
  21. 21:  
  22. 22:  
  23. 23: require_once dirname(__FILE__'/../Object.php';
  24. 24:  
  25. 25:  
  26. 26:  
  27. 27: /**
  28. 28:  * URI Syntax (RFC 3986).
  29. 29:  *
  30. 30:  * <pre>
  31. 31:  * http://user:pass@nette.org:8042/en/manual.html?name=param#fragment
  32. 32:  * \__/^^^\_________________________/\_____________/^\________/^\______/
  33. 33:  *   |                |                     |            |         |
  34. 34:  * scheme         authority               path         query    fragment
  35. 35:  * </pre>
  36. 36:  *
  37. 37:  * - authority:   [user[:pass]@]host[:port]
  38. 38:  * - hostUri:     http://user:pass@nette.org:8042
  39. 39:  *
  40. 40:  * @author     David Grudl
  41. 41:  * @copyright  Copyright (c) 2004, 2009 David Grudl
  42. 42:  * @package    Nette\Web
  43. 43:  */
  44. 44: class Uri extends Object
  45. 45: {
  46. 46:     /** @var array */
  47. 47:     static public $defaultPorts = array(
  48. 48:         'http' => 80,
  49. 49:         'https' => 443,
  50. 50:         'ftp' => 21,
  51. 51:         'news' => 119,
  52. 52:         'nntp' => 119,
  53. 53:     );
  54. 54:  
  55. 55:     /** @var string */
  56. 56:     public $scheme = '';
  57. 57:  
  58. 58:     /** @var string */
  59. 59:     public $user = '';
  60. 60:  
  61. 61:     /** @var string */
  62. 62:     public $pass = '';
  63. 63:  
  64. 64:     /** @var string */
  65. 65:     public $host = '';
  66. 66:  
  67. 67:     /** @var int */
  68. 68:     public $port = NULL;
  69. 69:  
  70. 70:     /** @var string */
  71. 71:     public $path = '';
  72. 72:  
  73. 73:     /** @var string */
  74. 74:     public $query = '';
  75. 75:  
  76. 76:     /** @var string */
  77. 77:     public $fragment = '';
  78. 78:  
  79. 79:  
  80. 80:  
  81. 81:     /**
  82. 82:      * @param  string  URL
  83. 83:      * @throws InvalidArgumentException
  84. 84:      */
  85. 85:     public function __construct($uri NULL)
  86. 86:     {
  87. 87:         if ($uri !== NULL{
  88. 88:             $parts @parse_url($uri)// intentionally @
  89. 89:             if ($parts === FALSE{
  90. 90:                 throw new InvalidArgumentException("Malformed or unsupported URI '$uri'.");
  91. 91:             }
  92. 92:  
  93. 93:             foreach ($parts as $key => $val{
  94. 94:                 $this->$key $val;
  95. 95:             }
  96. 96:  
  97. 97:             if (!$this->port && isset(self::$defaultPorts[$this->scheme])) {
  98. 98:                 $this->port = self::$defaultPorts[$this->scheme];
  99. 99:             }
  100. 100:         }
  101. 101:     }
  102. 102:  
  103. 103:  
  104. 104:  
  105. 105:     /**
  106. 106:      * Returns the entire URI including query string and fragment.
  107. 107:      * @return string 
  108. 108:      */
  109. 109:     public function getAbsoluteUri()
  110. 110:     {
  111. 111:         return $this->scheme . '://' $this->getAuthority($this->path
  112. 112:             . ($this->query == '' '' '?' $this->query)
  113. 113:             . ($this->fragment == '' '' '#' $this->fragment);
  114. 114:     }
  115. 115:  
  116. 116:  
  117. 117:  
  118. 118:     /**
  119. 119:      * Returns the [user[:pass]@]host[:port] part of URI.
  120. 120:      * @return string 
  121. 121:      */
  122. 122:     public function getAuthority()
  123. 123:     {
  124. 124:         $authority $this->host;
  125. 125:         if ($this->port && isset(self::$defaultPorts[$this->scheme]&& $this->port !== self::$defaultPorts[$this->scheme]{
  126. 126:             $authority .= ':' $this->port;
  127. 127:         }
  128. 128:  
  129. 129:         if ($this->user != '' && $this->scheme !== 'http' && $this->scheme !== 'https'{
  130. 130:             $authority $this->user . ($this->pass == '' '' ':' $this->pass'@' $authority;
  131. 131:         }
  132. 132:  
  133. 133:         return $authority;
  134. 134:     }
  135. 135:  
  136. 136:  
  137. 137:  
  138. 138:     /**
  139. 139:      * Returns the scheme and authority part of URI.
  140. 140:      * @return string 
  141. 141:      */
  142. 142:     public function getHostUri()
  143. 143:     {
  144. 144:         return $this->scheme . '://' $this->getAuthority();
  145. 145:     }
  146. 146:  
  147. 147:  
  148. 148:  
  149. 149:     /**
  150. 150:      * URI comparsion (this object must be in canonical form).
  151. 151:      * @param  string 
  152. 152:      * @return bool 
  153. 153:      */
  154. 154:     public function isEqual($uri)
  155. 155:     {
  156. 156:         // compare host + path
  157. 157:         $part self::unescape(strtok($uri'?#')'%/');
  158. 158:         if (strncmp($part'//'2=== 0// absolute URI without scheme
  159. 159:             if ($part !== '//' $this->getAuthority($this->pathreturn FALSE;
  160. 160:  
  161. 161:         elseif (strncmp($part'/'1=== 0// absolute path
  162. 162:             if ($part !== $this->pathreturn FALSE;
  163. 163:  
  164. 164:         else {
  165. 165:             if ($part !== $this->scheme . '://' $this->getAuthority($this->pathreturn FALSE;
  166. 166:         }
  167. 167:  
  168. 168:         // compare query strings
  169. 169:         $part = (string) strtok('?#');
  170. 170:         if ($part !== ''{
  171. 171:             $tmp explode('&'self::unescape(strtr($part'+'' ')'%&'));
  172. 172:             sort($tmp);
  173. 173:             $part implode('&'$tmp);
  174. 174:         }
  175. 175:         return $part === $this->query;
  176. 176:     }
  177. 177:  
  178. 178:  
  179. 179:  
  180. 180:     /**
  181. 181:      * Transform to canonical form.
  182. 182:      * @return void 
  183. 183:      */
  184. 184:     public function canonicalize()
  185. 185:     {
  186. 186:         $this->path = $this->path == '' '/' self::unescape($this->path'%/');
  187. 187:  
  188. 188:         $this->host = strtolower(rawurldecode($this->host));
  189. 189:  
  190. 190:         if ($this->query !== ''{
  191. 191:             $tmp explode('&'self::unescape(strtr($this->query'+'' ')'%&'));
  192. 192:             sort($tmp);
  193. 193:             $this->query = implode('&'$tmp);
  194. 194:         }
  195. 195:     }
  196. 196:  
  197. 197:  
  198. 198:  
  199. 199:     /**
  200. 200:      * @return string 
  201. 201:      */
  202. 202:     public function __toString()
  203. 203:     {
  204. 204:         return $this->getAbsoluteUri();
  205. 205:     }
  206. 206:  
  207. 207:  
  208. 208:  
  209. 209:     /**
  210. 210:      * Similar to rawurldecode, but preserve reserved chars encoded.
  211. 211:      * @param  string to decode
  212. 212:      * @param  string reserved characters
  213. 213:      * @return string 
  214. 214:      */
  215. 215:     public static function unescape($s$reserved '%;/?:@&=+$,')
  216. 216:     {
  217. 217:         // reserved (@see RFC 2396) = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","
  218. 218:         // within a path segment, the characters "/", ";", "=", "?" are reserved
  219. 219:         // within a query component, the characters ";", "/", "?", ":", "@", "&", "=", "+", ",", "$" are reserved.
  220. 220:         $offset 0;
  221. 221:         $max strlen($s3;
  222. 222:         $res '';
  223. 223:         do {
  224. 224:             $pos strpos($s'%'$offset);
  225. 225:             if ($pos === FALSE || $pos $max{
  226. 226:                 return $res substr($s$offset);
  227. 227:             }
  228. 228:             $ch chr(hexdec($s[$pos 1$s[$pos 2]));
  229. 229:             if (strpos($reserved$ch=== FALSE{
  230. 230:                 $res .= substr($s$offset$pos $offset$ch;
  231. 231:             else {
  232. 232:                 $res .= substr($s$offset$pos $offset 3);
  233. 233:             }
  234. 234:             $offset $pos 3;
  235. 235:         while (TRUE);
  236. 236:     }
  237. 237: