router.php 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. <?php
  2. /**
  3. * Functions for URL routing: given an URL, call the corresponding handler
  4. * function (the 'route' to the corresponding output).
  5. *
  6. * @author Taddeus Kroes
  7. * @version 1.0
  8. * @date 14-07-2012
  9. */
  10. namespace WebBasics;
  11. require_once 'base.php';
  12. /**
  13. * A Router is used to call a handler function with corresponding to an URL.
  14. *
  15. * Simple example: a website with the pages 'home' and 'contact'.
  16. * <code>
  17. * function home() {
  18. * return 'This is the home page.';
  19. * }
  20. *
  21. * function contact() {
  22. * return 'This is the contact page.';
  23. * }
  24. *
  25. * $router = new Router(array(
  26. * '/home' => 'home',
  27. * '/contact' => 'contact'
  28. * ));
  29. * $response = $router->call_handler('/home'); // 'This is the home page.'
  30. * $response = $router->call_handler('/contact'); // 'This is the contact page.'
  31. * </code>
  32. *
  33. * You can use regular expression patterns to specify an URL. Any matches are
  34. * passed to the handler function as parameters:
  35. * <code>
  36. * function page($pagename) {
  37. * return "This is the $pagename page.";
  38. * }
  39. *
  40. * $router = new Router(array(
  41. * '/(home|contact)' => 'page'
  42. * ));
  43. * $response = $router->call_handler('/home'); // 'This is the home page.'
  44. * $response = $router->call_handler('/contact'); // 'This is the contact page.'
  45. * </code>
  46. *
  47. * @package WebBasics
  48. */
  49. class Router extends Base {
  50. /**
  51. * The regex delimiter that is added to the begin and end of patterns.
  52. *
  53. * @var string
  54. */
  55. const DELIMITER = '%';
  56. /**
  57. * An associative array of regex patterns pointing to handler functions.
  58. *
  59. * @var array
  60. */
  61. private $routes = array();
  62. /**
  63. * Create a new Router instance.
  64. *
  65. * @param array $routes An initial list of routes to set.
  66. */
  67. function __construct(array $routes=array()) {
  68. foreach( $routes as $pattern => $handler )
  69. $this->add_route($pattern, $handler);
  70. }
  71. /**
  72. * Add a route as a (pattern, handler) pair.
  73. *
  74. * The pattern is regular expression pattern without delimiters. The
  75. * function adds '%^' at the begin and '$%' at the end of the pattern as
  76. * delimiters.
  77. *
  78. * The handler function must receive no arguments if the regex pattern
  79. * does not contain groups (which are contained in parentheses). If there
  80. * are groups, the matches for these are passed to the handler fucntion in
  81. * an array.
  82. *
  83. * @param string $pattern A regex pattern to mach URL's against.
  84. * @param mixed $handler The handler function to call when $pattern is matched.
  85. * @throws \InvalidArgumentException If $handler is not callable.
  86. */
  87. function add_route($pattern, $handler) {
  88. if( !is_callable($handler) )
  89. throw new \InvalidArgumentException(sprintf('Handler for patterns "%s" is not callable.', $pattern));
  90. $this->routes[self::DELIMITER.'^'.$pattern.'$'.self::DELIMITER] = $handler;
  91. }
  92. /**
  93. * Call the handler function corresponding to the specified url.
  94. *
  95. * If any groups are in the matched regex pattern, a list of matches is
  96. * passed to the handler function. If the handler function returns FALSE,
  97. * the url has not been 'handled' and the next pattern will be checked for
  98. * a match. Otherwise, the return value of the handler function is
  99. * returned as the result.
  100. *
  101. * @param string $url An url to match the saved patterns against.
  102. * @return mixed FALSE if no pattern was matched, the return value of the
  103. * corresponding handler function otherwise.
  104. */
  105. function call_handler($url) {
  106. foreach( $this->routes as $pattern => $handler ) {
  107. if( preg_match($pattern, $url, $matches) ) {
  108. array_shift($matches);
  109. $result = call_user_func_array($handler, $matches);
  110. if( $result !== false )
  111. return $result;
  112. }
  113. }
  114. return false;
  115. }
  116. }
  117. ?>