PageRenderTime 60ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/api/vendor/peej/tonic/src/Tonic/Request.php

https://gitlab.com/x33n/respond
PHP | 288 lines | 208 code | 32 blank | 48 comment | 30 complexity | 729cda97769ac7724a5f1f0e80edfc35 MD5 | raw file
  1. <?php
  2. namespace Tonic;
  3. /**
  4. * Model a HTTP request
  5. */
  6. class Request
  7. {
  8. public $uri;
  9. public $params = array();
  10. public $method;
  11. public $contentType;
  12. public $data;
  13. public $accept = array();
  14. public $acceptLanguage = array();
  15. public $ifMatch = array();
  16. public $ifNoneMatch = array();
  17. /**
  18. * Map of file/URI extensions to mimetypes
  19. * @var str[]
  20. */
  21. public $mimetypes = array(
  22. 'html' => 'text/html',
  23. 'txt' => 'text/plain',
  24. 'php' => 'application/php',
  25. 'css' => 'text/css',
  26. 'js' => 'application/javascript',
  27. 'json' => 'application/json',
  28. 'xml' => 'application/xml',
  29. 'rss' => 'application/rss+xml',
  30. 'atom' => 'application/atom+xml',
  31. 'gz' => 'application/x-gzip',
  32. 'tar' => 'application/x-tar',
  33. 'zip' => 'application/zip',
  34. 'gif' => 'image/gif',
  35. 'png' => 'image/png',
  36. 'jpg' => 'image/jpeg',
  37. 'ico' => 'image/x-icon',
  38. 'swf' => 'application/x-shockwave-flash',
  39. 'flv' => 'video/x-flv',
  40. 'avi' => 'video/mpeg',
  41. 'mpeg' => 'video/mpeg',
  42. 'mpg' => 'video/mpeg',
  43. 'mov' => 'video/quicktime',
  44. 'mp3' => 'audio/mpeg'
  45. );
  46. public function __construct($options = array())
  47. {
  48. if (isset($options['mimetypes']) && is_array($options['mimetypes'])) {
  49. foreach ($options['mimetypes'] as $ext => $mimetype) {
  50. $this->mimetypes[$ext] = $mimetype;
  51. }
  52. }
  53. $this->uri = $this->getURIFromEnvironment($options);
  54. $this->params = $this->getOption($options, 'params', null, array());
  55. $this->method = $this->getMethod($options);
  56. $this->contentType = $this->getContentType($options);
  57. $this->data = $this->getData($options);
  58. $this->accept = array_unique(array_merge($this->accept, $this->getAcceptArray($this->getOption($options, 'accept'))));
  59. $this->acceptLanguage = array_unique(array_merge($this->acceptLanguage, $this->getAcceptArray($this->getOption($options, 'acceptLanguage'))));
  60. $this->ifMatch = $this->getMatchArray($this->getOption($options, 'ifMatch'));
  61. $this->ifNoneMatch = $this->getMatchArray($this->getOption($options, 'ifNoneMatch'));
  62. }
  63. /**
  64. * Get an item from the given options array if it exists, otherwise fetch from HTTP header
  65. * or return the given default
  66. *
  67. * @param str[] $options
  68. * @param str $configVar Name of item to get
  69. * @param str|str[] $headers Name of HTTP header(s)
  70. * @param str $default Fallback value
  71. * @return str
  72. */
  73. public function getOption($options, $configVar, $headers = null, $default = null)
  74. {
  75. if (isset($options[$configVar])) {
  76. return $options[$configVar];
  77. } else {
  78. if (!is_array($headers)) {
  79. $headers = array($headers);
  80. }
  81. $headers[] = $configVar;
  82. foreach ($headers as $header) {
  83. if ($val = $this->getHeader($header)) {
  84. return $val;
  85. }
  86. }
  87. return $default;
  88. }
  89. }
  90. /**
  91. * Magic PHP method to retrieve a HTTP request header.
  92. *
  93. * For example, to retrieve the content-type header, camelcase the header name:
  94. *
  95. * $request->userAgent
  96. *
  97. * @param str name
  98. * @return str
  99. */
  100. public function __get($name)
  101. {
  102. return $this->getHeader($name);
  103. }
  104. private function getHeader($name)
  105. {
  106. $name = strtoupper(preg_replace('/([A-Z])/', '_$1', $name));
  107. if (isset($_SERVER['HTTP_'.$name])) {
  108. return $_SERVER['HTTP_'.$name];
  109. } elseif (isset($_SERVER[$name])) {
  110. return $_SERVER[$name];
  111. } else {
  112. return NULL;
  113. }
  114. }
  115. private function getMethod($options)
  116. {
  117. // get HTTP method from HTTP header
  118. $method = strtoupper($this->getHeader('requestMethod'));
  119. if (!$method) {
  120. $method = 'GET';
  121. }
  122. // get override value from override HTTP header and use if applicable
  123. $override = strtoupper($this->getHeader('xHttpMethodOverride'));
  124. if ($override && $method == 'POST') {
  125. $method = $override;
  126. } else {
  127. // get override value from URL and use if applicable
  128. if (
  129. isset($options['uriMethodOverride']) &&
  130. $method == 'POST'
  131. ) {
  132. // get override value from appended bang syntax
  133. if (preg_match('/![A-Z]+$/', $this->uri, $match, PREG_OFFSET_CAPTURE)) {
  134. $method = strtoupper(substr($this->uri, $match[0][1] + 1));
  135. $this->uri = substr($this->uri, 0, $match[0][1]);
  136. // get override value from _method querystring
  137. } elseif (isset($_GET['_method'])) {
  138. $method = strtoupper($_GET['_method']);
  139. }
  140. }
  141. }
  142. return $this->getOption($options, 'method', null, $method);
  143. }
  144. private function getContentType($options)
  145. {
  146. $contentType = $this->getOption($options, 'contentType');
  147. $parts = explode(';', $contentType);
  148. return $parts[0];
  149. }
  150. private function getData($options)
  151. {
  152. if ($this->getOption($options, 'contentLength') > 0) {
  153. return file_get_contents('php://input');
  154. } elseif (isset($options['data'])) {
  155. return $options['data'];
  156. }
  157. }
  158. /**
  159. * Fetch the request URI from the server environment
  160. * @param str $options
  161. * @return str
  162. */
  163. private function getURIFromEnvironment($options)
  164. {
  165. $uri = $this->getOption($options, 'uri');
  166. if (!$uri) { // use given URI in config options
  167. if (isset($_SERVER['REDIRECT_URL']) && isset($_SERVER['SCRIPT_NAME'])) { // use redirection URL from Apache environment
  168. $dirname = dirname($_SERVER['SCRIPT_NAME']);
  169. $uri = substr($_SERVER['REDIRECT_URL'], strlen($dirname == DIRECTORY_SEPARATOR ? '' : $dirname));
  170. } elseif (isset($_SERVER['REQUEST_URI'])) { // use request URI from environment
  171. $uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
  172. } elseif (isset($_SERVER['PHP_SELF']) && isset($_SERVER['SCRIPT_NAME'])) { // use PHP_SELF from Apache environment
  173. $uri = substr($_SERVER['PHP_SELF'], strlen($_SERVER['SCRIPT_NAME']));
  174. } else { // fail
  175. throw new \Exception('URI not provided');
  176. }
  177. }
  178. // get mimetype
  179. $parts = explode('/', rtrim($uri, '/'));
  180. $lastPart = array_pop($parts);
  181. $uri = join('/', $parts);
  182. $parts = explode('.', $lastPart);
  183. $firstPart = array_shift($parts);
  184. $uri .= '/'.$firstPart;
  185. foreach ($parts as $part) {
  186. if (isset($this->mimetypes[$part])) {
  187. $this->accept[] = $this->mimetypes[$part];
  188. } elseif (preg_match('/^[a-z]{2}(-[a-z]{2})?$/', $part)) {
  189. $this->acceptLanguage[] = $part;
  190. } else {
  191. $uri .= '.'.$part;
  192. }
  193. }
  194. return $uri;
  195. }
  196. /**
  197. * Get accepted content mimetypes from request header
  198. * @param str $acceptString
  199. * @return str[]
  200. */
  201. private function getAcceptArray($acceptString)
  202. {
  203. $accept = $acceptArray = array();
  204. foreach (explode(',', strtolower($acceptString)) as $part) {
  205. $parts = preg_split('/\s*;\s*q=/', $part);
  206. if (isset($parts) && isset($parts[1]) && $parts[1]) {
  207. $num = $parts[1] * 10;
  208. } else {
  209. $num = 10;
  210. }
  211. if ($parts[0]) {
  212. $accept[$num][] = $parts[0];
  213. }
  214. }
  215. krsort($accept);
  216. foreach ($accept as $parts) {
  217. foreach ($parts as $part) {
  218. $acceptArray[] = trim($part);
  219. }
  220. }
  221. return $acceptArray;
  222. }
  223. /**
  224. * Get if-match data from request header
  225. * @param str $matchString
  226. * @return str[]
  227. */
  228. private function getMatchArray($matchString)
  229. {
  230. $matches = array();
  231. foreach (explode(',', $matchString) as $etag) {
  232. $matches[] = trim($etag, '" ');
  233. }
  234. return $matches;
  235. }
  236. public function __toString()
  237. {
  238. $accept = join(', ', $this->accept);
  239. $acceptLanguage = join(', ', $this->acceptLanguage);
  240. $ifMatch = join(', ', $this->ifMatch);
  241. $ifNoneMatch = join(', ', $this->ifNoneMatch);
  242. return <<<EOF
  243. =============
  244. Tonic\Request
  245. =============
  246. URI: $this->uri
  247. HTTP method: $this->method
  248. Content type: $this->contentType
  249. Request data: $this->data
  250. Accept: $accept
  251. Accept language: $acceptLanguage
  252. If match: $ifMatch
  253. If none match: $ifNoneMatch
  254. EOF;
  255. }
  256. }