PageRenderTime 49ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/vicious/Application.php

http://github.com/brysonian/vicious
PHP | 340 lines | 249 code | 53 blank | 38 comment | 44 complexity | 6d800e51c6380679c3cd555ced0de800 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. declare(encoding='UTF-8');
  3. namespace vicious
  4. {
  5. require_once(__DIR__.'/Helpers.php');
  6. require_once(__DIR__.'/Config.php');
  7. require_once(__DIR__.'/Router.php');
  8. require_once(__DIR__.'/Renderable.php');
  9. require_once(__DIR__.'/Request.php');
  10. class Application
  11. {
  12. protected $router;
  13. protected $route;
  14. protected $before = array();
  15. protected $error_handler = false;
  16. protected $not_found_handler = false;
  17. protected $error_shown = false;
  18. protected $config_handlers = array();
  19. /**
  20. * Initialize a vicious based app.
  21. * Not strictly necessary if you don't want to use auto dispatch and
  22. * want to manage your own paths.
  23. */
  24. public static function init() {
  25. $inc = get_include_path();
  26. if (!strpos($inc, __DIR__)) set_include_path(__DIR__.PATH_SEPARATOR.$inc);
  27. # grab an instance to get it constructed
  28. $s = self::instance();
  29. if (options('auto_dispatch') === true) {
  30. register_shutdown_function(array($s, 'auto_dispatch'));
  31. }
  32. }
  33. public function auto_dispatch() {
  34. try {
  35. $this->dispatch(request('uri'));
  36. } catch (\Exception $e) {
  37. $this->handle_error($e);
  38. }
  39. }
  40. protected function __construct() {
  41. # set app file location
  42. set('app_file', $_SERVER['SCRIPT_FILENAME']);
  43. # handle errors our way
  44. set_error_handler(array(&$this, 'default_error_handler'));
  45. set_exception_handler(array(&$this, 'default_exception_handler'));
  46. # need a router instance
  47. $this->router = new Router();
  48. }
  49. /**
  50. * Singleton instance method
  51. */
  52. public static function instance() {
  53. static $instance;
  54. if (!$instance) {
  55. $instance = new Application;
  56. }
  57. return $instance;
  58. }
  59. /**
  60. * Dispatch is the main point of execution.
  61. */
  62. public function dispatch($uri, $verb=false) {
  63. # some PHP polymorphism here.
  64. if ($uri instanceof Request) {
  65. if ($verb === false) $verb = $uri->method;
  66. $uri = $uri->uri;
  67. }
  68. if ($verb === false) $verb = request('method');
  69. # first run configs
  70. if (array_key_exists('ALL', $this->config_handlers)) {
  71. foreach($this->config_handlers['ALL'] as $handler) {
  72. call_user_func($handler);
  73. }
  74. }
  75. if (array_key_exists(options('environment'), $this->config_handlers)) {
  76. foreach($this->config_handlers[options('environment')] as $handler) {
  77. call_user_func($handler);
  78. }
  79. }
  80. # find the right route
  81. $this->route = $this->router->route_for_request($verb, $uri);
  82. # run filters and catch output
  83. ob_start();
  84. foreach($this->before as $filter) {
  85. call_user_func($filter);
  86. }
  87. $filter_output = ob_get_clean();
  88. # exec the method
  89. $out = $this->route->execute();
  90. # show the results
  91. if ($out != null) {
  92. if (is_string($out)) {
  93. echo $filter_output;
  94. echo $out;
  95. } else if ($out instanceof Renderable) {
  96. $out->send_content_type_header();
  97. echo $filter_output;
  98. $out->render();
  99. }
  100. }
  101. }
  102. public function get($pattern, $handler) { $this->router->get($pattern, $handler); }
  103. public function put($pattern, $handler) { $this->router->put($pattern, $handler); }
  104. public function post($pattern, $handler) { $this->router->post($pattern, $handler); }
  105. public function delete($pattern, $handler) { $this->router->delete($pattern, $handler); }
  106. /**
  107. * Add a filter before
  108. */
  109. public function before($handler) { $this->before[] = $handler; }
  110. /**
  111. * Set a function to be called as setup depending on the environment
  112. */
  113. public function configure($environment, $handler=false) {
  114. if ($handler == false) {
  115. $handler = $environment;
  116. $environment = 'ALL';
  117. }
  118. $this->config_handlers[$environment][] = $handler;
  119. }
  120. /**
  121. * Errors
  122. */
  123. public function error($e) { $this->error_handler = $e; }
  124. public function not_found($h) { $this->not_found_handler = $h; }
  125. public function handle_error($e) {
  126. if ($this->error_shown) return;
  127. $this->error_shown = true;
  128. $logo = 'data:image/png;base64,' . base64_encode(file_get_contents(__DIR__.'/images/vicious.png'));
  129. if (!($e instanceof ViciousException)) $e = ViciousException::fromException($e);
  130. if ($e instanceof NotFound) {
  131. $this->status(404);
  132. if (options('environment') != PRODUCTION) {
  133. $out = "<!DOCTYPE html>
  134. <html><head><title>404 Not Found</title>
  135. <style type='text/css'>
  136. body { font-family:helvetica,arial;font-size:18px; margin:50px; letter-spacing: .1em;}
  137. div, h1 {margin:0px auto;width:500px;}
  138. h1 { background-color:#FC63CD; color: #FFF; padding:125px 0px 10px 10px; background-image:url($logo); background-repeat:no-repeat;width:490px;}
  139. h2 { background-color:#888; color:#FFF; margin: 0px; padding: 3px 10px;}
  140. pre { background-color:#FF0; color:#000; padding: 10px; margin: 0px; white-space: pre-wrap;}
  141. </style>
  142. </head>
  143. <body>
  144. <h1>I dunno what you&rsquo;re after.</h1>
  145. <div><h2>Try this:</h2><pre>get('".request('uri')."', function() {
  146. return 'Hello World';
  147. });
  148. </pre></div>
  149. </body></html>";
  150. } else if ($this->not_found_handler) {
  151. $out = call_user_func($this->not_found_handler, $e);
  152. } else {
  153. # standard apache 404 page
  154. $out = '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"><html><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL '.request('uri').' was not found on this server.</p></body></html>';
  155. }
  156. } else {
  157. $this->status(500);
  158. if (options('environment') != PRODUCTION) {
  159. $t = $e->trace();
  160. $backtrace = explode("\n", $e->trace_as_string());
  161. array_shift($backtrace);
  162. $backtrace = join('</pre></li><li><pre>', $backtrace);
  163. $vars = array('GET' => $_GET, 'POST' => $_POST, 'SESSION' => isset($_SESSION) ? $_SESSION : array(), 'SERVER' => $_SERVER);
  164. foreach($vars as $type => $sg) {
  165. $html = "";
  166. if (empty($sg)) {
  167. $html .= "<tr class='empty'><th class='type'>$type</th><th colspan='2'>No $type data.</th></tr><tr><td class='blank'></td><td class='empty' colspan='2'>&nbsp;</td></tr>";
  168. } else {
  169. $html .= "<tr><th class='type'>$type</th><th>Variable</th><th>Value</th></tr>";
  170. foreach($sg as $k => $v) {
  171. if (is_array($v)) {
  172. ob_start();
  173. var_export($v);
  174. $v = nl2br(ob_get_clean());
  175. }
  176. $html .= "<tr><td class='blank'></td><td class='key'>$k</td><td>".wordwrap($v, 150, "<br />\n", true)."</td></tr>";
  177. }
  178. }
  179. $vars[$type] = $html;
  180. }
  181. $out = sprintf("<!DOCTYPE html>
  182. <html><head><title>500 Internal Server Error</title>
  183. <style type='text/css'>
  184. body { font-family:helvetica,arial;font-size:18px; margin:50px; letter-spacing: .1em;}
  185. #c { width: 960px; margin:0 auto; position: relative; }
  186. #h { display: table-cell; vertical-align: bottom; height: 109px; background-color:#FC63CD; color: #FFF; padding:0px 0px 10px 510px; background-image:url($logo); background-repeat:no-repeat;width:460px;}
  187. h1, h2 { margin:0; }
  188. h2 { font-size: 16px; color: white; }
  189. h2 span { font-weight: normal; }
  190. h3 { background-color:#888; color:#FFF; margin: 0px; padding: 3px 10px;}
  191. pre { background-color:#FF0; color:#000; padding: 10px; margin: 0px; font-size:12px; line-height: 1.5em; white-space: pre-wrap;}
  192. ul {margin:0px; padding: 0px; list-style: none; }
  193. li { border-bottom: 1px solid white; }
  194. table { width: 960px; border: 0px; border-spacing: 0px; }
  195. table th.type { font-size: 21px; font-weight: bold; width: 110px; border-right: 1px solid white;}
  196. th { text-align: left; background-color:#888; color:#FFF; padding: 0px 10px; height: 30px; font-weight: normal; font-size: 14px;}
  197. td { border-bottom: 1px solid white; background-color:#FF0; color:#000; padding: 10px; margin: 0px; font-size:12px; line-height: 1.5em; }
  198. td.key { width: 170px; border-right: 1px solid white; }
  199. td.blank { background-color: white; border: none; }
  200. tr.empty td { border: none; }
  201. </style>
  202. </head>
  203. <body>
  204. <div id='c'>
  205. <div id='h'><h1>%s</h1><h2>file: <span>%s</span> line: <span>%s</span> location: <span>%s</span></h2></div>
  206. <div><pre>%s</pre></div>
  207. <h3>Backtrace</h3>
  208. <ul><li><pre>%s</pre></li></ul>
  209. <table>%s
  210. %s
  211. %s
  212. %s</table>
  213. <div style='clear: both'></div>
  214. </div></body></html>", str_replace(array("vicious\\", 'Vicious'), '', get_class($e)), pathinfo($e->file(), PATHINFO_BASENAME), $e->line(), request('uri'), $e->message(), $backtrace, $vars['GET'], $vars['POST'], $vars['SESSION'], $vars['SERVER']);
  215. } else if ($this->error_handler) {
  216. $out = call_user_func($this->error_handler, $e);
  217. } else {
  218. # standard default 500 page
  219. $out = '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"><html><head><title>500 Internal Server Error</title></head><body><h1>Internal Server Error</h1><p>Please try again later.</p></body></html>';
  220. }
  221. }
  222. if ($out != null) {
  223. if (is_string($out)) {
  224. die($out);
  225. } else if ($out instanceof Renderable) {
  226. $out->send_content_type_header();
  227. $out->render();
  228. }
  229. }
  230. }
  231. public function default_error_handler($errno, $errstr, $errfile, $errline) {
  232. $this->default_exception_handler(new InvalidStatement($errstr, $errno, $errfile, $errline));
  233. }
  234. public function default_exception_handler($e) {
  235. $l = ob_get_level();
  236. while($l--) ob_end_clean();
  237. $this->handle_error($e);
  238. exit;
  239. }
  240. public function params($p=false) {
  241. if (!$this->route) return false;
  242. $params = $this->route->params();
  243. if ($p === false) return $params;
  244. if (is_array($params) && array_key_exists($p, $params)) {
  245. return $params[$p];
  246. } else {
  247. return false;
  248. }
  249. }
  250. public function route() {
  251. return $this->route;
  252. }
  253. // ===========================================================
  254. // - RESPONSE HELPERS
  255. // ===========================================================
  256. public function status($s) {
  257. if (is_numeric($s)) {
  258. switch($s) {
  259. case 404:
  260. header("HTTP/1.0 404 Not Found");
  261. header("Status: 404 Not Found");
  262. return;
  263. case 500:
  264. header('HTTP/1.1 500 Internal Server Error');
  265. header("Status: 500 Internal Server Error");
  266. return;
  267. default:
  268. header("Status: $s");
  269. return;
  270. }
  271. }
  272. }
  273. public function redirect($loc=false, $code=false) {
  274. if ($code !== false) status($code);
  275. $loc = $loc ? $loc : '/';
  276. header("Location: $loc");
  277. exit();
  278. }
  279. }
  280. }
  281. namespace
  282. {
  283. # get single static instance of a Application
  284. function app() { return vicious\Application::instance(); }
  285. function application() { return vicious\Application::instance(); }
  286. }
  287. ?>