Creando nuestro framework de desarrollo con PHP (rutas y controladores)

Hace unas semanas, empezé el proyecto de desarrollar un framework que permitirera montar aplicaciones con PHP. Algo similar a Codeigniter o Laravel, pero obviamente mucho más modesto.

Pasado este tiempo, se ha avanzado un poco y quiero contaros estos avances.

Configurando rutas

Para hacerlo más manejable, he creado un sistema de gestión de rutas, que permite mediante la edición de un solo fichero, poder generar las urls necesaarias para algunas acciones. El sistema de rutas se divide entre el gestor de rutas (router.php) y las rutas propiamente dichas (routes.php).

// config/router.php
<?php 

function routeRequest($request_uri)
{
    // Incluir el archivo de rutas
    $routes = require 'routes.php';

    // Buscar una coincidencia en las rutas
    $matchedRoute = null;
    $function = [];
    $params = [];

    foreach ($routes as $route => $controller) {
        // Convertir la ruta en una expresión regular
        $pattern = str_replace('/', '\/', $route);
        $pattern = "/^" . preg_replace('/\{(\w+)\}/', '([^\/]+)', $pattern) . "$/";

        // Verificar si la URI coincide con el patrón
        if (preg_match($pattern, $request_uri, $matches)) {
            $matchedRoute = $route;
            $params = array_slice($matches, 1);
            // echo "<pre>";
            // print_r($params);
            break;
        }
    }

    // Encontrar el controlador correspondiente para la ruta solicitada
    $controller = $routes[$matchedRoute] ?? false;

    if ($controller) {
        // Dividir la ruta en partes (controlador, función, parámetros)
        $routeParts = explode('/', trim($matchedRoute, '/'));
        $controllerFile = "./app/controllers/{$controller}.php";

        if (file_exists($controllerFile)) {
            require $controllerFile;
            $controllerInstance = new $controller();

            // Llamar a la función correspondiente
            $function = $params[0] ?? 'index';
            
            // Convertir $params[1] en un array llamado $params
            $params = isset($params[1]) ? ['params' => $params[1]] : [];

            if (method_exists($controllerInstance, $function)) {
                // Llamar a la función con los parámetros obtenidos
                call_user_func_array([$controllerInstance, $function], $params);
            } else {
                http_response_code(404);
                echo 'La funcion no existe';
            }
        } else {
            http_response_code(404);
            echo 'El controlador no existe';
        }
    } else {
        http_response_code(404);
        echo 'Página no encontrada';
    }
}

El fichero de rutas, está además preparado para recibir datos como pueden ser el nombre de la función y los parametros que puedan ser necesarios

// config/routes.php
<?php

return [
    '/' => 'ExampleController',
    '/about' => 'AboutController',
    '/contact' => 'ContactController',
    // La llamada de about, recibe el nombre de la función y también un parámetro si es necesario.
    '/about/{function}/{param}' => 'AboutController'
    // Agrega más rutas y controladores según tus necesidades
];

Controlador básico

De esta manera, si la url contiene los datos de función y parametros, ejecuta la función referenciada. En caso de no recibir ninguna función, abre por defecto la función index.

<?php
require_once 'BaseController.php';

class AboutController extends BaseController
{
    private $config;
    private $view;

    public function __construct()
    {
        // Cargar configuración y ruta de la vista una sola vez en el constructor
        $this->config = require __DIR__ . '/../config/config.php';
        $this->view = __DIR__ . '/../views/about_view.php';
    }


    public function index()
    {

        $this->render($this->view, $this->config);
    }

Por ejemplo si la ruta fuera http://localhost:8080/framework/about/saludo/hola, llamaría a la función saludo, del controlador about, con el parámetro hola

//.. codigo anterior

 // Funcion para saludar
    public function saludo($params)
    {
        // Puedes pasar el parámetro a la vista de alguna manera (por ejemplo, a través del array $config)
        $this->config['saludo_param'] = $params;
        $this->render($this->view, $this->config, ['saludo_param' => $params]);


    }
//.. codigo posterior

Aquí un ejemplo de la salida:

O por ejemplo, si se lanza la url http://localhost:8080/framework/about/multiplica/5, lo que hace es llamar a la función multiplica, del controlador about y usa el parámetro 5, para crear una tabla de multiplicar.

 //.. codigo anterior

 //Funcion para multiplicar
    public function multiplica($params)
    {
 
        for ($i = 0; $i <= 10; $i++) {
            $multiplica = $i * $params;
            $resultado = "$i X $params = $multiplica";
            $resultados[] = $resultado;
        }

        $this->config['multiplica'] = $resultados;
        $this->render($this->view, $this->config, ['multiplica' => $resultados]);

    }

 // ...codigo posterior

La salida de esta url sería:

Para poder gestionar todo esto, obviamente hace falta un punto de inicio, en concreto el fichero index.php, que se encuentra en la raíz del proyecto.

<?php
// Autocarga de clases usando composer
//require_once __DIR__ . './vendor/autoload.php';

// Cargar la configuración
$config = require_once __DIR__ . '/app/config/config.php';

// Ruta base de la aplicación
$base_path = $config['base_url'];

// Obtener la URI después de la ruta base
$request_uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$request_uri = str_replace($base_path, '', $request_uri);

// Enrutador básico
// Incluir el archivo del enrutador
require_once __DIR__ . '/app/config/router.php';

// Enrutamiento de la solicitud
routeRequest($request_uri);

El fichero, lo que hace es cargar la configuración del sitio web, de la que hablaremos en otra ocasión, que se encuentra en el fichero config/config.php, y montar las rutas correspondientes con la configuración de rutas que hemos visto antes.

Poco más de momento. Seguiremos hablando del proyecto en otra ocasión donde, como decía, veremos un poco más en detalle el fichero de configuración e iremos avanzado en el desarrollo de más características: acceso a base de datos, consumo de API, etc.

Pero de momento, tenemos un proyecto que arranca y responde a algunas peticiones.

Teneís ya el código de este proyecto en mi repositorio de GitHub para que podáis ir viendo los progresos del mismo y hacer las modificaciones que os parezcan para vuestro propio proyecto.

PD: el proyecto es simplemente un reto personal, como forma de estudio y está siempre en versión Alpha. No es recomendable usar ninguna de las partes del código en un proyecto en producción.

Publicada el
Categorizado como Desarollo Etiquetado como

Por Jose Manuel Sanz Prieto

Desarrollador web. En este blog hablo de fotografía, programación con Django, Python, PHP y privacidad.

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *