A while back a question came up from
The PHP Practitioner Series
That got me sparked up to finish a router form an older custom framework I have kept updated, it's even php 7.3 compatible now. It is a combination of the old SMVC framework, Mini 3 framework, and a lot of my code combined. Mini only comes with front controller routing.
This is NOT for laravel, again it is just to show how a router could be built and protect some routes requiring a login for php.
First of all this is not a copy and paste router, it is just to show how a router can be built.
Also I use static methods, but instance methods could be used as well.
I have two ways to route,
- Routes not needing a login I auto dispatch (front controller method)
- Routes needing login I verify against an Auth array and route
It starts with a simple routes file, just what I named it:
<?php
use Mini\Core\Router;
use Mini\Helpers\Clnsantize as Cln;
use Mini\Core\AuthRoutes;
$auth = "notauth";
if (Cln::chkAdmin() === 'authadmin') {
$auth = "isauth";
$routes = AuthRoutes::chkAuth($auth);
}
Router::checkAdmin($routes, $auth);
A required login routes list:
<?php
namespace Mini\Core;
class AuthRoutes
{
public static function chkAuth($auth = 'notauth')
{
if ($auth = 'isauth') {
$admin = [
'dog/indexadmin' => '\Mini\Controller\DogController@indexAdmin',
'dog/edit' => '\Mini\Controller\DogController@edit',
'admin/logout' => '\Mini\Controller\AdminController@logout'
];
return $admin;
}
}
}
And the router:
<?php
namespace Mini\Core;
use Mini\Helpers\Clnsantize as Cln;
class Router
{
/** The routes */
protected static $routes = [];
/** Any parameters, not querystring */
protected static $last = [];
/** uri */
protected static $uri = [];
/** Check if authorized */
protected static $auth = '';
/** Run any authorized routes */
public static function checkAdmin($routes = array(), $auth)
{
self::$uri = self::getUri();
if ($auth === 'isauth') {
self::$routes = $routes;
$key_to_check = self::chkUri(self::$uri[2], self::$uri[3]);
if (array_key_exists($key_to_check, self::$routes)) {
$torun = self::$routes[$key_to_check];
self::run($torun, $auth);
} else {
self::autoDispatch();
}
} else {
self::autoDispatch();
}
}
public static function run($torun = null, $auth)
{
self::$auth = $auth;
foreach (self::$uri as $i => $key) {
$i > 0;
$key;
if ($i > 3) {
$newkey = self::getKey($key);
array_push(self::$last, $newkey);
}
}
self::dispatch($torun);
}
public static function dispatch($torun)
{
if (self::$auth === 'isauth') {
$uparts = explode('@', $torun);
$controller = isset($uparts[0]) ? $uparts[0] : null;
$action = isset($uparts[1]) ? $uparts[1] : null;
self::load($controller, $action, self::$last);
} else {
self::defaultController();
}
}
public static function load($controller, $action, $last = array())
{
$c = new $controller;
return $c->$action(...array_values($last));
}
public static function getKey($key)
{
$fixedKey = Cln::fixUri($key);
return $fixedKey;
}
public static function chkUri($u1 = null, $u2 = null)
{
$uricheck = Cln::chkForSingle($u1, $u2);
return $uricheck;
}
public static function getUri()
{
$uri = explode('/', parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
return $uri;
}
public static function autoDispatch()
{
foreach (self::$uri as $i => $key) {
$i > 0;
$key;
if ($i > 3) {
$newkey = self::getKey($key);
//echo $newkey;
array_push(self::$last, $newkey);
}
}
$key_to_check = self::chkUri(self::$uri[2], self::$uri[3]);
$uparts = explode('/', $key_to_check);
if (file_exists(APP . 'Controller' . '/' . ucfirst($uparts[0]) . 'Controller.php')) {
$controller = isset($uparts[0]) ? '\Mini\Controller\' . ucfirst($uparts[0]) . 'Controller' : null;
$action = isset($uparts[1]) ? $uparts[1] : null;
if (method_exists($controller, $action) && is_callable(array($controller, $action))) {
self::load($controller, $action, self::$last);
} else {
self::defaultController();
}
} else {
self::defaultController();
}
}
public static function defaultController()
{
$c = new \Mini\Controller\HomeController();
$action = 'index';
$c->$action();
}
}
Parts like
$fixedKey = Cln::fixUri($key);
is just calling a method in a class that:
public static function fixUri($rvalue)
{
$rvalue = empty($rvalue) && !is_numeric($rvalue) ? NULL : trim(strip_tags($rvalue));
$rvalue = strtok($rvalue, '?');
$rvalue = htmlspecialchars($rvalue);
return $rvalue;
}
And this function:
public static function chkUri($u1 = null, $u2 = null)
{
$uricheck = Cln::chkForSingle($u1, $u2);
return $uricheck;
}
makes it work if a second part isn't used, like
site/dog/index
site/dog //would work if in routes that way
The $last is getting any extra passed parameters, not counting query string:
site/dog/indexadmin
// adding params
site/dog/indexadmin/hello/world/1457
Query string is taken care of by HTTP anyway. No routing is required. Like:
site/dog/indexadmin/hello/world?page=2
?page=2 // is done no routing required.
I moved this project to laravel a while back, but had never completed the router. This router works, but I imagine most will want instance methods. But static is perfect routing, get route and it's done.
For more on the splat operator see:
(...array_values($last)
See https://laracasts.com/discuss/channels/general-discussion/callstatic
and
https://laracasts.com/discuss/channels/general-discussion/php-general-question-1