Files
routes/README.md
T
2026-08-20 07:49:40 -03:00

14 KiB

InforRoutes

A simple and dependency free routing system providing:

  • Non blocking dispatch of system WEB and CLI functionalities
  • Parameters detection on the URL segments
  • Route grouping based on namespaces
  • Error handling and 404 detection
  • Attachable and global middlewares

Usage

Installation

First install the package with composer require inforsistemas/routes. You might need to add a custom repository on the composer.json

"repositories": [{
    "type": "composer",
    "url": "https://satis.domain.com"
}],

Hello World

With the dependencies and the server with the correct configuration, create a index.php with the content

<?php

include_once 'vendor/autoload.php';
use Routes\RouteCollection as RouteCollection;

// Add a route to the collection
RouteCollection::get ("/", function(){
    return "Hello World";
});

// Dispatch the router
RouteCollection::getInstance()->submit();

Registering routes

The simple API to register routes

<?php
use Routes\RouteCollection as RouteCollection;

RouteCollection::get("/",          function(){ });
RouteCollection::get("/form",      function(){ });
RouteCollection::post("/",         function(){ });
RouteCollection::get("/[i:id]",    function($id){ });
RouteCollection::put("/[i:id]",    function($id){ });
RouteCollection::patch("/[i:id]",  function($id){ });
RouteCollection::delete("/[i:id]", function($id){ });

Passing a ClassPath string as argument.

Based on the defined autoloader, the library will instanciate the controller and execute the method.

The library will also try to infer the controller method signature and parse the URL parameters to the required type

<?php
use Routes\RouteCollection as RouteCollection;

RouteCollection::get("/",          "\App\SubPlugin\Plugin\PluginController@index");
RouteCollection::get("/form",      "\App\SubPlugin\Plugin\PluginController@create");
RouteCollection::post("/",         "\App\SubPlugin\Plugin\PluginController@store");
RouteCollection::get("/[i:id]",    "\App\SubPlugin\Plugin\PluginController@show");
RouteCollection::put("/[i:id]",    "\App\SubPlugin\Plugin\PluginController@update");
RouteCollection::patch("/[i:id]",  "\App\SubPlugin\Plugin\PluginController@update");
RouteCollection::delete("/[i:id]", "\App\SubPlugin\Plugin\PluginController@destroy");

Laravel Compatibility

Routes can be added with the Laravel way

<?php
use Routes\RouteCollection as RouteCollection;

RouteCollection::get("/",          [PluginController:class, "index"]);
RouteCollection::get("/form",      [PluginController:class, "create"]);
RouteCollection::post("/",         [PluginController:class, "store"]);
RouteCollection::get("/[i:id]",    [PluginController:class, "show"]);
RouteCollection::put("/[i:id]",    [PluginController:class, "update"]);
RouteCollection::patch("/[i:id]",  [PluginController:class, "update"]);
RouteCollection::delete("/[i:id]", [PluginController:class, "destroy"]);

RouteGroup

The library allows to nest routes on a namespace, applying the prefix on each route defined

<?php
use Routes\RouteCollection as RouteCollection;

RouteCollection::group("/subplugin/plugin", function(){
  RouteCollection::get("/",          "\App\SubPlugin\Plugin\PluginController@index");
  RouteCollection::get("/form",      "\App\SubPlugin\Plugin\PluginController@create");
  RouteCollection::post("/",         "\App\SubPlugin\Plugin\PluginController@store");
  RouteCollection::get("/[i:id]",    "\App\SubPlugin\Plugin\PluginController@show");
  RouteCollection::put("/[i:id]",    "\App\SubPlugin\Plugin\PluginController@update");
  RouteCollection::patch("/[i:id]",  "\App\SubPlugin\Plugin\PluginController@update");
  RouteCollection::delete("/[i:id]", "\App\SubPlugin\Plugin\PluginController@destroy");
});

Setup/Service Routes

This library allows you to create sintetic routes with housekeeping features, for example:

  • Setup plugins configurations
  • Building menus
  • Manipulate data before executing routes

This snippet merges the content from php://input and $_POST on weight -10

<?php
use Routes\RouteCollection as RouteCollection;

RouteCollection::add("WEB", "*", function () {

    if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] != 'POST') {
        return;
    }

    $_POST = array_merge($_POST, (array) json_decode(file_get_contents('php://input')));

}, -10)->notBlock()->doIgnore();

This snippet create menus and submenus

<?php
use Routes\RouteCollection as RouteCollection;

RouteCollection::get('*', function() {
    Output::addSubmenu('menuname', 'Item name', "<i class='fa fa-globe'></i>", ['class' => 'nav-link'] );
}, -11)->doIgnore();

RouteCollection::get('*', function () {
    Output::addOnSubmenu('menuname', '/url', 'SubItem name', "", ['class' => 'nav-link']);
}, -10)->doIgnore();

Routes Properties

The routes can have some properties configured on them. This section will Describe them

Blocking

The routing system will iterate over all the registered routes, when a Route is matched and it's not configured to block the execution, more routes on the chain might match and be executed.

The default behavior of a Route is TO BLOCK the executions when it is executed, but this behavior can be changed with the notBlock() method

<?php
use Routes\RouteCollection as RouteCollection;

// If executed, this route will not stop the matchin chain
RouteCollection::get('*', function() { })->notBlock();

Ignore

If during the Routing process, the library didn't match any Route, a fallback 404 route will be executed. If you want a specific Route not to count on this process, you can ignore it with the function doIgnore(). The default behavior of routese is to NOT IGNORE the route.

Routes with this property assigned, will not prevent a 404 code. It is usefull for Setup/Service Routes.

<?php
use Routes\RouteCollection as RouteCollection;

// This route will execute, but will not count. Make some setup or preparation with this functionality
RouteCollection::get('*', function() { })->doIgnore();

Weight

When adding a Route, you can define it's weight. Before dispatching the routing process, the route set will be sorted and the routes will be checked on the specified order. The library will execute all routes until it get blocked, so the Route order might matter on the execution.

The weight is the third argument on get|post|put|patch|delete functions and the fouth on the add method. You can also mannually define the weight with the setWeight(int $weight) function.

<?php
use Routes\RouteCollection as RouteCollection;

// This route has the wight 0, but it's redefined to -1
RouteCollection::get('*', function() { }, 0)->setWeight(-1);

Name (#ToDo)

You can name your routes with the name(string $name) function

<?php
use Routes\RouteCollection as RouteCollection;

RouteCollection::get("*", function(){ })->name('routeName');

Tag (#ToDo)

You can tag your routes with the setTag(string|array $key, string $value) function

<?php
use Routes\RouteCollection as RouteCollection;

RouteCollection::get("*", function(){ })->setTag('group', 'value');

// Not implemented yet
RouteCollection::get("*", function () { })->setTag(
    [
        ['group', "value1"],
        ["group2", "value2"]
    ]
);

Middlewares

The library provides a simple Middleware functionality.

You can register

  • Named Middlewares
  • AdHock Middlewares
  • Global Middlewares

This middlewares can be attached or removed from the Routes

General Middlewares

A general Middlewares can be created with the API RouteCollection::registerMiddleware(string $name, callable $function) and will be stored as available on the RouteCollection Singleton.

<?php
use Routes\RouteCollection as RouteCollection;

RouteCollection::registerMiddleware("myMiddleware", function($param){
  // Do some validation or stuff
});

This Middleware can be latter attached to Routes or RouteGroups with the function middlewareAdd(string $name)

<?php
use Routes\RouteCollection as RouteCollection;

RouteCollection::get('*', function() { })->middlewareAdd("myMiddleware", Array("someValue"));

// #ToDo - Group Middlewares are not receaving parameters
RouteCollection::group("/group", function(){ })->middlewareAdd("myMiddleware", Array("someValue"));

AdHock Middlewares

You can simply attach a function to be executed before the Route/Group execute with the middlewareAppend(string $name, callable $callback) function

<?php
use Routes\RouteCollection as RouteCollection;

// #ToDo Routes do not have this functionality yet
RouteCollection::get('*', function() { })->middlewareAppend("myMiddleware", Array("someValue"));

// #ToDo Refactor the middleware API for the RouteGroup
RouteCollection::group("/group", function(){ })->middlewareAppend("myMiddleware", Array("someValue"));

Default Middleware

Default middlewares can be registered on the RouteCollection with the function addDefaultMiddleware(string $name, callable $function) Singleton and will be executed before all Routes

<?php
use Routes\RouteCollection as RouteCollection;

RouteCollection::addDefaultMiddleware("auth", function(Route $route){
  // Do some auth validation here
});

A Route/RouteGroup can also ignore a Default Middleware with the function middlewareIgnore(string $name)

<?php
use Routes\RouteCollection as RouteCollection;

// #ToDo Routes do not have this functionality yet
RouteCollection::get('*', function() { })->middlewareIgnore("auth");

Extending functionality

This library allows you to change and customize some behaviors

Content Parser

This librar has as default the return types for the Routes

  • string (Raw print the content)
  • Array (JSON Encode The Return)
  • stdClass|object|mixed (JSON Encode The Return)

But you can extend this functionality to process a return type in a customizible way with the API RouteCollection::addParser(string $type, callable $function)

<?php
use Routes\RouteCollection;

// When the returned object is the type Collection. This parser will be used
RouteCollection::addParser("Collection", function(Collection $collection): void{
  header('Content-Type: application/json; charset=utf-8');
  echo json_encode($collection->get());
});

Object Spawners

The functions called by the Routing system might have a Data Type defined as a parameter. With the spawners, you can configure how this Objects will be created and passed prepared to the target function.

With the default Behavior, in order, we grab the function parameters, match with the variable segments of the URL and instanciate a object from the defined type passing the URI parameter as the __construct main parameter

Server configuration

Requirements

The following packages need to be installed for the project to run

  • Apache
    • apache2 phpX.X phpX.X-fpm libapache2-mod-phpX.X
  • Nginx
    • nginx phpX.X phpX.X-fpm

The ports 80 and 443 must be open: ufw allow 80 && ufw allow 443

Apache

For apache, the basic required configuration

<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html/public
    ServerName example.com
     
    <Directory /var/www >
        Options Indexes FollowSymLinks MultiViews
        AllowOverride All
        Order allow,deny
        Allow from all
    </Directory>
     
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

For a server with HTTPs, the configuration with redirection

<VirtualHost *:80> 
    ServerName example.com
    Redirect / https://example.com
 </VirtualHost>
 
<IfModule mod_ssl.c>
    <VirtualHost *:443>
     
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html/public
        ServerName urfat.com.br
     
     
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
     
        SSLCertificateFile /etc/letsencrypt/live/example.com.br/fullchain.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/example.com.br/privkey.pem
        Include /etc/letsencrypt/options-ssl-apache.conf
    </VirtualHost>
</IfModule>

Nginx

For Nginx,this is the basic required configuration with PHP-FPM

server {
    listen 82 default_server;
    listen [::]:82 default_server;

    server_name example.com;

    root /var/www/html/public;
    index index.php index.html index.htm index.nginx-debian.html;

    server_name localhost;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        #Check fpm-version
        fastcgi_pass unix:/run/php/php7.2-fpm.sock;
    }

    location ~ /\.ht {
        deny all;
    }
}

For a server with HTTPs, the configuration with redirection

server {
    listen   80;
    listen   [::]:80;
    listen   443 default_server ssl;

    server_name example.com;

    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key    /path/to/my/key;

    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers "ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS:!AES256";
    ssl_prefer_server_ciphers on;
    ssl_dhparam /path/to/my/dhp-4096.pem #sudo openssl dhparam -out /path/to/my/dhp-4096.pem 4096

    if ($scheme = http) {
        return 301 https://$server_name$request_uri;
    }
}

Inner workings

The package consist in 3 main files, with RouteCollection.php providing the main API

  • RouteCollection.php => Aggregate all routes and provide a interface to interact with the routes dataset
  • RouteGroup.php => Aggregate a group of routes based on their namespaces
  • Route.php => Stores each route information

The class RouteCollection is treated as a Singleton that return it's own global instance when loaded. The main interface would be something like: RouteCollection::add($verb, $uri, $callback, $weight = 0), this method will return the instance of the Route object, so that the Route properties can be chained

Other resources

https://stackoverflow.com/questions/8054165/using-put-method-in-html-form