print('this could be a middleware, and it would print on every single route!') ], //[ // fn() => print('this is what it would look like if a new group was created and a middleware was // added inside of it!') //] ]; /** * Get request path relative to the entrypoint of the application */ static function getRelativeRequestPath(): string { // try to save a negligible amount of performance by caching the computated result static $request_url_path; if (isset($request_url_path)) { return $request_url_path; } $request_url_path = substr_replace( urldecode(strtok($_SERVER['REQUEST_URI'], '?')), '', 0, strlen($_SERVER['SCRIPT_NAME']) ); if ($request_url_path === '') $request_url_path = '/'; return $request_url_path; } /** * Run all middlewares then callback if supplied arguments correspond with the request */ static function match(string $methods, string $url_path, callable $callback): void { if (!in_array( $_SERVER['REQUEST_METHOD'], array_map(strtoupper(...), explode('|', $methods)) )) return; $request_url_path_parts = explode('/', self::getRelativeRequestPath()); $url_path_parts = explode('/', self::$prefix . $url_path); $callback_args = []; for ($i = 1; $i < count($url_path_parts); $i++) { if ($url_path_parts[$i] === '') continue; if ($url_path_parts[$i][0] !== '$') continue; if (!isset($request_url_path_parts[$i])) return; if ($url_path_parts[$i][-1] !== '?' && $request_url_path_parts[$i] === '' ) return; if ($url_path_parts[$i][-1] === '?' && $i !== (count($url_path_parts) - 1) ) throw new Exception( 'Only the last route parameter is allowed to be optional.' ); if ($request_url_path_parts[$i] !== '') array_push( $callback_args, htmlspecialchars($request_url_path_parts[$i]) ); $request_url_path_parts[$i] = $url_path_parts[$i]; } if ( implode('/', $url_path_parts) !== implode('/', $request_url_path_parts) ) return; foreach (self::$groups as $middlewares) { foreach ($middlewares as $middleware) { $middleware(); } } $callback(...$callback_args); die(); } // Shorthand for self::match(...) static function get(string $url_path, callable $callback): void {self::match('get', $url_path, $callback);} // Shorthand for self::match(...) static function post(string $url_path, callable $callback): void {self::match('post', $url_path, $callback);} // Shorthand for self::match(...) static function put(string $url_path, callable $callback): void {self::match('put', $url_path, $callback);} // Shorthand for self::match(...) static function patch(string $url_path, callable $callback): void {self::match('patch', $url_path, $callback);} // Shorthand for self::match(...) static function delete(string $url_path, callable $callback): void {self::match('delete', $url_path, $callback);} // Shorthand for self::match(...) static function options(string $url_path, callable $callback): void {self::match('options', $url_path, $callback);} // Shorthand for self::match(...) static function form(string $url_path, callable $callback): void {self::match('get|post', $url_path, $callback);} // Shorthand for self::match(...) static function any(string $url_path, callable $callback): void {self::match('get|post|put|patch|delete|options', $url_path, $callback);} /** * Adds middleware to its respective group */ static function use(callable $middleware): void { array_push( self::$groups[array_key_last(self::$groups)], $middleware ); } /** * Create new route group * * @param string $prefix Optional prefix can be supplied; the group is ignored if getRelativeRequestPath * does not correspond with the supplied value. * @param callable $callback Required; the insides of the group */ static function group(string $prefix = '', ?callable $callback = null): void { if ($callback === null) { throw new InvalidArgumentException('Argument callback must be of type callable.'); } if (!str_starts_with( self::getRelativeRequestPath(), self::$prefix . $prefix )) return; self::$prefix = self::$prefix . $prefix; array_push(self::$groups, []); $callback(); array_pop(self::$groups); self::$prefix = rtrim(self::$prefix, $prefix); } }