PageRenderTime 72ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 1ms

/system/rocket.php

https://bitbucket.org/ryanfaerman/rocket
PHP | 463 lines | 248 code | 90 blank | 125 comment | 48 complexity | 7643a868c239603e78c76e90d2940c19 MD5 | raw file
  1. <?
  2. /**
  3. * Rocket Framework
  4. *
  5. * @package Rocket
  6. * @author Ryan Faerman
  7. **/
  8. class rocket {
  9. public $server, $mongo, $cache, $callback, $callback_arguments;
  10. public $post, $get, $files, $storage, $config, $paint, $session;
  11. protected $routes;
  12. private $loaded_classes, $events, $eventdb;
  13. /**
  14. * Rocket Constructor
  15. *
  16. * @param array $routes will override routes.php
  17. */
  18. function __construct($routes = array()) {
  19. if(file_exists('config/system.php')) {
  20. include 'config/system.php';
  21. }
  22. $this->config = (object) $config;
  23. if(!count($routes)) {
  24. if(file_exists('config/system.php')) {
  25. include 'config/routes.php';
  26. }
  27. }
  28. $this->routes = $routes;
  29. $this->loaded_classes = array();
  30. spl_autoload_register(array($this, 'loader'));
  31. $this->server = (object) array_change_key_case($_SERVER);
  32. $this->server->request_uri = ($this->server->request_uri) ? rtrim($this->server->request_uri, '/') : '/';
  33. $this->server->request_uri = str_replace(
  34. array($this->server->query_string, '?', $this->config->paths->base),
  35. '',
  36. $this->server->request_uri
  37. );
  38. $this->callback = false;
  39. $this->callback_arguments = array();
  40. $this->mongo = false;
  41. if($this->config->database->enabled) {
  42. $this->mongo = new Mongo();
  43. $db = $this->config->database->name;
  44. $this->storage = $this->mongo->$db;
  45. }
  46. $this->session = false;
  47. if($this->config->database->enabled && $this->config->sessions->enabled) {
  48. $this->session = new tracer($this);
  49. }
  50. $this->events = array();
  51. if($this->config->events->enabled && $this->config->events->global) {
  52. $collection = $this->config->events->collection;
  53. $this->eventdb = $this->storage->$collection;
  54. }
  55. $this->post = (object) array_change_key_case($_POST);
  56. $this->get = (object) array_change_key_case($_GET);
  57. unset($_GET);
  58. unset($_POST);
  59. }
  60. /**
  61. * Autoloader implementation
  62. *
  63. * Any auto-loaded class is searched for in the following locations, in order:
  64. * - system
  65. * - controllers
  66. * - exceptions
  67. * the actual paths are determined by their respective configuration values.
  68. *
  69. * Once included, a class will not be included again.
  70. *
  71. * @param string $class
  72. */
  73. protected function loader($class) {
  74. $class = strtolower($class);
  75. if(in_array($class, $this->loaded_classes)) {
  76. return;
  77. }
  78. $file = $this->config->paths->system.$class.'.php';
  79. if(file_exists($file)) {
  80. include $file;
  81. $this->loaded_classes[] = $class;
  82. return;
  83. } else {
  84. $file = $this->config->paths->controllers.$class.'.php';
  85. if(file_exists($file)) {
  86. include $file;
  87. $this->loaded_classes[] = $class;
  88. return;
  89. }
  90. }
  91. if(strstr($class, 'exception_')) {
  92. $file = $this->config->paths->exceptions.$class.'.php';
  93. if(file_exists($file)) {
  94. include $file;
  95. } else {
  96. #throw new Exception('Exception Not Found');
  97. }
  98. }
  99. }
  100. /**
  101. * Accessor for routes
  102. *
  103. * route() -> returns routes array
  104. * route(someroute, false) -> remove route
  105. * route(someroute, callback) -> assigns callback to route
  106. * route(someroute, callback_class, callback_method)
  107. * route(someroute) -> returns callback
  108. */
  109. function route() {
  110. $args = func_get_args();
  111. switch(func_num_args()) {
  112. case 0:
  113. return $this->routes;
  114. case 1:
  115. return $this->routes[$args[0]];
  116. case 2:
  117. if($args[1] === false) {
  118. unset($this->routes[$args[0]]);
  119. } else {
  120. $this->routes[$args[0]] = $args[1];
  121. }
  122. return;
  123. case 3:
  124. $this->routes[$args[0]] = array($args[1], $args[2]);
  125. return;
  126. }
  127. }
  128. /**
  129. * An alias for $this->route()
  130. */
  131. function routes() {
  132. return $this->route();
  133. }
  134. /**
  135. * path() -> the current route path
  136. * path(callback)
  137. */
  138. function path() {
  139. }
  140. /**
  141. * Process the route and execute the callback
  142. *
  143. * Launching determines the best matching route and executes its callback.
  144. * The callback is loaded based on the request type and if it came via XHR.
  145. * The _most specific_ callback will be executed for the route.
  146. *
  147. * Some Examples:
  148. *
  149. * A regular POST request would first attempt to call the method 'method_post'
  150. * if that callback does not exist, the assigned callback would be called.
  151. *
  152. * Routes do not need to be created for each request type.
  153. *
  154. * For a POST via XHR, the following callbacks would be attempted, in order:
  155. * - method_xhr_post
  156. * - method_xhr
  157. * - method_post
  158. * - method
  159. */
  160. function launch() {
  161. $this->trigger('system.pre_launch');
  162. $output = '';
  163. foreach($this->routes as $pattern => $handler) {
  164. $pattern = trim($pattern, '/');
  165. if($pattern == $this->server->request_uri) {
  166. # direct route found
  167. $this->callback = $handler;
  168. break;
  169. } else {
  170. # indirect match?
  171. $pattern = str_replace('/', '\/', $pattern);
  172. if(preg_match('/^\/' . $pattern . '\/?$/', $this->server->request_uri, $matches)) {
  173. # indirect route found
  174. $this->callback = $handler;
  175. array_shift($matches);
  176. $this->callback_arguments = $matches;
  177. break;
  178. }
  179. }
  180. }
  181. if(!$this->callback) {
  182. #throw new Exception('Route Not Found for '.$this->server->request_uri, 404);
  183. }
  184. $xhr = $this->xhr();
  185. $this->trigger('system.route_found', array('callback'=> $this->callback, 'arguments' => $this->callback_arguments));
  186. if(is_array($this->callback)) {
  187. # callback is a class
  188. list($class, $method) = $this->callback;
  189. $discovered_method = $method;
  190. # This enables ROUTE(_xhr)?(_method)?
  191. if($xhr && method_exists($class, $method.'_xhr_'.$this->server->request_method)) {
  192. $discovered_method = $method.'_xhr_'.$this->server->request_method;
  193. } else if($xhr && method_exists($class, $method.'_xhr')) {
  194. $discovered_method = $method.'_xhr';
  195. } else if(method_exists($class, $method.'_'.$this->server->request_method)) {
  196. $discovered_method = $method.'_'.$this->server->request_method;
  197. } else if(!method_exists($class, $method)) {
  198. # invalid route
  199. throw new ErrorException('Invalid Route/Callback', 701);
  200. }
  201. # pass the callback the system instance then call the method
  202. $handler = new $class($this);
  203. $this->trigger('system.pre_handler');
  204. $output = call_user_func_array(array(&$handler, $discovered_method), $this->callback_arguments);
  205. $this->trigger('system.post_handler');
  206. } else {
  207. # callback is a function
  208. if(!function_exists($this->callback)) {
  209. # invalid route
  210. } else {
  211. # call the callback
  212. $this->trigger('system.pre_handler');
  213. $output = call_user_func_array($this->callback, $this->callback_arguments);
  214. $this->trigger('system.post_handler');
  215. }
  216. }
  217. $this->trigger('system.post_launch');
  218. return $output;
  219. }
  220. /**
  221. * A static helper for launching
  222. *
  223. * @param array $routes optional
  224. * @return void
  225. * @author Ryan Faerman
  226. */
  227. static function fly($routes = array()) {
  228. $systemName = __CLASS__;
  229. $system = new $systemName($routes);
  230. return $system->launch();
  231. }
  232. /**
  233. * Prepare to launch
  234. *
  235. * Executes all _preflight() methods, builds the events etc.
  236. *
  237. * @return void
  238. * @author Ryan Faerman
  239. */
  240. function prepare() {
  241. # reset the events database
  242. $this->eventdb->drop();
  243. $data = array();
  244. $i = 0;
  245. $grade = array();
  246. foreach(scandir($this->config->paths->controllers) as $file) {
  247. if(preg_match('/\.php$/', $file)) {
  248. $class = str_replace('.php', '', $file);
  249. $handler = new $class($this);
  250. if(method_exists($handler, '_preflight')) {
  251. $preflight = (object) $handler->_preflight();
  252. $data['controllers'][$i] = array();
  253. $data['controllers'][$i]['name'] = $class;
  254. # events registration
  255. $event_passed = array();
  256. foreach($preflight->events as $event => $callback) {
  257. $_id = $this->eventdb->insert(array(
  258. 'event' => $event,
  259. 'callback' => $callback
  260. ));
  261. $event_passed[] = ($_id) ? true : false ;
  262. }
  263. if(!in_array(false, $event_passed)) {
  264. $data['controllers'][$i]['events'] = 'passed';
  265. }
  266. $grade[] = !in_array(false, $event_passed);
  267. # setup each controller
  268. $setup_passed = array();
  269. foreach($preflight->setup as $class => $method) {
  270. # coming soon.
  271. }
  272. if(!in_array(false, $setup_passed)) {
  273. $data['controllers'][$i]['setup'] = 'passed';
  274. }
  275. $grade[] = !in_array(false, $setup_passed);
  276. $i++;
  277. }
  278. }
  279. }
  280. $data['grade'] = in_array(false, $grade) ? 'Failed' : 'Passed';
  281. $this->render('system/preflight', $data);
  282. }
  283. /**
  284. * A static helper for preparing
  285. *
  286. * @return void
  287. * @author Ryan Faerman
  288. */
  289. static function preflight() {
  290. $systemName = __CLASS__;
  291. $system = new $systemName();
  292. return $system->prepare();
  293. }
  294. /**
  295. * Determines if the current request is via XHR
  296. *
  297. * @return boolean
  298. * @author Ryan Faerman
  299. */
  300. function xhr() {
  301. $this->trigger('system.xhr_request');
  302. return isset($this->server->http_x_requested_with) && $this->server->http_x_requested_with == 'XMLHttpRequest';
  303. }
  304. /**
  305. * Register a callback for an event
  306. *
  307. * Registration of this type is only valid for the current request
  308. */
  309. function register($event, $callback) {
  310. $this->events[$event][] = $callback;
  311. }
  312. /**
  313. * Trigger an event and pass it arguments
  314. *
  315. * This will trigger both current request and global events
  316. *
  317. * @param string $event
  318. * @param array $params
  319. * @return void
  320. * @author Ryan Faerman
  321. */
  322. function trigger($event, $params = array()) {
  323. if(!$this->config->events->enabled) {
  324. return;
  325. }
  326. $return = array();
  327. if(array_key_exists($event, $this->events)) {
  328. foreach($this->events[$event] as $callback) {
  329. $return[] = call_user_func($callback, $params);
  330. }
  331. }
  332. if($this->config->events->global) {
  333. foreach($this->eventdb->find(array('event' => $event)) as $global_event) {
  334. #if method exist
  335. $global_event = (object) $global_event;
  336. list($class, $method) = $global_event->callback;
  337. $handler = new $class($this);
  338. $return[] = call_user_func(array(&$handler, $method), $params);
  339. }
  340. }
  341. return $return;
  342. }
  343. /**
  344. * A wrapper for the paint class, scope helps protect the system from the template
  345. */
  346. function render($template, $variables = array(), $partials = array()) {
  347. if(!$this->paint) {
  348. $this->paint = new paint($this);
  349. }
  350. $base_url = $this->config->paths->base;
  351. $variables['base_url'] = ($base_url != '') ? '/'.$base_url.'/' : '/';
  352. $event_vars = $this->trigger('system.variables');
  353. foreach($event_vars as $ev) {
  354. $variables = array_merge($variables, (array) $ev);
  355. }
  356. return $this->paint->render($template, $variables, $partials);
  357. }
  358. function partial($template, $variables = array(), $partials = array()) {
  359. if(!$this->paint) {
  360. $this->paint = new paint($this);
  361. }
  362. $base_url = $this->config->paths->base;
  363. $variables['base_url'] = ($base_url != '') ? '/'.$base_url.'/' : '/';
  364. return $this->paint->render($template, $variables, $partials, true);
  365. }
  366. /**
  367. * Simple HTTP redirection
  368. */
  369. function redirect($path) {
  370. header(sprintf('Location: //%s/%s', $this->server->server_name, $path));
  371. }
  372. }
  373. ?>