Skip to content

Instantly share code, notes, and snippets.

@jason-napolitano
Last active January 5, 2021 04:46
Show Gist options
  • Save jason-napolitano/208d0e5e27a69957f4af21cedfdbfb86 to your computer and use it in GitHub Desktop.
Save jason-napolitano/208d0e5e27a69957f4af21cedfdbfb86 to your computer and use it in GitHub Desktop.
Router.php
<?php
/**
* A facade class for the Core Router Module. Used to make routed calls using
* more practical and simple invocations for each Route.
*
* @author Jason Napolitano <jnapolitanoit@gmail.com>
* @license MIT
*/
abstract class RouteHandler
{
/**
* Router instance
*
* @var Router $router The Router class
*/
public static Router $router;
// --------------------------------------------------------------------
/**
* Router constructor
*
* @return Router
*/
public static function start(): Router
{
self::$router = new Router();
return self::$router;
}
// --------------------------------------------------------------------
/**
* A facade function to preform GET requests
*
* @param string $uri
* @param callable $callable
*
*/
public static function get(string $uri, callable $callable): void
{
self::$router->create('GET', $uri, $callable);
}
// --------------------------------------------------------------------
/**
* A facade function to preform POST requests
*
* @param string $uri
* @param callable $callable
*
*/
public static function post(string $uri, callable $callable): void
{
self::$router->create('POST', $uri, $callable);
}
// --------------------------------------------------------------------
/**
* A facade function to preform PUT requests
*
* @param string $uri
* @param callable $callable
*
*/
public static function put(string $uri, callable $callable): void
{
self::$router->create('PUT', $uri, $callable);
}
// --------------------------------------------------------------------
/**
* A facade function to preform PATCH requests
*
* @param string $uri
* @param callable $callable
*
*/
public static function patch(string $uri, callable $callable): void
{
self::$router->create('PATCH', $uri, $callable);
}
// --------------------------------------------------------------------
/**
* A facade function to preform DELETE requests
*
* @param string $uri
* @param callable $callable
*
*/
public static function delete(string $uri, callable $callable): void
{
self::$router->create('DELETE', $uri, $callable);
}
// ------------------------------------------------------------------------
/**
* A facade function to preform DELETE requests
*
* @param string $uri
* @param callable $callable
*
*/
public static function options(string $uri, callable $callable): void
{
self::$router->create('OPTIONS', $uri, $callable);
}
// ------------------------------------------------------------------------
}
<?php
/**
* A simple yet effective, Object Oriented, REST capable PHP micro router. Can
* easily be used as a `stand-alone` Router or implemented with composers PSR4
* or your own PSR4 autoloader
*
* @package Core\Services\Router
*/
class Router
{
/**
* Request URI
*
* @var string
*/
protected string $requestUri;
/**
* Request method
*
* @var string
*/
protected string $requestMethod;
/**
* Array of HTTP methods
*
* @var array
*/
private array $httpMethods = [
'get',
'post',
'put',
'patch',
'delete',
];
/**
* Wildcard patterns
*
* @var array $wildCards
*/
private array $wildCards = [
'int' => '/^[0-9]+$/',
'any' => '/^[0-9A-Za-z]+$/',
];
// ------------------------------------------------------------------------
/**
* If all routes will share a common base path, it can be set here in the constructor
*
* @param string $basePath
*/
public function __construct(
protected string $basePath = ''
)
{
// Remove query string and trim trailing slash
$this->requestUri = rtrim(strtok($_SERVER['REQUEST_URI'], '?'), '/');
$this->requestMethod = $this->determineHttpMethod();
}
// ------------------------------------------------------------------------
/**
* Point a method/route to its target and create a new route request
*
* @param string $method The HTTP method to respond to (GET, POST, PUT, DELETE, or PATCH)
* @param string $route The uri route (with any wild cards) to respond to
* @param callable $callable The method to execute when a successful route is matched
*/
public function create(string $method, string $route, callable $callable): void
{
$method = strtolower($method);
if ( $route === '/' ) {
$route = $this->basePath;
} else {
$route = $this->basePath . $route;
}
$matches = $this->matchWilCards($route);
if ( is_array($matches) && $method === $this->requestMethod ) {
// Router match and request method matches
call_user_func_array($callable, $matches);
}
}
// ------------------------------------------------------------------------
/**
* Determine method
*
* Determine which HTTP method was sent
*
* @return string
*/
private function determineHttpMethod(): string
{
$method = strtolower($_SERVER['REQUEST_METHOD']);
if ( in_array($method, $this->httpMethods, true) ) {
return $method;
}
return 'get';
}
// ------------------------------------------------------------------------
/**
* Match wild cards
*
* Check if any wild cards are supplied.
*
* This will return false if there is a mis-match anywhere in the route,
* or it will return an array with the key => values being the user supplied variable names.
*
* If no variable names are supplied an empty array will be returned.
*
* TODO - Support for custom regex
*
* @param string $route The user-supplied route (with wild cards) to match against
*
* @return bool|array
*/
private function matchWilCards(string $route): bool|array
{
$variables = [];
$exp_request = explode('/', $this->requestUri);
$exp_route = explode('/', $route);
if ( count($exp_request) === count($exp_route) ) {
foreach ( $exp_route as $key => $value ) {
if ( $value === $exp_request[$key] ) {
// So far the routes are matching
continue;
}
if ( $value[0] === '(' && substr($value, -1) === ')' ) {
// A wild card has been supplied in the route at this position
$strip = str_replace(['(', ')'], '', $value);
$exp = explode(':', $strip);
$wc_type = $exp[0];
if ( array_key_exists($wc_type, $this->wildCards) ) {
// Check if the regex pattern matches the supplied route segment
$pattern = $this->wildCards[$wc_type];
if ( preg_match($pattern, $exp_request[$key]) ) {
if ( isset($exp[1]) ) {
// A variable was supplied, let's assign it
$variables[$exp[1]] = $exp_request[$key];
}
// We have a matching pattern
continue;
}
}
}
// There is a mis-match
return false;
}
// All segments match
return $variables;
}
return false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment