summaryrefslogtreecommitdiff
path: root/src/lib/Router.class.php
blob: 238e3f840fd5e455b580095c253526ac33e235a4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
<?php

require_once('Controller.class.php');

class Router {
	/**
	 * Array mapping URIs to controllers.
	 * A controller may register itself either by using
	 * Router::register($URI, $controller[, $function]);
	 * or by adding itself to the $ROUTER global.
	 * 
	 * The default here just gives us a 404 handler.
	 */
	private $routes = array('/*' => 'Http404');
	
	/**
	 * Instantiate a router that looks for controllers in $controllerpath.
	 */
	public function Router($controllerpath) {
		// create a $ROUTES global that can be used to set up our
		// $this->routes.
		global $ROUTES;
		$ROUTES = $this->routes;

		// Split $controllerpath into directories, and load the
		// controllers in each.
		$dirs = explode(PATH_SEPARATOR, $controllerpath);
		foreach ($dirs as $dir) {
			// Find all files in $dir with the ext `.class.php'
			$files = glob($dir.'/*.class.php');
			foreach ($files as $file) {
				// and include them
				require_once($file);
			}
		}
		
		$this->routes = $ROUTES;
		unset($ROUTES);
	}

	/**
	 * Route the page at the relative URL $page to the appropriate
	 * controller, and call the appropriate function.
	 */
	public function route($page) {
		$parts = explode('/', $page);
		$length = count($parts); // the # of segments in $controllerpart
		
		// if $page ends in "/", strip that off
		if ($parts[$length-1]=='') {
			array_pop($parts);
			$length--;
		}

		$controllerpart = implode('/', $parts);
		
		// Keep shortening $controllerpart until it matches something in
		// $this->routes.  The shortest it will ever become is '/*'.
		// If no key exists for '/*', that's an infinite loop.
		// Fortunately, the default value of $this->routes directs '/*'
		// to the Http404 controller.
		while(!isset($this->routes[$controllerpart])) {
			$some_parts = array_slice($parts, 0, $length);
			$controllerpart = implode('/', $some_parts).'/*';
			$length--;
		}
		$length++;
		
		// Figure what function to call on what controller
		// Grammar Nazi Warning: `what' or `which'?
		$controller = $this->routes[$controllerpart];
		if (strpos($controller, '->')===false) {
			// imply function
			$function = $parts[$length];
		} else {
			preg_match('/(.*)->(.*)/', $controller, $matches);
			$controller = $matches[1];
			$function = $matches[2];
		}
		
		// Default to the `index' function, provided by all controllers
		if ($function=='') {
			$function = 'index';
		}
		
		// We will pass these arrays to the function.
		$routed = array_slice($parts, 0, $length);
		$remainder = array_slice($parts, $length);
		
		// Finally, run the controller
		$obj = new $controller();
		if (in_array($function, get_class_methods($obj))) {
			call_user_func(array($obj, $function),
			               $routed, $remainder);
		} else {
			$obj->http404($routed, $remainder);
		}
	}
	
	/**
	 * This is to allow controllers to register themselves to the router.
	 * If $function=='', then the function will be determined by the segment
	 * to the right of the last segment in $path
	 */
	public static function register($path, $controller, $function='') {
		$str = $controller.(($function=='')?'':'->'.$function);
		global $ROUTES;
		$ROUTES[$path] = $str;
	}
}