When a request to the server is received, it is primarily processed by the Router, which analyzes the url address and transmits the request to the desired controller, which can process it. If the controller is missing it shows a 404 or other error.
First of all we will create the table of routes which will consist of regular expressions. In the /config folder, create a new file that will contain a table of routes, call it routes.php
We will add to the file:
use core\Router; //create a rule for the admin panel of our site. //If admin is found in the url then the controller from the admin folder will be activated. The fact that this request to the admin panel will indicate an additional parameter 'admin_prefix' => 'admin' Router::add('^admin/?$', ['controller' => 'Main', 'action' => 'index', 'admin_prefix' => 'admin']); //If the page is of type admin/somethings //^(?P<controller>[a-z-]+)/(?P<action>[a-z-]+)/?$ - a regular expression in which variables controller and action will be passed Router::add('^admin/(?P<controller>[a-z-]+)/?(?P<action>[a-z-]+)?$', ['admin_prefix' => 'admin']); //['controller' => 'Main', 'action' => 'index'] - in the parameters specify the controller and action Router::add('^$', ['controller' => 'Main', 'action' => 'index']); //^(?P<controller>[a-z-]+)/(?P<action>[a-z-]+)/?$ - a regular expression in which the variables will be passed controller and action Router::add('^(?P<controller>[a-z-]+)/(?P<action>[a-z-]+)/?$');
now we can connect our router to the main file /public/index.php to which all requests are sent:
require_once dirname(__DIR__) . '/config/routes.php';
We will also need to get the request url for the router. To do this, add the following code to the /vendor/core/app.php file:
$query = trim(urldecode($_SERVER['QUERY_STRING']), '/'); Router::dispatch($query);//pass our processed url to the dispatch method of the Router class
Now we can write the router class /vendor/core/Router.php
namespace core; class Router { //array in which the route table will be stored protected static array $routes = []; //the route being performed protected static array $route = []; //method to add to the route table public static function add($regexp, $route = []) { //take an array of all routes and add a new one with the key $regexp and route $route self::$routes[$regexp] = $route; } //method that will return all routes public static function getRoutes(): array { return self::$routes; } //the current route public static function getRoute(): array { return self::$route; } public static function dispatch($url) { //a method that will not take into account the get parameters in our url $url = self::removeQueryString($url); if (self::matchRoute($url)) { //create a route to our controller $controller = 'app\controllers\\' . self::$route['admin_prefix'] . self::$route['controller'] . 'Controller';//the name of the controllers will consist of the name + Controller //if the controller class exists if (class_exists($controller)) {//the controller is a class, so the class_exists function is suitable //create a new controller object $controllerObject = new $controller(self::$route);//transfer to the __constructor route which in the future we will need $controllerObject->get_Model();//call the model //add a method to our controller that will act as an action $action = self::lowerCamelCase(self::$route['action'] . 'Action');//the name Action will consist of the name + Action if (method_exists($controllerObject, $action)) {//Action is a method, so the method_exists function is suitable $controllerObject->$action();//call our method Action in class } else { //throw an exception if our controller does not have such a method throw new \Exception("{$controller} {$action} not found", 404); } } else { //if the controller is not found throw new \Exception("{$controller} not found ", 404); } } else { throw new \Exception("Page not found", 404); } } // a method that will not take into account the get parameters in our url protected static function removeQueryString($url) { if ($url) { $params = explode('&', $url, 2); //if in url there is no character "=" we will take away / from the end if (false === str_contains($params[0], '=')) { return rtrim($params[0], '/'); } } return ''; } // function that will compare the query with the regular expression template (/config/routes.php) and return true or false to matchRoute public static function matchRoute($url): bool { //paths are placed in the array $ routes, we will walk on it using a loop foreach (self::$routes as $pattern => $route) { if (preg_match("#{$pattern}#", $url, $matces)) {//# - regular expression template border foreach ($matches as $k => $v) { // remove the extra elements from the array if (is_string($k)) { $route[$k] = $v; } } //if no action if (empty($route['action'])) { $route['action'] = 'index'; } //if the admin prefix is removed if (!isset($route['admin_prefix'])) { $route['admin_prefix'] = ''; } else { $route['admin_prefix'] = '\\';//required for namespace } //transform the names of our controllers into the CamelCase style $route['controller'] = self::upperCamelCase($route['controller']); return true; } } return false; } //a method that will translate our url to CamelCase for class naming protected static function upperCamelCase($name): string { return str_replace(' ', '', ucwords(str_replace('-', '', $name))); } //method which will translate action for naming of methods of classes in style camelCase for methods protected static function lowerCamelCase($name): string { return lcfirst(self::upperCamelCase($name)); } }