/src/php/App.php

https://github.com/cordoval/genghis · PHP · 200 lines · 168 code · 24 blank · 8 comment · 33 complexity · d123c4ff6bc51543f036ec7eb71153f4 MD5 · raw file

  1. <?php
  2. class App
  3. {
  4. protected $assets = array();
  5. protected $assetEtags = array();
  6. protected $baseUrl;
  7. public function run()
  8. {
  9. try {
  10. $response = $this->route($this->getRequestMethod(), $this->getRequestPath());
  11. if ($response instanceof Response) {
  12. $response->render();
  13. } else {
  14. throw new HttpException(500);
  15. }
  16. } catch (HttpException $e) {
  17. $errorResponse = $this->renderTemplate(
  18. 'error.html.mustache',
  19. $e->getStatus(),
  20. array(
  21. 'message' => $e->getMessage(),
  22. 'status' => $e->getStatus(),
  23. )
  24. );
  25. $errorResponse->render();
  26. }
  27. }
  28. public function route($method, $path)
  29. {
  30. if ($this->isJsonRequest()) {
  31. try {
  32. $api = new Api();
  33. return $api->route($method, $path);
  34. } catch (HttpException $e) {
  35. $msg = $e->getMessage() ? $e->getMessage() : Response::getStatusText($e->getStatus());
  36. return new JsonResponse(array('error' => $msg, 'status' => $e->getStatus()), $e->getStatus());
  37. }
  38. } else {
  39. if (strpos($path, '/assets/') === 0) {
  40. return $this->getAsset(substr($path, 8));
  41. } else {
  42. // not an api request, we'll return index.html and render the page in javascript.
  43. return $this->renderTemplate('index.html.mustache');
  44. }
  45. break;
  46. }
  47. }
  48. protected function isJsonRequest()
  49. {
  50. if (in_array($this->getRequestMethod(), array('POST', 'PUT'))) {
  51. $type = isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : 'x-www-form-urlencoded';
  52. } else {
  53. $type = isset($_SERVER['HTTP_ACCEPT']) ? $_SERVER['HTTP_ACCEPT'] : 'text/html';
  54. }
  55. return strpos($type, 'application/json') !== false || strpos($type, 'application/javascript') !== false;
  56. }
  57. protected function getBaseUrl()
  58. {
  59. if (!isset($this->baseUrl)) {
  60. $this->baseUrl = $this->prepareBaseUrl();
  61. }
  62. return $this->baseUrl;
  63. }
  64. protected function prepareBaseUrl()
  65. {
  66. $filename = basename($_SERVER['SCRIPT_FILENAME']);
  67. foreach (array('SCRIPT_NAME', 'PHP_SELF', 'ORIG_SCRIPT_NAME') as $key) {
  68. if (isset($_SERVER[$key]) && basename($_SERVER[$key]) == $filename) {
  69. $baseUrl = $_SERVER[$key];
  70. break;
  71. }
  72. }
  73. if (!isset($baseUrl)) {
  74. $path = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : '';
  75. $file = isset($_SERVER['SCRIPT_FILENAME']) ? $_SERVER['SCRIPT_FILENAME'] : '';
  76. $chunks = array_reverse(explode('/', trim($file, '/')));
  77. $index = 0;
  78. $last = count($chunks);
  79. $baseUrl = '';
  80. do {
  81. $seg = $segs[$index];
  82. $baseUrl = '/'.$seg.$baseUrl;
  83. ++$index;
  84. } while (($last > $index) && (false !== ($pos = strpos($path, $baseUrl))) && (0 != $pos));
  85. }
  86. // Does the baseUrl have anything in common with the request_uri?
  87. $requestUri = $_SERVER['REQUEST_URI'];
  88. if ($baseUrl && 0 === strpos($requestUri, $baseUrl)) {
  89. // full $baseUrl matches
  90. return $baseUrl;
  91. }
  92. if ($baseUrl && 0 === strpos($requestUri, dirname($baseUrl))) {
  93. // directory portion of $baseUrl matches
  94. return rtrim(dirname($baseUrl), '/');
  95. }
  96. $truncatedRequestUri = $requestUri;
  97. if (($pos = strpos($requestUri, '?')) !== false) {
  98. $truncatedRequestUri = substr($requestUri, 0, $pos);
  99. }
  100. $basename = basename($baseUrl);
  101. if (empty($basename) || !strpos($truncatedRequestUri, $basename)) {
  102. // no match whatsoever; set it blank
  103. return '';
  104. }
  105. // If using mod_rewrite or ISAPI_Rewrite strip the script filename
  106. // out of baseUrl. $pos !== 0 makes sure it is not matching a value
  107. // from PATH_INFO or QUERY_STRING
  108. if ((strlen($requestUri) >= strlen($baseUrl)) && ((false !== ($pos = strpos($requestUri, $baseUrl))) && ($pos !== 0))) {
  109. $baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl));
  110. }
  111. return rtrim($baseUrl, '/');
  112. }
  113. protected function getRequestMethod()
  114. {
  115. return $_SERVER['REQUEST_METHOD'];
  116. }
  117. protected function getRequestPath()
  118. {
  119. return isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '/';
  120. }
  121. protected function getQueryParams()
  122. {
  123. global $_GET;
  124. return $_GET;
  125. }
  126. protected function getQueryParam($name, $default = null)
  127. {
  128. $params = $this->getQueryParams();
  129. if (isset($params[$name])) {
  130. return $params[$name];
  131. } else {
  132. return $default;
  133. }
  134. }
  135. protected function renderTemplate($name, $status = 200, array $vars = array())
  136. {
  137. $this->initAssets();
  138. $defaults = array(
  139. 'base_url' => $this->getBaseUrl(),
  140. );
  141. return new Response(strtr($this->assets[$name], $this->prepareVars(array_merge($defaults, $vars))), $status);
  142. }
  143. protected function prepareVars($vars)
  144. {
  145. $ret = array();
  146. foreach ($vars as $name => $var) {
  147. $ret['{{ '.$name.' }}'] = $var;
  148. }
  149. return $ret;
  150. }
  151. protected function getAsset($name)
  152. {
  153. $this->initAssets();
  154. if (isset($this->assets[$name])) {
  155. return new AssetResponse($name, $this->assets[$name], array(
  156. 'Last-Modified' => gmdate("D, d M Y H:i:s", filemtime(__FILE__))." GMT",
  157. 'Etag' => sprintf('"%s"', $this->assetEtags[$name]),
  158. ));
  159. }
  160. throw new HttpException(404);
  161. }
  162. protected function initAssets()
  163. {
  164. if (empty($this->assets)) {
  165. $data = file_get_contents(__FILE__, false, null, __COMPILER_HALT_OFFSET__);
  166. foreach (preg_split("/^@@(?=[\w\d\.]+( [\w\d\.]+)?$)/m", $data, -1) as $asset) {
  167. if (trim($asset)) {
  168. list($line, $content) = explode("\n", $asset, 2);
  169. list($name, $etag) = explode(" ", $line, 2);
  170. $this->assets[$name] = trim($content);
  171. $this->assetEtags[$name] = $etag;
  172. }
  173. }
  174. }
  175. }
  176. }