PageRenderTime 49ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/api/vendor/guzzle/guzzle/src/Guzzle/Http/Url.php

https://gitlab.com/x33n/respond
PHP | 548 lines | 293 code | 67 blank | 188 comment | 67 complexity | fcaf1700c1cf21660974367768ec2535 MD5 | raw file
  1. <?php
  2. namespace Guzzle\Http;
  3. use Guzzle\Common\Exception\InvalidArgumentException;
  4. use Guzzle\Parser\ParserRegistry;
  5. /**
  6. * Parses and generates URLs based on URL parts. In favor of performance, URL parts are not validated.
  7. */
  8. class Url
  9. {
  10. protected $scheme;
  11. protected $host;
  12. protected $port;
  13. protected $username;
  14. protected $password;
  15. protected $path = '';
  16. protected $fragment;
  17. /** @var QueryString Query part of the URL */
  18. protected $query;
  19. /**
  20. * Factory method to create a new URL from a URL string
  21. *
  22. * @param string $url Full URL used to create a Url object
  23. *
  24. * @return Url
  25. * @throws InvalidArgumentException
  26. */
  27. public static function factory($url)
  28. {
  29. static $defaults = array('scheme' => null, 'host' => null, 'path' => null, 'port' => null, 'query' => null,
  30. 'user' => null, 'pass' => null, 'fragment' => null);
  31. if (false === ($parts = parse_url($url))) {
  32. throw new InvalidArgumentException('Was unable to parse malformed url: ' . $url);
  33. }
  34. $parts += $defaults;
  35. // Convert the query string into a QueryString object
  36. if ($parts['query'] || 0 !== strlen($parts['query'])) {
  37. $parts['query'] = QueryString::fromString($parts['query']);
  38. }
  39. return new static($parts['scheme'], $parts['host'], $parts['user'],
  40. $parts['pass'], $parts['port'], $parts['path'], $parts['query'],
  41. $parts['fragment']);
  42. }
  43. /**
  44. * Build a URL from parse_url parts. The generated URL will be a relative URL if a scheme or host are not provided.
  45. *
  46. * @param array $parts Array of parse_url parts
  47. *
  48. * @return string
  49. */
  50. public static function buildUrl(array $parts)
  51. {
  52. $url = $scheme = '';
  53. if (isset($parts['scheme'])) {
  54. $scheme = $parts['scheme'];
  55. $url .= $scheme . ':';
  56. }
  57. if (isset($parts['host'])) {
  58. $url .= '//';
  59. if (isset($parts['user'])) {
  60. $url .= $parts['user'];
  61. if (isset($parts['pass'])) {
  62. $url .= ':' . $parts['pass'];
  63. }
  64. $url .= '@';
  65. }
  66. $url .= $parts['host'];
  67. // Only include the port if it is not the default port of the scheme
  68. if (isset($parts['port'])
  69. && !(($scheme == 'http' && $parts['port'] == 80) || ($scheme == 'https' && $parts['port'] == 443))
  70. ) {
  71. $url .= ':' . $parts['port'];
  72. }
  73. }
  74. // Add the path component if present
  75. if (isset($parts['path']) && 0 !== strlen($parts['path'])) {
  76. // Always ensure that the path begins with '/' if set and something is before the path
  77. if ($url && $parts['path'][0] != '/' && substr($url, -1) != '/') {
  78. $url .= '/';
  79. }
  80. $url .= $parts['path'];
  81. }
  82. // Add the query string if present
  83. if (isset($parts['query'])) {
  84. $url .= '?' . $parts['query'];
  85. }
  86. // Ensure that # is only added to the url if fragment contains anything.
  87. if (isset($parts['fragment'])) {
  88. $url .= '#' . $parts['fragment'];
  89. }
  90. return $url;
  91. }
  92. /**
  93. * Create a new URL from URL parts
  94. *
  95. * @param string $scheme Scheme of the URL
  96. * @param string $host Host of the URL
  97. * @param string $username Username of the URL
  98. * @param string $password Password of the URL
  99. * @param int $port Port of the URL
  100. * @param string $path Path of the URL
  101. * @param QueryString|array|string $query Query string of the URL
  102. * @param string $fragment Fragment of the URL
  103. */
  104. public function __construct($scheme, $host, $username = null, $password = null, $port = null, $path = null, QueryString $query = null, $fragment = null)
  105. {
  106. $this->scheme = $scheme;
  107. $this->host = $host;
  108. $this->port = $port;
  109. $this->username = $username;
  110. $this->password = $password;
  111. $this->fragment = $fragment;
  112. if (!$query) {
  113. $this->query = new QueryString();
  114. } else {
  115. $this->setQuery($query);
  116. }
  117. $this->setPath($path);
  118. }
  119. /**
  120. * Clone the URL
  121. */
  122. public function __clone()
  123. {
  124. $this->query = clone $this->query;
  125. }
  126. /**
  127. * Returns the URL as a URL string
  128. *
  129. * @return string
  130. */
  131. public function __toString()
  132. {
  133. return self::buildUrl($this->getParts());
  134. }
  135. /**
  136. * Get the parts of the URL as an array
  137. *
  138. * @return array
  139. */
  140. public function getParts()
  141. {
  142. return array(
  143. 'scheme' => $this->scheme,
  144. 'user' => $this->username,
  145. 'pass' => $this->password,
  146. 'host' => $this->host,
  147. 'port' => $this->port,
  148. 'path' => $this->getPath(),
  149. 'query' => (string) $this->query ?: null,
  150. 'fragment' => $this->fragment,
  151. );
  152. }
  153. /**
  154. * Set the host of the request.
  155. *
  156. * @param string $host Host to set (e.g. www.yahoo.com, yahoo.com)
  157. *
  158. * @return Url
  159. */
  160. public function setHost($host)
  161. {
  162. if (strpos($host, ':') === false) {
  163. $this->host = $host;
  164. } else {
  165. list($host, $port) = explode(':', $host);
  166. $this->host = $host;
  167. $this->setPort($port);
  168. }
  169. return $this;
  170. }
  171. /**
  172. * Get the host part of the URL
  173. *
  174. * @return string
  175. */
  176. public function getHost()
  177. {
  178. return $this->host;
  179. }
  180. /**
  181. * Set the scheme part of the URL (http, https, ftp, etc)
  182. *
  183. * @param string $scheme Scheme to set
  184. *
  185. * @return Url
  186. */
  187. public function setScheme($scheme)
  188. {
  189. $this->scheme = $scheme;
  190. return $this;
  191. }
  192. /**
  193. * Get the scheme part of the URL
  194. *
  195. * @return string
  196. */
  197. public function getScheme()
  198. {
  199. return $this->scheme;
  200. }
  201. /**
  202. * Set the port part of the URL
  203. *
  204. * @param int $port Port to set
  205. *
  206. * @return Url
  207. */
  208. public function setPort($port)
  209. {
  210. $this->port = $port;
  211. return $this;
  212. }
  213. /**
  214. * Get the port part of the URl. Will return the default port for a given scheme if no port has been set.
  215. *
  216. * @return int|null
  217. */
  218. public function getPort()
  219. {
  220. if ($this->port) {
  221. return $this->port;
  222. } elseif ($this->scheme == 'http') {
  223. return 80;
  224. } elseif ($this->scheme == 'https') {
  225. return 443;
  226. }
  227. return null;
  228. }
  229. /**
  230. * Set the path part of the URL
  231. *
  232. * @param array|string $path Path string or array of path segments
  233. *
  234. * @return Url
  235. */
  236. public function setPath($path)
  237. {
  238. static $pathReplace = array(' ' => '%20', '?' => '%3F');
  239. if (is_array($path)) {
  240. $path = '/' . implode('/', $path);
  241. }
  242. $this->path = strtr($path, $pathReplace);
  243. return $this;
  244. }
  245. /**
  246. * Normalize the URL so that double slashes and relative paths are removed
  247. *
  248. * @return Url
  249. */
  250. public function normalizePath()
  251. {
  252. if (!$this->path || $this->path == '/' || $this->path == '*') {
  253. return $this;
  254. }
  255. $results = array();
  256. $segments = $this->getPathSegments();
  257. foreach ($segments as $segment) {
  258. if ($segment == '..') {
  259. array_pop($results);
  260. } elseif ($segment != '.' && $segment != '') {
  261. $results[] = $segment;
  262. }
  263. }
  264. // Combine the normalized parts and add the leading slash if needed
  265. $this->path = ($this->path[0] == '/' ? '/' : '') . implode('/', $results);
  266. // Add the trailing slash if necessary
  267. if ($this->path != '/' && end($segments) == '') {
  268. $this->path .= '/';
  269. }
  270. return $this;
  271. }
  272. /**
  273. * Add a relative path to the currently set path
  274. *
  275. * @param string $relativePath Relative path to add
  276. *
  277. * @return Url
  278. */
  279. public function addPath($relativePath)
  280. {
  281. if (!$relativePath || $relativePath == '/') {
  282. return $this;
  283. }
  284. // Add a leading slash if needed
  285. if ($relativePath[0] != '/') {
  286. $relativePath = '/' . $relativePath;
  287. }
  288. return $this->setPath(str_replace('//', '/', $this->getPath() . $relativePath));
  289. }
  290. /**
  291. * Get the path part of the URL
  292. *
  293. * @return string
  294. */
  295. public function getPath()
  296. {
  297. return $this->path;
  298. }
  299. /**
  300. * Get the path segments of the URL as an array
  301. *
  302. * @return array
  303. */
  304. public function getPathSegments()
  305. {
  306. return array_slice(explode('/', $this->getPath()), 1);
  307. }
  308. /**
  309. * Set the password part of the URL
  310. *
  311. * @param string $password Password to set
  312. *
  313. * @return Url
  314. */
  315. public function setPassword($password)
  316. {
  317. $this->password = $password;
  318. return $this;
  319. }
  320. /**
  321. * Get the password part of the URL
  322. *
  323. * @return null|string
  324. */
  325. public function getPassword()
  326. {
  327. return $this->password;
  328. }
  329. /**
  330. * Set the username part of the URL
  331. *
  332. * @param string $username Username to set
  333. *
  334. * @return Url
  335. */
  336. public function setUsername($username)
  337. {
  338. $this->username = $username;
  339. return $this;
  340. }
  341. /**
  342. * Get the username part of the URl
  343. *
  344. * @return null|string
  345. */
  346. public function getUsername()
  347. {
  348. return $this->username;
  349. }
  350. /**
  351. * Get the query part of the URL as a QueryString object
  352. *
  353. * @return QueryString
  354. */
  355. public function getQuery()
  356. {
  357. return $this->query;
  358. }
  359. /**
  360. * Set the query part of the URL
  361. *
  362. * @param QueryString|string|array $query Query to set
  363. *
  364. * @return Url
  365. */
  366. public function setQuery($query)
  367. {
  368. if (is_string($query)) {
  369. $output = null;
  370. parse_str($query, $output);
  371. $this->query = new QueryString($output);
  372. } elseif (is_array($query)) {
  373. $this->query = new QueryString($query);
  374. } elseif ($query instanceof QueryString) {
  375. $this->query = $query;
  376. }
  377. return $this;
  378. }
  379. /**
  380. * Get the fragment part of the URL
  381. *
  382. * @return null|string
  383. */
  384. public function getFragment()
  385. {
  386. return $this->fragment;
  387. }
  388. /**
  389. * Set the fragment part of the URL
  390. *
  391. * @param string $fragment Fragment to set
  392. *
  393. * @return Url
  394. */
  395. public function setFragment($fragment)
  396. {
  397. $this->fragment = $fragment;
  398. return $this;
  399. }
  400. /**
  401. * Check if this is an absolute URL
  402. *
  403. * @return bool
  404. */
  405. public function isAbsolute()
  406. {
  407. return $this->scheme && $this->host;
  408. }
  409. /**
  410. * Combine the URL with another URL. Follows the rules specific in RFC 3986 section 5.4.
  411. *
  412. * @param string $url Relative URL to combine with
  413. * @param bool $strictRfc386 Set to true to use strict RFC 3986 compliance when merging paths. When first
  414. * released, Guzzle used an incorrect algorithm for combining relative URL paths. In
  415. * order to not break users, we introduced this flag to allow the merging of URLs based
  416. * on strict RFC 3986 section 5.4.1. This means that "http://a.com/foo/baz" merged with
  417. * "bar" would become "http://a.com/foo/bar". When this value is set to false, it would
  418. * become "http://a.com/foo/baz/bar".
  419. * @return Url
  420. * @throws InvalidArgumentException
  421. * @link http://tools.ietf.org/html/rfc3986#section-5.4
  422. */
  423. public function combine($url, $strictRfc386 = false)
  424. {
  425. $url = self::factory($url);
  426. // Use the more absolute URL as the base URL
  427. if (!$this->isAbsolute() && $url->isAbsolute()) {
  428. $url = $url->combine($this);
  429. }
  430. // Passing a URL with a scheme overrides everything
  431. if ($buffer = $url->getScheme()) {
  432. $this->scheme = $buffer;
  433. $this->host = $url->getHost();
  434. $this->port = $url->getPort();
  435. $this->username = $url->getUsername();
  436. $this->password = $url->getPassword();
  437. $this->path = $url->getPath();
  438. $this->query = $url->getQuery();
  439. $this->fragment = $url->getFragment();
  440. return $this;
  441. }
  442. // Setting a host overrides the entire rest of the URL
  443. if ($buffer = $url->getHost()) {
  444. $this->host = $buffer;
  445. $this->port = $url->getPort();
  446. $this->username = $url->getUsername();
  447. $this->password = $url->getPassword();
  448. $this->path = $url->getPath();
  449. $this->query = $url->getQuery();
  450. $this->fragment = $url->getFragment();
  451. return $this;
  452. }
  453. $path = $url->getPath();
  454. $query = $url->getQuery();
  455. if (!$path) {
  456. if (count($query)) {
  457. $this->addQuery($query, $strictRfc386);
  458. }
  459. } else {
  460. if ($path[0] == '/') {
  461. $this->path = $path;
  462. } elseif ($strictRfc386) {
  463. $this->path .= '/../' . $path;
  464. } else {
  465. $this->path .= '/' . $path;
  466. }
  467. $this->normalizePath();
  468. $this->addQuery($query, $strictRfc386);
  469. }
  470. $this->fragment = $url->getFragment();
  471. return $this;
  472. }
  473. private function addQuery(QueryString $new, $strictRfc386)
  474. {
  475. if ($strictRfc386) {
  476. $this->query = $new;
  477. } else {
  478. $this->query->merge($new);
  479. }
  480. }
  481. }