Request.php 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. <?php
  2. /**
  3. * Lithium: the most rad php framework
  4. *
  5. * @copyright Copyright 2013, Union of RAD (http://union-of-rad.org)
  6. * @license http://opensource.org/licenses/bsd-license.php The BSD License
  7. */
  8. namespace lithium\net\http;
  9. use lithium\util\String;
  10. /**
  11. * Facilitates HTTP request creation by assembling connection and path info, `GET` and `POST` data,
  12. * and authentication credentials in a single, stateful object.
  13. */
  14. class Request extends \lithium\net\http\Message {
  15. /**
  16. * Key=value pairs after the ?.
  17. *
  18. * @var array
  19. */
  20. public $query = array();
  21. /**
  22. * The Authorization type: Basic or Digest
  23. *
  24. * @var string
  25. */
  26. public $auth = null;
  27. /**
  28. * The method of the request, typically one of the following: `GET`, `POST`, `PUT`, `DELETE`,
  29. * `OPTIONS`, `HEAD`, `TRACE` or `CONNECT`.
  30. *
  31. * @var string
  32. */
  33. public $method;
  34. /**
  35. * Cookies.
  36. *
  37. * @var array
  38. */
  39. public $cookies = array();
  40. /**
  41. * Adds config values to the public properties when a new object is created.
  42. *
  43. * @param array $config Configuration options : default value
  44. * - `scheme`: http
  45. * - `host`: localhost
  46. * - `port`: null
  47. * - `username`: null
  48. * - `password`: null
  49. * - `path`: null
  50. * - `query`: array - after the question mark ?
  51. * - `fragment`: null - after the hashmark #
  52. * - `auth` - the Authorization method (Basic|Digest)
  53. * - `method` - GET
  54. * - `type`: null
  55. * - `version`: 1.1
  56. * - `headers`: array
  57. * - `body`: null
  58. */
  59. public function __construct(array $config = array()) {
  60. $defaults = array(
  61. 'scheme' => 'http',
  62. 'host' => 'localhost',
  63. 'port' => null,
  64. 'username' => null,
  65. 'password' => null,
  66. 'path' => null,
  67. 'query' => array(),
  68. 'fragment' => null,
  69. 'headers' => array(),
  70. 'body' => null,
  71. 'auth' => null,
  72. 'method' => 'GET',
  73. 'type' => null,
  74. 'proxy' => null,
  75. 'ignoreErrors' => true,
  76. 'followLocation' => true
  77. );
  78. parent::__construct($config + $defaults);
  79. $this->method = $this->method ?: $this->_config['method'];
  80. $this->headers = array(
  81. 'Host' => $this->port ? "{$this->host}:{$this->port}" : $this->host,
  82. 'Connection' => 'Close',
  83. 'User-Agent' => 'Mozilla/5.0'
  84. );
  85. if ($type = $this->_config['type']) {
  86. $this->type($type);
  87. }
  88. $this->headers($this->_config['headers']);
  89. }
  90. /**
  91. * Add body parts and encodes it into formatted string
  92. *
  93. * @see lithium\net\Message::body()
  94. * @see lithium\net\http\Message::_encode()
  95. * @param mixed $data
  96. * @param array $options
  97. * - `'buffer'`: split the body string
  98. * @return array
  99. */
  100. public function body($data = null, $options = array()) {
  101. $defaults = array('encode' => true);
  102. return parent::body($data, $options + $defaults);
  103. }
  104. /**
  105. * Get the full query string queryString.
  106. *
  107. * @param array $params
  108. * @param string $format
  109. * @return string
  110. */
  111. public function queryString($params = array(), $format = null) {
  112. $result = array();
  113. foreach (array_filter(array($this->query, $params)) as $query) {
  114. if (is_string($query)) {
  115. $result[] = $query;
  116. continue;
  117. }
  118. $query = array_filter($query);
  119. if (!$format) {
  120. $result[] = http_build_query($query);
  121. continue;
  122. }
  123. $q = null;
  124. foreach ($params as $key => $value) {
  125. if (!is_array($value)) {
  126. $q .= String::insert($format, array(
  127. 'key' => urlencode($key),
  128. 'value' => urlencode($value)
  129. ));
  130. continue;
  131. }
  132. foreach ($value as $val) {
  133. $q .= String::insert($format, array(
  134. 'key' => urlencode("{$key}[]"),
  135. 'value' => urlencode($val)
  136. ));
  137. }
  138. }
  139. $result[] = substr($q, 0, -1);
  140. }
  141. $result = array_filter($result);
  142. return $result ? "?" . join("&", $result) : null;
  143. }
  144. /**
  145. * Converts the data in the record set to a different format, i.e. an array. Available
  146. * options: array, URL, stream context configuration, or string.
  147. *
  148. * @see lithium\net\Message::to()
  149. * @param string $format Format to convert to. Should be either `'url'`, which returns a string
  150. * representation of the URL that this request points to, or `'context'`, which
  151. * returns an array usable with PHP's `stream_context_create()` function. For
  152. * more available formats, see the parent method, `lithium\net\Message::to()`.
  153. * @param array $options Allows overriding of specific portions of the URL, as follows. These
  154. * options should only be specified if you intend to replace the values that are
  155. * already in the `Request` object.
  156. * - `'scheme'` _string_: The protocol scheme of the URL.
  157. * - `'method'` _string_: If applicable, the HTTP method to use in the request.
  158. * Mainly applies to the `'context'` format.
  159. * - `'host'` _string_: The host name the request is pointing at.
  160. * - `'port'` _string_: The host port, if any. If specified, should be prefixed
  161. * with `':'`.
  162. * - `'path'` _string_: The URL path.
  163. * - `'query'` _mixed_: The query string of the URL as a string or array. If passed
  164. * as a string, should be prefixed with `'?'`.
  165. * - `'auth'` _string_: Authentication information. See the constructor for
  166. * details.
  167. * - `'content'` _string_: The body of the request.
  168. * - `'headers'` _array_: The request headers.
  169. * - `'version'` _string_: The HTTP version of the request, where applicable.
  170. * @return mixed Varies; see the `$format` parameter for possible return values.
  171. */
  172. public function to($format, array $options = array()) {
  173. $defaults = array(
  174. 'method' => $this->method,
  175. 'scheme' => $this->scheme,
  176. 'host' => $this->host,
  177. 'port' => $this->port ? ":{$this->port}" : '',
  178. 'path' => $this->path,
  179. 'query' => null,
  180. 'auth' => $this->auth,
  181. 'username' => $this->username,
  182. 'password' => $this->password,
  183. 'headers' => array(),
  184. 'proxy' => $this->_config['proxy'],
  185. 'body' => null,
  186. 'version' => $this->version,
  187. 'ignore_errors' => $this->_config['ignoreErrors'],
  188. 'follow_location' => $this->_config['followLocation'],
  189. 'request_fulluri' => (boolean) $this->_config['proxy']
  190. );
  191. $options += $defaults;
  192. if ($options['auth']) {
  193. $data = array();
  194. if (is_array($options['auth']) && !empty($options['auth']['nonce'])) {
  195. $data = array('method' => $options['method'], 'uri' => $options['path']);
  196. $data += $options['auth'];
  197. }
  198. $auth = $this->_classes['auth'];
  199. $data = $auth::encode($options['username'], $options['password'], $data);
  200. $this->headers('Authorization', $auth::header($data));
  201. }
  202. $body = $this->body($options['body']);
  203. $this->headers('Content-Length', strlen($body));
  204. switch ($format) {
  205. case 'url':
  206. $options['query'] = $this->queryString($options['query']);
  207. $options['path'] = str_replace('//', '/', $options['path']);
  208. return String::insert("{:scheme}://{:host}{:port}{:path}{:query}", $options);
  209. case 'context':
  210. $base = array(
  211. 'content' => $body,
  212. 'method' => $options['method'],
  213. 'header' => $this->headers($options['headers']),
  214. 'protocol_version' => $options['version'],
  215. 'ignore_errors' => $options['ignore_errors'],
  216. 'follow_location' => $options['follow_location'],
  217. 'request_fulluri' => $options['request_fulluri'],
  218. 'proxy' => $options['proxy']
  219. );
  220. return array('http' => array_diff_key($options, $defaults) + $base);
  221. case 'string':
  222. $path = str_replace('//', '/', $this->path) . $this->queryString($options['query']);
  223. $status = "{$this->method} {$path} {$this->protocol}";
  224. return join("\r\n", array($status, join("\r\n", $this->headers()), "", $body));
  225. default:
  226. return parent::to($format, $options);
  227. }
  228. }
  229. /**
  230. * Magic method to convert object to string.
  231. *
  232. * @return string
  233. */
  234. public function __toString() {
  235. return $this->to('string');
  236. }
  237. }
  238. ?>