TORNADO
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:
- Toro - http://toroweb.org/
- Flight - http://flightphp.com/
- Shield - https://github.com/enygma/shieldframework
- Slim - http://www.slimframework.com/
- AltoRouter - http://altorouter.com/
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.
-
Instale composer ( puede obtener ayuda en https://getcomposer.org/download/ )
-
Cree un archivo composer.json con los paquetes a instalar
{
"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.