>

Cómo hacer tu propio framework

Juanma Cabello     Colaboraciones    28/04/2017

Volviendo a leer mi anterior post sobre la programación minimalista he pensado que podría mostrarte un ejemplo práctico. Vamos a hacer un pequeño framework desde cero juntos, con el que podrás hacer proyecto de pequeña envergadura como páginas corporativas.

Para ello usaremos (cómo no) mi amado Slim. Terminaremos con algo parecido a un "Hola mundo" ¿Te animas?

Preparativos

Lo primero es usar el todopoderoso composer para definir las dependencias que vamos a necesitar. Te pego aquí el código:

{
  "require": {
    "slim/slim": "^3.0",
    "league/plates": "^3.1"
  },
  "autoload": {
    "psr-4": {
        "MiWeb\\": "app/"
    }
  }
}

Como ya he comentado más arriba, usaremos Slim. El motor de renderizado que he elegido es Plates. Me gusta Plates porque se basa en PHP pelado y mondado, con lo que ganamos en: tiempo, al no tener que aprender otra sintaxis, y eficiencia, al no tener que gastar ciclos de CPU parseando dicha sintaxis. También incluímos el mapeo de nuestro namespace a la carpeta donde guardaremos nuestro código.

Ejecuta un $ composer install para que empiece la magia.

Directorios

La estructura de directorios que vamos a usar es la siguiente:

/
|- app
   |--- Controllers
   |--- Views
|- config
|- public

En app vamos a guardar tanto los controladores (que no harán más que renderizar las vistas) y las vistas de nuestra web. En config guardaremos las distintas configuraciones de la web y en public todo lo que queramos que esté visible al mundo

Fácil, ¿no? Como puedes comprobar es muy parecida a lo que estamos acostumbrados a ver en muchos otros de los grandes frameworks disponibles.

Punto de entrada

El punto de entrada de nuestra web va a estar en un archivo index.php que se ubicará en la carpeta public.

<?php
// File: /public/index.php

// Se inserta el autoload de composer
require __DIR__ . '/../vendor/autoload.php';

// Se instancia la aplicación con la configuración del archivo app.php
$app = new Slim\App();

// Se inserta las diferentes configuraciones
require __DIR__ . '/../config/dependencies.php';
require __DIR__ . '/../config/routes.php';

// Se arranca la aplicación
$app->run();

Esto sí puede asustar un poco más, pero no temas que no es para tanto. Iremos creando estos archivo poco a poco.

Dependencias

Lo que vamos a hacer en esta sección es meter en el contenedor de la aplicación los diferentes servicios que queramos tener disponible a lo largo de la ejecución del código. En nuestro caso, el motor de plantillas:

<?php
// File: /config/dependencies.php

// Se añaden las dependencias
$container = $app->getContainer();

// Motor de plantillas
$container['templates'] = function($c) {
    return new League\Plates\Engine(__DIR__ . '/../app/Views');
};

Rutas

En el archivo de rutas básicamente vamos a declarar las… rutas de nuestra web.

<?php
// File: /config/routes.php

$app->get('/hola/{nombre}', '\MiWeb\Controllers\HolaController:index');
$app->get('/', '\MiWeb\Controllers\HolaController:index');

En el método index del HolaController, se dispondrá de un índice llamado 'nombre' en el array $args, que contendrá el último slug de la URL.

Slim framework ya se encarga de pasar el container a cada acción automáticamente. Es por eso que, si quieres usar algo declarado en el container, se debe implementar el constructor de cada controller.

No te olvides de crear un .htaccess en la carpeta public para que funcionen las urls amigables más adelante

RewriteEngine On
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule ^ index.php [QSA,L]

Controladores

Vamos a necesitar dos controladores, uno base del que tendrán que heredar todos los demás y otro que será el que despache nuestra cadena "Hola mundo".

<?php
// File: /app/Controllers/BaseController.php

namespace MiWeb\Controllers;
/**
 * Controlador base del que deben heredar todos los controladores de la
 * aplicación. De esta manera, se asegura que los controladores tengan acceso a
 * unos atributos mínimos para poder funcionar.
 */
class BaseController
{
    /** @var \League\Plates\Engine Acceso al motor de plantillas */
    protected $view;

    /**
     * Usando inyección de dependencia se le pasa al constructor
     * el contenedor de la aplicación. Así se dispone de las
     * dependencias establecidas en config/dependencies.php.
     *
     * @param $container \Pimple\Container Contenedor de la aplicación
     */
    public function __construct($container)
    {
        $this->view = $container->get('templates');
    }
}
<?php
// File: /app/Controllers/HolaController.php

namespace MiWeb\Controllers;

use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;

class HolaController extends BaseController
{
    /**
     * Usando inyección de dependencia se le pasa al constructor
     * el contenedor de la aplicación. Así se dispone de las
     * dependencias establecidas en config/dependencies.php.
     *
     * @param $container \Pimple\Container Contenedor de la aplicación
     */
    public function __construct($container)
    {
        parent::__construct($container);
    }

    /**
     * Acción única de la aplicación que demuestra el paso de argumentos
     * a la platilla.
     *
     * @param $request \Psr\Http\Message\ServerRequestInterface Petición a la
     * que servir.
     * @param $response \Psr\Http\Message\ResponseInterface Respuesta a la
     * petición.
     * @param $array array Contiene cada uno de los parámetros de la petición
     *
     * @return string con la plantilla ya renderizada.
     */
    public function index(Request $request, Response $response, $args): string
    {
        // Ejemplo de uso del array $args. El contenido de este array estará
        // disponible en la plantilla en el array $data.
        $nombre = $args['nombre'] ?? 'mundo';

        return $this->view->render('hola/index', [
            'nombre' => $nombre
        ]);
    }
}

Vistas

Ya, por último, nos queda crear nuestra plantilla para renderizar la vista de nuestro "Hola mundo". Para ello creamos la plantilla maestra y otra para el "Hola mundo" en sí mismo.

<!-- File: /app/Views/master.php -->

<html>
  <head>
    <meta charset="UTF-8">
    <title>¡Hola mundo!</title>
  </head>
  <body>
    <?= $this->section('content') ?>
  </body>
</html>
<!-- File: /app/Views/hola/index.php -->

<?php $this->layout('master') ?>

¡Hola, <?= $this->e($nombre) ?>!

Resultados

Si todo ha ido bien… ¡ya podrías ir a la carpeta public, hacer un $ php -S 0.0.0.0:4000 y visitar la web para ver un bonito mensaje! Es más, si vas a http://0.0.0.0:4000/hola/Pepe, la aplicación saludará a Pepe en lugar de al mundo.

Conclusión

¡Buff! No ha estado mal, ¿eh? Como ves, crear un pequeño framework a base de usar componentes que nos parezcan interesantes es muy fácil; Solo hay que saber un poco lo que se está haciendo. Este pequeño framework podrá crecer todo lo que quieras ya que los engranajes los haces tú y podrás ir enlazando más componentes conforme te hagan falta como un ORM, un logger, etc.

¿Qué te ha parecido? ¿Intentarás hacerte tu propio framework? ¿Tenías ya uno? ¿Qué librerías has incluído en él? ¡Dínoslo en los comentarios!

Foto: Heimarbeit de Andreas Levers


Sobre el autor

Juanma Cabello   

Fullstack #php developer en Freepik y organizador de @betabeersMLG