PageRenderTime 57ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/Zend/Uri/Http.php

https://bitbucket.org/claudiu_marginean/magento-hg-mirror
PHP | 769 lines | 328 code | 101 blank | 340 comment | 43 complexity | 52b92f6c785c6618a6813221edb13467 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-2.1, GPL-2.0, WTFPL
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Uri
  17. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. * @version $Id: Http.php 23409 2010-11-19 19:55:25Z bittarman $
  20. */
  21. /**
  22. * @see Zend_Uri
  23. */
  24. #require_once 'Zend/Uri.php';
  25. /**
  26. * @see Zend_Validate_Hostname
  27. */
  28. #require_once 'Zend/Validate/Hostname.php';
  29. /**
  30. * HTTP(S) URI handler
  31. *
  32. * @category Zend
  33. * @package Zend_Uri
  34. * @uses Zend_Uri
  35. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  36. * @license http://framework.zend.com/license/new-bsd New BSD License
  37. */
  38. class Zend_Uri_Http extends Zend_Uri
  39. {
  40. /**
  41. * Character classes for validation regular expressions
  42. */
  43. const CHAR_ALNUM = 'A-Za-z0-9';
  44. const CHAR_MARK = '-_.!~*\'()\[\]';
  45. const CHAR_RESERVED = ';\/?:@&=+$,';
  46. const CHAR_SEGMENT = ':@&=+$,;';
  47. const CHAR_UNWISE = '{}|\\\\^`';
  48. /**
  49. * HTTP username
  50. *
  51. * @var string
  52. */
  53. protected $_username = '';
  54. /**
  55. * HTTP password
  56. *
  57. * @var string
  58. */
  59. protected $_password = '';
  60. /**
  61. * HTTP host
  62. *
  63. * @var string
  64. */
  65. protected $_host = '';
  66. /**
  67. * HTTP post
  68. *
  69. * @var string
  70. */
  71. protected $_port = '';
  72. /**
  73. * HTTP part
  74. *
  75. * @var string
  76. */
  77. protected $_path = '';
  78. /**
  79. * HTTP query
  80. *
  81. * @var string
  82. */
  83. protected $_query = '';
  84. /**
  85. * HTTP fragment
  86. *
  87. * @var string
  88. */
  89. protected $_fragment = '';
  90. /**
  91. * Regular expression grammar rules for validation; values added by constructor
  92. *
  93. * @var array
  94. */
  95. protected $_regex = array();
  96. /**
  97. * Constructor accepts a string $scheme (e.g., http, https) and a scheme-specific part of the URI
  98. * (e.g., example.com/path/to/resource?query=param#fragment)
  99. *
  100. * @param string $scheme The scheme of the URI
  101. * @param string $schemeSpecific The scheme-specific part of the URI
  102. * @throws Zend_Uri_Exception When the URI is not valid
  103. */
  104. protected function __construct($scheme, $schemeSpecific = '')
  105. {
  106. // Set the scheme
  107. $this->_scheme = $scheme;
  108. // Set up grammar rules for validation via regular expressions. These
  109. // are to be used with slash-delimited regular expression strings.
  110. // Escaped special characters (eg. '%25' for '%')
  111. $this->_regex['escaped'] = '%[[:xdigit:]]{2}';
  112. // Unreserved characters
  113. $this->_regex['unreserved'] = '[' . self::CHAR_ALNUM . self::CHAR_MARK . ']';
  114. // Segment can use escaped, unreserved or a set of additional chars
  115. $this->_regex['segment'] = '(?:' . $this->_regex['escaped'] . '|[' .
  116. self::CHAR_ALNUM . self::CHAR_MARK . self::CHAR_SEGMENT . '])*';
  117. // Path can be a series of segmets char strings seperated by '/'
  118. $this->_regex['path'] = '(?:\/(?:' . $this->_regex['segment'] . ')?)+';
  119. // URI characters can be escaped, alphanumeric, mark or reserved chars
  120. $this->_regex['uric'] = '(?:' . $this->_regex['escaped'] . '|[' .
  121. self::CHAR_ALNUM . self::CHAR_MARK . self::CHAR_RESERVED .
  122. // If unwise chars are allowed, add them to the URI chars class
  123. (self::$_config['allow_unwise'] ? self::CHAR_UNWISE : '') . '])';
  124. // If no scheme-specific part was supplied, the user intends to create
  125. // a new URI with this object. No further parsing is required.
  126. if (strlen($schemeSpecific) === 0) {
  127. return;
  128. }
  129. // Parse the scheme-specific URI parts into the instance variables.
  130. $this->_parseUri($schemeSpecific);
  131. // Validate the URI
  132. if ($this->valid() === false) {
  133. #require_once 'Zend/Uri/Exception.php';
  134. throw new Zend_Uri_Exception('Invalid URI supplied');
  135. }
  136. }
  137. /**
  138. * Creates a Zend_Uri_Http from the given string
  139. *
  140. * @param string $uri String to create URI from, must start with
  141. * 'http://' or 'https://'
  142. * @throws InvalidArgumentException When the given $uri is not a string or
  143. * does not start with http:// or https://
  144. * @throws Zend_Uri_Exception When the given $uri is invalid
  145. * @return Zend_Uri_Http
  146. */
  147. public static function fromString($uri)
  148. {
  149. if (is_string($uri) === false) {
  150. #require_once 'Zend/Uri/Exception.php';
  151. throw new Zend_Uri_Exception('$uri is not a string');
  152. }
  153. $uri = explode(':', $uri, 2);
  154. $scheme = strtolower($uri[0]);
  155. $schemeSpecific = isset($uri[1]) === true ? $uri[1] : '';
  156. if (in_array($scheme, array('http', 'https')) === false) {
  157. #require_once 'Zend/Uri/Exception.php';
  158. throw new Zend_Uri_Exception("Invalid scheme: '$scheme'");
  159. }
  160. $schemeHandler = new Zend_Uri_Http($scheme, $schemeSpecific);
  161. return $schemeHandler;
  162. }
  163. /**
  164. * Parse the scheme-specific portion of the URI and place its parts into instance variables.
  165. *
  166. * @param string $schemeSpecific The scheme-specific portion to parse
  167. * @throws Zend_Uri_Exception When scheme-specific decoposition fails
  168. * @throws Zend_Uri_Exception When authority decomposition fails
  169. * @return void
  170. */
  171. protected function _parseUri($schemeSpecific)
  172. {
  173. // High-level decomposition parser
  174. $pattern = '~^((//)([^/?#]*))([^?#]*)(\?([^#]*))?(#(.*))?$~';
  175. $status = @preg_match($pattern, $schemeSpecific, $matches);
  176. if ($status === false) {
  177. #require_once 'Zend/Uri/Exception.php';
  178. throw new Zend_Uri_Exception('Internal error: scheme-specific decomposition failed');
  179. }
  180. // Failed decomposition; no further processing needed
  181. if ($status === false) {
  182. return;
  183. }
  184. // Save URI components that need no further decomposition
  185. $this->_path = isset($matches[4]) === true ? $matches[4] : '';
  186. $this->_query = isset($matches[6]) === true ? $matches[6] : '';
  187. $this->_fragment = isset($matches[8]) === true ? $matches[8] : '';
  188. // Additional decomposition to get username, password, host, and port
  189. $combo = isset($matches[3]) === true ? $matches[3] : '';
  190. $pattern = '~^(([^:@]*)(:([^@]*))?@)?([^:]+)(:(.*))?$~';
  191. $status = @preg_match($pattern, $combo, $matches);
  192. if ($status === false) {
  193. #require_once 'Zend/Uri/Exception.php';
  194. throw new Zend_Uri_Exception('Internal error: authority decomposition failed');
  195. }
  196. // Failed decomposition; no further processing needed
  197. if ($status === false) {
  198. return;
  199. }
  200. // Save remaining URI components
  201. $this->_username = isset($matches[2]) === true ? $matches[2] : '';
  202. $this->_password = isset($matches[4]) === true ? $matches[4] : '';
  203. $this->_host = isset($matches[5]) === true ? $matches[5] : '';
  204. $this->_port = isset($matches[7]) === true ? $matches[7] : '';
  205. }
  206. /**
  207. * Returns a URI based on current values of the instance variables. If any
  208. * part of the URI does not pass validation, then an exception is thrown.
  209. *
  210. * @throws Zend_Uri_Exception When one or more parts of the URI are invalid
  211. * @return string
  212. */
  213. public function getUri()
  214. {
  215. if ($this->valid() === false) {
  216. #require_once 'Zend/Uri/Exception.php';
  217. throw new Zend_Uri_Exception('One or more parts of the URI are invalid');
  218. }
  219. $password = strlen($this->_password) > 0 ? ":$this->_password" : '';
  220. $auth = strlen($this->_username) > 0 ? "$this->_username$password@" : '';
  221. $port = strlen($this->_port) > 0 ? ":$this->_port" : '';
  222. $query = strlen($this->_query) > 0 ? "?$this->_query" : '';
  223. $fragment = strlen($this->_fragment) > 0 ? "#$this->_fragment" : '';
  224. return $this->_scheme
  225. . '://'
  226. . $auth
  227. . $this->_host
  228. . $port
  229. . $this->_path
  230. . $query
  231. . $fragment;
  232. }
  233. /**
  234. * Validate the current URI from the instance variables. Returns true if and only if all
  235. * parts pass validation.
  236. *
  237. * @return boolean
  238. */
  239. public function valid()
  240. {
  241. // Return true if and only if all parts of the URI have passed validation
  242. return $this->validateUsername()
  243. and $this->validatePassword()
  244. and $this->validateHost()
  245. and $this->validatePort()
  246. and $this->validatePath()
  247. and $this->validateQuery()
  248. and $this->validateFragment();
  249. }
  250. /**
  251. * Returns the username portion of the URL, or FALSE if none.
  252. *
  253. * @return string
  254. */
  255. public function getUsername()
  256. {
  257. return strlen($this->_username) > 0 ? $this->_username : false;
  258. }
  259. /**
  260. * Returns true if and only if the username passes validation. If no username is passed,
  261. * then the username contained in the instance variable is used.
  262. *
  263. * @param string $username The HTTP username
  264. * @throws Zend_Uri_Exception When username validation fails
  265. * @return boolean
  266. * @link http://www.faqs.org/rfcs/rfc2396.html
  267. */
  268. public function validateUsername($username = null)
  269. {
  270. if ($username === null) {
  271. $username = $this->_username;
  272. }
  273. // If the username is empty, then it is considered valid
  274. if (strlen($username) === 0) {
  275. return true;
  276. }
  277. // Check the username against the allowed values
  278. $status = @preg_match('/^(?:' . $this->_regex['escaped'] . '|[' .
  279. self::CHAR_ALNUM . self::CHAR_MARK . ';:&=+$,' . '])+$/', $username);
  280. if ($status === false) {
  281. #require_once 'Zend/Uri/Exception.php';
  282. throw new Zend_Uri_Exception('Internal error: username validation failed');
  283. }
  284. return $status === 1;
  285. }
  286. /**
  287. * Sets the username for the current URI, and returns the old username
  288. *
  289. * @param string $username The HTTP username
  290. * @throws Zend_Uri_Exception When $username is not a valid HTTP username
  291. * @return string
  292. */
  293. public function setUsername($username)
  294. {
  295. if ($this->validateUsername($username) === false) {
  296. #require_once 'Zend/Uri/Exception.php';
  297. throw new Zend_Uri_Exception("Username \"$username\" is not a valid HTTP username");
  298. }
  299. $oldUsername = $this->_username;
  300. $this->_username = $username;
  301. return $oldUsername;
  302. }
  303. /**
  304. * Returns the password portion of the URL, or FALSE if none.
  305. *
  306. * @return string
  307. */
  308. public function getPassword()
  309. {
  310. return strlen($this->_password) > 0 ? $this->_password : false;
  311. }
  312. /**
  313. * Returns true if and only if the password passes validation. If no password is passed,
  314. * then the password contained in the instance variable is used.
  315. *
  316. * @param string $password The HTTP password
  317. * @throws Zend_Uri_Exception When password validation fails
  318. * @return boolean
  319. * @link http://www.faqs.org/rfcs/rfc2396.html
  320. */
  321. public function validatePassword($password = null)
  322. {
  323. if ($password === null) {
  324. $password = $this->_password;
  325. }
  326. // If the password is empty, then it is considered valid
  327. if (strlen($password) === 0) {
  328. return true;
  329. }
  330. // If the password is nonempty, but there is no username, then it is considered invalid
  331. if (strlen($password) > 0 and strlen($this->_username) === 0) {
  332. return false;
  333. }
  334. // Check the password against the allowed values
  335. $status = @preg_match('/^(?:' . $this->_regex['escaped'] . '|[' .
  336. self::CHAR_ALNUM . self::CHAR_MARK . ';:&=+$,' . '])+$/', $password);
  337. if ($status === false) {
  338. #require_once 'Zend/Uri/Exception.php';
  339. throw new Zend_Uri_Exception('Internal error: password validation failed.');
  340. }
  341. return $status == 1;
  342. }
  343. /**
  344. * Sets the password for the current URI, and returns the old password
  345. *
  346. * @param string $password The HTTP password
  347. * @throws Zend_Uri_Exception When $password is not a valid HTTP password
  348. * @return string
  349. */
  350. public function setPassword($password)
  351. {
  352. if ($this->validatePassword($password) === false) {
  353. #require_once 'Zend/Uri/Exception.php';
  354. throw new Zend_Uri_Exception("Password \"$password\" is not a valid HTTP password.");
  355. }
  356. $oldPassword = $this->_password;
  357. $this->_password = $password;
  358. return $oldPassword;
  359. }
  360. /**
  361. * Returns the domain or host IP portion of the URL, or FALSE if none.
  362. *
  363. * @return string
  364. */
  365. public function getHost()
  366. {
  367. return strlen($this->_host) > 0 ? $this->_host : false;
  368. }
  369. /**
  370. * Returns true if and only if the host string passes validation. If no host is passed,
  371. * then the host contained in the instance variable is used.
  372. *
  373. * @param string $host The HTTP host
  374. * @return boolean
  375. * @uses Zend_Filter
  376. */
  377. public function validateHost($host = null)
  378. {
  379. if ($host === null) {
  380. $host = $this->_host;
  381. }
  382. // If the host is empty, then it is considered invalid
  383. if (strlen($host) === 0) {
  384. return false;
  385. }
  386. // Check the host against the allowed values; delegated to Zend_Filter.
  387. $validate = new Zend_Validate_Hostname(Zend_Validate_Hostname::ALLOW_ALL);
  388. return $validate->isValid($host);
  389. }
  390. /**
  391. * Sets the host for the current URI, and returns the old host
  392. *
  393. * @param string $host The HTTP host
  394. * @throws Zend_Uri_Exception When $host is nota valid HTTP host
  395. * @return string
  396. */
  397. public function setHost($host)
  398. {
  399. if ($this->validateHost($host) === false) {
  400. #require_once 'Zend/Uri/Exception.php';
  401. throw new Zend_Uri_Exception("Host \"$host\" is not a valid HTTP host");
  402. }
  403. $oldHost = $this->_host;
  404. $this->_host = $host;
  405. return $oldHost;
  406. }
  407. /**
  408. * Returns the TCP port, or FALSE if none.
  409. *
  410. * @return string
  411. */
  412. public function getPort()
  413. {
  414. return strlen($this->_port) > 0 ? $this->_port : false;
  415. }
  416. /**
  417. * Returns true if and only if the TCP port string passes validation. If no port is passed,
  418. * then the port contained in the instance variable is used.
  419. *
  420. * @param string $port The HTTP port
  421. * @return boolean
  422. */
  423. public function validatePort($port = null)
  424. {
  425. if ($port === null) {
  426. $port = $this->_port;
  427. }
  428. // If the port is empty, then it is considered valid
  429. if (strlen($port) === 0) {
  430. return true;
  431. }
  432. // Check the port against the allowed values
  433. return ctype_digit((string) $port) and 1 <= $port and $port <= 65535;
  434. }
  435. /**
  436. * Sets the port for the current URI, and returns the old port
  437. *
  438. * @param string $port The HTTP port
  439. * @throws Zend_Uri_Exception When $port is not a valid HTTP port
  440. * @return string
  441. */
  442. public function setPort($port)
  443. {
  444. if ($this->validatePort($port) === false) {
  445. #require_once 'Zend/Uri/Exception.php';
  446. throw new Zend_Uri_Exception("Port \"$port\" is not a valid HTTP port.");
  447. }
  448. $oldPort = $this->_port;
  449. $this->_port = $port;
  450. return $oldPort;
  451. }
  452. /**
  453. * Returns the path and filename portion of the URL.
  454. *
  455. * @return string
  456. */
  457. public function getPath()
  458. {
  459. return strlen($this->_path) > 0 ? $this->_path : '/';
  460. }
  461. /**
  462. * Returns true if and only if the path string passes validation. If no path is passed,
  463. * then the path contained in the instance variable is used.
  464. *
  465. * @param string $path The HTTP path
  466. * @throws Zend_Uri_Exception When path validation fails
  467. * @return boolean
  468. */
  469. public function validatePath($path = null)
  470. {
  471. if ($path === null) {
  472. $path = $this->_path;
  473. }
  474. // If the path is empty, then it is considered valid
  475. if (strlen($path) === 0) {
  476. return true;
  477. }
  478. // Determine whether the path is well-formed
  479. $pattern = '/^' . $this->_regex['path'] . '$/';
  480. $status = @preg_match($pattern, $path);
  481. if ($status === false) {
  482. #require_once 'Zend/Uri/Exception.php';
  483. throw new Zend_Uri_Exception('Internal error: path validation failed');
  484. }
  485. return (boolean) $status;
  486. }
  487. /**
  488. * Sets the path for the current URI, and returns the old path
  489. *
  490. * @param string $path The HTTP path
  491. * @throws Zend_Uri_Exception When $path is not a valid HTTP path
  492. * @return string
  493. */
  494. public function setPath($path)
  495. {
  496. if ($this->validatePath($path) === false) {
  497. #require_once 'Zend/Uri/Exception.php';
  498. throw new Zend_Uri_Exception("Path \"$path\" is not a valid HTTP path");
  499. }
  500. $oldPath = $this->_path;
  501. $this->_path = $path;
  502. return $oldPath;
  503. }
  504. /**
  505. * Returns the query portion of the URL (after ?), or FALSE if none.
  506. *
  507. * @return string
  508. */
  509. public function getQuery()
  510. {
  511. return strlen($this->_query) > 0 ? $this->_query : false;
  512. }
  513. /**
  514. * Returns the query portion of the URL (after ?) as a
  515. * key-value-array. If the query is empty an empty array
  516. * is returned
  517. *
  518. * @return array
  519. */
  520. public function getQueryAsArray()
  521. {
  522. $query = $this->getQuery();
  523. $querryArray = array();
  524. if ($query !== false) {
  525. parse_str($query, $querryArray);
  526. }
  527. return $querryArray;
  528. }
  529. /**
  530. * Returns true if and only if the query string passes validation. If no query is passed,
  531. * then the query string contained in the instance variable is used.
  532. *
  533. * @param string $query The query to validate
  534. * @throws Zend_Uri_Exception When query validation fails
  535. * @return boolean
  536. * @link http://www.faqs.org/rfcs/rfc2396.html
  537. */
  538. public function validateQuery($query = null)
  539. {
  540. if ($query === null) {
  541. $query = $this->_query;
  542. }
  543. // If query is empty, it is considered to be valid
  544. if (strlen($query) === 0) {
  545. return true;
  546. }
  547. // Determine whether the query is well-formed
  548. $pattern = '/^' . $this->_regex['uric'] . '*$/';
  549. $status = @preg_match($pattern, $query);
  550. if ($status === false) {
  551. #require_once 'Zend/Uri/Exception.php';
  552. throw new Zend_Uri_Exception('Internal error: query validation failed');
  553. }
  554. return $status == 1;
  555. }
  556. /**
  557. * Add or replace params in the query string for the current URI, and
  558. * return the old query.
  559. *
  560. * @param array $queryParams
  561. * @return string Old query string
  562. */
  563. public function addReplaceQueryParameters(array $queryParams)
  564. {
  565. $queryParams = array_merge($this->getQueryAsArray(), $queryParams);
  566. return $this->setQuery($queryParams);
  567. }
  568. /**
  569. * Remove params in the query string for the current URI, and
  570. * return the old query.
  571. *
  572. * @param array $queryParamKeys
  573. * @return string Old query string
  574. */
  575. public function removeQueryParameters(array $queryParamKeys)
  576. {
  577. $queryParams = array_diff_key($this->getQueryAsArray(), array_fill_keys($queryParamKeys, 0));
  578. return $this->setQuery($queryParams);
  579. }
  580. /**
  581. * Set the query string for the current URI, and return the old query
  582. * string This method accepts both strings and arrays.
  583. *
  584. * @param string|array $query The query string or array
  585. * @throws Zend_Uri_Exception When $query is not a valid query string
  586. * @return string Old query string
  587. */
  588. public function setQuery($query)
  589. {
  590. $oldQuery = $this->_query;
  591. // If query is empty, set an empty string
  592. if (empty($query) === true) {
  593. $this->_query = '';
  594. return $oldQuery;
  595. }
  596. // If query is an array, make a string out of it
  597. if (is_array($query) === true) {
  598. $query = http_build_query($query, '', '&');
  599. } else {
  600. // If it is a string, make sure it is valid. If not parse and encode it
  601. $query = (string) $query;
  602. if ($this->validateQuery($query) === false) {
  603. parse_str($query, $queryArray);
  604. $query = http_build_query($queryArray, '', '&');
  605. }
  606. }
  607. // Make sure the query is valid, and set it
  608. if ($this->validateQuery($query) === false) {
  609. #require_once 'Zend/Uri/Exception.php';
  610. throw new Zend_Uri_Exception("'$query' is not a valid query string");
  611. }
  612. $this->_query = $query;
  613. return $oldQuery;
  614. }
  615. /**
  616. * Returns the fragment portion of the URL (after #), or FALSE if none.
  617. *
  618. * @return string|false
  619. */
  620. public function getFragment()
  621. {
  622. return strlen($this->_fragment) > 0 ? $this->_fragment : false;
  623. }
  624. /**
  625. * Returns true if and only if the fragment passes validation. If no fragment is passed,
  626. * then the fragment contained in the instance variable is used.
  627. *
  628. * @param string $fragment Fragment of an URI
  629. * @throws Zend_Uri_Exception When fragment validation fails
  630. * @return boolean
  631. * @link http://www.faqs.org/rfcs/rfc2396.html
  632. */
  633. public function validateFragment($fragment = null)
  634. {
  635. if ($fragment === null) {
  636. $fragment = $this->_fragment;
  637. }
  638. // If fragment is empty, it is considered to be valid
  639. if (strlen($fragment) === 0) {
  640. return true;
  641. }
  642. // Determine whether the fragment is well-formed
  643. $pattern = '/^' . $this->_regex['uric'] . '*$/';
  644. $status = @preg_match($pattern, $fragment);
  645. if ($status === false) {
  646. #require_once 'Zend/Uri/Exception.php';
  647. throw new Zend_Uri_Exception('Internal error: fragment validation failed');
  648. }
  649. return (boolean) $status;
  650. }
  651. /**
  652. * Sets the fragment for the current URI, and returns the old fragment
  653. *
  654. * @param string $fragment Fragment of the current URI
  655. * @throws Zend_Uri_Exception When $fragment is not a valid HTTP fragment
  656. * @return string
  657. */
  658. public function setFragment($fragment)
  659. {
  660. if ($this->validateFragment($fragment) === false) {
  661. #require_once 'Zend/Uri/Exception.php';
  662. throw new Zend_Uri_Exception("Fragment \"$fragment\" is not a valid HTTP fragment");
  663. }
  664. $oldFragment = $this->_fragment;
  665. $this->_fragment = $fragment;
  666. return $oldFragment;
  667. }
  668. }