1: <?php
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: class Router {
28:
29: 30: 31: 32: 33: 34:
35: var $routes = array();
36:
37: 38: 39: 40: 41: 42: 43:
44: var $__prefixes = array();
45:
46: 47: 48: 49: 50: 51:
52: var $__parseExtensions = false;
53:
54: 55: 56: 57: 58: 59:
60: var $__validExtensions = null;
61:
62: 63: 64: 65: 66: 67:
68: var $__named = array(
69: 'Action' => 'index|show|add|create|edit|update|remove|del|delete|view|item',
70: 'Year' => '[12][0-9]{3}',
71: 'Month' => '0[1-9]|1[012]',
72: 'Day' => '0[1-9]|[12][0-9]|3[01]',
73: 'ID' => '[0-9]+',
74: 'UUID' => '[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}'
75: );
76:
77: 78: 79: 80: 81: 82:
83: var $named = array(
84: 'default' => array('page', 'fields', 'order', 'limit', 'recursive', 'sort', 'direction', 'step'),
85: 'greedy' => true,
86: 'separator' => ':',
87: 'rules' => false,
88: );
89:
90: 91: 92: 93: 94: 95:
96: var $__currentRoute = array();
97:
98: 99: 100: 101: 102: 103:
104: var $__resourceMap = array(
105: array('action' => 'index', 'method' => 'GET', 'id' => false),
106: array('action' => 'view', 'method' => 'GET', 'id' => true),
107: array('action' => 'add', 'method' => 'POST', 'id' => false),
108: array('action' => 'edit', 'method' => 'PUT', 'id' => true),
109: array('action' => 'delete', 'method' => 'DELETE', 'id' => true),
110: array('action' => 'edit', 'method' => 'POST', 'id' => true)
111: );
112:
113: 114: 115: 116: 117: 118:
119: var $__resourceMapped = array();
120:
121: 122: 123: 124: 125: 126:
127: var $__params = array();
128:
129: 130: 131: 132: 133: 134:
135: var $__paths = array();
136:
137: 138: 139: 140: 141: 142:
143: var $__defaultsMapped = false;
144:
145: 146: 147: 148: 149: 150:
151: var $__connectDefaults = true;
152:
153: 154: 155: 156: 157: 158:
159: function Router() {
160: $this->__setPrefixes();
161: }
162:
163: 164: 165: 166: 167: 168: 169: 170:
171: function __setPrefixes() {
172: $routing = Configure::read('Routing');
173: if (!empty($routing['admin'])) {
174: $this->__prefixes[] = $routing['admin'];
175: }
176: if (!empty($routing['prefixes'])) {
177: $this->__prefixes = array_merge($this->__prefixes, (array)$routing['prefixes']);
178: }
179: }
180:
181: 182: 183: 184: 185: 186: 187:
188: function &getInstance() {
189: static $instance = array();
190:
191: if (!$instance) {
192: $instance[0] =& new Router();
193: }
194: return $instance[0];
195: }
196:
197: 198: 199: 200: 201: 202: 203: 204:
205: function getNamedExpressions() {
206: $self =& Router::getInstance();
207: return $self->__named;
208: }
209:
210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 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: 257: 258: 259: 260: 261: 262:
263: function connect($route, $defaults = array(), $options = array()) {
264: $self =& Router::getInstance();
265:
266: foreach ($self->__prefixes as $prefix) {
267: if (isset($defaults[$prefix])) {
268: $defaults['prefix'] = $prefix;
269: break;
270: }
271: }
272: if (isset($defaults['prefix'])) {
273: $self->__prefixes[] = $defaults['prefix'];
274: $self->__prefixes = array_keys(array_flip($self->__prefixes));
275: }
276: $defaults += array('plugin' => null);
277: if (empty($options['action'])) {
278: $defaults += array('action' => 'index');
279: }
280: $routeClass = 'CakeRoute';
281: if (isset($options['routeClass'])) {
282: $routeClass = $options['routeClass'];
283: unset($options['routeClass']);
284: }
285:
286: $Route =& new $routeClass($route, $defaults, $options);
287: if ($routeClass !== 'CakeRoute' && !is_subclass_of($Route, 'CakeRoute')) {
288: trigger_error(__('Route classes must extend CakeRoute', true), E_USER_WARNING);
289: return false;
290: }
291: $self->routes[] =& $Route;
292: return $self->routes;
293: }
294:
295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337: 338:
339: function connectNamed($named, $options = array()) {
340: $self =& Router::getInstance();
341:
342: if (isset($options['argSeparator'])) {
343: $self->named['separator'] = $options['argSeparator'];
344: unset($options['argSeparator']);
345: }
346:
347: if ($named === true || $named === false) {
348: $options = array_merge(array('default' => $named, 'reset' => true, 'greedy' => $named), $options);
349: $named = array();
350: } else {
351: $options = array_merge(array('default' => false, 'reset' => false, 'greedy' => true), $options);
352: }
353:
354: if ($options['reset'] == true || $self->named['rules'] === false) {
355: $self->named['rules'] = array();
356: }
357:
358: if ($options['default']) {
359: $named = array_merge($named, $self->named['default']);
360: }
361:
362: foreach ($named as $key => $val) {
363: if (is_numeric($key)) {
364: $self->named['rules'][$val] = true;
365: } else {
366: $self->named['rules'][$key] = $val;
367: }
368: }
369: $self->named['greedy'] = $options['greedy'];
370: return $self->named;
371: }
372:
373: 374: 375: 376: 377: 378: 379: 380: 381: 382: 383:
384: function defaults($connect = true) {
385: $self =& Router::getInstance();
386: $self->__connectDefaults = $connect;
387: }
388:
389: 390: 391: 392: 393: 394: 395: 396: 397: 398: 399: 400: 401: 402: 403:
404: function mapResources($controller, $options = array()) {
405: $self =& Router::getInstance();
406: $options = array_merge(array('prefix' => '/', 'id' => $self->__named['ID'] . '|' . $self->__named['UUID']), $options);
407: $prefix = $options['prefix'];
408:
409: foreach ((array)$controller as $ctlName) {
410: $urlName = Inflector::underscore($ctlName);
411:
412: foreach ($self->__resourceMap as $params) {
413: extract($params);
414: $url = $prefix . $urlName . (($id) ? '/:id' : '');
415:
416: Router::connect($url,
417: array('controller' => $urlName, 'action' => $action, '[method]' => $params['method']),
418: array('id' => $options['id'], 'pass' => array('id'))
419: );
420: }
421: $self->__resourceMapped[] = $urlName;
422: }
423: }
424:
425: 426: 427: 428: 429: 430: 431:
432: function prefixes() {
433: $self =& Router::getInstance();
434: return $self->__prefixes;
435: }
436:
437: 438: 439: 440: 441: 442: 443: 444: 445:
446: function parse($url) {
447: $self =& Router::getInstance();
448: if (!$self->__defaultsMapped && $self->__connectDefaults) {
449: $self->__connectDefaultRoutes();
450: }
451: $out = array(
452: 'pass' => array(),
453: 'named' => array(),
454: );
455: $r = $ext = null;
456:
457: if (ini_get('magic_quotes_gpc') === '1') {
458: $url = stripslashes_deep($url);
459: }
460:
461: if ($url && strpos($url, '/') !== 0) {
462: $url = '/' . $url;
463: }
464: if (strpos($url, '?') !== false) {
465: $url = substr($url, 0, strpos($url, '?'));
466: }
467: extract($self->__parseExtension($url));
468:
469: for ($i = 0, $len = count($self->routes); $i < $len; $i++) {
470: $route =& $self->routes[$i];
471: if (($r = $route->parse($url)) !== false) {
472: $self->__currentRoute[] =& $route;
473:
474: $params = $route->options;
475: $argOptions = array();
476:
477: if (array_key_exists('named', $params)) {
478: $argOptions['named'] = $params['named'];
479: unset($params['named']);
480: }
481: if (array_key_exists('greedy', $params)) {
482: $argOptions['greedy'] = $params['greedy'];
483: unset($params['greedy']);
484: }
485: $out = $r;
486:
487: if (isset($out['_args_'])) {
488: $argOptions['context'] = array('action' => $out['action'], 'controller' => $out['controller']);
489: $parsedArgs = $self->getArgs($out['_args_'], $argOptions);
490: $out['pass'] = array_merge($out['pass'], $parsedArgs['pass']);
491: $out['named'] = $parsedArgs['named'];
492: unset($out['_args_']);
493: }
494:
495: if (isset($params['pass'])) {
496: $j = count($params['pass']);
497: while($j--) {
498: if (isset($out[$params['pass'][$j]])) {
499: array_unshift($out['pass'], $out[$params['pass'][$j]]);
500: }
501: }
502: }
503: break;
504: }
505: }
506:
507: if (!empty($ext) && !isset($out['url']['ext'])) {
508: $out['url']['ext'] = $ext;
509: }
510: return $out;
511: }
512:
513: 514: 515: 516: 517: 518: 519:
520: function __parseExtension($url) {
521: $ext = null;
522:
523: if ($this->__parseExtensions) {
524: if (preg_match('/\.[0-9a-zA-Z]*$/', $url, $match) === 1) {
525: $match = substr($match[0], 1);
526: if (empty($this->__validExtensions)) {
527: $url = substr($url, 0, strpos($url, '.' . $match));
528: $ext = $match;
529: } else {
530: foreach ($this->__validExtensions as $name) {
531: if (strcasecmp($name, $match) === 0) {
532: $url = substr($url, 0, strpos($url, '.' . $name));
533: $ext = $match;
534: break;
535: }
536: }
537: }
538: }
539: if (empty($ext)) {
540: $ext = 'html';
541: }
542: }
543: return compact('ext', 'url');
544: }
545:
546: 547: 548: 549: 550: 551: 552: 553: 554: 555: 556: 557: 558: 559: 560: 561: 562: 563: 564: 565: 566: 567: 568: 569: 570: 571: 572: 573: 574: 575: 576:
577: function __connectDefaultRoutes() {
578: if ($plugins = App::objects('plugin')) {
579: foreach ($plugins as $key => $value) {
580: $plugins[$key] = Inflector::underscore($value);
581: }
582: $pluginPattern = implode('|', $plugins);
583: $match = array('plugin' => $pluginPattern);
584: $shortParams = array('routeClass' => 'PluginShortRoute', 'plugin' => $pluginPattern);
585:
586: foreach ($this->__prefixes as $prefix) {
587: $params = array('prefix' => $prefix, $prefix => true);
588: $indexParams = $params + array('action' => 'index');
589: $this->connect("/{$prefix}/:plugin", $indexParams, $shortParams);
590: $this->connect("/{$prefix}/:plugin/:controller", $indexParams, $match);
591: $this->connect("/{$prefix}/:plugin/:controller/:action/*", $params, $match);
592: }
593: $this->connect('/:plugin', array('action' => 'index'), $shortParams);
594: $this->connect('/:plugin/:controller', array('action' => 'index'), $match);
595: $this->connect('/:plugin/:controller/:action/*', array(), $match);
596: }
597:
598: foreach ($this->__prefixes as $prefix) {
599: $params = array('prefix' => $prefix, $prefix => true);
600: $indexParams = $params + array('action' => 'index');
601: $this->connect("/{$prefix}/:controller", $indexParams);
602: $this->connect("/{$prefix}/:controller/:action/*", $params);
603: }
604: $this->connect('/:controller', array('action' => 'index'));
605: $this->connect('/:controller/:action/*');
606:
607: if ($this->named['rules'] === false) {
608: $this->connectNamed(true);
609: }
610: $this->__defaultsMapped = true;
611: }
612:
613: 614: 615: 616: 617: 618: 619: 620: 621: 622:
623: function setRequestInfo($params) {
624: $self =& Router::getInstance();
625: $defaults = array('plugin' => null, 'controller' => null, 'action' => null);
626: $params[0] = array_merge($defaults, (array)$params[0]);
627: $params[1] = array_merge($defaults, (array)$params[1]);
628: list($self->__params[], $self->__paths[]) = $params;
629:
630: if (count($self->__paths)) {
631: if (isset($self->__paths[0]['namedArgs'])) {
632: foreach ($self->__paths[0]['namedArgs'] as $arg => $value) {
633: $self->named['rules'][$arg] = true;
634: }
635: }
636: }
637: }
638:
639: 640: 641: 642: 643: 644: 645: 646:
647: function getParams($current = false) {
648: $self =& Router::getInstance();
649: if ($current) {
650: return $self->__params[count($self->__params) - 1];
651: }
652: if (isset($self->__params[0])) {
653: return $self->__params[0];
654: }
655: return array();
656: }
657:
658: 659: 660: 661: 662: 663: 664: 665: 666:
667: function getParam($name = 'controller', $current = false) {
668: $params = Router::getParams($current);
669: if (isset($params[$name])) {
670: return $params[$name];
671: }
672: return null;
673: }
674:
675: 676: 677: 678: 679: 680: 681: 682:
683: function getPaths($current = false) {
684: $self =& Router::getInstance();
685: if ($current) {
686: return $self->__paths[count($self->__paths) - 1];
687: }
688: if (!isset($self->__paths[0])) {
689: return array('base' => null);
690: }
691: return $self->__paths[0];
692: }
693:
694: 695: 696: 697: 698: 699: 700: 701:
702: function reload() {
703: $self =& Router::getInstance();
704: foreach (get_class_vars('Router') as $key => $val) {
705: $self->{$key} = $val;
706: }
707: $self->__setPrefixes();
708: }
709:
710: 711: 712: 713: 714: 715: 716: 717: 718:
719: function promote($which = null) {
720: $self =& Router::getInstance();
721: if ($which === null) {
722: $which = count($self->routes) - 1;
723: }
724: if (!isset($self->routes[$which])) {
725: return false;
726: }
727: $route =& $self->routes[$which];
728: unset($self->routes[$which]);
729: array_unshift($self->routes, $route);
730: return true;
731: }
732:
733: 734: 735: 736: 737: 738: 739: 740: 741: 742: 743: 744: 745: 746: 747: 748: 749: 750: 751: 752: 753: 754: 755: 756: 757: 758: 759: 760: 761: 762: 763:
764: function url($url = null, $full = false) {
765: $self =& Router::getInstance();
766: $defaults = $params = array('plugin' => null, 'controller' => null, 'action' => 'index');
767:
768: if (is_bool($full)) {
769: $escape = false;
770: } else {
771: extract($full + array('escape' => false, 'full' => false));
772: }
773:
774: if (!empty($self->__params)) {
775: if (isset($this) && !isset($this->params['requested'])) {
776: $params = $self->__params[0];
777: } else {
778: $params = end($self->__params);
779: }
780: }
781: $path = array('base' => null);
782:
783: if (!empty($self->__paths)) {
784: if (isset($this) && !isset($this->params['requested'])) {
785: $path = $self->__paths[0];
786: } else {
787: $path = end($self->__paths);
788: }
789: }
790: $base = $path['base'];
791: $extension = $output = $mapped = $q = $frag = null;
792:
793: if (is_array($url)) {
794: if (isset($url['base']) && $url['base'] === false) {
795: $base = null;
796: unset($url['base']);
797: }
798: if (isset($url['full_base']) && $url['full_base'] === true) {
799: $full = true;
800: unset($url['full_base']);
801: }
802: if (isset($url['?'])) {
803: $q = $url['?'];
804: unset($url['?']);
805: }
806: if (isset($url['#'])) {
807: $frag = '#' . urlencode($url['#']);
808: unset($url['#']);
809: }
810: if (empty($url['action'])) {
811: if (empty($url['controller']) || $params['controller'] === $url['controller']) {
812: $url['action'] = $params['action'];
813: } else {
814: $url['action'] = 'index';
815: }
816: }
817:
818: $prefixExists = (array_intersect_key($url, array_flip($self->__prefixes)));
819: foreach ($self->__prefixes as $prefix) {
820: if (!empty($params[$prefix]) && !$prefixExists) {
821: $url[$prefix] = true;
822: } elseif (isset($url[$prefix]) && !$url[$prefix]) {
823: unset($url[$prefix]);
824: }
825: if (isset($url[$prefix]) && strpos($url['action'], $prefix . '_') === 0) {
826: $url['action'] = substr($url['action'], strlen($prefix) + 1);
827: }
828: }
829:
830: $url += array('controller' => $params['controller'], 'plugin' => $params['plugin']);
831:
832: if (isset($url['ext'])) {
833: $extension = '.' . $url['ext'];
834: unset($url['ext']);
835: }
836: $match = false;
837:
838: for ($i = 0, $len = count($self->routes); $i < $len; $i++) {
839: $originalUrl = $url;
840:
841: if (isset($self->routes[$i]->options['persist'], $params)) {
842: $url = $self->routes[$i]->persistParams($url, $params);
843: }
844:
845: if ($match = $self->routes[$i]->match($url)) {
846: $output = trim($match, '/');
847: break;
848: }
849: $url = $originalUrl;
850: }
851: if ($match === false) {
852: $output = $self->_handleNoRoute($url);
853: }
854: $output = str_replace('//', '/', $base . '/' . $output);
855: } else {
856: if (((strpos($url, '://')) !== false || (strpos($url, 'javascript:') === 0) || (strpos($url, 'mailto:') === 0)) || (!strncmp($url, '#', 1))) {
857: return $url;
858: }
859: if (empty($url)) {
860: if (!isset($path['here'])) {
861: $path['here'] = '/';
862: }
863: $output = $path['here'];
864: } elseif (substr($url, 0, 1) === '/') {
865: $output = $base . $url;
866: } else {
867: $output = $base . '/';
868: foreach ($self->__prefixes as $prefix) {
869: if (isset($params[$prefix])) {
870: $output .= $prefix . '/';
871: break;
872: }
873: }
874: if (!empty($params['plugin']) && $params['plugin'] !== $params['controller']) {
875: $output .= Inflector::underscore($params['plugin']) . '/';
876: }
877: $output .= Inflector::underscore($params['controller']) . '/' . $url;
878: }
879: $output = str_replace('//', '/', $output);
880: }
881: if ($full && defined('FULL_BASE_URL')) {
882: $output = FULL_BASE_URL . $output;
883: }
884: if (!empty($extension) && substr($output, -1) === '/') {
885: $output = substr($output, 0, -1);
886: }
887:
888: return $output . $extension . $self->queryString($q, array(), $escape) . $frag;
889: }
890:
891: 892: 893: 894: 895: 896: 897: 898: 899:
900: function _handleNoRoute($url) {
901: $named = $args = array();
902: $skip = array_merge(
903: array('bare', 'action', 'controller', 'plugin', 'prefix'),
904: $this->__prefixes
905: );
906:
907: $keys = array_values(array_diff(array_keys($url), $skip));
908: $count = count($keys);
909:
910:
911: for ($i = 0; $i < $count; $i++) {
912: if (is_numeric($keys[$i])) {
913: $args[] = $url[$keys[$i]];
914: } else {
915: $named[$keys[$i]] = $url[$keys[$i]];
916: }
917: }
918:
919: list($args, $named) = array(Set::filter($args, true), Set::filter($named, true));
920: foreach ($this->__prefixes as $prefix) {
921: if (!empty($url[$prefix])) {
922: $url['action'] = str_replace($prefix . '_', '', $url['action']);
923: break;
924: }
925: }
926:
927: if (empty($named) && empty($args) && (!isset($url['action']) || $url['action'] === 'index')) {
928: $url['action'] = null;
929: }
930:
931: $urlOut = array_filter(array($url['controller'], $url['action']));
932:
933: if (isset($url['plugin'])) {
934: array_unshift($urlOut, $url['plugin']);
935: }
936:
937: foreach ($this->__prefixes as $prefix) {
938: if (isset($url[$prefix])) {
939: array_unshift($urlOut, $prefix);
940: break;
941: }
942: }
943: $output = implode('/', $urlOut);
944:
945: if (!empty($args)) {
946: $output .= '/' . implode('/', $args);
947: }
948:
949: if (!empty($named)) {
950: foreach ($named as $name => $value) {
951: if (is_array($value)) {
952: $flattend = Set::flatten($value, '][');
953: foreach ($flattend as $namedKey => $namedValue) {
954: $output .= '/' . $name . "[$namedKey]" . $this->named['separator'] . $namedValue;
955: }
956: } else {
957: $output .= '/' . $name . $this->named['separator'] . $value;
958: }
959: }
960: }
961: return $output;
962: }
963:
964: 965: 966: 967: 968: 969: 970: 971: 972: 973:
974: function getNamedElements($params, $controller = null, $action = null) {
975: $self =& Router::getInstance();
976: $named = array();
977:
978: foreach ($params as $param => $val) {
979: if (isset($self->named['rules'][$param])) {
980: $rule = $self->named['rules'][$param];
981: if (Router::matchNamed($param, $val, $rule, compact('controller', 'action'))) {
982: $named[$param] = $val;
983: unset($params[$param]);
984: }
985: }
986: }
987: return array($named, $params);
988: }
989:
990: 991: 992: 993: 994: 995: 996: 997: 998: 999: 1000: 1001:
1002: function matchNamed($param, $val, $rule, $context = array()) {
1003: if ($rule === true || $rule === false) {
1004: return $rule;
1005: }
1006: if (is_string($rule)) {
1007: $rule = array('match' => $rule);
1008: }
1009: if (!is_array($rule)) {
1010: return false;
1011: }
1012:
1013: $controllerMatches = !isset($rule['controller'], $context['controller']) || in_array($context['controller'], (array)$rule['controller']);
1014: if (!$controllerMatches) {
1015: return false;
1016: }
1017: $actionMatches = !isset($rule['action'], $context['action']) || in_array($context['action'], (array)$rule['action']);
1018: if (!$actionMatches) {
1019: return false;
1020: }
1021: return (!isset($rule['match']) || preg_match('/' . $rule['match'] . '/', $val));
1022: }
1023:
1024: 1025: 1026: 1027: 1028: 1029: 1030: 1031: 1032: 1033:
1034: function queryString($q, $extra = array(), $escape = false) {
1035: if (empty($q) && empty($extra)) {
1036: return null;
1037: }
1038: $join = '&';
1039: if ($escape === true) {
1040: $join = '&';
1041: }
1042: $out = '';
1043:
1044: if (is_array($q)) {
1045: $q = array_merge($extra, $q);
1046: } else {
1047: $out = $q;
1048: $q = $extra;
1049: }
1050: $out .= http_build_query($q, null, $join);
1051: if (isset($out[0]) && $out[0] != '?') {
1052: $out = '?' . $out;
1053: }
1054: return $out;
1055: }
1056:
1057: 1058: 1059: 1060: 1061: 1062: 1063: 1064: 1065: 1066: 1067: 1068: 1069:
1070: function reverse($params) {
1071: $pass = $params['pass'];
1072: $named = $params['named'];
1073: if (isset($params['url'])) {
1074: $url = $params['url'];
1075: } else {
1076: $url = null;
1077: }
1078: unset(
1079: $params['pass'], $params['named'], $params['paging'], $params['models'], $params['url'], $url['url'],
1080: $params['autoRender'], $params['bare'], $params['requested'], $params['return']
1081: );
1082: $params = array_merge($params, $pass, $named);
1083: if (!empty($url)) {
1084: $params['?'] = $url;
1085: }
1086: return Router::url($params);
1087: }
1088:
1089: 1090: 1091: 1092: 1093: 1094: 1095: 1096: 1097: 1098:
1099: function normalize($url = '/') {
1100: if (is_array($url)) {
1101: $url = Router::url($url);
1102: } elseif (preg_match('/^[a-z\-]+:\/\//', $url)) {
1103: return $url;
1104: }
1105: $paths = Router::getPaths();
1106:
1107: if (!empty($paths['base']) && stristr($url, $paths['base'])) {
1108: $url = preg_replace('/^' . preg_quote($paths['base'], '/') . '/', '', $url, 1);
1109: }
1110: $url = '/' . $url;
1111:
1112: while (strpos($url, '//') !== false) {
1113: $url = str_replace('//', '/', $url);
1114: }
1115: $url = preg_replace('/(?:(\/$))/', '', $url);
1116:
1117: if (empty($url)) {
1118: return '/';
1119: }
1120: return $url;
1121: }
1122:
1123: 1124: 1125: 1126: 1127: 1128: 1129:
1130: function &requestRoute() {
1131: $self =& Router::getInstance();
1132: return $self->__currentRoute[0];
1133: }
1134:
1135: 1136: 1137: 1138: 1139: 1140: 1141:
1142: function ¤tRoute() {
1143: $self =& Router::getInstance();
1144: return $self->__currentRoute[count($self->__currentRoute) - 1];
1145: }
1146:
1147: 1148: 1149: 1150: 1151: 1152: 1153: 1154: 1155:
1156: function stripPlugin($base, $plugin = null) {
1157: if ($plugin != null) {
1158: $base = preg_replace('/(?:' . $plugin . ')/', '', $base);
1159: $base = str_replace('//', '', $base);
1160: $pos1 = strrpos($base, '/');
1161: $char = strlen($base) - 1;
1162:
1163: if ($pos1 === $char) {
1164: $base = substr($base, 0, $char);
1165: }
1166: }
1167: return $base;
1168: }
1169:
1170: 1171: 1172: 1173: 1174: 1175: 1176: 1177: 1178: 1179: 1180: 1181: 1182: 1183: 1184: 1185:
1186: function parseExtensions() {
1187: $self =& Router::getInstance();
1188: $self->__parseExtensions = true;
1189: if (func_num_args() > 0) {
1190: $self->__validExtensions = func_get_args();
1191: }
1192: }
1193:
1194: 1195: 1196: 1197: 1198: 1199: 1200: 1201:
1202: function getArgs($args, $options = array()) {
1203: $self =& Router::getInstance();
1204: $pass = $named = array();
1205: $args = explode('/', $args);
1206:
1207: $greedy = isset($options['greedy']) ? $options['greedy'] : $self->named['greedy'];
1208: $context = array();
1209: if (isset($options['context'])) {
1210: $context = $options['context'];
1211: }
1212: $rules = $self->named['rules'];
1213: if (isset($options['named'])) {
1214: $greedy = isset($options['greedy']) && $options['greedy'] === true;
1215: foreach ((array)$options['named'] as $key => $val) {
1216: if (is_numeric($key)) {
1217: $rules[$val] = true;
1218: continue;
1219: }
1220: $rules[$key] = $val;
1221: }
1222: }
1223:
1224: foreach ($args as $param) {
1225: if (empty($param) && $param !== '0' && $param !== 0) {
1226: continue;
1227: }
1228:
1229: $separatorIsPresent = strpos($param, $self->named['separator']) !== false;
1230: if ((!isset($options['named']) || !empty($options['named'])) && $separatorIsPresent) {
1231: list($key, $val) = explode($self->named['separator'], $param, 2);
1232: $hasRule = isset($rules[$key]);
1233: $passIt = (!$hasRule && !$greedy) || ($hasRule && !$self->matchNamed($key, $val, $rules[$key], $context));
1234: if ($passIt) {
1235: $pass[] = $param;
1236: } else {
1237: $named[$key] = $val;
1238: }
1239: } else {
1240: $pass[] = $param;
1241: }
1242: }
1243: return compact('pass', 'named');
1244: }
1245: }
1246:
1247: 1248: 1249: 1250: 1251: 1252: 1253: 1254: 1255: 1256: 1257: 1258:
1259: class CakeRoute {
1260:
1261: 1262: 1263: 1264: 1265: 1266: 1267:
1268: var $keys = array();
1269:
1270: 1271: 1272: 1273: 1274: 1275:
1276: var $options = array();
1277:
1278: 1279: 1280: 1281: 1282: 1283:
1284: var $defaults = array();
1285:
1286: 1287: 1288: 1289: 1290: 1291:
1292: var $template = null;
1293:
1294: 1295: 1296: 1297: 1298: 1299: 1300:
1301: var $_greedy = false;
1302:
1303: 1304: 1305: 1306: 1307: 1308:
1309: var $_compiledRoute = null;
1310:
1311: 1312: 1313: 1314: 1315: 1316:
1317: var $__headerMap = array(
1318: 'type' => 'content_type',
1319: 'method' => 'request_method',
1320: 'server' => 'server_name'
1321: );
1322:
1323: 1324: 1325: 1326: 1327: 1328: 1329: 1330: 1331:
1332: function CakeRoute($template, $defaults = array(), $options = array()) {
1333: $this->template = $template;
1334: $this->defaults = (array)$defaults;
1335: $this->options = (array)$options;
1336: }
1337:
1338: 1339: 1340: 1341: 1342: 1343:
1344: function compiled() {
1345: return !empty($this->_compiledRoute);
1346: }
1347:
1348: 1349: 1350: 1351: 1352: 1353: 1354:
1355: function compile() {
1356: if ($this->compiled()) {
1357: return $this->_compiledRoute;
1358: }
1359: $this->_writeRoute();
1360: return $this->_compiledRoute;
1361: }
1362:
1363: 1364: 1365: 1366: 1367: 1368: 1369:
1370: function _writeRoute() {
1371: if (empty($this->template) || ($this->template === '/')) {
1372: $this->_compiledRoute = '#^/*$#';
1373: $this->keys = array();
1374: return;
1375: }
1376: $route = $this->template;
1377: $names = $routeParams = array();
1378: $parsed = preg_quote($this->template, '#');
1379: $parsed = str_replace('\\-', '-', $parsed);
1380:
1381: preg_match_all('#:([A-Za-z0-9_-]+[A-Z0-9a-z])#', $parsed, $namedElements);
1382: foreach ($namedElements[1] as $i => $name) {
1383: $search = '\\' . $namedElements[0][$i];
1384: if (isset($this->options[$name])) {
1385: $option = null;
1386: if ($name !== 'plugin' && array_key_exists($name, $this->defaults)) {
1387: $option = '?';
1388: }
1389: $slashParam = '/\\' . $namedElements[0][$i];
1390: if (strpos($parsed, $slashParam) !== false) {
1391: $routeParams[$slashParam] = '(?:/(' . $this->options[$name] . ')' . $option . ')' . $option;
1392: } else {
1393: $routeParams[$search] = '(?:(' . $this->options[$name] . ')' . $option . ')' . $option;
1394: }
1395: } else {
1396: $routeParams[$search] = '(?:([^/]+))';
1397: }
1398: $names[] = $name;
1399: }
1400: if (preg_match('#\/\*$#', $route, $m)) {
1401: $parsed = preg_replace('#/\\\\\*$#', '(?:/(?P<_args_>.*))?', $parsed);
1402: $this->_greedy = true;
1403: }
1404: krsort($routeParams);
1405: $parsed = str_replace(array_keys($routeParams), array_values($routeParams), $parsed);
1406: $this->_compiledRoute = '#^' . $parsed . '[/]*$#';
1407: $this->keys = $names;
1408: }
1409:
1410: 1411: 1412: 1413: 1414: 1415: 1416: 1417: 1418:
1419: function parse($url) {
1420: if (!$this->compiled()) {
1421: $this->compile();
1422: }
1423: if (!preg_match($this->_compiledRoute, $url, $parsed)) {
1424: return false;
1425: } else {
1426: foreach ($this->defaults as $key => $val) {
1427: if ($key[0] === '[' && preg_match('/^\[(\w+)\]$/', $key, $header)) {
1428: if (isset($this->__headerMap[$header[1]])) {
1429: $header = $this->__headerMap[$header[1]];
1430: } else {
1431: $header = 'http_' . $header[1];
1432: }
1433:
1434: $val = (array)$val;
1435: $h = false;
1436:
1437: foreach ($val as $v) {
1438: if (env(strtoupper($header)) === $v) {
1439: $h = true;
1440: }
1441: }
1442: if (!$h) {
1443: return false;
1444: }
1445: }
1446: }
1447: array_shift($parsed);
1448: $route = array();
1449: foreach ($this->keys as $i => $key) {
1450: if (isset($parsed[$i])) {
1451: $route[$key] = $parsed[$i];
1452: }
1453: }
1454: $route['pass'] = $route['named'] = array();
1455: $route += $this->defaults;
1456: if (isset($parsed['_args_'])) {
1457: $route['_args_'] = $parsed['_args_'];
1458: }
1459: foreach ($route as $key => $value) {
1460: if (is_integer($key)) {
1461: $route['pass'][] = $value;
1462: unset($route[$key]);
1463: }
1464: }
1465: return $route;
1466: }
1467: }
1468:
1469: 1470: 1471: 1472: 1473: 1474: 1475: 1476: 1477: 1478:
1479: function persistParams($url, $params) {
1480: foreach ($this->options['persist'] as $persistKey) {
1481: if (array_key_exists($persistKey, $params) && !isset($url[$persistKey])) {
1482: $url[$persistKey] = $params[$persistKey];
1483: }
1484: }
1485: return $url;
1486: }
1487:
1488: 1489: 1490: 1491: 1492: 1493: 1494: 1495: 1496:
1497: function match($url) {
1498: if (!$this->compiled()) {
1499: $this->compile();
1500: }
1501: $defaults = $this->defaults;
1502:
1503: if (isset($defaults['prefix'])) {
1504: $url['prefix'] = $defaults['prefix'];
1505: }
1506:
1507:
1508: $keyNames = array_flip($this->keys);
1509: if (array_intersect_key($keyNames, $url) != $keyNames) {
1510: return false;
1511: }
1512:
1513: $diffUnfiltered = Set::diff($url, $defaults);
1514: $diff = array();
1515:
1516: foreach ($diffUnfiltered as $key => $var) {
1517: if ($var === 0 || $var === '0' || !empty($var)) {
1518: $diff[$key] = $var;
1519: }
1520: }
1521:
1522:
1523: if (!$this->_greedy && array_diff_key($diff, $keyNames) != array()) {
1524: return false;
1525: }
1526:
1527:
1528: foreach ($this->keys as $key) {
1529: unset($defaults[$key]);
1530: }
1531: $filteredDefaults = array_filter($defaults);
1532:
1533:
1534: if (array_intersect_key($filteredDefaults, $diffUnfiltered) !== array()) {
1535: return false;
1536: }
1537:
1538: $passedArgsAndParams = array_diff_key($diff, $filteredDefaults, $keyNames);
1539: list($named, $params) = Router::getNamedElements($passedArgsAndParams, $url['controller'], $url['action']);
1540:
1541:
1542: $pass = array();
1543: $i = 0;
1544: while (isset($url[$i])) {
1545: if (!isset($diff[$i])) {
1546: $i++;
1547: continue;
1548: }
1549: $pass[] = $url[$i];
1550: unset($url[$i], $params[$i]);
1551: $i++;
1552: }
1553:
1554:
1555: if (!empty($params)) {
1556: return false;
1557: }
1558:
1559:
1560: if (!empty($this->options)) {
1561: foreach ($this->options as $key => $pattern) {
1562: if (array_key_exists($key, $url) && !preg_match('#^' . $pattern . '$#', $url[$key])) {
1563: return false;
1564: }
1565: }
1566: }
1567: return $this->_writeUrl(array_merge($url, compact('pass', 'named')));
1568: }
1569:
1570: 1571: 1572: 1573: 1574: 1575: 1576: 1577:
1578: function _writeUrl($params) {
1579: if (isset($params['prefix'], $params['action'])) {
1580: $params['action'] = str_replace($params['prefix'] . '_', '', $params['action']);
1581: unset($params['prefix']);
1582: }
1583:
1584: if (is_array($params['pass'])) {
1585: $params['pass'] = implode('/', $params['pass']);
1586: }
1587:
1588: $instance =& Router::getInstance();
1589: $separator = $instance->named['separator'];
1590:
1591: if (!empty($params['named']) && is_array($params['named'])) {
1592: $named = array();
1593: foreach ($params['named'] as $key => $value) {
1594: $named[] = $key . $separator . $value;
1595: }
1596: $params['pass'] = $params['pass'] . '/' . implode('/', $named);
1597: }
1598: $out = $this->template;
1599:
1600: $search = $replace = array();
1601: foreach ($this->keys as $key) {
1602: $string = null;
1603: if (isset($params[$key])) {
1604: $string = $params[$key];
1605: } elseif (strpos($out, $key) != strlen($out) - strlen($key)) {
1606: $key .= '/';
1607: }
1608: $search[] = ':' . $key;
1609: $replace[] = $string;
1610: }
1611: $out = str_replace($search, $replace, $out);
1612:
1613: if (strpos($this->template, '*')) {
1614: $out = str_replace('*', $params['pass'], $out);
1615: }
1616: $out = str_replace('//', '/', $out);
1617: return $out;
1618: }
1619: }
1620:
1621: 1622: 1623: 1624: 1625: 1626: 1627:
1628: class PluginShortRoute extends CakeRoute {
1629:
1630: 1631: 1632: 1633: 1634: 1635: 1636:
1637: function parse($url) {
1638: $params = parent::parse($url);
1639: if (!$params) {
1640: return false;
1641: }
1642: $params['controller'] = $params['plugin'];
1643: return $params;
1644: }
1645:
1646: 1647: 1648: 1649: 1650: 1651: 1652:
1653: function match($url) {
1654: if (isset($url['controller']) && isset($url['plugin']) && $url['plugin'] != $url['controller']) {
1655: return false;
1656: }
1657: $this->defaults['controller'] = $url['controller'];
1658: $result = parent::match($url);
1659: unset($this->defaults['controller']);
1660: return $result;
1661: }
1662: }
1663: