PageRenderTime 57ms CodeModel.GetById 22ms app.highlight 16ms RepoModel.GetById 14ms app.codeStats 0ms

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