1: <?php
2: namespace Sleepy;
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: class Router {
38: 39: 40: 41: 42: 43:
44: private static $_routes = array();
45:
46: 47: 48: 49: 50:
51: public static $routeFound = false;
52:
53: 54: 55: 56: 57:
58: public static $delimiter = '/';
59:
60: 61: 62: 63: 64:
65: public static $querystring = false;
66:
67: 68: 69: 70: 71:
72: public static $parameters = array();
73:
74: 75: 76: 77: 78: 79:
80: public static function getArray($string) {
81: if (substr($string, strlen($string) - 1, 1) == self::$delimiter) {
82: $string = substr($string, 0, strlen($string) - 1);
83: }
84:
85: if (substr($string, 0, 1) != self::$delimiter) {
86: $string = self::$delimiter . $string;
87: }
88:
89: return explode(self::$delimiter, $string);;
90: }
91:
92: 93: 94: 95: 96: 97: 98:
99: public static function route($pattern, $func) {
100: if (is_array($pattern)) {
101: $route = new _Route(md5($pattern[0]));
102: } else {
103: $route = new _Route(md5($pattern));
104: }
105:
106: array_push(self::$_routes, $route);
107: $route->add($pattern, $func);
108: return $route;
109: }
110:
111: 112: 113: 114: 115: 116: 117:
118: public static function mvc($pattern, $defaults = array()) {
119: self::route($pattern, function ($route) use ($defaults) {
120:
121: $defaults['controller'] = (array_key_exists('controller', $defaults)) ? $defaults['controller'] : 'home';
122: $defaults['action'] = (array_key_exists('action', $defaults)) ? $defaults['action'] : 'index';
123: $defaults['id'] = (array_key_exists('id', $defaults)) ? $defaults['id'] : '';
124:
125: if (!is_array($route->params)) $route->params = array();
126:
127:
128: $controller = (array_key_exists('controller', $route->params)) ? $route->params['controller'] : $defaults['controller'];
129: $action = (array_key_exists('action', $route->params)) ? $route->params['action'] : $defaults['action'];
130: $id = (array_key_exists('id', $route->params)) ? $route->params['id'] : $defaults['id'];
131:
132:
133: $route->params = array_merge($defaults, $route->params);
134:
135: $controller_file = $_SERVER['DOCUMENT_ROOT'] . '/app/controllers/';
136: $controller_file .= strtolower($controller) . '.php';
137:
138:
139: $controller = strtolower($controller);
140: $controller = str_replace('-', '', $controller);
141: $action = str_replace('-', '_', $action);
142:
143:
144: if (file_exists($controller_file)) {
145: require_once($controller_file);
146: if (class_exists($controller)) {
147: $c = new $controller;
148: if (method_exists($c, $action)) {
149: $c->$action($route);
150: } else {
151: throw new RouteNotFound("Router: Action ($action) does not exist in Controller ($controller).");
152: }
153: } else {
154: throw new RouteNotFound("Router: Controller ($controller) does not exist.");
155: }
156: } else {
157: throw new RouteNotFound("Router: Controller File ($controller_file) does not exist.");
158: }
159: });
160: }
161:
162: 163: 164: 165: 166: 167: 168:
169: public static function redirect($controller, $action='index', $params='') {
170: $route = new _Route(md5("{{ $controller }}/{{ $action }}/{{ id }}/*"));
171: $route->params = $params;
172:
173: $controller_file = $_SERVER['DOCUMENT_ROOT'] . '/app/controllers/';
174: $controller_file .= strtolower($controller) . '.php';
175:
176:
177: $controller = strtolower($controller);
178: $controller = str_replace('-', '', $controller);
179: $action = str_replace('-', '_', $action);
180:
181:
182: if (file_exists($controller_file)) {
183: require_once($controller_file);
184: if (class_exists($controller)) {
185: $c = new $controller;
186: if (method_exists($c, $action)) {
187: $c->$action($route);
188: } else {
189: throw new \Exception("Router: Action ($action) does not exist.");
190: }
191: } else {
192: throw new \Exception("Router: Controller ($controller) does not exist.");
193: }
194: } else {
195: throw new \Exception("Router: Controller File ($controller_file) does not exist.");
196: }
197: }
198:
199: 200: 201: 202: 203:
204: public static function start($currentPath='') {
205: self::$routeFound = false;
206:
207: if ($currentPath == '') {
208: $currentPath = $_SERVER['REQUEST_URI'];
209: }
210:
211: if (self::$querystring) {
212: $currentPath = str_replace('/?q=', '', $currentPath);
213: }
214:
215:
216: self::$parameters = self::getArray($currentPath);
217:
218: foreach (self::$_routes as $route) {
219: $route->method = $_SERVER['REQUEST_METHOD'];
220: $route->execute();
221: }
222:
223: if (!self::$routeFound) {
224: throw new RouteNotFound('Router: Route not found.');
225: }
226:
227: return self::$routeFound;
228: }
229:
230: public static function reset() {
231: self::$_routes = array();
232: }
233: }
234:
235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255:
256: class _Route {
257: 258: 259: 260: 261:
262: private $_functions = array();
263:
264: 265: 266: 267: 268:
269: public $name;
270:
271: 272: 273: 274: 275:
276: public $params;
277:
278: 279: 280: 281: 282:
283: public $method;
284:
285: 286: 287: 288: 289:
290: public $splat;
291:
292: 293: 294: 295: 296: 297:
298: private function _cleanPlaceholder($placeholder) {
299: $key = str_replace('{{', '', $placeholder);
300: $key = str_replace('}}', '', $key);
301: return trim($key);
302: }
303:
304: 305: 306: 307: 308: 309:
310: private function _isPlaceholder($string) {
311: return substr($string, 0,2) == '{{' && substr($string, strlen($string) - 2, 2) == '}}';
312: }
313:
314: 315: 316: 317: 318: 319: 320: 321:
322: private function _runFilters($key, $string) {
323: if (class_exists('\Sleepy\Hook')) {
324: $string = \Sleepy\Hook::addFilter('route_parameters', $string);
325: $string = \Sleepy\Hook::addFilter('route_parameter_' . $key, $string);
326: $string = \Sleepy\Hook::addFilter('route_' . $this->name . '_parameters', $string);
327: $string = \Sleepy\Hook::addFilter('route_' . $this->name . '_parameter_' . $key, $string);
328: }
329:
330: return $string;
331: }
332:
333: 334: 335: 336: 337: 338:
339: private function _hasWildcard($pattern) {
340: if (strlen($pattern) == 0) {
341: return false;
342: } else {
343: return strpos($pattern, '*') !== false;
344: }
345: }
346:
347: 348: 349: 350: 351: 352: 353: 354:
355: private function _storeVariable($key, $value) {
356: if ($value == '') {
357: return false;
358: }
359:
360: $key = $this->_cleanPlaceholder($key);
361: $value = $this->_runFilters($key, $value);
362:
363:
364: if (isset($this->params[$key])) {
365: if ($value != $this->params[$key]) {
366: return false;
367: }
368: }
369:
370: $this->params[$key] = $value;
371: return true;
372: }
373:
374: 375: 376: 377: 378:
379: public function __construct($name='') {
380: $this->name = $name;
381: }
382:
383: 384: 385: 386: 387: 388:
389: public function add($pattern, $func) {
390:
391: if (is_array($pattern)) {
392: foreach ($pattern as $p) {
393: $this->add($p, $func);
394: }
395: } else {
396: array_push($this->_functions, array($pattern, $func));
397: }
398:
399: return $this;
400: }
401:
402: 403: 404:
405: public function execute() {
406: $noMatch = false;
407:
408:
409: if (count($this->_functions) < 1) {
410: return;
411: }
412:
413:
414: $r = array_shift($this->_functions);
415: $rawPattern = $r[0];
416: $func = $r[1];
417:
418: if (Router::$routeFound) {
419: $noMatch = true;
420: } else {
421:
422: $pattern = Router::getArray($rawPattern);
423:
424:
425: if (count(Router::$parameters) == count($pattern) || $this->_hasWildcard($rawPattern)) {
426:
427: foreach ($pattern as $idx => $value) {
428:
429: if ($this->_isPlaceholder($value)) {
430: if (!$this->_storeVariable($value, @Router::$parameters[$idx])) {
431: $noMatch = true;
432: break;
433: }
434:
435: continue;
436: }
437:
438:
439: if ($value == '*') {
440: $this->splat = implode(Router::$delimiter, array_slice(Router::$parameters, $idx));
441: break;
442: }
443:
444:
445: if (!isset(Router::$parameters[$idx]) || $value != Router::$parameters[$idx]) {
446: $noMatch = true;
447: break;
448: }
449: }
450: } else {
451: $noMatch = true;
452: }
453: }
454:
455: if ($noMatch) {
456: if (class_exists('\Sleepy\Hook')) {
457: \Sleepy\Hook::addAction('route_failed');
458: \Sleepy\Hook::addAction('route_failed_' . $this->name);
459: }
460: } else {
461:
462: if (class_exists('\Sleepy\Hook')) {
463: \Sleepy\Hook::addAction('route_start');
464: \Sleepy\Hook::addAction('route_start_' . $this->name);
465: }
466:
467: $this->pattern = $rawPattern;
468: Router::$routeFound = true;
469: $func($this);
470:
471:
472: if (class_exists('\Sleepy\Hook')) {
473: \Sleepy\Hook::addAction('route_end');
474: \Sleepy\Hook::addAction('route_end_' . $this->name);
475: }
476: }
477:
478:
479: $this->execute();
480: }
481: }
482:
483: 484: 485:
486: class RouteNotFound extends \Exception {}
487: