PageRenderTime 71ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 0ms

/library/Zend/Http/Request.php

http://github.com/zendframework/zf2
PHP | 560 lines | 282 code | 65 blank | 213 comment | 33 complexity | 4ec1eaad4908750f12f3cb680881e3e3 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. */
  9. namespace Zend\Http;
  10. use Zend\Stdlib\Parameters;
  11. use Zend\Stdlib\ParametersInterface;
  12. use Zend\Stdlib\RequestInterface;
  13. use Zend\Uri\Exception as UriException;
  14. use Zend\Uri\Http as HttpUri;
  15. /**
  16. * HTTP Request
  17. *
  18. * @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5
  19. */
  20. class Request extends AbstractMessage implements RequestInterface
  21. {
  22. /**#@+
  23. * @const string METHOD constant names
  24. */
  25. const METHOD_OPTIONS = 'OPTIONS';
  26. const METHOD_GET = 'GET';
  27. const METHOD_HEAD = 'HEAD';
  28. const METHOD_POST = 'POST';
  29. const METHOD_PUT = 'PUT';
  30. const METHOD_DELETE = 'DELETE';
  31. const METHOD_TRACE = 'TRACE';
  32. const METHOD_CONNECT = 'CONNECT';
  33. const METHOD_PATCH = 'PATCH';
  34. const METHOD_PROPFIND = 'PROPFIND';
  35. /**#@-*/
  36. /**
  37. * @var string
  38. */
  39. protected $method = self::METHOD_GET;
  40. /**
  41. * @var bool
  42. */
  43. protected $allowCustomMethods = true;
  44. /**
  45. * @var string|HttpUri
  46. */
  47. protected $uri = null;
  48. /**
  49. * @var ParametersInterface
  50. */
  51. protected $queryParams = null;
  52. /**
  53. * @var ParametersInterface
  54. */
  55. protected $postParams = null;
  56. /**
  57. * @var ParametersInterface
  58. */
  59. protected $fileParams = null;
  60. /**
  61. * A factory that produces a Request object from a well-formed Http Request string
  62. *
  63. * @param string $string
  64. * @param bool $allowCustomMethods
  65. * @throws Exception\InvalidArgumentException
  66. * @return Request
  67. */
  68. public static function fromString($string, $allowCustomMethods = true)
  69. {
  70. $request = new static();
  71. $request->setAllowCustomMethods($allowCustomMethods);
  72. $lines = explode("\r\n", $string);
  73. // first line must be Method/Uri/Version string
  74. $matches = null;
  75. $methods = $allowCustomMethods
  76. ? '[\w-]+'
  77. : implode(
  78. '|',
  79. array(
  80. self::METHOD_OPTIONS,
  81. self::METHOD_GET,
  82. self::METHOD_HEAD,
  83. self::METHOD_POST,
  84. self::METHOD_PUT,
  85. self::METHOD_DELETE,
  86. self::METHOD_TRACE,
  87. self::METHOD_CONNECT,
  88. self::METHOD_PATCH
  89. )
  90. );
  91. $regex = '#^(?P<method>' . $methods . ')\s(?P<uri>[^ ]*)(?:\sHTTP\/(?P<version>\d+\.\d+)){0,1}#';
  92. $firstLine = array_shift($lines);
  93. if (!preg_match($regex, $firstLine, $matches)) {
  94. throw new Exception\InvalidArgumentException(
  95. 'A valid request line was not found in the provided string'
  96. );
  97. }
  98. $request->setMethod($matches['method']);
  99. $request->setUri($matches['uri']);
  100. $parsedUri = parse_url($matches['uri']);
  101. if (array_key_exists('query', $parsedUri)) {
  102. $parsedQuery = array();
  103. parse_str($parsedUri['query'], $parsedQuery);
  104. $request->setQuery(new Parameters($parsedQuery));
  105. }
  106. if (isset($matches['version'])) {
  107. $request->setVersion($matches['version']);
  108. }
  109. if (count($lines) == 0) {
  110. return $request;
  111. }
  112. $isHeader = true;
  113. $headers = $rawBody = array();
  114. while ($lines) {
  115. $nextLine = array_shift($lines);
  116. if ($nextLine == '') {
  117. $isHeader = false;
  118. continue;
  119. }
  120. if ($isHeader) {
  121. if (preg_match("/[\r\n]/", $nextLine)) {
  122. throw new Exception\RuntimeException('CRLF injection detected');
  123. }
  124. $headers[] = $nextLine;
  125. continue;
  126. }
  127. if (empty($rawBody)
  128. && preg_match('/^[a-z0-9!#$%&\'*+.^_`|~-]+:$/i', $nextLine)
  129. ) {
  130. throw new Exception\RuntimeException('CRLF injection detected');
  131. }
  132. $rawBody[] = $nextLine;
  133. }
  134. if ($headers) {
  135. $request->headers = implode("\r\n", $headers);
  136. }
  137. if ($rawBody) {
  138. $request->setContent(implode("\r\n", $rawBody));
  139. }
  140. return $request;
  141. }
  142. /**
  143. * Set the method for this request
  144. *
  145. * @param string $method
  146. * @return Request
  147. * @throws Exception\InvalidArgumentException
  148. */
  149. public function setMethod($method)
  150. {
  151. $method = strtoupper($method);
  152. if (!defined('static::METHOD_' . $method) && ! $this->getAllowCustomMethods()) {
  153. throw new Exception\InvalidArgumentException('Invalid HTTP method passed');
  154. }
  155. $this->method = $method;
  156. return $this;
  157. }
  158. /**
  159. * Return the method for this request
  160. *
  161. * @return string
  162. */
  163. public function getMethod()
  164. {
  165. return $this->method;
  166. }
  167. /**
  168. * Set the URI/URL for this request, this can be a string or an instance of Zend\Uri\Http
  169. *
  170. * @throws Exception\InvalidArgumentException
  171. * @param string|HttpUri $uri
  172. * @return Request
  173. */
  174. public function setUri($uri)
  175. {
  176. if (is_string($uri)) {
  177. try {
  178. $uri = new HttpUri($uri);
  179. } catch (UriException\InvalidUriPartException $e) {
  180. throw new Exception\InvalidArgumentException(
  181. sprintf('Invalid URI passed as string (%s)', (string) $uri),
  182. $e->getCode(),
  183. $e
  184. );
  185. }
  186. } elseif (!($uri instanceof HttpUri)) {
  187. throw new Exception\InvalidArgumentException(
  188. 'URI must be an instance of Zend\Uri\Http or a string'
  189. );
  190. }
  191. $this->uri = $uri;
  192. return $this;
  193. }
  194. /**
  195. * Return the URI for this request object
  196. *
  197. * @return HttpUri
  198. */
  199. public function getUri()
  200. {
  201. if ($this->uri === null || is_string($this->uri)) {
  202. $this->uri = new HttpUri($this->uri);
  203. }
  204. return $this->uri;
  205. }
  206. /**
  207. * Return the URI for this request object as a string
  208. *
  209. * @return string
  210. */
  211. public function getUriString()
  212. {
  213. if ($this->uri instanceof HttpUri) {
  214. return $this->uri->toString();
  215. }
  216. return $this->uri;
  217. }
  218. /**
  219. * Provide an alternate Parameter Container implementation for query parameters in this object,
  220. * (this is NOT the primary API for value setting, for that see getQuery())
  221. *
  222. * @param \Zend\Stdlib\ParametersInterface $query
  223. * @return Request
  224. */
  225. public function setQuery(ParametersInterface $query)
  226. {
  227. $this->queryParams = $query;
  228. return $this;
  229. }
  230. /**
  231. * Return the parameter container responsible for query parameters or a single query parameter
  232. *
  233. * @param string|null $name Parameter name to retrieve, or null to get the whole container.
  234. * @param mixed|null $default Default value to use when the parameter is missing.
  235. * @return \Zend\Stdlib\ParametersInterface|mixed
  236. */
  237. public function getQuery($name = null, $default = null)
  238. {
  239. if ($this->queryParams === null) {
  240. $this->queryParams = new Parameters();
  241. }
  242. if ($name === null) {
  243. return $this->queryParams;
  244. }
  245. return $this->queryParams->get($name, $default);
  246. }
  247. /**
  248. * Provide an alternate Parameter Container implementation for post parameters in this object,
  249. * (this is NOT the primary API for value setting, for that see getPost())
  250. *
  251. * @param \Zend\Stdlib\ParametersInterface $post
  252. * @return Request
  253. */
  254. public function setPost(ParametersInterface $post)
  255. {
  256. $this->postParams = $post;
  257. return $this;
  258. }
  259. /**
  260. * Return the parameter container responsible for post parameters or a single post parameter.
  261. *
  262. * @param string|null $name Parameter name to retrieve, or null to get the whole container.
  263. * @param mixed|null $default Default value to use when the parameter is missing.
  264. * @return \Zend\Stdlib\ParametersInterface|mixed
  265. */
  266. public function getPost($name = null, $default = null)
  267. {
  268. if ($this->postParams === null) {
  269. $this->postParams = new Parameters();
  270. }
  271. if ($name === null) {
  272. return $this->postParams;
  273. }
  274. return $this->postParams->get($name, $default);
  275. }
  276. /**
  277. * Return the Cookie header, this is the same as calling $request->getHeaders()->get('Cookie');
  278. *
  279. * @convenience $request->getHeaders()->get('Cookie');
  280. * @return Header\Cookie|bool
  281. */
  282. public function getCookie()
  283. {
  284. return $this->getHeaders()->get('Cookie');
  285. }
  286. /**
  287. * Provide an alternate Parameter Container implementation for file parameters in this object,
  288. * (this is NOT the primary API for value setting, for that see getFiles())
  289. *
  290. * @param ParametersInterface $files
  291. * @return Request
  292. */
  293. public function setFiles(ParametersInterface $files)
  294. {
  295. $this->fileParams = $files;
  296. return $this;
  297. }
  298. /**
  299. * Return the parameter container responsible for file parameters or a single file.
  300. *
  301. * @param string|null $name Parameter name to retrieve, or null to get the whole container.
  302. * @param mixed|null $default Default value to use when the parameter is missing.
  303. * @return ParametersInterface|mixed
  304. */
  305. public function getFiles($name = null, $default = null)
  306. {
  307. if ($this->fileParams === null) {
  308. $this->fileParams = new Parameters();
  309. }
  310. if ($name === null) {
  311. return $this->fileParams;
  312. }
  313. return $this->fileParams->get($name, $default);
  314. }
  315. /**
  316. * Return the header container responsible for headers or all headers of a certain name/type
  317. *
  318. * @see \Zend\Http\Headers::get()
  319. * @param string|null $name Header name to retrieve, or null to get the whole container.
  320. * @param mixed|null $default Default value to use when the requested header is missing.
  321. * @return \Zend\Http\Headers|bool|\Zend\Http\Header\HeaderInterface|\ArrayIterator
  322. */
  323. public function getHeaders($name = null, $default = false)
  324. {
  325. if ($this->headers === null || is_string($this->headers)) {
  326. // this is only here for fromString lazy loading
  327. $this->headers = (is_string($this->headers)) ? Headers::fromString($this->headers) : new Headers();
  328. }
  329. if ($name === null) {
  330. return $this->headers;
  331. }
  332. if ($this->headers->has($name)) {
  333. return $this->headers->get($name);
  334. }
  335. return $default;
  336. }
  337. /**
  338. * Get all headers of a certain name/type.
  339. *
  340. * @see Request::getHeaders()
  341. * @param string|null $name Header name to retrieve, or null to get the whole container.
  342. * @param mixed|null $default Default value to use when the requested header is missing.
  343. * @return \Zend\Http\Headers|bool|\Zend\Http\Header\HeaderInterface|\ArrayIterator
  344. */
  345. public function getHeader($name, $default = false)
  346. {
  347. return $this->getHeaders($name, $default);
  348. }
  349. /**
  350. * Is this an OPTIONS method request?
  351. *
  352. * @return bool
  353. */
  354. public function isOptions()
  355. {
  356. return ($this->method === self::METHOD_OPTIONS);
  357. }
  358. /**
  359. * Is this a PROPFIND method request?
  360. *
  361. * @return bool
  362. */
  363. public function isPropFind()
  364. {
  365. return ($this->method === self::METHOD_PROPFIND);
  366. }
  367. /**
  368. * Is this a GET method request?
  369. *
  370. * @return bool
  371. */
  372. public function isGet()
  373. {
  374. return ($this->method === self::METHOD_GET);
  375. }
  376. /**
  377. * Is this a HEAD method request?
  378. *
  379. * @return bool
  380. */
  381. public function isHead()
  382. {
  383. return ($this->method === self::METHOD_HEAD);
  384. }
  385. /**
  386. * Is this a POST method request?
  387. *
  388. * @return bool
  389. */
  390. public function isPost()
  391. {
  392. return ($this->method === self::METHOD_POST);
  393. }
  394. /**
  395. * Is this a PUT method request?
  396. *
  397. * @return bool
  398. */
  399. public function isPut()
  400. {
  401. return ($this->method === self::METHOD_PUT);
  402. }
  403. /**
  404. * Is this a DELETE method request?
  405. *
  406. * @return bool
  407. */
  408. public function isDelete()
  409. {
  410. return ($this->method === self::METHOD_DELETE);
  411. }
  412. /**
  413. * Is this a TRACE method request?
  414. *
  415. * @return bool
  416. */
  417. public function isTrace()
  418. {
  419. return ($this->method === self::METHOD_TRACE);
  420. }
  421. /**
  422. * Is this a CONNECT method request?
  423. *
  424. * @return bool
  425. */
  426. public function isConnect()
  427. {
  428. return ($this->method === self::METHOD_CONNECT);
  429. }
  430. /**
  431. * Is this a PATCH method request?
  432. *
  433. * @return bool
  434. */
  435. public function isPatch()
  436. {
  437. return ($this->method === self::METHOD_PATCH);
  438. }
  439. /**
  440. * Is the request a Javascript XMLHttpRequest?
  441. *
  442. * Should work with Prototype/Script.aculo.us, possibly others.
  443. *
  444. * @return bool
  445. */
  446. public function isXmlHttpRequest()
  447. {
  448. $header = $this->getHeaders()->get('X_REQUESTED_WITH');
  449. return false !== $header && $header->getFieldValue() == 'XMLHttpRequest';
  450. }
  451. /**
  452. * Is this a Flash request?
  453. *
  454. * @return bool
  455. */
  456. public function isFlashRequest()
  457. {
  458. $header = $this->getHeaders()->get('USER_AGENT');
  459. return false !== $header && stristr($header->getFieldValue(), ' flash');
  460. }
  461. /**
  462. * Return the formatted request line (first line) for this http request
  463. *
  464. * @return string
  465. */
  466. public function renderRequestLine()
  467. {
  468. return $this->method . ' ' . (string) $this->uri . ' HTTP/' . $this->version;
  469. }
  470. /**
  471. * @return string
  472. */
  473. public function toString()
  474. {
  475. $str = $this->renderRequestLine() . "\r\n";
  476. $str .= $this->getHeaders()->toString();
  477. $str .= "\r\n";
  478. $str .= $this->getContent();
  479. return $str;
  480. }
  481. /**
  482. * @return boolean
  483. */
  484. public function getAllowCustomMethods()
  485. {
  486. return $this->allowCustomMethods;
  487. }
  488. /**
  489. * @param boolean $strictMethods
  490. */
  491. public function setAllowCustomMethods($strictMethods)
  492. {
  493. $this->allowCustomMethods = (bool) $strictMethods;
  494. }
  495. }