router.php 3.5 KB

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