danielspk/tornado

Reducido nĂșcleo de trabajo para PHP

v2.0.0 2015-06-20 14:35 UTC

README

Build Status Latest Stable Version Total Downloads License

ScreenShot

TORNADO es un reducido marco de trabajo para PHP que permite implementar el patrón HMVC y/o servicios RESTfull

Puede obtener más información en su web http://tornado-php.com

Filosofia:

TORNADO no intenta ser un framework PHP full-stack. Contrariamente intenta ser
un núcleo de trabajo muy reducido para implementar patrones de arquitectura HMVC y/o servicios REST, con la menor parametrización y utilización de código posible, apoyado en un core que organice su proyecto junto a un sistema de configuración y gestión de errores simple.

TORNADO no incluye librerías de soporte para tareas comunes como acceso a base de datos, gestión de plantillas, envío de mais, etc. Utilice Composer para incluir paquetes de terceros de acuerdo a las necesidades particulares del proyecto a desarrollar.

Inspiración:

TORNADO se inspiro en varios microframeworks PHP, entre ellos cabe mencionar:

Metas:

TORNADO se desarrollo tratando de respetar las siguiente metas:

  • ser rápido
  • fácil de entender (tanto su API como su construcción interna)
  • tener la menor cantidad de métodos posibles dentro de su API
  • permitir el uso de ganchos para extender el mismo
  • incluir librerías/paquetes de terceros con suma facilidad
  • tener la menor cantidad de líneas de código posible
  • ser un core de trabajo (NUNCA un framework)

Características:

  • Enrutamientos para utilizar módulos HMVC y/o servicios REST (apoyado en URL amigables)
  • Configuración general de la aplicación
  • Ganchos para extender las características del core
  • Captura de errores y excepciones
  • Inyección de dependencias

Codificación:

TORNADO apoya la iniciativa del PHP Framework Interop Group e implementa los estándares PSR-2 y PSR-4.

Puede obtener más información en http://www.php-fig.org/

Instalación:

La instalación recomendada requiere el uso de Composer.

{
    "require": {
        "danielspk/tornado" : "2.*"
    }
}
  • Inicie la consola de comando y ejecute el siguiente comando:
composer install

Manual de uso:

La versión actual difiere totalmente de la versión inicial 1.0.0

Si va a actualizar su aplicación lea en detalle el archivo de cambios CHANGELOG.md

Uso básico:

Ejemplo de uso básico (con dos tipos de enrutamientos)

<?php

    // incluir el autoload
    require 'vendor/autoload.php';
    
    // obtener una instancia del core
    $app = \DMS\Tornado\Tornado::getInstance();
    
    // enrutamiento a módulo desde raíz
    $app->route('/', 'demo|demo|index');
    
    // enrutamiento a función anónima
    $app->route(array(
        '/saludar/:string'	=> function($pNombre = null){
            echo 'Hola ' . $pNombre;
        }
    ));

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

API:

Obtener Instancia del core:
    $app = \DMS\Tornado\Tornado::getInstance();
Ejecutar el core:
	// con una instancia del core en una variable
    $app = \DMS\Tornado\Tornado::getInstance();
    $app->run();

    // sin ninguna instancia anterior del core
    \DMS\Tornado\Tornado::getInstance()->run();
Setear configuraciones:
    $app = \DMS\Tornado\Tornado::getInstance();

    // configuración simple
    $app->config('nombre', 'valor del nombre');
    $app->config('nombres', array('nombre1'=>'valor1', 'nombre2'=>'valor2'));
    
    // configuración múltiple
    $app->config([
        'clave1' => 'valor uno',
        'clave2' => 'valor dos'
    ]);
Leer configuraciones:
    $app = \DMS\Tornado\Tornado::getInstance();

    // configuración simple
    echo $app->config('nombre');

    // configuración array
    $nombres = $app->config('nombres');
    echo $nombres[0]['nombre1'];
    echo $nombres[1]['nombre2'];
Variables de configuración propias de Tornado:

Tornado permite configurar el ambiente de trabajo de la aplicación. De esta forma se puede cambiar el comportamiento interno del core:

    // configurar la aplicación para un ambiente de desarrollo
    // - errores visibles
    // - parse de anotaciones en módulos HMVC para generar enrutamientos automáticos
    $app->config('tornado_environment_development', true);

Otras configuraciones:

    // - indica si se van a utilizar módulos hmvc
    $app->config('tornado_hmvc_use', true);

    // - ruta donde se alojarán los módulos hmvc
    // (relativa a donde se inicia Tornado)
    $app->config('tornado_hmvc_module_path', true);
        
    // - ruta donde se serializaran las rutas de los módulos hmvc
    // (relativa a donde se inicia Tornado)
    $app->config('tornado_hmvc_serialize_path', true);
        
Uso de Hooks:

Existen 6 tipos de hooks:

  • init: antes de parsear la url en busca de una ruta coincidente
  • before: antes de ejecutar la ruta coincidente
  • after: despues de ejecutar la ruta coincidente
  • end: al finalizar la ejecución de la petición
  • 404: al no encontrarse una ruta coincidente con la url
  • error: al atraparse un error o excepción en aplicación
    $app = \DMS\Tornado\Tornado::getInstance();

    // utilizando una clase / método / parámetros
    $app->hook('error', array('ErrorUser', 'display', array()));

    // utilizando una función anónima
    $app->hook('404', function(){
        echo '404';
    });
    

También es posible crear ganchos personalizados. Ejemplo usando una clase de usuario:

    class Saludador
    {
        public function persona($nombre, $apellido)
        {
            echo 'Hola ' . $nombre . ', ' . $apellido;
        }
    }

    $app->hook('saludar', array('Saludador', 'persona', array('Tornado', 'PHP')));
    

La forma de ejecutar un gancho por código es la siguiente:

    $app = \DMS\Tornado\Tornado::getInstance();

    $app->hook('fueraDeLinea');
    

Pueden crearse n cantidad de hooks con un mismo nombre. Los mismos se ejecutarán secuencialmente en el orden en que fueron definidos. Puede, opcionalmente, alterar este orden indicando explicitamente el orden deseado:

    $app = \DMS\Tornado\Tornado::getInstance();

    $app->hook('before', function(){
        echo 'Declarado primero - ejecutado despues';
    }, 1);
    
    $app->hook('before', function(){
        echo 'Declarado despues - ejecutado primero';
    }, 0);
    

Si declara más de un hook con el mismo nombre puede impedir que se ejecuten los hooks subsiguientes haciendo que el hook devuelva false en su ejecución.

A excepción de los hook init puede consultar que ruta se va o se esta ejecutandose de la siguiente forma:

    $app = \DMS\Tornado\Tornado::getInstance();

    $app->hook('before', function() use ($app){
        $ruta = $app->getRouteMatch()
    });
    

Esto devolverá un array con la siguiente información:

  • Método de la petición (GET, POST, etc)
  • Ruta
  • Callback
  • Parámetros
Hooks y flujo de ejecución:

La secuencia de ejecución del core es la siguiente:

  • se ejecutan los hooks init
  • se parsea la url en busca de la ruta coincidente
    • si no hay coincidencias se ejecuta:
      • hooks 404
      • hooks end
      • se finaliza la ejecución
  • se ejecutan los hooks before
    • si alguno devuelve false se ejecuta:
      • hooks end
      • se finaliza la ejecución
  • se ejecuta la ruta coincidente
  • se ejecutan los hooks after
  • se ejecutan los hooks end
Definir Enrutamientos:

Los enrutamientos pueden ser:

  • (vacio) - cualquier tipo de petición
  • GET - RESTfull por método GET
  • POST - RESTfull por método POST
  • PUT - RESTfull por método PUT
  • DELETE - RESTfull por método DELETE

En caso de que el servidor no soporte los métodos PUT y DELETE se pueden simular los mismos enviando una petición POST con una variable "REST_METHOD" cuyo valor sea PUT o DELETE

Existen cuatro tipos de parámetros para enrutar una URL:

  • :string - sólo acepta letras
  • :number - sólo acepta números
  • :alpha - acepta números y letras
  • :* - acepta cualquier cantidad y tipo de parámetros (sólo puede incluirse uno solo y al final)

En caso de incluir parámetros opcionales la sintaxis es la siguiente:

  • [/:string]
  • [/:number]
  • [/:alpha]
    $app = \DMS\Tornado\Tornado::getInstance();

    // utilizando un módulo y cualquier tipo de petición
    $app->route('/', 'demo|demo|index');

    // utilizando una función anónima y cualquier tipo de petición
    $app->route('/saludar/:alpha', function($pNombre = null) {
        echo 'Hola ' . $pNombre;
    });

    // utilizando parámetros opcionales y cualquier tipo de petición
    $app->route('/mostrar[/:alpha][/:number]', function ($pNombre = null, $pEdad = null) {
        echo 'Hola ' . $pNombre . ', ' . $pEdad;
    });

    // utilizando un comodín (n cantidad de parámetros) y cualquier tipo de petición
    $app->route('/felicitador/:*', function () {
        $params = func_get_args();
        echo 'Felicitaciones ' . (isset($params[0]) ? $params[0] : '');
    });

    // utilizando un módulo y petición POST
    $app->route('POST /', 'demo|demo|guardar');

    // utilizando un módulo y petición GET o POST
    $app->route('GET|POST /', 'demo|demo|listar');
    

También es posible definir parámetros con nombre. En dicho caso puede omitirse el uso de parámetros de entrada en las funciones anónimas o métodos de los módulos HMVC. Ejemplo:

    $app->route('/bienvenida/@nombre:alpha/tornado/@edad:number', function () use ($app) {
        echo 'Hola ' . $app->param('nombre') . ', Edad: ' . $app->param('edad');
    });
    

Puede agregar tipos de parámetros auxiliares de la siguiente forma:

    $app = \DMS\Tornado\Tornado::getInstance();

    $app->addTypeParam(':custom', '([123]+)');
    
    $app->route('/personalizado/:custom', function ($pCustom = null) {
        echo 'Parametro personalizado ' . $pCustom;
    });
    

Nota: El único enrutamiento obligatorio es el del nodo raíz ya que indica cuál será el callback a ejecutar por defecto al ingresar a la aplicación.

Delegaciones:

Es posible delegar la acción de un módulo/ruta hacia otro sin necesidad de realizar una redirección por http. Esta delegación invoca al otro módulo/ruta dentro del mismo request original. Ejemplo:

    // a módulo sin parámetros
    $app->forwardModule('modulo|clase|metodo');

    // a módulo con parámetros
    $app->forwardModule('modulo|clase|metodo', array('param1', 'param2'));

    // a url (parámetros incluidos en la url)
    $app->forwardUrl('/otra/ruta/1234');
    

Si se encuentra instalado FPM en el Servidor puede devolver el resultado al cliente y continuar con la ejecución de la petición actual en segundo plano de la siguiente forma:

    $app->finishRequest();
 
Anotaciones:

Algunas acciones pueden ser establecidas mediante anotaciones DocBlocks.

Enrutamientos:

En los controladores de los módulos HMVC puede utilizar el tag @T_ROUTE para setear un enrutamiento. Esto generará un archivo de configuración denominado "route_serialize.php".

Siempre que la aplicación se encuentre en modo de desarrollo (variable de configuración "tornado_environment_development" en true) se recorrerán los métodos de los controladores para actualizar este archivo de configuración.

Ejemplo:

    class Demo extends \DMS\Tornado\Controller
    {
        /**
         * Ejemplo de enrutamientos mediante anotaciones
         * @T_ROUTE /demo/anotacion
         * @T_ROUTE GET|POST /demo/otra/anotacion
         */
        public function index()
        {
            echo 'Hola Mundo Tornado';
        }
    }
Vistas

Puede incluir archivos de vistas/templates dentro de una ruta manejada por clousures de la siguiente forma:

    $app = \DMS\Tornado\Tornado::getInstance();

    $app->render('ruta/archivo.php');  // vista sin parámetros
    $app->render('ruta/archivo.php', array('nombre'=>'valor')); // vista con parámetros

Los parámetros pasados a la vista/template se manejan de la misma forma que los parámetros pasados a una vista de un módulo HMVC.

Gestión de errores y excepciones:

El manejo de errores y excepciones viene habilitado por defecto. Puede alterar su comportamiento de la siguiente forma:

    $app = \DMS\Tornado\Tornado::getInstance();

    $app->error(true);  // habilita el manejador
    $app->error(false); // deshabilita el manejador
    

Puede acceder a la última excepción lanzada de la siguiente forma:

    $app = \DMS\Tornado\Tornado::getInstance();

    $exc = $app->error();
    
Inyección de Dependencias:

Es posible extender el core mediante la inyección de nuevas clases. La forma de registrar una nueva dependencia es:

    $app->register('fecha', function($fecha = '2014-12-31'){
        return new \DateTime($fecha);
    });
    

Este registro creará un dependencia para la clase 'DateTime' denominada 'fecha'. Podrá hacer uso de la misma de la siguiente forma:

    $app = \DMS\Tornado\Tornado::getInstance();

    echo $app->container('fecha')->format('d/m/Y') . '<br />';
    

Por defecto todas las dependencias inyectadas crean una nueva instancia de la clase. Puede registrar el servicio como Singleton seteando el tercer parámetro opcional en true:

    $app->register('fecha', function(){
        return new \DateTime('2014-12-31');
    }, true);
    

Si las dependencias requieren parámetros en sus constructores puede definir los mismos de la siguiente forma:

    $app->register('fecha.config', '2014-12-31');
    
    $app->register('fecha', function(\DMS\Tornado\Service $c){
        return new \DateTime($c->get('fecha.config'));
    });
    
Organización de proyecto:

Existe un proyecto que dispone de un esqueleto para una aplicación base. Puede descargar el mismo desde https://github.com/danielspk/TornadoSkeletonApplication

Módulos:

Tornado PHP permite utilizar módulos HMVC de forma conjunta con las funciones anónimas.

Si utiliza Composer, se recomienda registrar la ubicación de los módulos en el autoload. Ejemplo:

    "autoload": {
        "psr-4": {
            "App\\Modules\\": "app/modules/"
        }
    },
Controladores:

Todos los controladores deben extender de \DMS\Tornado\Controller y deben definir un namespace que respete la especificación PSR-4. Ejemplo:

Asumiendo que los módulos HMVC se encuentran en App\Modules[Modulo HMVC]\Controller

    namespace App\Modules\Demo\Controller;

    use \DMS\Tornado\Controller;
    
    class Demo extends Controller {
        public function index($param = null){
            echo ' Hola ' . $param . '<br>';
        }
    }
    

Los Controladores poseen una instancia de tornado PHP como propiedad propia. Puede acceder a la misma de la siguiente forma:

    // permite acceder a una instancia de Tornado
    $app = $this->app;
    
Modelos:

Todos los controladores deben definir un namespace que respete la siguiente jerarquía: App\Modules[Modulo HMVC]\Model

    namespace App\Modules\Demo\Model;

    class Demo {
        public function getDemos($param = null){
            return true;
        }
    }
    
Vistas:

Dado que el controlador posee una instancia de Tornado es posible usar el método render() para invocar a una vista.

Resumen de Métodos:

DMS\Tornado\Tornado

Método Detalle
getInstance() Devuelve la instancia de Tornado (si no existe la crea)
run() Arranca el core
config(string) Devuelve el valor de la variable de configuración
config(array) Setea un array de configuración
config(string, mixed) Setea el valor en la variable de configuración
error() Devuelve la última excepción atrapada
error(bool) Habilita/deshabilita el manejador interno de errores y excepciones
hook(string) Ejecuta el gancho indicado
hook(string mixed) Registra un gancho y su callback
route(string, mixed) Registra un enrutamiento y su callback
addTypeParam(string, string) Registra un nuevo tipo de parámetro
register(string, callable, [bool]) Registra una clase/servicio para extender la aplicación
container(string) Devuelve un servicio o parámetro
render(string) Incluye una vista/template
render(string, array) Incluye una vista/template junto a un array de variables
param(string) Devuelve el valor de un parámetro del enrutamiento
getRouteMatch() Devuelve la ruta que se esta procesando
forwardModule(string) Delega la acción hacia otro módulo
forwardModule(string, array) Delega la acción hacia otro módulo
forwardUrl(string) Delega la acción hacia otra ruta
finishRequest() Devuelve el request al cliente y continua la ejecución del script actual

DMS\Tornado\Service

Método Detalle
get Devuelve un servicio o parámetro

DMS\Tornado\Controller

Atributo Detalle
app Instancia de Tornado

Licencia:

El proyecto se distribuye bajo la licencia MIT.

Tests unitarios:

Para ejecutar los test es necesario descargar PHPUnit. Sitúese en la carpeta raíz de Tornado y ejecute la siguiente instrucción por línea de comando:

    phpunit.phar

Ante errores o sugerencias escriba a la dirección de email de contacto.

Sugerencias y colaboración:

Email: info@daniel.spiridione.com.ar