55class Router
66{
77 private static array $ routes = [];
8+ private static array $ constraints = [];
89 private static $ pathNotFound ;
910 private static $ methodNotAllowed ;
11+ private static string $ defaultConstraint = '([\w\-]+) ' ;
1012
1113 /**
1214 * A quick static function to register a route in the router. Used by the shorthand methods as well.
15+ *
1316 * @param string $route The path to be used as the route.
1417 * @param callable|string $action Either a callable to be executed, or a string reference to a method.
1518 * @param string|array $methods The HTTP verb(s) this route accepts.
19+ * @return Router
1620 */
1721 public static function add (string $ route , callable |string $ action , string |array $ methods = 'GET ' )
1822 {
@@ -21,43 +25,136 @@ public static function add(string $route, callable|string $action, string|array
2125 'action ' => $ action ,
2226 'methods ' => $ methods
2327 ];
28+
29+ return new self ;
2430 }
2531
32+ /**
33+ * Shorthand function to define a GET route
34+ *
35+ * @param string $route
36+ * @param callable $action
37+ */
2638 public static function get (string $ route , callable $ action )
2739 {
28- self ::add ($ route , $ action , 'GET ' );
40+ return self ::add ($ route , $ action , 'GET ' );
2941 }
3042
43+ /**
44+ * Default function to define a POST route
45+ *
46+ * @param string $route
47+ * @param callable $action
48+ */
3149 public static function post (string $ route , callable $ action )
3250 {
33- self ::add ($ route , $ action , 'POST ' );
51+ return self ::add ($ route , $ action , 'POST ' );
3452 }
3553
54+ /**
55+ * Return all routes currently registered
56+ *
57+ * @return array
58+ */
3659 public static function getAllRoutes ()
3760 {
3861 return self ::$ routes ;
3962 }
4063
64+ /**
65+ * Defines an action to be called when a path isn't found - i.e. a 404
66+ *
67+ * @param callable $action
68+ */
4169 public static function pathNotFound (callable $ action )
4270 {
4371 self ::$ pathNotFound = $ action ;
4472 }
4573
74+ /**
75+ * Defines an action to be called with a method isn't allowed on a route - i.e. a 405
76+ *
77+ * @param callable $action
78+ */
4679 public static function methodNotAllowed (callable $ action )
4780 {
4881 self ::$ methodNotAllowed = $ action ;
4982 }
5083
84+ /**
85+ * Redefine the default constraint for route parameters. Default is '([\w\-]+)'
86+ *
87+ * @param string $constraint The RegEx you want parameters to adhere to by default. Defaults to '([\w\-]+)'
88+ * @return void
89+ */
90+ public static function setDefaultConstraint (string $ constraint = '([\w\-]+) ' )
91+ {
92+ self ::$ defaultConstraint = $ constraint ;
93+ }
94+
95+ /**
96+ * Define a constraint for a route parameter. If only passing one parameter,
97+ * provide the parameter name as first argument and constraint as second. If
98+ * adding constraints for multiple parameters, pass an array of 'parameter' => 'constraint'
99+ * pairs.
100+ *
101+ * @param string|array $parameter
102+ * @param string $constraint
103+ * @return Router
104+ */
105+ public static function with (string |array $ parameter , string $ constraint = '' )
106+ {
107+ if (is_array ($ parameter )) {
108+ foreach ($ parameter as $ param => $ constraint ) {
109+ self ::$ constraints [$ param ] = $ constraint ;
110+ }
111+
112+ return new self ;
113+ }
114+
115+ self ::$ constraints [$ parameter ] = $ constraint ;
116+
117+ return new self ;
118+ }
119+
120+ /**
121+ * Tokenizes the given URI using our constraint rules and returns the tokenized URI
122+ *
123+ * @param string $uri
124+ * @return string
125+ */
51126 private static function tokenize (string $ uri )
52127 {
53- return preg_replace ('/(?:{([A-Za-z]+)})+/ ' , '([\w]+) ' , $ uri );
128+ $ constraints = array_keys (self ::$ constraints );
129+
130+ preg_match_all ('/(?:{([\w\-]+)})+/ ' , $ uri , $ matches );
131+ $ matches = $ matches [1 ];
132+
133+ foreach ($ matches as $ match ) {
134+ $ pattern = "/(?:{ " .$ match ."})+/ " ;
135+
136+ if (in_array ($ match , $ constraints )) {
137+ $ uri = preg_replace ($ pattern , '( ' .self ::$ constraints [$ match ].') ' , $ uri );
138+ } else {
139+ $ uri = preg_replace ($ pattern , self ::$ defaultConstraint , $ uri );
140+ }
141+ }
142+
143+ return $ uri ;
54144 }
55145
146+ /**
147+ * Runs the router. Accepts a base path from which to serve the routes, and optionally whether you want to try
148+ * and match multiple routes.
149+ *
150+ * @param string $basePath
151+ * @param boolean $multimatch
152+ */
56153 public static function run (string $ basePath = '' , bool $ multimatch = false )
57154 {
58155 $ basePath = rtrim ($ basePath , '/ ' );
59- $ uri = parse_url ($ _SERVER ['REQUEST_URI ' ])['path ' ];
60156 $ method = $ _SERVER ['REQUEST_METHOD ' ];
157+ $ uri = parse_url ($ _SERVER ['REQUEST_URI ' ])['path ' ];
61158 $ path = urldecode (rtrim ($ uri , '/ ' ));
62159
63160 // If the path is empty (no slash in URI) place one to satisfy the root route ('/')
0 commit comments