GuzzleHttpPsr7Uri

PSR-7 URI implementation.

Defined (1)

The class is defined in the following location(s).

/lib/Azure/GuzzleHttp/Psr7/Uri.php  
  1. class Uri implements UriInterface 
  2. private static $schemes = [ 
  3. 'http' => 80,  
  4. 'https' => 443,  
  5. ]; 
  6.  
  7. private static $charUnreserved = 'a-zA-Z0-9_\-\.~'; 
  8. private static $charSubDelims = '!\$&\'\(\)\*\+, ;='; 
  9. private static $replaceQuery = ['=' => '%3D', '&' => '%26']; 
  10.  
  11. /** @var string Uri scheme. */ 
  12. private $scheme = ''; 
  13.  
  14. /** @var string Uri user info. */ 
  15. private $userInfo = ''; 
  16.  
  17. /** @var string Uri host. */ 
  18. private $host = ''; 
  19.  
  20. /** @var int|null Uri port. */ 
  21. private $port; 
  22.  
  23. /** @var string Uri path. */ 
  24. private $path = ''; 
  25.  
  26. /** @var string Uri query string. */ 
  27. private $query = ''; 
  28.  
  29. /** @var string Uri fragment. */ 
  30. private $fragment = ''; 
  31.  
  32. /** 
  33. * @param string $uri URI to parse 
  34. */ 
  35. public function __construct($uri = '') 
  36. if ($uri != '') { 
  37. $parts = parse_url($uri); 
  38. if ($parts === false) { 
  39. throw new \InvalidArgumentException("Unable to parse URI: $uri"); 
  40. $this->applyParts($parts); 
  41.  
  42. public function __toString() 
  43. return self::createUriString( 
  44. $this->scheme,  
  45. $this->getAuthority(),  
  46. $this->path,  
  47. $this->query,  
  48. $this->fragment 
  49. ); 
  50.  
  51. /** 
  52. * Removes dot segments from a path and returns the new path. 
  53. * @param string $path 
  54. * @return string 
  55. * @link http://tools.ietf.org/html/rfc3986#section-5.2.4 
  56. */ 
  57. public static function removeDotSegments($path) 
  58. static $noopPaths = ['' => true, '/' => true, '*' => true]; 
  59. static $ignoreSegments = ['.' => true, '..' => true]; 
  60.  
  61. if (isset($noopPaths[$path])) { 
  62. return $path; 
  63.  
  64. $results = []; 
  65. $segments = explode('/', $path); 
  66. foreach ($segments as $segment) { 
  67. if ($segment === '..') { 
  68. array_pop($results); 
  69. } elseif (!isset($ignoreSegments[$segment])) { 
  70. $results[] = $segment; 
  71.  
  72. $newPath = implode('/', $results); 
  73. // Add the leading slash if necessary 
  74. if (substr($path, 0, 1) === '/' && 
  75. substr($newPath, 0, 1) !== '/' 
  76. ) { 
  77. $newPath = '/' . $newPath; 
  78.  
  79. // Add the trailing slash if necessary 
  80. if ($newPath !== '/' && isset($ignoreSegments[end($segments)])) { 
  81. $newPath .= '/'; 
  82.  
  83. return $newPath; 
  84.  
  85. /** 
  86. * Resolve a base URI with a relative URI and return a new URI. 
  87. * @param UriInterface $base Base URI 
  88. * @param string|UriInterface $rel Relative URI 
  89. * @return UriInterface 
  90. * @link http://tools.ietf.org/html/rfc3986#section-5.2 
  91. */ 
  92. public static function resolve(UriInterface $base, $rel) 
  93. if (!($rel instanceof UriInterface)) { 
  94. $rel = new self($rel); 
  95.  
  96. if ((string) $rel === '') { 
  97. // we can simply return the same base URI instance for this same-document reference 
  98. return $base; 
  99.  
  100. if ($rel->getScheme() != '') { 
  101. return $rel->withPath(self::removeDotSegments($rel->getPath())); 
  102.  
  103. if ($rel->getAuthority() != '') { 
  104. $targetAuthority = $rel->getAuthority(); 
  105. $targetPath = self::removeDotSegments($rel->getPath()); 
  106. $targetQuery = $rel->getQuery(); 
  107. } else { 
  108. $targetAuthority = $base->getAuthority(); 
  109. if ($rel->getPath() === '') { 
  110. $targetPath = $base->getPath(); 
  111. $targetQuery = $rel->getQuery() != '' ? $rel->getQuery() : $base->getQuery(); 
  112. } else { 
  113. if ($rel->getPath()[0] === '/') { 
  114. $targetPath = $rel->getPath(); 
  115. } else { 
  116. if ($targetAuthority != '' && $base->getPath() === '') { 
  117. $targetPath = '/' . $rel->getPath(); 
  118. } else { 
  119. $lastSlashPos = strrpos($base->getPath(), '/'); 
  120. if ($lastSlashPos === false) { 
  121. $targetPath = $rel->getPath(); 
  122. } else { 
  123. $targetPath = substr($base->getPath(), 0, $lastSlashPos + 1) . $rel->getPath(); 
  124. $targetPath = self::removeDotSegments($targetPath); 
  125. $targetQuery = $rel->getQuery(); 
  126.  
  127. return new self(self::createUriString( 
  128. $base->getScheme(),  
  129. $targetAuthority,  
  130. $targetPath,  
  131. $targetQuery,  
  132. $rel->getFragment() 
  133. )); 
  134.  
  135. /** 
  136. * Create a new URI with a specific query string value removed. 
  137. * Any existing query string values that exactly match the provided key are 
  138. * removed. 
  139. * @param UriInterface $uri URI to use as a base. 
  140. * @param string $key Query string key to remove. 
  141. * @return UriInterface 
  142. */ 
  143. public static function withoutQueryValue(UriInterface $uri, $key) 
  144. $current = $uri->getQuery(); 
  145. if ($current == '') { 
  146. return $uri; 
  147.  
  148. $decodedKey = rawurldecode($key); 
  149. $result = array_filter(explode('&', $current), function ($part) use ($decodedKey) { 
  150. return rawurldecode(explode('=', $part)[0]) !== $decodedKey; 
  151. }); 
  152.  
  153. return $uri->withQuery(implode('&', $result)); 
  154.  
  155. /** 
  156. * Create a new URI with a specific query string value. 
  157. * Any existing query string values that exactly match the provided key are 
  158. * removed and replaced with the given key value pair. 
  159. * A value of null will set the query string key without a value, e.g. "key" 
  160. * instead of "key=value". 
  161. * @param UriInterface $uri URI to use as a base. 
  162. * @param string $key Key to set. 
  163. * @param string|null $value Value to set 
  164. * @return UriInterface 
  165. */ 
  166. public static function withQueryValue(UriInterface $uri, $key, $value) 
  167. $current = $uri->getQuery(); 
  168.  
  169. if ($current == '') { 
  170. $result = []; 
  171. } else { 
  172. $decodedKey = rawurldecode($key); 
  173. $result = array_filter(explode('&', $current), function ($part) use ($decodedKey) { 
  174. return rawurldecode(explode('=', $part)[0]) !== $decodedKey; 
  175. }); 
  176.  
  177. // Query string separators ("=", "&") within the key or value need to be encoded 
  178. // (while preventing double-encoding) before setting the query string. All other 
  179. // chars that need percent-encoding will be encoded by withQuery(). 
  180. $key = strtr($key, self::$replaceQuery); 
  181.  
  182. if ($value !== null) { 
  183. $result[] = $key . '=' . strtr($value, self::$replaceQuery); 
  184. } else { 
  185. $result[] = $key; 
  186.  
  187. return $uri->withQuery(implode('&', $result)); 
  188.  
  189. /** 
  190. * Create a URI from a hash of parse_url parts. 
  191. * @param array $parts 
  192. * @return self 
  193. */ 
  194. public static function fromParts(array $parts) 
  195. $uri = new self(); 
  196. $uri->applyParts($parts); 
  197. return $uri; 
  198.  
  199. public function getScheme() 
  200. return $this->scheme; 
  201.  
  202. public function getAuthority() 
  203. if ($this->host == '') { 
  204. return ''; 
  205.  
  206. $authority = $this->host; 
  207. if ($this->userInfo != '') { 
  208. $authority = $this->userInfo . '@' . $authority; 
  209.  
  210. if ($this->port !== null) { 
  211. $authority .= ':' . $this->port; 
  212.  
  213. return $authority; 
  214.  
  215. public function getUserInfo() 
  216. return $this->userInfo; 
  217.  
  218. public function getHost() 
  219. return $this->host; 
  220.  
  221. public function getPort() 
  222. return $this->port; 
  223.  
  224. public function getPath() 
  225. return $this->path; 
  226.  
  227. public function getQuery() 
  228. return $this->query; 
  229.  
  230. public function getFragment() 
  231. return $this->fragment; 
  232.  
  233. public function withScheme($scheme) 
  234. $scheme = $this->filterScheme($scheme); 
  235.  
  236. if ($this->scheme === $scheme) { 
  237. return $this; 
  238.  
  239. $new = clone $this; 
  240. $new->scheme = $scheme; 
  241. $new->port = $new->filterPort($new->port); 
  242. return $new; 
  243.  
  244. public function withUserInfo($user, $password = null) 
  245. $info = $user; 
  246. if ($password != '') { 
  247. $info .= ':' . $password; 
  248.  
  249. if ($this->userInfo === $info) { 
  250. return $this; 
  251.  
  252. $new = clone $this; 
  253. $new->userInfo = $info; 
  254. return $new; 
  255.  
  256. public function withHost($host) 
  257. $host = $this->filterHost($host); 
  258.  
  259. if ($this->host === $host) { 
  260. return $this; 
  261.  
  262. $new = clone $this; 
  263. $new->host = $host; 
  264. return $new; 
  265.  
  266. public function withPort($port) 
  267. $port = $this->filterPort($port); 
  268.  
  269. if ($this->port === $port) { 
  270. return $this; 
  271.  
  272. $new = clone $this; 
  273. $new->port = $port; 
  274. return $new; 
  275.  
  276. public function withPath($path) 
  277. $path = $this->filterPath($path); 
  278.  
  279. if ($this->path === $path) { 
  280. return $this; 
  281.  
  282. $new = clone $this; 
  283. $new->path = $path; 
  284. return $new; 
  285.  
  286. public function withQuery($query) 
  287. $query = $this->filterQueryAndFragment($query); 
  288.  
  289. if ($this->query === $query) { 
  290. return $this; 
  291.  
  292. $new = clone $this; 
  293. $new->query = $query; 
  294. return $new; 
  295.  
  296. public function withFragment($fragment) 
  297. $fragment = $this->filterQueryAndFragment($fragment); 
  298.  
  299. if ($this->fragment === $fragment) { 
  300. return $this; 
  301.  
  302. $new = clone $this; 
  303. $new->fragment = $fragment; 
  304. return $new; 
  305.  
  306. /** 
  307. * Apply parse_url parts to a URI. 
  308. * @param array $parts Array of parse_url parts to apply. 
  309. */ 
  310. private function applyParts(array $parts) 
  311. $this->scheme = isset($parts['scheme']) 
  312. ? $this->filterScheme($parts['scheme']) 
  313. : ''; 
  314. $this->userInfo = isset($parts['user']) ? $parts['user'] : ''; 
  315. $this->host = isset($parts['host']) 
  316. ? $this->filterHost($parts['host']) 
  317. : ''; 
  318. $this->port = isset($parts['port']) 
  319. ? $this->filterPort($parts['port']) 
  320. : null; 
  321. $this->path = isset($parts['path']) 
  322. ? $this->filterPath($parts['path']) 
  323. : ''; 
  324. $this->query = isset($parts['query']) 
  325. ? $this->filterQueryAndFragment($parts['query']) 
  326. : ''; 
  327. $this->fragment = isset($parts['fragment']) 
  328. ? $this->filterQueryAndFragment($parts['fragment']) 
  329. : ''; 
  330. if (isset($parts['pass'])) { 
  331. $this->userInfo .= ':' . $parts['pass']; 
  332.  
  333. /** 
  334. * Create a URI string from its various parts 
  335. * @param string $scheme 
  336. * @param string $authority 
  337. * @param string $path 
  338. * @param string $query 
  339. * @param string $fragment 
  340. * @return string 
  341. */ 
  342. private static function createUriString($scheme, $authority, $path, $query, $fragment) 
  343. $uri = ''; 
  344.  
  345. if ($scheme != '') { 
  346. $uri .= $scheme . ':'; 
  347.  
  348. if ($authority != '') { 
  349. $uri .= '//' . $authority; 
  350.  
  351. if ($path != '') { 
  352. if ($path[0] !== '/') { 
  353. if ($authority != '') { 
  354. // If the path is rootless and an authority is present, the path MUST be prefixed by "/" 
  355. $path = '/' . $path; 
  356. } elseif (isset($path[1]) && $path[1] === '/') { 
  357. if ($authority == '') { 
  358. // If the path is starting with more than one "/" and no authority is present, the 
  359. // starting slashes MUST be reduced to one. 
  360. $path = '/' . ltrim($path, '/'); 
  361.  
  362. $uri .= $path; 
  363.  
  364. if ($query != '') { 
  365. $uri .= '?' . $query; 
  366.  
  367. if ($fragment != '') { 
  368. $uri .= '#' . $fragment; 
  369.  
  370. return $uri; 
  371.  
  372. /** 
  373. * Is a given port non-standard for the current scheme? 
  374. * @param string $scheme 
  375. * @param int $port 
  376. * @return bool 
  377. */ 
  378. private static function isNonStandardPort($scheme, $port) 
  379. return !isset(self::$schemes[$scheme]) || $port !== self::$schemes[$scheme]; 
  380.  
  381. /** 
  382. * @param string $scheme 
  383. * @return string 
  384. * @throws \InvalidArgumentException If the scheme is invalid. 
  385. */ 
  386. private function filterScheme($scheme) 
  387. if (!is_string($scheme)) { 
  388. throw new \InvalidArgumentException('Scheme must be a string'); 
  389.  
  390. return strtolower($scheme); 
  391.  
  392. /** 
  393. * @param string $host 
  394. * @return string 
  395. * @throws \InvalidArgumentException If the host is invalid. 
  396. */ 
  397. private function filterHost($host) 
  398. if (!is_string($host)) { 
  399. throw new \InvalidArgumentException('Host must be a string'); 
  400.  
  401. return strtolower($host); 
  402.  
  403. /** 
  404. * @param int|null $port 
  405. * @return int|null 
  406. * @throws \InvalidArgumentException If the port is invalid. 
  407. */ 
  408. private function filterPort($port) 
  409. if ($port === null) { 
  410. return null; 
  411.  
  412. $port = (int) $port; 
  413. if (1 > $port || 0xffff < $port) { 
  414. throw new \InvalidArgumentException( 
  415. sprintf('Invalid port: %d. Must be between 1 and 65535', $port) 
  416. ); 
  417.  
  418. return self::isNonStandardPort($this->scheme, $port) ? $port : null; 
  419.  
  420. /** 
  421. * Filters the path of a URI 
  422. * @param string $path 
  423. * @return string 
  424. * @throws \InvalidArgumentException If the path is invalid. 
  425. */ 
  426. private function filterPath($path) 
  427. if (!is_string($path)) { 
  428. throw new \InvalidArgumentException('Path must be a string'); 
  429.  
  430. return preg_replace_callback( 
  431. '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/]++|%(?![A-Fa-f0-9]{2}))/',  
  432. [$this, 'rawurlencodeMatchZero'],  
  433. $path 
  434. ); 
  435.  
  436. /** 
  437. * Filters the query string or fragment of a URI. 
  438. * @param string $str 
  439. * @return string 
  440. * @throws \InvalidArgumentException If the query or fragment is invalid. 
  441. */ 
  442. private function filterQueryAndFragment($str) 
  443. if (!is_string($str)) { 
  444. throw new \InvalidArgumentException('Query and fragment must be a string'); 
  445.  
  446. return preg_replace_callback( 
  447. '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/',  
  448. [$this, 'rawurlencodeMatchZero'],  
  449. $str 
  450. ); 
  451.  
  452. private function rawurlencodeMatchZero(array $match) 
  453. return rawurlencode($match[0]);