PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/libs/Zend/Uri/Http.php

https://bitbucket.org/herloct/gagapi
PHP | 765 lines | 347 code | 99 blank | 319 comment | 42 complexity | aa4108f0452e11719eb0b36a3fd020e4 MD5 | raw file
  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-2011 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 23970 2011-05-03 15:46:57Z ralph $
  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-2011 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. // Save remaining URI components
  197. $this->_username = isset($matches[2]) === true ? $matches[2] : '';
  198. $this->_password = isset($matches[4]) === true ? $matches[4] : '';
  199. $this->_host = isset($matches[5]) === true
  200. ? preg_replace('~^\[([^]]+)\]$~', '\1', $matches[5]) // Strip wrapper [] from IPv6 literal
  201. : '';
  202. $this->_port = isset($matches[7]) === true ? $matches[7] : '';
  203. }
  204. /**
  205. * Returns a URI based on current values of the instance variables. If any
  206. * part of the URI does not pass validation, then an exception is thrown.
  207. *
  208. * @throws Zend_Uri_Exception When one or more parts of the URI are invalid
  209. * @return string
  210. */
  211. public function getUri()
  212. {
  213. if ($this->valid() === false) {
  214. require_once 'Zend/Uri/Exception.php';
  215. throw new Zend_Uri_Exception('One or more parts of the URI are invalid');
  216. }
  217. $password = strlen($this->_password) > 0 ? ":$this->_password" : '';
  218. $auth = strlen($this->_username) > 0 ? "$this->_username$password@" : '';
  219. $port = strlen($this->_port) > 0 ? ":$this->_port" : '';
  220. $query = strlen($this->_query) > 0 ? "?$this->_query" : '';
  221. $fragment = strlen($this->_fragment) > 0 ? "#$this->_fragment" : '';
  222. return $this->_scheme
  223. . '://'
  224. . $auth
  225. . $this->_host
  226. . $port
  227. . $this->_path
  228. . $query
  229. . $fragment;
  230. }
  231. /**
  232. * Validate the current URI from the instance variables. Returns true if and only if all
  233. * parts pass validation.
  234. *
  235. * @return boolean
  236. */
  237. public function valid()
  238. {
  239. // Return true if and only if all parts of the URI have passed validation
  240. return $this->validateUsername()
  241. and $this->validatePassword()
  242. and $this->validateHost()
  243. and $this->validatePort()
  244. and $this->validatePath()
  245. and $this->validateQuery()
  246. and $this->validateFragment();
  247. }
  248. /**
  249. * Returns the username portion of the URL, or FALSE if none.
  250. *
  251. * @return string
  252. */
  253. public function getUsername()
  254. {
  255. return strlen($this->_username) > 0 ? $this->_username : false;
  256. }
  257. /**
  258. * Returns true if and only if the username passes validation. If no username is passed,
  259. * then the username contained in the instance variable is used.
  260. *
  261. * @param string $username The HTTP username
  262. * @throws Zend_Uri_Exception When username validation fails
  263. * @return boolean
  264. * @link http://www.faqs.org/rfcs/rfc2396.html
  265. */
  266. public function validateUsername($username = null)
  267. {
  268. if ($username === null) {
  269. $username = $this->_username;
  270. }
  271. // If the username is empty, then it is considered valid
  272. if (strlen($username) === 0) {
  273. return true;
  274. }
  275. // Check the username against the allowed values
  276. $status = @preg_match('/^(?:' . $this->_regex['escaped'] . '|[' .
  277. self::CHAR_ALNUM . self::CHAR_MARK . ';:&=+$,' . '])+$/', $username);
  278. if ($status === false) {
  279. require_once 'Zend/Uri/Exception.php';
  280. throw new Zend_Uri_Exception('Internal error: username validation failed');
  281. }
  282. return $status === 1;
  283. }
  284. /**
  285. * Sets the username for the current URI, and returns the old username
  286. *
  287. * @param string $username The HTTP username
  288. * @throws Zend_Uri_Exception When $username is not a valid HTTP username
  289. * @return string
  290. */
  291. public function setUsername($username)
  292. {
  293. if ($this->validateUsername($username) === false) {
  294. require_once 'Zend/Uri/Exception.php';
  295. throw new Zend_Uri_Exception("Username \"$username\" is not a valid HTTP username");
  296. }
  297. $oldUsername = $this->_username;
  298. $this->_username = $username;
  299. return $oldUsername;
  300. }
  301. /**
  302. * Returns the password portion of the URL, or FALSE if none.
  303. *
  304. * @return string
  305. */
  306. public function getPassword()
  307. {
  308. return strlen($this->_password) > 0 ? $this->_password : false;
  309. }
  310. /**
  311. * Returns true if and only if the password passes validation. If no password is passed,
  312. * then the password contained in the instance variable is used.
  313. *
  314. * @param string $password The HTTP password
  315. * @throws Zend_Uri_Exception When password validation fails
  316. * @return boolean
  317. * @link http://www.faqs.org/rfcs/rfc2396.html
  318. */
  319. public function validatePassword($password = null)
  320. {
  321. if ($password === null) {
  322. $password = $this->_password;
  323. }
  324. // If the password is empty, then it is considered valid
  325. if (strlen($password) === 0) {
  326. return true;
  327. }
  328. // If the password is nonempty, but there is no username, then it is considered invalid
  329. if (strlen($password) > 0 and strlen($this->_username) === 0) {
  330. return false;
  331. }
  332. // Check the password against the allowed values
  333. $status = @preg_match('/^(?:' . $this->_regex['escaped'] . '|[' .
  334. self::CHAR_ALNUM . self::CHAR_MARK . ';:&=+$,' . '])+$/', $password);
  335. if ($status === false) {
  336. require_once 'Zend/Uri/Exception.php';
  337. throw new Zend_Uri_Exception('Internal error: password validation failed.');
  338. }
  339. return $status == 1;
  340. }
  341. /**
  342. * Sets the password for the current URI, and returns the old password
  343. *
  344. * @param string $password The HTTP password
  345. * @throws Zend_Uri_Exception When $password is not a valid HTTP password
  346. * @return string
  347. */
  348. public function setPassword($password)
  349. {
  350. if ($this->validatePassword($password) === false) {
  351. require_once 'Zend/Uri/Exception.php';
  352. throw new Zend_Uri_Exception("Password \"$password\" is not a valid HTTP password.");
  353. }
  354. $oldPassword = $this->_password;
  355. $this->_password = $password;
  356. return $oldPassword;
  357. }
  358. /**
  359. * Returns the domain or host IP portion of the URL, or FALSE if none.
  360. *
  361. * @return string
  362. */
  363. public function getHost()
  364. {
  365. return strlen($this->_host) > 0 ? $this->_host : false;
  366. }
  367. /**
  368. * Returns true if and only if the host string passes validation. If no host is passed,
  369. * then the host contained in the instance variable is used.
  370. *
  371. * @param string $host The HTTP host
  372. * @return boolean
  373. * @uses Zend_Filter
  374. */
  375. public function validateHost($host = null)
  376. {
  377. if ($host === null) {
  378. $host = $this->_host;
  379. }
  380. // If the host is empty, then it is considered invalid
  381. if (strlen($host) === 0) {
  382. return false;
  383. }
  384. // Check the host against the allowed values; delegated to Zend_Filter.
  385. $validate = new Zend_Validate_Hostname(Zend_Validate_Hostname::ALLOW_ALL);
  386. return $validate->isValid($host);
  387. }
  388. /**
  389. * Sets the host for the current URI, and returns the old host
  390. *
  391. * @param string $host The HTTP host
  392. * @throws Zend_Uri_Exception When $host is nota valid HTTP host
  393. * @return string
  394. */
  395. public function setHost($host)
  396. {
  397. if ($this->validateHost($host) === false) {
  398. require_once 'Zend/Uri/Exception.php';
  399. throw new Zend_Uri_Exception("Host \"$host\" is not a valid HTTP host");
  400. }
  401. $oldHost = $this->_host;
  402. $this->_host = $host;
  403. return $oldHost;
  404. }
  405. /**
  406. * Returns the TCP port, or FALSE if none.
  407. *
  408. * @return string
  409. */
  410. public function getPort()
  411. {
  412. return strlen($this->_port) > 0 ? $this->_port : false;
  413. }
  414. /**
  415. * Returns true if and only if the TCP port string passes validation. If no port is passed,
  416. * then the port contained in the instance variable is used.
  417. *
  418. * @param string $port The HTTP port
  419. * @return boolean
  420. */
  421. public function validatePort($port = null)
  422. {
  423. if ($port === null) {
  424. $port = $this->_port;
  425. }
  426. // If the port is empty, then it is considered valid
  427. if (strlen($port) === 0) {
  428. return true;
  429. }
  430. // Check the port against the allowed values
  431. return ctype_digit((string) $port) and 1 <= $port and $port <= 65535;
  432. }
  433. /**
  434. * Sets the port for the current URI, and returns the old port
  435. *
  436. * @param string $port The HTTP port
  437. * @throws Zend_Uri_Exception When $port is not a valid HTTP port
  438. * @return string
  439. */
  440. public function setPort($port)
  441. {
  442. if ($this->validatePort($port) === false) {
  443. require_once 'Zend/Uri/Exception.php';
  444. throw new Zend_Uri_Exception("Port \"$port\" is not a valid HTTP port.");
  445. }
  446. $oldPort = $this->_port;
  447. $this->_port = $port;
  448. return $oldPort;
  449. }
  450. /**
  451. * Returns the path and filename portion of the URL.
  452. *
  453. * @return string
  454. */
  455. public function getPath()
  456. {
  457. return strlen($this->_path) > 0 ? $this->_path : '/';
  458. }
  459. /**
  460. * Returns true if and only if the path string passes validation. If no path is passed,
  461. * then the path contained in the instance variable is used.
  462. *
  463. * @param string $path The HTTP path
  464. * @throws Zend_Uri_Exception When path validation fails
  465. * @return boolean
  466. */
  467. public function validatePath($path = null)
  468. {
  469. if ($path === null) {
  470. $path = $this->_path;
  471. }
  472. // If the path is empty, then it is considered valid
  473. if (strlen($path) === 0) {
  474. return true;
  475. }
  476. // Determine whether the path is well-formed
  477. $pattern = '/^' . $this->_regex['path'] . '$/';
  478. $status = @preg_match($pattern, $path);
  479. if ($status === false) {
  480. require_once 'Zend/Uri/Exception.php';
  481. throw new Zend_Uri_Exception('Internal error: path validation failed');
  482. }
  483. return (boolean) $status;
  484. }
  485. /**
  486. * Sets the path for the current URI, and returns the old path
  487. *
  488. * @param string $path The HTTP path
  489. * @throws Zend_Uri_Exception When $path is not a valid HTTP path
  490. * @return string
  491. */
  492. public function setPath($path)
  493. {
  494. if ($this->validatePath($path) === false) {
  495. require_once 'Zend/Uri/Exception.php';
  496. throw new Zend_Uri_Exception("Path \"$path\" is not a valid HTTP path");
  497. }
  498. $oldPath = $this->_path;
  499. $this->_path = $path;
  500. return $oldPath;
  501. }
  502. /**
  503. * Returns the query portion of the URL (after ?), or FALSE if none.
  504. *
  505. * @return string
  506. */
  507. public function getQuery()
  508. {
  509. return strlen($this->_query) > 0 ? $this->_query : false;
  510. }
  511. /**
  512. * Returns the query portion of the URL (after ?) as a
  513. * key-value-array. If the query is empty an empty array
  514. * is returned
  515. *
  516. * @return array
  517. */
  518. public function getQueryAsArray()
  519. {
  520. $query = $this->getQuery();
  521. $querryArray = array();
  522. if ($query !== false) {
  523. parse_str($query, $querryArray);
  524. }
  525. return $querryArray;
  526. }
  527. /**
  528. * Returns true if and only if the query string passes validation. If no query is passed,
  529. * then the query string contained in the instance variable is used.
  530. *
  531. * @param string $query The query to validate
  532. * @throws Zend_Uri_Exception When query validation fails
  533. * @return boolean
  534. * @link http://www.faqs.org/rfcs/rfc2396.html
  535. */
  536. public function validateQuery($query = null)
  537. {
  538. if ($query === null) {
  539. $query = $this->_query;
  540. }
  541. // If query is empty, it is considered to be valid
  542. if (strlen($query) === 0) {
  543. return true;
  544. }
  545. // Determine whether the query is well-formed
  546. $pattern = '/^' . $this->_regex['uric'] . '*$/';
  547. $status = @preg_match($pattern, $query);
  548. if ($status === false) {
  549. require_once 'Zend/Uri/Exception.php';
  550. throw new Zend_Uri_Exception('Internal error: query validation failed');
  551. }
  552. return $status == 1;
  553. }
  554. /**
  555. * Add or replace params in the query string for the current URI, and
  556. * return the old query.
  557. *
  558. * @param array $queryParams
  559. * @return string Old query string
  560. */
  561. public function addReplaceQueryParameters(array $queryParams)
  562. {
  563. $queryParams = array_merge($this->getQueryAsArray(), $queryParams);
  564. return $this->setQuery($queryParams);
  565. }
  566. /**
  567. * Remove params in the query string for the current URI, and
  568. * return the old query.
  569. *
  570. * @param array $queryParamKeys
  571. * @return string Old query string
  572. */
  573. public function removeQueryParameters(array $queryParamKeys)
  574. {
  575. $queryParams = array_diff_key($this->getQueryAsArray(), array_fill_keys($queryParamKeys, 0));
  576. return $this->setQuery($queryParams);
  577. }
  578. /**
  579. * Set the query string for the current URI, and return the old query
  580. * string This method accepts both strings and arrays.
  581. *
  582. * @param string|array $query The query string or array
  583. * @throws Zend_Uri_Exception When $query is not a valid query string
  584. * @return string Old query string
  585. */
  586. public function setQuery($query)
  587. {
  588. $oldQuery = $this->_query;
  589. // If query is empty, set an empty string
  590. if (empty($query) === true) {
  591. $this->_query = '';
  592. return $oldQuery;
  593. }
  594. // If query is an array, make a string out of it
  595. if (is_array($query) === true) {
  596. $query = http_build_query($query, '', '&');
  597. } else {
  598. // If it is a string, make sure it is valid. If not parse and encode it
  599. $query = (string) $query;
  600. if ($this->validateQuery($query) === false) {
  601. parse_str($query, $queryArray);
  602. $query = http_build_query($queryArray, '', '&');
  603. }
  604. }
  605. // Make sure the query is valid, and set it
  606. if ($this->validateQuery($query) === false) {
  607. require_once 'Zend/Uri/Exception.php';
  608. throw new Zend_Uri_Exception("'$query' is not a valid query string");
  609. }
  610. $this->_query = $query;
  611. return $oldQuery;
  612. }
  613. /**
  614. * Returns the fragment portion of the URL (after #), or FALSE if none.
  615. *
  616. * @return string|false
  617. */
  618. public function getFragment()
  619. {
  620. return strlen($this->_fragment) > 0 ? $this->_fragment : false;
  621. }
  622. /**
  623. * Returns true if and only if the fragment passes validation. If no fragment is passed,
  624. * then the fragment contained in the instance variable is used.
  625. *
  626. * @param string $fragment Fragment of an URI
  627. * @throws Zend_Uri_Exception When fragment validation fails
  628. * @return boolean
  629. * @link http://www.faqs.org/rfcs/rfc2396.html
  630. */
  631. public function validateFragment($fragment = null)
  632. {
  633. if ($fragment === null) {
  634. $fragment = $this->_fragment;
  635. }
  636. // If fragment is empty, it is considered to be valid
  637. if (strlen($fragment) === 0) {
  638. return true;
  639. }
  640. // Determine whether the fragment is well-formed
  641. $pattern = '/^' . $this->_regex['uric'] . '*$/';
  642. $status = @preg_match($pattern, $fragment);
  643. if ($status === false) {
  644. require_once 'Zend/Uri/Exception.php';
  645. throw new Zend_Uri_Exception('Internal error: fragment validation failed');
  646. }
  647. return (boolean) $status;
  648. }
  649. /**
  650. * Sets the fragment for the current URI, and returns the old fragment
  651. *
  652. * @param string $fragment Fragment of the current URI
  653. * @throws Zend_Uri_Exception When $fragment is not a valid HTTP fragment
  654. * @return string
  655. */
  656. public function setFragment($fragment)
  657. {
  658. if ($this->validateFragment($fragment) === false) {
  659. require_once 'Zend/Uri/Exception.php';
  660. throw new Zend_Uri_Exception("Fragment \"$fragment\" is not a valid HTTP fragment");
  661. }
  662. $oldFragment = $this->_fragment;
  663. $this->_fragment = $fragment;
  664. return $oldFragment;
  665. }
  666. }