Flight es un marco rápido, simple y extensible para PHP. Es bastante versátil y se puede utilizar para construir cualquier tipo de aplicación web. Está diseñado con simplicidad en mente y está escrito de una manera que es fácil de entender y usar.
Aquí tienes un breve artículo sobre por qué deberías usar un marco. Es una buena idea entender los beneficios de usar un marco antes de comenzar a usar uno.
Además, se ha creado un excelente tutorial por @lubiana. Aunque no entra en gran detalle sobre Flight específicamente, esta guía te ayudará a comprender algunos de los conceptos principales que rodean a un marco y por qué son beneficiosos de usar. Puedes encontrar el tutorial aquí.
Si estás migrando desde otro marco como Laravel, Slim, Fat-Free o Symfony a Flight, esta página te ayudará a entender las diferencias entre los dos.
Aprende cómo cargar automáticamente tus propias clases en tu aplicación.
Aprende cómo gestionar rutas para tu aplicación web. Esto también incluye grupos de rutas, parámetros de ruta e intermedios.
Aprende cómo utilizar intermedios para filtrar solicitudes y respuestas en tu aplicación.
Aprende cómo manejar solicitudes y respuestas en tu aplicación.
Aprende cómo enviar respuestas a tus usuarios.
Aprende cómo utilizar el motor de vistas incorporado para renderizar tus plantillas HTML.
Aprende cómo asegurar tu aplicación contra amenazas de seguridad comunes.
Aprende cómo configurar el marco para tu aplicación.
Aprende cómo extender el marco agregando tus propios métodos y clases.
Aprende cómo utilizar el sistema de eventos para añadir ganchos a tus métodos y métodos internos del marco.
Aprende cómo usar contenedores de inyección de dependencias (DIC) para gestionar las dependencias de tu aplicación.
Aprende acerca de los métodos principales del marco.
La compatibilidad con versiones anteriores se ha mantenido en su mayor parte, pero hay algunos cambios de los que debes ser consciente al migrar de v2 a v3.
Hay algunos problemas comunes con los que te puedes encontrar al usar Flight. Esta página te ayudará a resolver esos problemas.
Puedes detener el marco de trabajo en cualquier momento llamando al método halt:
halt
Flight::halt();
También puedes especificar un código de estado HTTP opcional y un mensaje:
HTTP
Flight::halt(200, 'Vuelvo enseguida...');
Llamar a halt descartará cualquier contenido de respuesta hasta ese punto. Si deseas detener el marco de trabajo y generar la respuesta actual, utiliza el método stop:
stop
Flight::stop();
Todos los errores y excepciones son capturados por Flight y pasados al método error. El comportamiento predeterminado es enviar una respuesta genérica de HTTP 500 Internal Server Error con información sobre el error.
error
HTTP 500 Internal Server Error
Puede anular este comportamiento según sus necesidades:
Flight::map('error', function (Throwable $error) { // Manejar error echo $error->getTraceAsString(); });
Por defecto, los errores no se registran en el servidor web. Puede habilitar esto cambiando la configuración:
Flight::set('flight.log_errors', true);
Cuando una URL no se puede encontrar, Flight llama al método notFound. El comportamiento predeterminado es enviar una respuesta de HTTP 404 Not Found con un mensaje simple.
notFound
HTTP 404 Not Found
Flight::map('notFound', function () { // Manejar no encontrado });
Laravel es un marco de trabajo completo que tiene todas las campanas y silbatos y un asombroso ecosistema enfocado en el desarrollador, pero a un costo en rendimiento y complejidad. El objetivo de Laravel es que el desarrollador tenga el más alto nivel de productividad y que las tareas comunes sean fáciles. Laravel es una excelente opción para desarrolladores que buscan construir una aplicación web empresarial completa. Eso conlleva algunos compromisos, especialmente en términos de rendimiento y complejidad. Aprender los conceptos básicos de Laravel puede ser fácil, pero adquirir competencia en el marco de trabajo puede llevar algún tiempo.
También hay tantos módulos de Laravel que los desarrolladores a menudo sienten que la única forma de resolver problemas es a través de estos módulos, cuando realmente podrían usar otra biblioteca o escribir su propio código.
El almacenamiento en búfer de salida es el proceso por el cual la salida generada por un script de PHP se almacena en un búfer (interno de PHP) antes de ser enviada al cliente. Esto te permite modificar la salida antes de enviarla al cliente.
En una aplicación MVC, el Controlador es el "gestor" y se encarga de lo que hace la vista. Tener una salida generada fuera del controlador (o en algunos casos de forma anónima en Flight) rompe el patrón MVC. Este cambio es para que se ajuste más al patrón MVC y hace que el marco sea más predecible y fácil de usar.
En v2, el almacenamiento en búfer de salida se manejaba de una manera en la que no cerraba consistentemente su propio búfer de salida, lo que hacía que las pruebas unitarias y la transmisión fueran más difíciles. Para la mayoría de los usuarios, este cambio puede que en realidad no les afecte. Sin embargo, si estás haciendo eco de contenido fuera de las llamadas de retorno y controladores (por ejemplo, en un gancho), es probable que tengas problemas. Hacer eco de contenido en ganchos y antes de que el marco realmente se ejecute puede haber funcionado en el pasado, pero no funcionará en el futuro.
// index.php require 'vendor/autoload.php'; // solo un ejemplo define('START_TIME', microtime(true)); function hello() { echo 'Hola Mundo'; } Flight::map('hello', 'hello'); Flight::after('hello', function(){ // esto en realidad estará bien echo '<p>Esta frase Hola Mundo fue traída a usted por la letra "H"</p>'; }); Flight::before('start', function(){ // cosas como esta causarán un error echo '<html><head><title>Mi Página</title></head><body>'; }); Flight::route('/', function(){ // esto está bien echo 'Hola Mundo'; // Esto también debería estar bien Flight::hello(); }); Flight::after('start', function(){ // esto causará un error echo '<div>Su página se cargó en '.(microtime(true) - START_TIME).' segundos</div></body></html>'; });
¿Todavía puedes mantener tu código antiguo tal como está sin necesidad de reescribirlo para que funcione con v3? ¡Sí, puedes! Puedes activar el comportamiento de renderizado v2 estableciendo la opción de configuración flight.v2.output_buffering en true. Esto te permitirá seguir usando el antiguo comportamiento de renderizado, pero se recomienda corregirlo de aquí en adelante. En la versión 4 del marco, esto será eliminado.
flight.v2.output_buffering
true
// index.php require 'vendor/autoload.php'; Flight::set('flight.v2.output_buffering', true); Flight::before('start', function(){ // Ahora esto estará bien echo '<html><head><title>Mi Página</title></head><body>'; }); // más código
Si has estado llamando directamente a métodos estáticos para Dispatcher como Dispatcher::invokeMethod(), Dispatcher::execute(), etc. necesitarás actualizar tu código para no llamar directamente a estos métodos. Dispatcher se ha convertido en una manera más orientada a objetos para que los Contenedores de Inyección de Dependencias puedan ser utilizados de una forma más sencilla. Si necesitas invocar un método de manera similar a como lo hacía Dispatcher, puedes usar manualmente algo como $result = $class->$method(...$params); o call_user_func_array() en su lugar.
Dispatcher
Dispatcher::invokeMethod()
Dispatcher::execute()
$result = $class->$method(...$params);
call_user_func_array()
halt()
stop()
redirect()
error()
El comportamiento predeterminado antes de la versión 3.10.0 era limpiar tanto los encabezados como el cuerpo de la respuesta. Esto fue cambiado para limpiar solo el cuerpo de la respuesta. Si necesitas limpiar también los encabezados, puedes usar Flight::response()->clear().
Flight::response()->clear()
Puede personalizar ciertos comportamientos de Flight configurando valores de configuración a través del método 'set'.
La siguiente es una lista de todas las configuraciones disponibles:
?string
bool
string
Content-Length
Adicionalmente, hay otra configuración del cargador. Esto le permitirá cargar clases con _ en el nombre de la clase.
_
// Habilitar la carga de clase con guiones bajos // Predeterminado a true Loader::$v2ClassLoading = false;
Flight le permite guardar variables para que puedan ser utilizadas en cualquier lugar de su aplicación.
// Guarde su variable Flight::set('id', 123); // En otro lugar de su aplicación $id = Flight::get('id');
Para ver si una variable ha sido establecida, puede hacerlo así:
if (Flight::has('id')) { // Hacer algo }
Puede borrar una variable haciendo:
// Borra la variable id Flight::clear('id'); // Borra todas las variables Flight::clear();
Flight también utiliza variables con fines de configuración.
Todos los errores y excepciones son capturados por Flight y pasados al método 'error'. El comportamiento predeterminado es enviar una respuesta genérica de 'HTTP 500 Internal Server Error' con alguna información de error.
Cuando no se puede encontrar una URL, Flight llama al método 'notFound'. El comportamiento predeterminado es enviar una respuesta de 'HTTP 404 Not Found' con un mensaje simple.
La seguridad es fundamental cuando se trata de aplicaciones web. Quieres asegurarte de que tu aplicación sea segura y de que los datos de tus usuarios estén protegidos. Flight proporciona una serie de funciones para ayudarte a asegurar tus aplicaciones web.
Las cabeceras HTTP son una de las formas más fáciles de asegurar tus aplicaciones web. Puedes utilizar cabeceras para prevenir el secuestro de clics, XSS y otros ataques. Hay varias formas de agregar estas cabeceras a tu aplicación.
Dos excelentes sitios web para verificar la seguridad de tus cabeceras son securityheaders.com y observatory.mozilla.org.
Puedes agregar manualmente estas cabeceras utilizando el método header en el objeto Flight\Response.
header
Flight\Response
// Establecer la cabecera X-Frame-Options para evitar el secuestro de clics Flight::response()->header('X-Frame-Options', 'SAMEORIGIN'); // Establecer la cabecera Content-Security-Policy para evitar XSS // Nota: esta cabecera puede ser muy compleja, así que es recomendable // consultar ejemplos en internet para tu aplicación Flight::response()->header("Content-Security-Policy", "default-src 'self'"); // Establecer la cabecera X-XSS-Protection para prevenir XSS Flight::response()->header('X-XSS-Protection', '1; mode=block'); // Establecer la cabecera X-Content-Type-Options para prevenir el sniffing de MIME Flight::response()->header('X-Content-Type-Options', 'nosniff'); // Establecer la cabecera Referrer-Policy para controlar cuánta información del referente se envía Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade'); // Establecer la cabecera Strict-Transport-Security para forzar HTTPS Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload'); // Establecer la cabecera Permissions-Policy para controlar qué características y APIs se pueden utilizar Flight::response()->header('Permissions-Policy', 'geolocation=()');
Estas se pueden agregar al principio de tus archivos bootstrap.php o index.php.
bootstrap.php
index.php
También puedes agregarlas en un filtro/gancho como se muestra a continuación:
// Agregar las cabeceras en un filtro Flight::before('start', function() { Flight::response()->header('X-Frame-Options', 'SAMEORIGIN'); Flight::response()->header("Content-Security-Policy", "default-src 'self'"); Flight::response()->header('X-XSS-Protection', '1; mode=block'); Flight::response()->header('X-Content-Type-Options', 'nosniff'); Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade'); Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload'); Flight::response()->header('Permissions-Policy', 'geolocation=()'); });
También puedes agregarlas como una clase de middleware. Esta es una buena manera de mantener tu código limpio y organizado.
// app/middleware/SecurityHeadersMiddleware.php namespace app\middleware; class SecurityHeadersMiddleware { public function before(array $params): void { Flight::response()->header('X-Frame-Options', 'SAMEORIGIN'); Flight::response()->header("Content-Security-Policy", "default-src 'self'"); Flight::response()->header('X-XSS-Protection', '1; mode=block'); Flight::response()->header('X-Content-Type-Options', 'nosniff'); Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade'); Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload'); Flight::response()->header('Permissions-Policy', 'geolocation=()'); } } // index.php o donde tengas tus rutas // Para tu información, este grupo de cadena vacía actúa como un middleware global para // todas las rutas. Por supuesto, podrías hacer lo mismo y agregar esto únicamente a rutas específicas. Flight::group('', function(Router $router) { $router->get('/users', [ 'UserController', 'getUsers' ]); // más rutas }, [ new SecurityHeadersMiddleware() ]);
La falsificación de solicitudes entre sitios (CSRF) es un tipo de ataque en el que un sitio web malicioso puede hacer que el navegador del usuario envíe una solicitud a tu sitio web. Esto se puede utilizar para realizar acciones en tu sitio web sin el conocimiento del usuario. Flight no proporciona un mecanismo integrado de protección CSRF, pero puedes implementar fácilmente el tuyo propio mediante el uso de middleware.
Primero debes generar un token CSRF y almacenarlo en la sesión del usuario. Luego puedes usar este token en tus formularios y verificarlo cuando se envíe el formulario.
// Generar un token CSRF y guardarlo en la sesión del usuario // (asumiendo que has creado un objeto de sesión y lo has adjuntado a Flight) // consulta la documentación de la sesión para obtener más información Flight::register('session', \Ghostff\Session\Session::class); // Solo necesitas generar un token por sesión (para que funcione // en múltiples pestañas y solicitudes para el mismo usuario) if(Flight::session()->get('csrf_token') === null) { Flight::session()->set('csrf_token', bin2hex(random_bytes(32)) ); }
<!-- Utilizar el token CSRF en tu formulario --> <form method="post"> <input type="hidden" name="csrf_token" value="<?= Flight::session()->get('csrf_token') ?>"> <!-- otros campos del formulario --> </form>
También puedes configurar una función personalizada para mostrar el token CSRF en tus plantillas de Latte.
// Configurar una función personalizada para mostrar el token CSRF // Nota: View ha sido configurado con Latte como motor de vista Flight::view()->addFunction('csrf', function() { $csrfToken = Flight::session()->get('csrf_token'); return new \Latte\Runtime\Html('<input type="hidden" name="csrf_token" value="' . $csrfToken . '">'); });
Y ahora en tus plantillas de Latte puedes usar la función csrf() para mostrar el token CSRF.
csrf()
<form method="post"> {csrf()} <!-- otros campos del formulario --> </form>
¡Breve y sencillo, ¿verdad?
Puedes verificar el token CSRF usando filtros de eventos:
// Este middleware verifica si la solicitud es una solicitud POST y, si lo es, verifica si el token CSRF es válido Flight::before('start', function() { if(Flight::request()->method == 'POST') { // capturar el token csrf de los valores del formulario $token = Flight::request()->data->csrf_token; if($token !== Flight::session()->get('csrf_token')) { Flight::halt(403, 'Token CSRF no válido'); // o para una respuesta JSON Flight::jsonHalt(['error' => 'Token CSRF no válido'], 403); } } });
O puedes usar una clase de middleware:
// app/middleware/CsrfMiddleware.php namespace app\middleware; class CsrfMiddleware { public function before(array $params): void { if(Flight::request()->method == 'POST') { $token = Flight::request()->data->csrf_token; if($token !== Flight::session()->get('csrf_token')) { Flight::halt(403, 'Token CSRF no válido'); } } } } // index.php o donde tengas tus rutas Flight::group('', function(Router $router) { $router->get('/users', [ 'UserController', 'getUsers' ]); // más rutas }, [ new CsrfMiddleware() ]);
Las secuencias de comandos entre sitios (XSS) son un tipo de ataque en el que un sitio web malicioso puede inyectar código en tu sitio web. La mayoría de estas oportunidades provienen de los valores de los formularios que completarán tus usuarios finales. ¡Nunca debes confiar en la salida de tus usuarios! Siempre asume que todos son los mejores hackers del mundo. Pueden inyectar JavaScript o HTML maliciosos en tu página. Este código se puede utilizar para robar información de tus usuarios o realizar acciones en tu sitio web. Utilizando la clase de vista de Flight, puedes escapar fácilmente la salida para prevenir ataques XSS.
// Vamos a asumir que el usuario es ingenioso e intenta usar esto como su nombre $nombre = '<script>alert("XSS")</script>'; // Esto escapará la salida Flight::view()->set('name', $name); // Esto mostrará: <script>alert("XSS")</script> // Si usas algo como Latte registrado como tu clase de vista, también se escapará automáticamente. Flight::view()->render('plantilla', ['name' => $nombre]);
La inyección SQL es un tipo de ataque en el que un usuario malintencionado puede inyectar código SQL en tu base de datos. Esto se puede utilizar para robar información de tu base de datos o realizar acciones en tu base de datos. De nuevo, ¡nunca debes confiar en la entrada de tus usuarios! Siempre asume que están en busca de sangre. Puedes utilizar declaraciones preparadas en tus objetos PDO para evitar la inyección SQL.
PDO
// Suponiendo que tienes Flight::db() registrado como tu objeto PDO $declaracion = Flight::db()->prepare('SELECT * FROM users WHERE username = :username'); $declaracion->execute([':username' => $nombre_de_usuario]); $usuarios = $declaracion->fetchAll(); // Si utilizas la clase PdoWrapper, esto se puede hacer fácilmente en una línea $usuarios = Flight::db()->fetchAll('SELECT * FROM users WHERE username = :username', [ 'username' => $nombre_de_usuario ]); // Puedes hacer lo mismo con un objeto PDO con marcadores de posición ? $declaracion = Flight::db()->fetchAll('SELECT * FROM users WHERE username = ?', [ $nombre_de_usuario ]); // Solo promete que nunca, JAMÁS, harás algo como esto... $usuarios = Flight::db()->fetchAll("SELECT * FROM users WHERE username = '{$nombre_de_usuario}' LIMIT 5"); // porque ¿qué pasa si $nombre_de_usuario = "' O 1=1; -- "; // Después de construir la consulta se vería así // SELECT * FROM users WHERE username = '' OR 1=1; -- LIMIT 5 // Parece extraño, ¡pero es una consulta válida que funcionará! De hecho, // es un ataque de inyección SQL muy común que devolverá a todos los usuarios.
El intercambio de recursos de origen cruzado (CORS) es un mecanismo que permite a muchos recursos (por ejemplo, fuentes, JavaScript, etc.) en una página web ser solicitados desde otro dominio fuera del dominio del cual se originó el recurso. Flight no tiene funcionalidad incorporada, pero esto se puede manejar fácilmente con un gancho que se ejecuta antes de que se llame al método Flight::start().
Flight::start()
// app/utils/CorsUtil.php namespace app\utils; class CorsUtil { public function set(array $params): void { $solicitud = Flight::request(); $respuesta = Flight::response(); if ($request->getVar('HTTP_ORIGIN') !== '') { $this->allowOrigins(); $response->header('Access-Control-Allow-Credentials', 'true'); $response->header('Access-Control-Max-Age', '86400'); } if ($request->method === 'OPTIONS') { if ($request->getVar('HTTP_ACCESS_CONTROL_REQUEST_METHOD') !== '') { $response->header( 'Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD' ); } if ($request->getVar('HTTP_ACCESS_CONTROL_REQUEST_HEADERS') !== '') { $response->header( "Access-Control-Allow-Headers", $request->getVar('HTTP_ACCESS_CONTROL_REQUEST_HEADERS') ); } $response->status(200); $response->send(); exit; } } private function allowOrigins(): void { // personaliza tus hosts permitidos aquí. $permitidos = [ 'capacitor://localhost', 'ionic://localhost', 'http://localhost', 'http://localhost:4200', 'http://localhost:8080', 'http://localhost:8100', ]; $solicitud = Flight::request(); if (in_array($request->getVar('HTTP_ORIGIN'), $permitidos, true) === true) { $respuesta = Flight::response(); $respuesta->header("Access-Control-Allow-Origin", $request->getVar('HTTP_ORIGIN')); } } } // index.php o donde tengas tus rutas $CorsUtil = new CorsUtil(); // Esto debe ejecutarse antes de que start se ejecute. Flight::before('start', [ $CorsUtil, 'setupCors' ]);
La seguridad es fundamental y es importante asegurarse de que tus aplicaciones web sean seguras. Flight proporciona una serie de funciones para ayudarte a asegurar tus aplicaciones web, pero es importante estar siempre vigilante y asegurarse de hacer todo lo posible para mantener seguros los datos de tus usuarios. Siempre asume lo peor y nunca confíes en la entrada de tus usuarios. Siempre escapa la salida y utiliza declaraciones preparadas para prevenir la inyección SQL. Utiliza siempre middleware para proteger tus rutas de ataques CSRF y CORS. Si haces todas estas cosas, estarás en buen camino para construir aplicaciones web seguras.
Flight te permite anular su funcionalidad predeterminada para adaptarla a tus propias necesidades, sin necesidad de modificar ningún código.
Por ejemplo, cuando Flight no puede encontrar una URL que coincida con una ruta, invoca el método notFound que envía una respuesta genérica de HTTP 404. Puedes anular este comportamiento utilizando el método map:
HTTP 404
map
Flight::map('notFound', function() { // Mostrar página de error 404 personalizada include 'errors/404.html'; });
Flight también te permite reemplazar componentes principales del framework. Por ejemplo, puedes reemplazar la clase Router predeterminada con tu propia clase personalizada:
// Registra tu clase personalizada Flight::register('router', MyRouter::class); // Cuando Flight carga la instancia del enrutador, cargará tu clase $myrouter = Flight::router();
Sin embargo, los métodos del framework como map y register no se pueden anular. Obtendrás un error si intentas hacerlo.
register
Nota: ¿Quieres entender más sobre el enrutamiento? Consulta la página "¿Por qué un framework?" para obtener una explicación más detallada.
El enrutamiento básico en Flight se realiza al hacer coincidir un patrón de URL con una función de devolución de llamada o una matriz de una clase y un método.
Flight::route('/', function(){ echo '¡Hola Mundo!'; });
Las rutas se emparejan en el orden en que se definen. La primera ruta que se empareje con una solicitud será invocada.
La devolución de llamada puede ser cualquier objeto que sea invocable. Por lo tanto, puedes usar una función regular:
function hello() { echo '¡Hola Mundo!'; } Flight::route('/', 'hello');
También puedes usar un método estático de una clase:
class Saludo { public static function hello() { echo '¡Hola Mundo!'; } } Flight::route('/', [ 'Saludo','hello' ]);
O creando un objeto primero y luego llamando al método:
// Greeting.php class Saludo { public function __construct() { $this->name = 'Juan Pérez'; } public function hello() { echo "¡Hola, {$this->name}!"; } } // index.php $saludo = new Saludo(); Flight::route('/', [ $saludo, 'hello' ]); // También puedes hacer esto sin crear primero el objeto // Nota: No se inyectarán argumentos en el constructor Flight::route('/', [ 'Saludo', 'hello' ]); // Adicionalmente puedes usar esta sintaxis más corta Flight::route('/', 'Saludo->hello'); // o Flight::route('/', Saludo::class.'->hello');
Si deseas utilizar la inyección de dependencias a través de un contenedor (PSR-11, PHP-DI, Dice, etc), el único tipo de rutas donde esto está disponible es creando directamente el objeto tú mismo y usando el contenedor para crear tu objeto o puedes usar cadenas para definir la clase y método a llamar. Puedes ir a la página de Inyección de Dependencias para obtener más información.
Aquí tienes un ejemplo rápido:
use flight\database\PdoWrapper; // Greeting.php class Saludo { protected PdoWrapper $pdoWrapper; public function __construct(PdoWrapper $pdoWrapper) { $this->pdoWrapper = $pdoWrapper; } public function hello(int $id) { // hacer algo con $this->pdoWrapper $nombre = $this->pdoWrapper->fetchField("SELECT nombre FROM usuarios WHERE id = ?", [ $id ]); echo "¡Hola, mundo! Mi nombre es {$nombre}!"; } } // index.php // Configura el contenedor con los parámetros que necesites // Consulta la página de Inyección de Dependencias para obtener más información sobre PSR-11 $dice = new \Dice\Dice(); // ¡No olvides reasignar la variable con '$dice = '!!!!! $dice = $dice->addRule('flight\database\PdoWrapper', [ 'shared' => true, 'constructParams' => [ 'mysql:host=localhost;dbname=test', 'root', 'password' ] ]); // Registra el controlador de contenedores Flight::registerContainerHandler(function($class, $params) use ($dice) { return $dice->create($class, $params); }); // Rutas como de costumbre Flight::route('/hola/@id', [ 'Saludo', 'hello' ]); // o Flight::route('/hola/@id', 'Saludo->hello'); // o Flight::route('/hola/@id', 'Saludo::hello'); Flight::start();
Por defecto, los patrones de rutas coinciden con todos los métodos de solicitud. Puedes responder a métodos específicos colocando un identificador antes de la URL.
Flight::route('GET /', function () { echo 'He recibido una solicitud GET.'; }); Flight::route('POST /', function () { echo 'He recibido una solicitud POST.'; }); // No puedes usar Flight::get() para rutas ya que ese es un método // para obtener variables, no crear una ruta. // Flight::post('/', function() { /* código */ }); // Flight::patch('/', function() { /* código */ }); // Flight::put('/', function() { /* código */ }); // Flight::delete('/', function() { /* código */ });
También puedes mapear varios métodos a una sola devolución de llamada usando el delimitador |:
|
Flight::route('GET|POST /', function () { echo 'He recibido una solicitud GET o POST.'; });
Además, puedes obtener el objeto Router que tiene algunos métodos auxiliares para que los uses:
$router = Flight::router(); // mapea todos los métodos $router->map('/', function() { echo '¡Hola Mundo!'; }); // solicitud GET $router->get('/usuarios', function() { echo 'usuarios'; }); // $router->post(); // $router->put(); // $router->delete(); // $router->patch();
Puedes usar expresiones regulares en tus rutas:
Flight::route('/usuario/[0-9]+', function () { // Esto coincidirá con /usuario/1234 });
Aunque este método está disponible, se recomienda usar parámetros nombrados, o parámetros nombrados con expresiones regulares, ya que son más legibles y fáciles de mantener.
Puedes especificar parámetros nombrados en tus rutas que se pasarán a tu función de devolución de llamada.
Flight::route('/@nombre/@id', function (string $nombre, string $id) { echo "¡hola, $nombre ($id)!"; });
También puedes incluir expresiones regulares con tus parámetros nombrados mediante el delimitador ::
:
Flight::route('/@nombre/@id:[0-9]{3}', function (string $nombre, string $id) { // Esto coincidirá con /bob/123 // Pero no coincidirá con /bob/12345 });
Nota: No se admite la coincidencia de grupos de expresiones regulares () con parámetros nombrados. :'(
()
Puedes especificar parámetros nombrados que sean opcionales para la coincidencia envolviendo segmentos entre paréntesis.
Flight::route( '/blog(/@año(/@mes(/@día)))', function(?string $año, ?string $mes, ?string $día) { // Esto coincidirá con las siguientes URL: // /blog/2012/12/10 // /blog/2012/12 // /blog/2012 // /blog } );
Cualquier parámetro opcional que no coincida se pasará como NULL.
NULL
La coincidencia se realiza solo en segmentos individuales de URL. Si deseas coincidir múltiples segmentos puedes usar el comodín *.
*
Flight::route('/blog/*', function () { // Esto coincidirá con /blog/2000/02/01 });
Para enrutar todas las solicitudes a una sola devolución de llamada, puedes hacer:
Flight::route('*', function () { // Haz algo });
Puedes pasar la ejecución a la siguiente ruta coincidente devolviendo true desde tu función de devolución de llamada.
Flight::route('/usuario/@nombre', function (string $nombre) { // Comprobar alguna condición if ($nombre !== "Bob") { // Continuar con la siguiente ruta return true; } }); Flight::route('/usuario/*', function () { // Esto se llamará });
Puedes asignar un alias a una ruta, para que la URL se pueda generar dinámicamente más tarde en tu código (como una plantilla, por ejemplo).
Flight::route('/usuarios/@id', function($id) { echo 'usuario:'.$id; }, false, 'vista_usuario'); // más tarde en algún lugar del código Flight::getUrl('vista_usuario', [ 'id' => 5 ]); // devolverá '/usuarios/5'
Esto es especialmente útil si tu URL cambia. En el ejemplo anterior, supongamos que los usuarios se movieron a /admin/usuarios/@id en cambio. Con el alias en su lugar, no tienes que cambiar en ningún lugar donde hagas referencia al alias, porque el alias ahora devolverá /admin/usuarios/5 como en el ejemplo anterior.
/admin/usuarios/@id
/admin/usuarios/5
El alias de ruta también funciona en grupos:
Flight::group('/usuarios', function() { Flight::route('/@id', function($id) { echo 'usuario:'.$id; }, false, 'vista_usuario'); }); // más tarde en algún lugar del código Flight::getUrl('vista_usuario', [ 'id' => 5 ]); // devolverá '/usuarios/5'
Si deseas inspeccionar la información de la ruta coincidente, puedes solicitar que se pase el objeto de ruta a tu devolución de llamada pasando true como tercer parámetro en el método de ruta. El objeto de ruta siempre será el último parámetro pasado a tu función de devolución de llamada.
Flight::route('/', function(\flight\net\Route $ruta) { // Matriz de métodos HTTP que coinciden $ruta->methods; // Matriz de parámetros nombrados $ruta->params; // Coincidencia de expresión regular $ruta->regex; // Contiene el contenido de cualquier '*' utilizado en el patrón de URL $ruta->splat; // Muestra la ruta de la URL.... si realmente la necesitas $ruta->pattern; // Muestra qué middleware está asignado a esta $ruta->middleware; // Muestra el alias asignado a esta ruta $ruta->alias; }, true);
Puede haber momentos en los que desees agrupar rutas relacionadas juntas (como /api/v1). Puedes hacer esto utilizando el método group:
/api/v1
group
Flight::group('/api/v1', function () { Flight::route('/usuarios', function () { // Coincide con /api/v1/usuarios }); Flight::route('/publicaciones', function () { // Coincide con /api/v1/publicaciones }); });
Incluso puedes anidar grupos de grupos:
Flight::group('/api', function () { Flight::group('/v1', function () { // Flight::get() obtiene variables, ¡no establece una ruta! Ver contexto de objeto abajo Flight::route('GET /usuarios', function () { // Coincide con GET /api/v1/usuarios }); Flight::post('/publicaciones', function () { // Coincide con POST /api/v1/publicaciones }); Flight::put('/publicaciones/1', function () { // Coincide con PUT /api/v1/publicaciones }); }); Flight::group('/v2', function () { // Flight::get() obtiene variables, ¡no establece una ruta! Ver contexto de objeto abajo Flight::route('GET /usuarios', function () { // Coincide con GET /api/v2/usuarios }); }); });
Todavía puedes usar la agrupación de rutas con el objeto Engine de la siguiente manera:
Engine
$app = new \flight\Engine(); $app->group('/api/v1', function (Router $enrutador) { // usa la variable $enrutador $enrutador->get('/usuarios', function () { // Coincide con GET /api/v1/usuarios }); $enrutador->post('/publicaciones', function () { // Coincide con POST /api/v1/publicaciones }); });
Ahora puedes transmitir respuestas al cliente utilizando el método streamWithHeaders(). Esto es útil para enviar archivos grandes, procesos de larga duración o generar respuestas grandes. La transmisión de una ruta se maneja de forma un poco diferente a una ruta normal.
streamWithHeaders()
Nota: Las respuestas en streaming solo están disponibles si tienes flight.v2.output_buffering configurado en falso.
Puedes transmitir una respuesta al cliente usando el método stream() en una ruta. Si lo haces esto, debes configurar todos los encabezados manualmente antes de enviar cualquier cosa al cliente. Esto se hace con la función header() de PHP o el método Flight::response()->setRealHeader().
stream()
header()
Flight::response()->setRealHeader()
Flight::route('/@nombreArchivo', function($nombreArchivo) { // obviamente sanitizarías la ruta y lo que sea necesario. $nombreArchivoSeguro = basename($nombreArchivo); // Si tienes encabezados adicionales para configurar aquí después de que la ruta se haya ejecutado // debes definirlos antes de imprimir algo. // Todos deben ser una llamada directa a la función header() o // una llamada a Flight::response()->setRealHeader() header('Content-Disposition: attachment; filename="'.$nombreArchivoSeguro.'"'); // o Flight::response()->setRealHeader('Content-Disposition', 'attachment; filename="'.$nombreArchivoSeguro.'"'); $datosArchivo = file_get_contents('/alguna/ruta/a/archivos/'.$nombreArchivoSeguro); // Captura de errores y lo que sea necesario if(empty($datosArchivo)) { Flight::halt(404, 'Archivo no encontrado'); } // configurar manualmente la longitud del contenido si así lo prefieres header('Content-Length: '.filesize($nombreArchivo)); // Transmite los datos al cliente echo $datosArchivo; // Esta es la línea mágica aquí })->stream();
También puedes usar el método streamWithHeaders() para establecer los encabezados antes de comenzar a transmitir.
Flight::route('/stream-usuarios', function() { // puedes agregar cualquier encabezado adicional que desees aquí // solo debes usar header() o Flight::response()->setRealHeader() // sin embargo extraigas tus datos, solo como ejemplo... $usuarios_stmt = Flight::db()->query("SELECT id, nombre, apellido FROM usuarios"); echo '{'; $conteoUsuarios = count($usuarios); while($usuario = $usuarios_stmt->fetch(PDO::FETCH_ASSOC)) { echo json_encode($usuario); if(--$conteoUsuarios > 0) { echo ','; } // Esto es necesario para enviar los datos al cliente ob_flush(); } echo '}'; // Así es como configurarás los encabezados antes de comenzar a transmitir. })->streamWithHeaders([ 'Content-Type' => 'application/json', 'Content-Disposition' => 'attachment; filename="usuarios.json"', // código de estado opcional, predeterminado a 200 'estado' => 200 ]);
Symfony es un conjunto de componentes reutilizables de PHP y un framework de PHP para proyectos web.
El fundamento estándar sobre el cual se construyen las mejores aplicaciones PHP. Elija cualquiera de los 50 componentes independientes disponibles para sus propias aplicaciones.
Acelere la creación y el mantenimiento de sus aplicaciones web de PHP. Finalice las tareas de codificación repetitivas y disfrute del poder de controlar su código.
Si estás migrando de otro framework como Laravel, Slim, Fat-Free o Symfony a Flight, esta página te ayudará a entender las diferencias entre los dos.
Laravel es un framework completo que tiene todas las funciones y una increíble comunidad enfocada en el desarrollador, pero a un costo en rendimiento y complejidad.
Ver la comparación entre Laravel y Flight.
Slim es un micro-framework similar a Flight. Está diseñado para ser ligero y fácil de usar, pero puede ser un poco más complejo que Flight.
Ver la comparación entre Slim y Flight.
Fat-Free es un framework full-stack en un paquete mucho más pequeño. Aunque tiene todas las herramientas necesarias, tiene una arquitectura de datos que puede hacer que algunos proyectos sean más complejos de lo necesario.
Ver la comparación entre Fat-Free y Flight.
Symfony es un framework modular a nivel empresarial que está diseñado para ser flexible y escalable. Para proyectos más pequeños o desarrolladores nuevos, Symfony puede resultar un poco abrumador.
Ver la comparación entre Symfony y Flight.
Flight permite guardar variables para que puedan ser utilizadas en cualquier lugar de tu aplicación.
// Guarda tu variable Flight::set('id', 123); // En otro lugar de tu aplicación $id = Flight::get('id');
Para verificar si una variable ha sido establecida, puedes hacer lo siguiente:
if (Flight::has('id')) { // Haz algo }
Puedes limpiar una variable haciendo:
// Elimina la variable id Flight::clear('id'); // Elimina todas las variables Flight::clear();
Flight también utiliza variables con propósitos de configuración.
El Contenedor de Inyección de Dependencias (DIC) es una herramienta potente que te permite gestionar las dependencias de tu aplicación. Es un concepto clave en los marcos de PHP modernos y se utiliza para gestionar la instanciación y configuración de objetos. Algunos ejemplos de bibliotecas DIC son: Dice, Pimple, PHP-DI y league/container.
Un DIC es una forma elegante de decir que te permite crear y gestionar tus clases en una ubicación centralizada. Esto es útil cuando necesitas pasar el mismo objeto a varias clases (como tus controladores). Un ejemplo sencillo podría ayudar a entender esto mejor.
La forma antigua de hacer las cosas podría verse así:
require 'vendor/autoload.php'; // clase para gestionar usuarios desde la base de datos class UserController { protected PDO $pdo; public function __construct(PDO $pdo) { $this->pdo = $pdo; } public function view(int $id) { $stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = :id'); $stmt->execute(['id' => $id]); print_r($stmt->fetch()); } } $User = new UserController(new PDO('mysql:host=localhost;dbname=test', 'user', 'pass')); Flight::route('/user/@id', [ $UserController, 'view' ]); Flight::start();
Se puede ver en el código anterior que estamos creando un nuevo objeto PDO y pasándolo a nuestra clase UserController. Esto está bien para una aplicación pequeña, pero a medida que tu aplicación crece, descubrirás que estás creando el mismo objeto PDO en múltiples lugares. Aquí es donde resulta útil un DIC.
UserController
Aquí tienes el mismo ejemplo utilizando un DIC (usando Dice):
require 'vendor/autoload.php'; // misma clase que arriba. Sin cambios class UserController { protected PDO $pdo; public function __construct(PDO $pdo) { $this->pdo = $pdo; } public function view(int $id) { $stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = :id'); $stmt->execute(['id' => $id]); print_r($stmt->fetch()); } } // crear un nuevo contenedor $container = new \Dice\Dice; // ¡no olvides volver a asignarlo a sí mismo como se muestra abajo! $container = $container->addRule('PDO', [ // shared significa que el mismo objeto se devolverá cada vez 'shared' => true, 'constructParams' => ['mysql:host=localhost;dbname=test', 'user', 'pass' ] ]); // Esto registra el controlador de contenedor para que Flight sepa usarlo. Flight::registerContainerHandler(function($class, $params) use ($container) { return $container->create($class, $params); }); // ahora podemos usar el contenedor para crear nuestro UserController Flight::route('/user/@id', [ 'UserController', 'view' ]); // o alternativamente puedes definir la ruta así Flight::route('/user/@id', 'UserController->view'); // o Flight::route('/user/@id', 'UserController::view'); Flight::start();
Apuesto a que puedes estar pensando que se añadió mucho código extra al ejemplo. La magia reside en cuando tienes otro controlador que necesita el objeto PDO.
// Si todos tus controladores tienen un constructor que necesita un objeto PDO // ¡¡¡Cada una de las rutas a continuación lo recibirán automáticamente inyectado!!! Flight::route('/empresa/@id', 'CompanyController->view'); Flight::route('/organización/@id', 'OrganizationController->view'); Flight::route('/categoría/@id', 'CategoryController->view'); Flight::route('/ajustes', 'SettingsController->view');
El beneficio adicional de utilizar un DIC es que las pruebas unitarias se vuelven mucho más fáciles. Puedes crear un objeto simulado y pasarlo a tu clase. ¡Este es un gran beneficio al escribir pruebas para tu aplicación!
Flight también puede utilizar cualquier contenedor compatible con PSR-11. Esto significa que puedes usar cualquier contenedor que implemente la interfaz PSR-11. Aquí tienes un ejemplo utilizando el contenedor PSR-11 de League:
require 'vendor/autoload.php'; // misma clase UserController que arriba $container = new \League\Container\Container(); $container->add(UserController::class)->addArgument(PdoWrapper::class); $container->add(PdoWrapper::class) ->addArgument('mysql:host=localhost;dbname=test') ->addArgument('user') ->addArgument('pass'); Flight::registerContainerHandler($container); Flight::route('/user', [ 'UserController', 'view' ]); Flight::start();
Aunque pueda ser un poco más detallado que el ejemplo anterior con Dice, aún se logra el mismo resultado con los mismos beneficios.
También puedes crear tu propio controlador DIC. Esto es útil si tienes un contenedor personalizado que quieres utilizar y que no es compatible con PSR-11 (Dice). Consulta el ejemplo básico para ver cómo hacerlo.
Además, existen algunas configuraciones útiles que facilitarán tu vida al usar Flight.
Si estás utilizando la instancia del Engine en tus controladores/middleware, así es como lo configurarías:
// En algún lugar de tu archivo de inicio $engine = Flight::app(); $container = new \Dice\Dice; $container = $container->addRule('*', [ 'substitutions' => [ // Aquí es donde pasas la instancia Engine::class => $engine ] ]); $engine->registerContainerHandler(function($class, $params) use ($container) { return $container->create($class, $params); }); // Ahora puedes usar la instancia del Motor en tus controladores/middleware class MyController { public function __construct(Engine $app) { $this->app = $app; } public function index() { $this->app->render('index'); } }
Si tienes otras clases que quieres agregar al contenedor, con Dice es fácil ya que serán resueltas automáticamente por el contenedor. Aquí tienes un ejemplo:
$container = new \Dice\Dice; // Si no necesitas inyectar nada en tu clase // ¡no necesitas definir nada! Flight::registerContainerHandler(function($class, $params) use ($container) { return $container->create($class, $params); }); class MyCustomClass { public function parseThing() { return 'cosa'; } } class UserController { protected MyCustomClass $MyCustomClass; public function __construct(MyCustomClass $MyCustomClass) { $this->MyCustomClass = $MyCustomClass; } public function index() { echo $this->MyCustomClass->parseThing(); } } Flight::route('/usuario', 'UserController->index');
Flight admite middleware de ruta y de grupo de ruta. El middleware es una función que se ejecuta antes (o después) de la devolución de llamada de la ruta. Esta es una excelente manera de agregar verificaciones de autenticación de API en su código, o para validar que el usuario tiene permiso para acceder a la ruta.
Aquí tienes un ejemplo básico:
// Si solo proporciona una función anónima, se ejecutará antes de la devolución de llamada de la ruta. // no hay funciones de middleware "después" excepto para las clases (ver abajo) Flight::route('/path', function() { echo '¡Aquí estoy!'; })->addMiddleware(function() { echo '¡Middleware primero!'; }); Flight::start(); // ¡Esto mostrará "¡Middleware primero! ¡Aquí estoy!"
Hay algunas notas muy importantes sobre el middleware que debes tener en cuenta antes de usarlo:
Flight::redirect()
function($params) { ... }
public function before($params) {}
flight\Engine
__construct()
El middleware también se puede registrar como una clase. Si necesitas la funcionalidad "después", deb usar una clase.
class MyMiddleware { public function before($params) { echo '¡Middleware primero!'; } public function after($params) { echo '¡Middleware último!'; } } $MyMiddleware = new MyMiddleware(); Flight::route('/path', function() { echo '¡Aquí estoy! '; })->addMiddleware($MyMiddleware); // también ->addMiddleware([ $MyMiddleware, $MyMiddleware2 ]); Flight::start(); // Esto mostrará "¡Middleware primero! ¡Aquí estoy! ¡Middleware último!"
Digamos que tienes un middleware de autenticación y quieres redirigir al usuario a una página de inicio de sesión si no está autenticado. Tienes algunas opciones a tu disposición:
Aquí tienes un ejemplo simple de return false;:
class MyMiddleware { public function before($params) { if (isset($_SESSION['user']) === false) { return false; } // dado que es verdadero, todo continúa } }
Aquí tienes un ejemplo de redirigir al usuario a una página de inicio de sesión:
class MyMiddleware { public function before($params) { if (isset($_SESSION['user']) === false) { Flight::redirect('/login'); exit; } } }
Digamos que necesitas lanzar un error JSON porque estás construyendo una API. Puedes hacerlo de la siguiente manera:
class MyMiddleware { public function before($params) { $authorization = Flight::request()->headers['Authorization']; if(empty($authorization)) { Flight::jsonHalt(['error' => 'Debes iniciar sesión para acceder a esta página.'], 403); // o Flight::json(['error' => 'Debes iniciar sesión para acceder a esta página.'], 403); exit; // o Flight::halt(403, json_encode(['error' => 'Debes iniciar sesión para acceder a esta página.']); } } }
Puedes agregar un grupo de rutas, y luego cada ruta en ese grupo tendrá el mismo middleware también. Esto es útil si necesitas agrupar varias rutas por un middleware de autenticación para verificar la clave de API en el encabezado.
// añadido al final del método de grupo Flight::group('/api', function() { // Esta ruta con aspecto "vacío" en realidad coincidirá con /api Flight::route('', function() { echo 'api'; }, false, 'api'); // Esto coincidirá con /api/usuarios Flight::route('/usuarios', function() { echo 'usuarios'; }, false, 'usuarios'); // Esto coincidirá con /api/usuarios/1234 Flight::route('/usuarios/@id', function($id) { echo 'usuario:'.$id; }, false, 'user_view'); }, [ new ApiAuthMiddleware() ]);
Si deseas aplicar un middleware global a todas tus rutas, puedes agregar un grupo "vacío":
// añadido al final del método de grupo Flight::group('', function() { // Esto sigue siendo /usuarios Flight::route('/usuarios', function() { echo 'usuarios'; }, false, 'usuarios'); // Y esto sigue siendo /usuarios/1234 Flight::route('/usuarios/@id', function($id) { echo 'usuario:'.$id; }, false, 'user_view'); }, [ new ApiAuthMiddleware() ]);
# Filtrado Flight te permite filtrar métodos antes y después de que sean llamados. No hay ganchos predefinidos que necesites memorizar. Puedes filtrar cualquiera de los métodos predeterminados del marco de trabajo, así como cualquier método personalizado que hayas mapeado. Una función de filtro se ve así: ```php function (array &$params, string &$output): bool { // Código de filtro }
Usando las variables pasadas puedes manipular los parámetros de entrada y/o la salida.
Puedes hacer que un filtro se ejecute antes de un método haciendo:
Flight::before('start', function (array &$params, string &$output): bool { // Haz algo });
Puedes hacer que un filtro se ejecute después de un método haciendo:
Flight::after('start', function (array &$params, string &$output): bool { // Haz algo });
Puedes añadir tantos filtros como quieras a cualquier método. Serán llamados en el orden en el que son declarados.
Aquí tienes un ejemplo del proceso de filtrado:
// Mapear un método personalizado Flight::map('hello', function (string $name) { return "¡Hola, $name!"; }); // Agregar un filtro antes Flight::before('hello', function (array &$params, string &$output): bool { // Manipular el parámetro $params[0] = 'Fred'; return true; }); // Agregar un filtro después Flight::after('hello', function (array &$params, string &$output): bool { // Manipular la salida $output .= " ¡Que tengas un buen día!"; return true; }); // Invocar el método personalizado echo Flight::hello('Bob');
Esto debería mostrar:
¡Hola Fred! ¡Que tengas un buen día!
Si has definido múltiples filtros, puedes romper la cadena devolviendo false en cualquiera de tus funciones de filtro:
false
Flight::before('start', function (array &$params, string &$output): bool { echo 'uno'; return true; }); Flight::before('start', function (array &$params, string &$output): bool { echo 'dos'; // Esto terminará la cadena return false; }); // Esto no se llamará Flight::before('start', function (array &$params, string &$output): bool { echo 'tres'; return true; });
Nota, los métodos principales como map y register no pueden ser filtrados porque son llamados directamente y no son invocados dinámicamente.
Flight encapsula la solicitud HTTP en un solo objeto, que puede ser accedido haciendo:
$request = Flight::request();
Cuando estás trabajando con una solicitud en una aplicación web, típicamente querrás extraer un encabezado, o un parámetro $_GET o $_POST, o quizás incluso el cuerpo de la solicitud en bruto. Flight proporciona una interfaz simple para hacer todas estas cosas.
$_GET
$_POST
Aquí hay un ejemplo de obtención de un parámetro de la cadena de consulta:
Flight::route('/search', function(){ $keyword = Flight::request()->query['keyword']; echo "Estás buscando: $keyword"; // consulta una base de datos o algo más con el $keyword });
Aquí hay un ejemplo de tal vez un formulario con un método POST:
Flight::route('POST /submit', function(){ $name = Flight::request()->data['name']; $email = Flight::request()->data['email']; echo "Has enviado: $name, $email"; // guarda en una base de datos o algo más con el $name y $email });
El objeto solicitud proporciona las siguientes propiedades:
$_SERVER
HTTP_CLIENT_IP
HTTP_X_FORWARDED_FOR
HTTP_X_FORWARDED
HTTP_X_CLUSTER_CLIENT_IP
HTTP_FORWARDED_FOR
HTTP_FORWARDED
Puedes acceder a las propiedades query, data, cookies y files como matrices u objetos.
query
data
cookies
files
Así que, para obtener un parámetro de la cadena de consulta, puedes hacer:
$id = Flight::request()->query['id'];
O puedes hacer:
$id = Flight::request()->query->id;
Para obtener el cuerpo de la solicitud HTTP en bruto, por ejemplo al manejar solicitudes PUT, puedes hacer:
$body = Flight::request()->getBody();
Si envías una solicitud con el tipo application/json y los datos {"id": 123} estará disponible desde la propiedad data:
application/json
{"id": 123}
$id = Flight::request()->data->id;
Puedes acceder al arreglo $_GET a través de la propiedad query:
Puedes acceder al arreglo $_POST a través de la propiedad data:
$id = Flight::request()->data['id'];
$_COOKIE
Puedes acceder al arreglo $_COOKIE a través de la propiedad cookies:
$myCookieValue = Flight::request()->cookies['myCookieName'];
Hay un acceso directo disponible para acceder al arreglo $_SERVER a través del método getVar():
getVar()
$host = Flight::request()->getVar['HTTP_HOST'];
$_FILES
Puedes acceder a archivos subidos a través de la propiedad files:
$uploadedFile = Flight::request()->files['myFile'];
Puedes procesar cargas de archivos utilizando el framework con algunos métodos de ayuda. Básicamente se reduce a extraer los datos del archivo de la solicitud y moverlos a una nueva ubicación.
Flight::route('POST /upload', function(){ // Si tuvieras un campo de entrada como <input type="file" name="myFile"> $uploadedFileData = Flight::request()->getUploadedFiles(); $uploadedFile = $uploadedFileData['myFile']; $uploadedFile->moveTo('/path/to/uploads/' . $uploadedFile->getClientFilename()); });
Si tienes múltiples archivos subidos, puedes recorrerlos:
Flight::route('POST /upload', function(){ // Si tuvieras un campo de entrada como <input type="file" name="myFiles[]"> $uploadedFiles = Flight::request()->getUploadedFiles()['myFiles']; foreach ($uploadedFiles as $uploadedFile) { $uploadedFile->moveTo('/path/to/uploads/' . $uploadedFile->getClientFilename()); } });
Nota de Seguridad: Siempre valida y desinfecta la entrada del usuario, especialmente al tratar con cargas de archivos. Siempre valida el tipo de extensiones que permitirás que se suban, pero también debes validar los "bytes mágicos" del archivo para asegurarte de que es realmente el tipo de archivo que el usuario dice que es. Hay artículos y bibliotecas disponibles para ayudar con esto.
Puedes acceder a los encabezados de solicitud usando el método getHeader() o getHeaders():
getHeader()
getHeaders()
// Tal vez necesites el encabezado de Autorización $host = Flight::request()->getHeader('Authorization'); // o $host = Flight::request()->header('Authorization'); // Si necesitas obtener todos los encabezados $headers = Flight::request()->getHeaders(); // o $headers = Flight::request()->headers();
Puedes acceder al cuerpo de la solicitud en bruto utilizando el método getBody():
getBody()
Puedes acceder al método de solicitud utilizando la propiedad method o el método getMethod():
method
getMethod()
$method = Flight::request()->method; // en realidad llama a getMethod() $method = Flight::request()->getMethod();
Nota: El método getMethod() primero obtiene el método de $_SERVER['REQUEST_METHOD'], luego puede ser sobrescrito por $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] si existe o $_REQUEST['_method'] si existe.
$_SERVER['REQUEST_METHOD']
$_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']
$_REQUEST['_method']
Hay un par de métodos auxiliares para ensamblar partes de una URL para tu conveniencia.
Puedes acceder a la URL completa de la solicitud usando el método getFullUrl():
getFullUrl()
$url = Flight::request()->getFullUrl(); // https://example.com/some/path?foo=bar
Puedes acceder a la URL base usando el método getBaseUrl():
getBaseUrl()
$url = Flight::request()->getBaseUrl(); // Nota, sin barra final. // https://example.com
Puedes pasar una URL al método parseQuery() para analizar la cadena de consulta en un arreglo asociativo:
parseQuery()
$query = Flight::request()->parseQuery('https://example.com/some/path?foo=bar'); // ['foo' => 'bar']
Flight está diseñado para ser fácil de usar y entender. Lo siguiente es el conjunto completo de métodos para el framework. Consiste en métodos centrales, que son métodos estáticos regulares, y métodos extensibles, que son métodos asignados que pueden ser filtrados o anulados.
Flight::map(string $nombre, callable $retorno, bool $pasar_ruta = false) // Crea un método personalizado para el framework. Flight::register(string $nombre, string $clase, array $params = [], ?callable $retorno = null) // Registra una clase a un método del framework. Flight::before(string $nombre, callable $retorno) // Agrega un filtro antes de un método del framework. Flight::after(string $nombre, callable $retorno) // Agrega un filtro después de un método del framework. Flight::path(string $ruta) // Agrega una ruta para la carga automática de clases. Flight::get(string $clave) // Obtiene una variable. Flight::set(string $clave, mixed $valor) // Establece una variable. Flight::has(string $clave) // Verifica si una variable está establecida. Flight::clear(array|string $clave = []) // Borra una variable. Flight::init() // Inicializa el framework a sus ajustes predeterminados. Flight::app() // Obtiene la instancia del objeto de la aplicación
Flight::start() // Inicia el framework. Flight::stop() // Detiene el framework y envía una respuesta. Flight::halt(int $codigo = 200, string $mensaje = '') // Detiene el framework con un código de estado opcional y mensaje. Flight::route(string $patrón, callable $retorno, bool $pasar_ruta = false) // Asigna un patrón de URL a un retorno. Flight::group(string $patrón, callable $retorno) // Crea agrupaciones para URLs, el patrón debe ser una cadena. Flight::redirect(string $url, int $codigo) // Redirige a otra URL. Flight::render(string $archivo, array $datos, ?string $clave = null) // Renderiza un archivo de plantilla. Flight::error(Throwable $error) // Envía una respuesta HTTP 500. Flight::notFound() // Envía una respuesta HTTP 404. Flight::etag(string $id, string $tipo = 'string') // Realiza almacenamiento en caché HTTP ETag. Flight::lastModified(int $tiempo) // Realiza almacenamiento en caché HTTP de Última Modificación. Flight::json(mixed $datos, int $codigo = 200, bool $codificar = true, string $charset = 'utf8', int $opción) // Envía una respuesta JSON. Flight::jsonp(mixed $datos, string $parametro = 'jsonp', int $codigo = 200, bool $codificar = true, string $charset = 'utf8', int $opción) // Envía una respuesta JSONP.
Cualquier método personalizado añadido con map y register también puede ser filtrado.
Flight está diseñado para ser fácil de usar y entender. A continuación se muestra el conjunto completo de métodos para el framework. Consta de métodos principales, que son métodos estáticos regulares, y métodos extensibles, que son métodos mapeados que pueden ser filtrados o anulados.
Estos métodos son fundamentales para el framework y no pueden ser anulados.
Flight::map(string $name, callable $callback, bool $pass_route = false) // Crea un método personalizado para el framework. Flight::register(string $name, string $class, array $params = [], ?callable $callback = null) // Registra una clase a un método del framework. Flight::unregister(string $name) // Anula el registro de una clase de un método del framework. Flight::before(string $name, callable $callback) // Añade un filtro antes de un método del framework. Flight::after(string $name, callable $callback) // Añade un filtro después de un método del framework. Flight::path(string $path) // Añade una ruta para cargar clases automáticamente. Flight::get(string $key) // Obtiene una variable establecida por Flight::set(). Flight::set(string $key, mixed $value) // Establece una variable dentro del motor de Flight. Flight::has(string $key) // Comprueba si una variable está establecida. Flight::clear(array|string $key = []) // Borra una variable. Flight::init() // Inicializa el framework con su configuración predeterminada. Flight::app() // Obtiene una instancia del objeto de la aplicación Flight::request() // Obtiene una instancia del objeto de solicitud Flight::response() // Obtiene una instancia del objeto de respuesta Flight::router() // Obtiene una instancia del objeto de enrutador Flight::view() // Obtiene una instancia del objeto de vista
Flight::start() // Inicia el framework. Flight::stop() // Detiene el framework y envía una respuesta. Flight::halt(int $code = 200, string $mensaje = '') // Detiene el framework con un código de estado y mensaje opcional. Flight::route(string $patrón, callable $callback, bool $pass_route = false, string $alias = '') // Mapea un patrón de URL a un callback. Flight::post(string $patrón, callable $callback, bool $pass_route = false, string $alias = '') // Mapea un patrón de URL de solicitud POST a un callback. Flight::put(string $patrón, callable $callback, bool $pass_route = false, string $alias = '') // Mapea un patrón de URL de solicitud PUT a un callback. Flight::patch(string $patrón, callable $callback, bool $pass_route = false, string $alias = '') // Mapea un patrón de URL de solicitud PATCH a un callback. Flight::delete(string $patrón, callable $callback, bool $pass_route = false, string $alias = '') // Mapea un patrón de URL de solicitud DELETE a un callback. Flight::group(string $patrón, callable $callback) // Crea agrupaciones para URLs, el patrón debe ser una cadena. Flight::getUrl(string $name, array $params = []) // Genera una URL basada en un alias de ruta. Flight::redirect(string $url, int $code) // Redirige a otra URL. Flight::download(string $filePath) // Descarga un archivo. Flight::render(string $archivo, array $datos, ?string $key = null) // Renderiza un archivo de plantilla. Flight::error(Throwable $error) // Envía una respuesta HTTP 500. Flight::notFound() // Envía una respuesta HTTP 404. Flight::etag(string $id, string $type = 'string') // Realiza el almacenamiento en caché HTTP ETag. Flight::lastModified(int $time) // Realiza el almacenamiento en caché HTTP de última modificación. Flight::json(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Envía una respuesta JSON. Flight::jsonp(mixed $data, string $param = 'jsonp', int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Envía una respuesta JSONP. Flight::jsonHalt(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Envía una respuesta JSON y detiene el framework.
Cualquier método personalizado añadido con map y register también se puede filtrar. Para ejemplos sobre cómo mapear estos métodos, consulta la guía Extender Flight.
Algunos programadores se oponen vehementemente a utilizar frameworks. Argumentan que los frameworks son pesados, lentos y difíciles de aprender. Dicen que los frameworks son innecesarios y que puedes escribir un código mejor sin ellos. Ciertamente se pueden hacer algunos puntos válidos sobre las desventajas de usar frameworks. Sin embargo, también existen muchas ventajas al utilizar frameworks.
Aquí hay algunas razones por las cuales podrías considerar utilizar un framework:
Flight es un micro-framework. Esto significa que es pequeño y ligero. No proporciona tanta funcionalidad como frameworks más grandes como Laravel o Symfony. Sin embargo, proporciona mucha de la funcionalidad que necesitas para construir aplicaciones web. También es fácil de aprender y usar. Esto lo convierte en una buena elección para construir aplicaciones web rápidamente y fácilmente. Si eres nuevo en los frameworks, Flight es un gran framework para principiantes con el que empezar. Te ayudará a entender las ventajas de usar frameworks sin abrumarte con demasiada complejidad. Después de tener algo de experiencia con Flight, será más fácil pasar a frameworks más complejos como Laravel o Symfony, sin embargo, Flight aún puede crear una aplicación robusta y exitosa.
El enrutamiento es el núcleo del framework Flight, ¿pero qué es exactamente? Enrutamiento es el proceso de tomar una URL y emparejarla con una función específica en tu código. Esto es cómo puedes hacer que tu sitio web haga cosas diferentes basadas en la URL que se solicita. Por ejemplo, es posible que desees mostrar el perfil de un usuario cuando visitan /usuario/1234, pero mostrar una lista de todos los usuarios cuando visitan /usuarios. Todo esto se hace a través del enrutamiento.
/usuario/1234
/usuarios
Podría funcionar algo así:
http://ejemplo.com/usuario/1234
Flight::route('/usuario/@id', [ 'ControladorUsuario', 'verPerfilUsuario' ]);
verPerfilUsuario($id)
ControladorUsuario
1234
$id
verPerfilUsuario()
¡Tener un enrutador centralizado adecuado puede realmente hacer tu vida mucho más fácil! Al principio, podría ser difícil verlo. Aquí hay algunas razones por las cuales:
vista_usuario
id
/admin/usuario/1234
Seguro que estás familiarizado con la forma guion por guion de crear un sitio web. Podrías tener un archivo llamado index.php que tiene un montón de declaraciones if para verificar la URL y luego ejecutar una función específica basada en la URL. Esto es una forma de enrutamiento, pero no es muy organizado y puede salirse de control rápidamente. El sistema de enrutamiento de Flight es una forma mucho más organizada y poderosa de manejar el enrutamiento.
if
¿Esto?
// /usuario/ver_perfil.php?id=1234 if ($_GET['id']) { $id = $_GET['id']; verPerfilUsuario($id); } // /usuario/editar_perfil.php?id=1234 if ($_GET['id']) { $id = $_GET['id']; editarPerfilUsuario($id); } // etc...
¿O esto?
// index.php Flight::route('/usuario/@id', [ 'ControladorUsuario', 'verPerfilUsuario' ]); Flight::route('/usuario/@id/editar', [ 'ControladorUsuario', 'editarPerfilUsuario' ]); // En tal vez tu app/controladores/ControladorUsuario.php class ControladorUsuario { public function verPerfilUsuario($id) { // hacer algo } public function editarPerfilUsuario($id) { // hacer algo } }
¡Espero que comiences a ver los beneficios de usar un sistema de enrutamiento centralizado. Es mucho más fácil de gestionar y entender a largo plazo!
Flight proporciona una forma simple y fácil de manejar solicitudes y respuestas. Esto es lo básico de lo que hace un framework web. Toma una solicitud de un navegador del usuario, la procesa, y luego envía una respuesta. Así es como puedes construir aplicaciones web que hagan cosas como mostrar el perfil de un usuario, permitir a un usuario iniciar sesión o permitir a un usuario publicar una nueva publicación en un blog.
Una solicitud es lo que un navegador del usuario envía a tu servidor cuando visita tu sitio web. Esta solicitud contiene información sobre lo que el usuario quiere hacer. Por ejemplo, podría contener información sobre qué URL quiere visitar el usuario, qué datos quiere enviar el usuario a tu servidor, o qué tipo de datos quiere recibir del servidor. Es importante tener en cuenta que una solicitud es de solo lectura. No puedes cambiar la solicitud, pero puedes leer de ella.
Flight proporciona una forma simple de acceder a información sobre la solicitud. Puedes acceder a información sobre la solicitud utilizando el método Flight::request() Este método devuelve un objeto Request que contiene información sobre la solicitud. Puedes usar este objeto para acceder a información sobre la solicitud, como la URL, el método o los datos que el usuario envió a tu servidor.
Flight::request()
Request
Una respuesta es lo que tu servidor envía de vuelta al navegador del usuario cuando visita tu sitio web. Esta respuesta contiene información sobre lo que tu servidor quiere hacer. Por ejemplo, podría contener información sobre qué tipo de datos tu servidor quiere enviar al usuario, qué tipo de datos tu servidor quiere recibir del usuario, o qué tipo de datos tu servidor quiere almacenar en la computadora del usuario.
Flight proporciona una forma simple de enviar una respuesta al navegador del usuario. Puedes enviar una respuesta usando el método Flight::response() Este método toma un objeto Response como argumento y envía la respuesta al navegador del usuario. Puedes usar este objeto para enviar una respuesta al navegador del usuario, como HTML, JSON o un archivo. Flight te ayuda a generar automáticamente algunas partes de la respuesta para facilitar las cosas, pero en última instancia tú tienes control sobre lo que envías de vuelta al usuario.
Flight::response()
Response
Flight proporciona soporte incorporado para el almacenamiento en caché a nivel HTTP. Si se cumple la condición de caché, Flight devolverá una respuesta HTTP 304 No modificado. La próxima vez que el cliente solicite el mismo recurso, se le pedirá que utilice su versión en caché local.
304 No modificado
Puedes utilizar el método lastModified y pasar un sello de tiempo UNIX para establecer la fecha y hora en que se modificó por última vez una página. El cliente seguirá utilizando su caché hasta que se cambie el valor de última modificación.
lastModified
Flight::route('/noticias', function () { Flight::lastModified(1234567890); echo 'Este contenido se almacenará en caché.'; });
La caché ETag es similar a Última modificación, excepto que puedes especificar cualquier identificación que desees para el recurso:
ETag
Última modificación
Flight::route('/noticias', function () { Flight::etag('mi-identificador-único'); echo 'Este contenido se almacenará en caché.'; });
Ten en cuenta que llamar a lastModified o etag establecerá y comprobará el valor de caché. Si el valor de caché es el mismo entre las solicitudes, Flight enviará inmediatamente una respuesta HTTP 304 y detendrá el procesamiento.
etag
HTTP 304
Flight ayuda a generar parte de las cabeceras de respuesta, pero tienes la mayor parte del control sobre lo que devuelves al usuario. A veces puedes acceder al objeto Response directamente, pero la mayoría de las veces usarás la instancia Flight para enviar una respuesta.
Flight
Flight utiliza ob_start() para almacenar en búfer la salida. Esto significa que puedes usar echo o print para enviar una respuesta al usuario y Flight la capturará y la enviará de vuelta al usuario con las cabeceras apropiadas.
ob_start()
echo
print
// Esto enviará "¡Hola, Mundo!" al navegador del usuario Flight::route('/', function() { echo "¡Hola, Mundo!"; }); // HTTP/1.1 200 OK // Content-Type: text/html // // ¡Hola, Mundo!
Como alternativa, puedes llamar al método write() para añadir al cuerpo también.
write()
// Esto enviará "¡Hola, Mundo!" al navegador del usuario Flight::route('/', function() { // detallado, pero hace el trabajo a veces cuando lo necesitas Flight::response()->write("¡Hola, Mundo!"); // si quieres recuperar el cuerpo que has establecido en este punto // puedes hacerlo así $body = Flight::response()->getBody(); });
Puedes establecer el código de estado de la respuesta usando el método status:
status
Flight::route('/@id', function($id) { if($id == 123) { Flight::response()->status(200); echo "¡Hola, Mundo!"; } else { Flight::response()->status(403); echo "Prohibido"; } });
Si quieres obtener el código de estado actual, puedes usar el método status sin argumentos:
Flight::response()->status(); // 200
Puedes establecer el cuerpo de la respuesta usando el método write, sin embargo, si haces un echo o print de algo, será capturado y enviado como el cuerpo de respuesta a través del almacenamiento en búfer de salida.
write
Flight::route('/', function() { Flight::response()->write("¡Hola, Mundo!"); }); // igual que Flight::route('/', function() { echo "¡Hola, Mundo!"; });
Si quieres borrar el cuerpo de la respuesta, puedes usar el método clearBody:
clearBody
Flight::route('/', function() { if($algunaCondicion) { Flight::response()->write("¡Hola, Mundo!"); } else { Flight::response()->clearBody(); } });
Puedes ejecutar una devolución de llamada en el cuerpo de respuesta usando el método addResponseBodyCallback:
addResponseBodyCallback
Flight::route('/usuarios', function() { $db = Flight::db(); $usuarios = $db->fetchAll("SELECT * FROM usuarios"); Flight::render('tabla_usuarios', ['usuarios' => $usuarios]); }); // Esto comprimirá todas las respuestas para cualquier ruta Flight::response()->addResponseBodyCallback(function($body) { return gzencode($body, 9); });
Puedes añadir múltiples devoluciones de llamada y se ejecutarán en el orden en que se añadieron. Dado que puede aceptar cualquier llamable, puede aceptar una matriz de clase [ $clase, 'método' ], un cierre $strReplace = function($body) { str_replace('hi', 'there', $body); };, o un nombre de función 'minify' si tuvieras una función para minificar tu código html, por ejemplo.
[ $clase, 'método' ]
$strReplace = function($body) { str_replace('hi', 'there', $body); };
'minify'
Nota: Las devoluciones de llamada de rutas no funcionarán si estás usando la opción de configuración flight.v2.output_buffering.
Si quieres que esto se aplique solo a una ruta específica, podrías añadir la devolución de llamada en la propia ruta:
Flight::route('/usuarios', function() { $db = Flight::db(); $usuarios = $db->fetchAll("SELECT * FROM usuarios"); Flight::render('tabla_usuarios', ['usuarios' => $usuarios]); // Esto comprimirá solo la respuesta para esta ruta Flight::response()->addResponseBodyCallback(function($body) { return gzencode($body, 9); }); });
También puedes usar middleware para aplicar la devolución de llamada a todas las rutas a través de middleware:
// MinifyMiddleware.php class MinifyMiddleware { public function before() { // Aplica la devolución de llamada aquí en el objeto response(). Flight::response()->addResponseBodyCallback(function($body) { return $this->minify($body); }); } protected function minify(string $body): string { // minificar el cuerpo de alguna forma return $body; } } // index.php Flight::group('/usuarios', function() { Flight::route('', function() { /* ... */ }); Flight::route('/@id', function($id) { /* ... */ }); }, [ new MinifyMiddleware() ]);
Puedes establecer una cabecera como el tipo de contenido de la respuesta usando el método header:
// Esto enviará "¡Hola, Mundo!" al navegador del usuario en texto plano Flight::route('/', function() { Flight::response()->header('Content-Type', 'text/plain'); // o Flight::response()->setHeader('Content-Type', 'text/plain'); echo "¡Hola, Mundo!"; });
Flight proporciona soporte para enviar respuestas JSON y JSONP. Para enviar una respuesta JSON pasas algunos datos para ser codificados en JSON:
Flight::json(['id' => 123]);
También puedes pasar un código de estado como segundo argumento:
Flight::json(['id' => 123], 201);
También puedes pasar un argumento a la última posición para habilitar una impresión bonita:
Flight::json(['id' => 123], 200, true, 'utf-8', JSON_PRETTY_PRINT);
Si estás cambiando opciones pasadas a Flight::json() y quieres una sintaxis más simple, puedes simplemente mapear el método JSON:
Flight::json()
Flight::map('json', function($datos, $código = 200, $opciones = 0) { Flight::_json($datos, $código, true, 'utf-8', $opciones); } // Y ahora se puede usar así Flight::json(['id' => 123], 200, JSON_PRETTY_PRINT);
Si quieres enviar una respuesta JSON y detener la ejecución, puedes usar el método jsonHalt. Esto es útil para casos donde estás comprobando tal vez algún tipo de autorización y si el usuario no está autorizado, puedes enviar una respuesta JSON inmediatamente, borrar el cuerpo existente del contenido y detener la ejecución.
jsonHalt
Flight::route('/usuarios', function() { $autorizado = algunaComprobaciónDeAutorización(); // Comprueba si el usuario está autorizado if($autorizado === false) { Flight::jsonHalt(['error' => 'No autorizado'], 401); } // Continúa con el resto de la ruta });
Antes de v3.10.0, tendrías que hacer algo como esto:
Flight::route('/usuarios', function() { $autorizado = algunaComprobaciónDeAutorización(); // Comprueba si el usuario está autorizado if($autorizado === false) { Flight::halt(401, json_encode(['error' => 'No autorizado'])); } // Continúa con el resto de la ruta });
Para solicitudes JSONP, puedes pasar opcionalmente el nombre del parámetro de consulta que estás usando para definir tu función de devolución de llamada:
Flight::jsonp(['id' => 123], 'q');
Entonces, al hacer una solicitud GET usando ?q=mi_func, deberías recibir la salida:
?q=mi_func
mi_func({"id":123});
Si no pasas un nombre de parámetro de consulta, será por defecto jsonp.
jsonp
Puedes redirigir la solicitud actual usando el método redirect() y pasar una nueva URL:
Flight::redirect('/nueva/ubicacion');
Por defecto, Flight envía un código de estado HTTP 303 ("See Other"). Opcionalmente puedes establecer un código personalizado:
Flight::redirect('/nueva/ubicacion', 401);
Puedes detener el framework en cualquier momento llamando al método halt:
También puedes especificar opcionalmente un código de estado HTTP y un mensaje:
Flight::halt(200, 'Regreso enseguida...');
Llamar a halt descartará cualquier contenido de respuesta hasta ese punto. Si quieres detener el framework y enviar la respuesta actual, usa el método stop:
Puedes borrar el cuerpo de la respuesta y las cabeceras usando el método clear(). Esto limpiará cualquier cabecera asignada a la respuesta, limpiará el cuerpo de la respuesta y establecerá el código de estado en 200.
clear()
200
Flight::response()->clear();
Si solo quieres borrar el cuerpo de la respuesta, puedes usar el método clearBody():
clearBody()
// Esto seguirá manteniendo cualquier cabecera establecida en el objeto response(). Flight::response()->clearBody();
Flight proporciona soporte integrado para la caché a nivel HTTP. Si se cumple la condición de caché, Flight devolverá una respuesta de HTTP 304 No modificado. La próxima vez que el cliente solicite el mismo recurso, se le pedirá que utilice su versión en caché local.
HTTP 304 No modificado
Si quieres cachear toda tu respuesta, puedes usar el método cache() y pasar el tiempo a cachear.
cache()
// Esto cacheará la respuesta durante 5 minutos Flight::route('/noticias', function () { Flight::response()->cache(time() + 300); echo 'Este contenido será cachéado.'; }); // Alternativamente, puedes usar una cadena que pasarías // al método strtotime() Flight::route('/noticias', function () { Flight::response()->cache('+5 minutos'); echo 'Este contenido será cachéado.'; });
Puedes usar el método lastModified y pasar una marca de tiempo UNIX para establecer la fecha y hora en la que una página fue modificada por última vez. El cliente seguirá utilizando su caché hasta que el valor de la última modificación se cambie.
Flight::route('/noticias', function () { Flight::lastModified(1234567890); echo 'Este contenido será cachéado.'; });
El caching ETag es similar a Última Modificación, excepto que puedes especificar cualquier id que desees para el recurso:
Última Modificación
Flight::route('/noticias', function () { Flight::etag('mi-id-único'); echo 'Este contenido será cachéado.'; });
Ten en cuenta que llamar tanto a lastModified como a etag establecerá y comprobará el valor del caché. Si el valor de caché es el mismo entre las solicitudes, Flight enviará inmediatamente una respuesta HTTP 304 y dejará de procesar.
Existe un método auxiliar para descargar un archivo. Puedes usar el método download y pasar la ruta.
download
Flight::route('/descargar', function () { Flight::download('/ruta/al/archivo.txt'); });
En lugar de ejecutar Flight como una clase estática global, puedes opcionalmente ejecutarlo como una instancia de objeto.
require 'flight/autoload.php'; $app = Flight::app(); $app->route('/', function () { echo '¡hola mundo!'; }); $app->start();
Así que en lugar de llamar al método estático, llamarías al método de instancia con el mismo nombre en el objeto Engine.
Puedes redirigir la solicitud actual utilizando el método redirect y pasando una nueva URL:
redirect
Por defecto, Flight envía un código de estado HTTP 303. Opcionalmente, puedes establecer un código personalizado:
Flight proporciona algunas funcionalidades básicas de plantillas de forma predeterminada. Para mostrar una vista de plantilla llame al método render con el nombre del archivo de plantilla y datos de plantilla opcionales:
render
Flight::render('hello.php', ['name' => 'Bob']);
Los datos de plantilla que pase se inyectan automáticamente en la plantilla y se pueden hacer referencia como una variable local. Los archivos de plantilla son simplemente archivos PHP. Si el contenido del archivo de plantilla hello.php es:
hello.php
¡Hola, <?= $name ?>!
La salida sería:
¡Hola, Bob!
También puede configurar manualmente variables de vista utilizando el método set:
set
Flight::view()->set('name', 'Bob');
La variable name ahora está disponible en todas sus vistas. Entonces simplemente puede hacer:
name
Flight::render('hello');
Tenga en cuenta que al especificar el nombre de la plantilla en el método render, puede omitir la extensión .php.
.php
Por defecto, Flight buscará un directorio views para los archivos de plantilla. Puede establecer una ruta alternativa para sus plantillas configurando lo siguiente:
views
Flight::set('flight.views.path', '/ruta/a/vistas');
Es común que los sitios web tengan un solo archivo de plantilla de diseño con contenido intercambiable. Para renderizar contenido que se utilizará en un diseño, puede pasar un parámetro opcional al método render.
Flight::render('header', ['heading' => 'Hola'], 'headerContent'); Flight::render('body', ['body' => 'Mundo'], 'bodyContent');
Su vista tendrá variables guardadas llamadas headerContent y bodyContent. Luego puede renderizar su diseño haciendo:
headerContent
bodyContent
Flight::render('layout', ['title' => 'Página de inicio']);
Si los archivos de plantilla se ven así:
header.php:
header.php
<h1><?= $heading ?></h1>
body.php:
body.php
<div><?= $body ?></div>
layout.php:
layout.php
<html> <head> <title><?= $title ?></title> </head> <body> <?= $headerContent ?> <?= $bodyContent ?> </body> </html>
<html> <head> <title>Página de inicio</title> </head> <body> <h1>Hola</h1> <div>Mundo</div> </body> </html>
Flight le permite cambiar el motor de vista predeterminado simplemente registrando su propia clase de vista. Así es como utilizaría el Smarty motor de plantillas para sus vistas:
// Cargar biblioteca Smarty require './Smarty/libs/Smarty.class.php'; // Registrar Smarty como clase de vista // También pase una función de devolución de llamada para configurar Smarty al cargar Flight::register('view', Smarty::class, [], function (Smarty $smarty) { $smarty->setTemplateDir('./templates/'); $smarty->setCompileDir('./templates_c/'); $smarty->setConfigDir('./config/'); $smarty->setCacheDir('./cache/'); }); // Asignar datos de la plantilla Flight::view()->assign('name', 'Bob'); // Mostrar la plantilla Flight::view()->display('hello.tpl');
Por completitud, también debería anular el método de renderizado predeterminado de Flight:
Flight::map('render', function(string $template, array $data): void { Flight::view()->assign($data); Flight::view()->display($template); });
Flight proporciona alguna funcionalidad básica de plantillas de forma predeterminada.
Si necesita necesidades de plantillas más complejas, consulte los ejemplos de Smarty y Latte en la sección Vistas Personalizadas.
Para mostrar una plantilla de vista, llame al método render con el nombre del archivo de plantilla y datos de plantilla opcionales:
Hola, <?= $name ?>!
Hola, Bob!
También puede establecer manualmente variables de vista utilizando el método set:
La variable name ahora está disponible en todas sus vistas. Por lo tanto, simplemente puede hacer:
Tenga en cuenta que al especificar el nombre de la plantilla en el método de renderizado, puede omitir la extensión .php por defecto Flight buscará un directorio views para archivos de plantilla. Puede establecer una ruta alternativa para sus plantillas configurando lo siguiente:
Flight::set('flight.views.path', '/ruta/a/visitas');
Es común que los sitios web tengan un solo archivo de plantilla de diseño con contenido intercambiable. Para renderizar el contenido que se utilizará en un diseño, puede pasar un parámetro opcional al método render.
Flight::render('layout', ['title' => 'Página de Inicio']);
<html> <head> <title>Página de Inicio</title> </head> <body> <h1>Hola</h1> <div>Mundo</div> </body> </html>
Flight le permite cambiar el motor de vista predeterminado simplemente registrando su propia clase de vista.
Así es como usaría el motor de plantillas Smarty para sus vistas:
// Cargar la biblioteca de Smarty require './Smarty/libs/Smarty.class.php'; // Registrar Smarty como la clase de vista // También pase una función de devolución de llamada para configurar Smarty en la carga Flight::register('view', Smarty::class, [], function (Smarty $smarty) { $smarty->setTemplateDir('./templates/'); $smarty->setCompileDir('./templates_c/'); $smarty->setConfigDir('./config/'); }); // Asignar datos de la plantilla Flight::view()->assign('name', 'Bob'); // Mostrar la plantilla Flight::view()->display('hello.tpl');
Para completitud, también debería anular el método de renderizado predeterminado de Flight:
Así es como usaría el motor de plantillas Latte para sus vistas:
// Registrar Latte como la clase de vista // También pase una función de devolución de llamada para configurar Latte en la carga Flight::register('view', Latte\Engine::class, [], function (Latte\Engine $latte) { // Aquí es donde Latte almacenará en caché sus plantillas para acelerar las cosas // ¡Una cosa genial sobre Latte es que actualiza automáticamente su // caché cuando realiza cambios en sus plantillas! $latte->setTempDirectory(__DIR__ . '/../cache/'); // Indique a Latte dónde estará el directorio raíz de sus vistas. $latte->setLoader(new \Latte\Loaders\FileLoader(__DIR__ . '/../views/')); }); // Y envuélvalo para que pueda usar Flight::render() correctamente Flight::map('render', function(string $template, array $data): void { // Esto es como $latte_engine->render($template, $data); echo Flight::view()->render($template, $data); });
Fat-Free (conocido afectuosamente como F3) es un micro marco PHP poderoso pero fácil de usar diseñado para ayudarte a construir aplicaciones web dinámicas y robustas - ¡rápido!
Flight se compara con Fat-Free en muchos aspectos y probablemente es el pariente más cercano en cuanto a características y simplicidad. Fat-Free tiene muchas características que Flight no tiene, pero también tiene muchas características que Flight sí tiene. Fat-Free está empezando a mostrar su edad y no es tan popular como solía ser.
Las actualizaciones son menos frecuentes y la comunidad no es tan activa como solía ser. El código es lo suficientemente simple, pero a veces la falta de disciplina sintáctica puede hacer que sea difícil de leer y entender. Funciona para PHP 8.3, pero el código en sí mismo todavía parece vivir en PHP 5.3.
GET
DB\SQL
active-record
Cache
beforeroute
afterroute
HIVE
Flight está diseñado para ser un marco extensible. El marco viene con un conjunto de métodos y componentes predeterminados, pero te permite mapear tus propios métodos, registrar tus propias clases o incluso anular clases y métodos existentes.
Si buscas un DIC (Contenedor de Inyección de Dependencias), ve a la página de Contenedor de Inyección de Dependencias.
Para mapear tu propio método personalizado simple, utiliza la función map:
// Mapea tu método Flight::map('hello', function (string $name) { echo "¡Hola $name!"; }); // Llama a tu método personalizado Flight::hello('Bob');
Aunque es posible crear métodos personalizados simples, se recomienda simplemente crear funciones estándar en PHP. Esto tiene autocompletado en los IDE y es más fácil de leer. El equivalente del código anterior sería:
function hello(string $name) { echo "¡Hola $name!"; } hello('Bob');
Esto se utiliza más cuando necesitas pasar variables a tu método para obtener un valor esperado. Utilizar el método register() como se muestra a continuación es más para pasar configuraciones y luego llamar a tu clase preconfigurada.
register()
Para registrar tu propia clase y configurarla, utiliza la función register:
// Registra tu clase Flight::register('user', User::class); // Obtén una instancia de tu clase $user = Flight::user();
El método de registro también te permite pasar parámetros al constructor de tu clase. Así que cuando cargues tu clase personalizada, vendrá preinicializada. Puedes definir los parámetros del constructor pasando un array adicional. Aquí tienes un ejemplo de carga de una conexión a base de datos:
// Registra clase con parámetros de constructor Flight::register('db', PDO::class, ['mysql:host=localhost;dbname=test', 'usuario', 'contraseña']); // Obtén una instancia de tu clase // Esto creará un objeto con los parámetros definidos // // new PDO('mysql:host=localhost;dbname=test','usuario','contraseña'); // $db = Flight::db(); // y si lo necesitas más tarde en tu código, simplemente llama al mismo método de nuevo class AlgunControlador { public function __construct() { $this->db = Flight::db(); } }
Si pasas un parámetro de devolución de llamada adicional, se ejecutará inmediatamente después de la construcción de la clase. Esto te permite realizar cualquier procedimiento de configuración para tu nuevo objeto. La función de devolución de llamada toma un parámetro, una instancia del nuevo objeto.
// La devolución de llamada recibirá el objeto que fue construido Flight::register( 'db', PDO::class, ['mysql:host=localhost;dbname=test', 'usuario', 'contraseña'], function (PDO $db) { $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } );
Por defecto, cada vez que cargas tu clase, obtendrás una instancia compartida. Para obtener una nueva instancia de una clase, simplemente pasa false como parámetro:
// Instancia compartida de la clase $compartido = Flight::db(); // Nueva instancia de la clase $nueva = Flight::db(false);
Ten en cuenta que los métodos mapeados tienen preferencia sobre las clases registradas. Si declaras ambos usando el mismo nombre, solo se invocará el método mapeado.
Flight te permite anular su funcionalidad predeterminada para adaptarla a tus necesidades, sin necesidad de modificar ningún código. Puedes ver todos los métodos que puedes anular aquí.
Por ejemplo, cuando Flight no puede hacer coincidir una URL con una ruta, invoca el método notFound, que envía una respuesta genérica de HTTP 404. Puedes anular este comportamiento usando el método map:
Flight::map('notFound', function() { // Mostrar página personalizada de error 404 include 'errores/404.html'; });
Flight también te permite reemplazar componentes principales del marco. Por ejemplo, puedes reemplazar la clase de Enrutador predeterminada con tu propia clase personalizada:
// Registra tu clase personalizada Flight::register('router', MiEnrutador::class); // Cuando Flight cargue la instancia de Enrutador, cargará tu clase $mienrutador = Flight::router();
Sin embargo, los métodos de marco como map y register no pueden ser anulados. Obtendrás un error si intentas hacerlo.
Flight proporciona soporte para enviar respuestas JSON y JSONP. Para enviar una respuesta JSON, pasas algunos datos para ser codificados en JSON:
Para solicitudes JSONP, opcionalmente puedes pasar el nombre del parámetro de consulta que estás utilizando para definir tu función de callback:
Entonces, al hacer una solicitud GET usando ?q=my_func, deberías recibir la salida:
?q=my_func
my_func({"id":123});
Si no pasas un nombre de parámetro de consulta, se utilizará por defecto jsonp.
Slim es un micro marco de PHP que te ayuda a escribir rápidamente aplicaciones web y APIs simples pero potentes.
Mucha de la inspiración para algunas de las características de la versión 3 de Flight realmente provino de Slim. Agrupar rutas y ejecutar middleware en un orden específico son dos características que fueron inspiradas por Slim. Slim v3 salió enfocado hacia la simplicidad, pero ha habido resenas mixtas con respecto a la v4.
La carga automática es un concepto en PHP donde especificas un directorio o directorios para cargar clases desde. Esto es mucho más beneficioso que usar require o include para cargar clases. También es un requisito para usar paquetes de Composer.
require
include
Por defecto, cualquier clase de Flight se carga automáticamente gracias a composer. Sin embargo, si deseas cargar automáticamente tus propias clases, puedes usar el método Flight::path() para especificar un directorio desde el cual cargar clases.
Flight::path()
Supongamos que tenemos una estructura de directorios como la siguiente:
# Ruta de ejemplo /home/user/project/my-flight-project/ ├── app │ ├── cache │ ├── config │ ├── controllers - contiene los controladores para este proyecto │ ├── translations │ ├── UTILS - contiene clases solo para esta aplicación (esto está en mayúsculas a propósito para un ejemplo posterior) │ └── views └── public └── css └── js └── index.php
Puede haber notado que esta es la misma estructura de archivos que este sitio de documentación.
Puedes especificar cada directorio para cargar desde así:
/** * public/index.php */ // Agregar una ruta al cargador automático Flight::path(__DIR__.'/../app/controllers/'); Flight::path(__DIR__.'/../app/utils/'); /** * app/controllers/MyController.php */ // no se requiere espacio de nombres // Se recomienda que todas las clases cargadas automáticamente estén en Pascal Case (cada palabra en mayúscula, sin espacios) // A partir de la versión 3.7.2, puedes utilizar Pascal_Snake_Case para los nombres de tus clases ejecutando Loader::setV2ClassLoading(false); class MyController { public function index() { // hacer algo } }
Si tienes espacios de nombres, en realidad se vuelve muy fácil de implementar. Deberías usar el método Flight::path() para especificar el directorio raíz (no el directorio de documentos o la carpeta public/) de tu aplicación.
public/
/** * public/index.php */ // Agregar una ruta al cargador automático Flight::path(__DIR__.'/../');
Así es como podría verse tu controlador. Observa el ejemplo a continuación, pero presta atención a los comentarios para obtener información importante.
/** * app/controllers/MyController.php */ // se requieren espacios de nombres // los espacios de nombres son iguales a la estructura de directorios // los espacios de nombres deben seguir el mismo caso que la estructura de directorios // los espacios de nombres y directorios no pueden tener guiones bajos (a menos que se establezca Loader::setV2ClassLoading(false)) namespace app\controllers; // Se recomienda que todas las clases cargadas automáticamente estén en Pascal Case (cada palabra en mayúscula, sin espacios) // A partir de la versión 3.7.2, puedes utilizar Pascal_Snake_Case para los nombres de tus clases ejecutando Loader::setV2ClassLoading(false); class MyController { public function index() { // hacer algo } }
Y si quisieras cargar automáticamente una clase en tu directorio de utilidades, harías básicamente lo mismo:
/** * app/UTILS/ArrayHelperUtil.php */ // el espacio de nombres debe coincidir con la estructura de directorios y el caso (nota que el directorio UTILS está en mayúsculas // como en el árbol de archivos anterior) namespace app\UTILS; class ArrayHelperUtil { public function changeArrayCase(array $array) { // hacer algo } }
A partir de la versión 3.7.2, puedes utilizar Pascal_Snake_Case para tus nombres de clases ejecutando Loader::setV2ClassLoading(false);. Esto te permitirá utilizar guiones bajos en los nombres de tus clases. Esto no se recomienda, pero está disponible para aquellos que lo necesiten.
Loader::setV2ClassLoading(false);
/** * public/index.php */ // Agregar una ruta al cargador automático Flight::path(__DIR__.'/../app/controllers/'); Flight::path(__DIR__.'/../app/utils/'); Loader::setV2ClassLoading(false); /** * app/controllers/My_Controller.php */ // no se requiere espacio de nombres class My_Controller { public function index() { // hacer algo } }
Esta página te ayudará a solucionar problemas comunes que puedes encontrar al usar Flight.
Si ves un error 404 No Encontrado (pero juras por tu vida que realmente está ahí y no es un error tipográfico) esto realmente podría ser un problema con que estás devolviendo un valor en el punto final de tu ruta en lugar de simplemente hacer un eco. La razón de esto es intencional pero podría sorprender a algunos desarrolladores.
Flight::route('/hello', function(){ // Esto podría causar un error 404 No Encontrado return '¡Hola Mundo!'; }); // Lo que probablemente deseas Flight::route('/hello', function(){ echo '¡Hola Mundo!'; });
La razón de esto es debido a un mecanismo especial incorporado en el enrutador que maneja la salida de retorno como un solo "ir a la siguiente ruta". Puedes ver el comportamiento documentado en la sección Enrutamiento.
Podría haber un par de razones por las que esto no está sucediendo. A continuación se muestran algunos ejemplos, pero asegúrate de revisar también la sección de carga automática.
Lo más común es que el nombre de la clase no coincida con el nombre del archivo.
Si tienes una clase llamada MiClase entonces el archivo debería llamarse MiClase.php. Si tienes una clase llamada MiClase y el archivo se llama miclase.php entonces el cargador automático no podrá encontrarlo.
MiClase
MiClase.php
miclase.php
Si estás utilizando espacios de nombres (namespaces), entonces el namespace debe coincidir con la estructura de directorios.
// código // si tu MyController está en el directorio app/controllers y tiene un espacio de nombres // esto no funcionará. Flight::route('/hello', 'MyController->hello'); // deberías elegir una de estas opciones Flight::route('/hello', 'app\controllers\MiControlador->hello'); // o si tienes una declaración use arriba use app\controllers\MiControlador; Flight::route('/hello', [ MiControlador::class, 'hello' ]); // también puede escribirse Flight::route('/hello', MiControlador::class.'->hello'); // también... Flight::route('/hello', [ 'app\controllers\MiControlador', 'hello' ]);
path()
En la aplicación esquelética, esto está definido dentro del archivo config.php, pero para que tus clases se encuentren, debes asegurarte de que el método path() esté definido (probablemente en la raíz de tu directorio) antes de intentar usarlo.
config.php
// Agregar una ruta al cargador automático Flight::path(__DIR__.'/../');
Si estás utilizando Composer, puedes ejecutar el siguiente comando:
composer require flightphp/core
O puedes descargar los archivos directamente y extraerlos en tu directorio web.
Para Apache, edita tu archivo .htaccess con lo siguiente:
.htaccess
RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [QSA,L]
Nota: Si necesitas usar flight en un subdirectorio, añade la línea RewriteBase /subdir/ justo después de RewriteEngine On. Nota: Si deseas proteger todos los archivos del servidor, como un archivo de base de datos o env. Agrega esto en tu archivo .htaccess:
Nota: Si necesitas usar flight en un subdirectorio, añade la línea RewriteBase /subdir/ justo después de RewriteEngine On.
RewriteBase /subdir/
RewriteEngine On
Nota: Si deseas proteger todos los archivos del servidor, como un archivo de base de datos o env. Agrega esto en tu archivo .htaccess:
RewriteEngine On RewriteRule ^(.*)$ index.php
Para Nginx, agrega lo siguiente a la declaración de tu servidor:
server { location / { try_files $uri $uri/ /index.php; } }
<?php // Si estás utilizando Composer, requiere el cargador automático. require 'vendor/autoload.php'; // si no estás utilizando Composer, carga el framework directamente // require 'flight/Flight.php'; // Luego define una ruta y asigna una función para manejar la solicitud. Flight::route('/', function () { echo '¡Hola Mundo!'; }); // Finalmente, inicia el framework. Flight::start();
Licencia MIT (MIT) ================== Derechos de autor © `2024` `@mikecao, @n0nag0n` Se concede permiso, de forma gratuita, a cualquier persona que obtenga una copia de este software y archivos de documentación asociados (el “Software”), para utilizar el Software sin restricciones, incluidos, entre otros, los derechos de usar, copiar, modificar, fusionar, publicar, distribuir, sublicenciar y/o vender copias del Software, y permitir a las personas a las que se les haya provisto el Software que lo hagan, sujeto a las siguientes condiciones: El aviso de derechos de autor anterior y este aviso de permiso deben estar incluidos en todas las copias o partes sustanciales del Software. EL SOFTWARE SE PROPORCIONA "TAL CUAL", SIN GARANTÍA DE NINGÚN TIPO, EXPRESA O IMPLÍCITA, INCLUIDAS, ENTRE OTRAS, LAS GARANTÍAS DE COMERCIABILIDAD, IDONEIDAD PARA UN PROPÓSITO PARTICULAR Y NO INFRACCIÓN. EN NINGÚN CASO LOS AUTORES O TITULARES DE LOS DERECHOS DE AUTOR SERÁN RESPONSABLES DE NINGUNA RECLAMACIÓN, DAÑOS U OTRA RESPONSABILIDAD, YA SEA EN UNA ACCIÓN DE CONTRATO, AGRAVIO O DE CUALQUIER OTRA ÍNDOLE, DERIVADA DE, FUERA DE O EN RELACIÓN CON EL SOFTWARE O EL USO U OTRO TIPO DE TRATAMIENTO EN EL SOFTWARE.
Flight es un marco de PHP rápido, simple y extensible. Es bastante versátil y se puede utilizar para construir cualquier tipo de aplicación web. Está construido con la simplicidad en mente y está escrito de una manera que es fácil de entender y usar.
Flight es un excelente marco para principiantes que son nuevos en PHP y desean aprender a construir aplicaciones web. También es un excelente marco para desarrolladores experimentados que desean tener más control sobre sus aplicaciones web. Está diseñado para construir fácilmente una API RESTful, una aplicación web simple o una aplicación web compleja.
<?php // si se instala con composer require 'vendor/autoload.php'; // o si se instala manualmente con un archivo zip // require 'flight/Flight.php'; Flight::route('/', function() { echo '¡hola mundo!'; }); Flight::route('/json', function() { Flight::json(['hello' => 'world']); }); Flight::start();
¿Suficientemente simple verdad? ¡Aprende más sobre Flight en la documentación!
Hay una aplicación de ejemplo que puede ayudarte a comenzar con el Marco de Flight. ¡Ve a flightphp/skeleton para obtener instrucciones sobre cómo comenzar! También puedes visitar la página de [ejemplos] para inspirarte en algunas de las cosas que puedes hacer con Flight.
Estamos en Matrix Chat con nosotros en #flight-php-framework:matrix.org.
Hay dos formas en las que puedes contribuir a Flight:
Flight requiere PHP 7.4 o superior.
Nota: PHP 7.4 es compatible porque en el momento actual de la escritura (2024) PHP 7.4 es la versión predeterminada para algunas distribuciones LTS de Linux. Forzar un cambio a PHP >8 causaría muchos dolores de cabeza para esos usuarios. El marco también es compatible con PHP >8.
Flight se publica bajo la licencia MIT.
overclokk/cookie es una biblioteca sencilla para administrar cookies dentro de su aplicación.
La instalación es sencilla con composer.
composer require overclokk/cookie
El uso es tan simple como registrar un nuevo método en la clase Flight.
use Overclokk\Cookie\Cookie; /* * Establezca en su archivo bootstrap o public/index.php */ Flight::register('cookie', Cookie::class); /** * ExampleController.php */ class ExampleController { public function login() { // Establecer una cookie // querrás que esto sea falso para obtener una nueva instancia // usa el comentario a continuación si deseas el autocompletado /** @var \Overclokk\Cookie\Cookie $cookie */ $cookie = Flight::cookie(false); $cookie->set( 'stay_logged_in', // nombre de la cookie '1', // el valor que deseas establecer 86400, // número de segundos que la cookie debe durar '/', // ruta en la que estará disponible la cookie 'example.com', // dominio en el que estará disponible la cookie true, // la cookie solo se transmitirá a través de una conexión segura HTTPS true // la cookie solo estará disponible a través del protocolo HTTP ); // opcionalmente, si deseas mantener los valores predeterminados // y tener una forma rápida de establecer una cookie por mucho tiempo $cookie->forever('stay_logged_in', '1'); } public function home() { // Verifica si tienes la cookie if (Flight::cookie()->has('stay_logged_in')) { // ponlos en el área del panel, por ejemplo. Flight::redirect('/dashboard'); } } }
defuse/php-encryption es una biblioteca que se puede utilizar para cifrar y descifrar datos. Ponerse en marcha es bastante simple para comenzar a cifrar y descifrar datos. Tienen un excelente tutorial que ayuda a explicar los conceptos básicos sobre cómo utilizar la biblioteca, así como las importantes implicaciones de seguridad relacionadas con el cifrado.
composer require defuse/php-encryption
Luego necesitarás generar una clave de cifrado.
vendor/bin/generate-defuse-key
Esto generará una clave que deberás mantener segura. Podrías guardar la clave en tu archivo app/config/config.php en el array al final del archivo. Aunque no es el lugar perfecto, al menos es algo.
app/config/config.php
Ahora que tienes la biblioteca y una clave de cifrado, puedes empezar a cifrar y descifrar datos.
use Defuse\Crypto\Crypto; use Defuse\Crypto\Key; /* * Establecerlo en tu archivo de inicio (bootstrap) o public/index.php */ // Método de cifrado Flight::map('encrypt', function($datos_crudos) { $clave_cifrado = /* $config['clave_cifrado'] o un file_get_contents de dónde pusiste la clave */; return Crypto::encrypt($datos_crudos, Key::loadFromAsciiSafeString($clave_cifrado)); }); // Método de descifrado Flight::map('decrypt', function($datos_cifrados) { $clave_cifrado = /* $config['clave_cifrado'] o un file_get_contents de dónde pusiste la clave */; try { $datos_crudos = Crypto::decrypt($datos_cifrados, Key::loadFromAsciiSafeString($clave_cifrado)); } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) { // ¡Un ataque! Se cargó la clave incorrecta o el texto cifrado // ha cambiado desde que fue creado, ya sea corrompido en la base de datos o modificado intencionalmente por Eve tratando de llevar a cabo un ataque. // ... manejar este caso de una manera adecuada para tu aplicación ... } return $datos_crudos; }); Flight::route('/cifrar', function() { $datos_cifrados = Flight::encrypt('Esto es un secreto'); echo $datos_cifrados; }); Flight::route('/descifrar', function() { $datos_cifrados = '...'; // Obtener los datos cifrados de algún lugar $datos_descifrados = Flight::decrypt($datos_cifrados); echo $datos_descifrados; });
Clase PHP de almacenamiento en archivo ligera, simple y autónoma
Ventajas
Haga clic aquí para ver el código.
Instalar a través de composer:
composer require wruczek/php-file-cache
El uso es bastante sencillo.
use Wruczek\PhpFileCache\PhpFileCache; $app = Flight::app(); // Pasa el directorio en el que se almacenará el caché al constructor $app->register('cache', PhpFileCache::class, [ __DIR__ . '/../cache/' ], function(PhpFileCache $cache) { // Esto asegura que el caché solo se utilice en modo de producción // ENVIRONMENT es una constante que se establece en tu archivo de inicio (bootstrap) o en otro lugar de tu aplicación $cache->setDevMode(ENVIRONMENT === 'development'); });
Entonces puedes usarlo en tu código así:
// Obtener instancia de caché $cache = Flight::cache(); $data = $cache->refreshIfExpired('simple-cache-test', function () { return date("H:i:s"); // devuelve los datos a cachear }, 10); // 10 segundos // o $data = $cache->retrieve('simple-cache-test'); if(empty($data)) { $data = date("H:i:s"); $cache->store('simple-cache-test', $data, 10); // 10 segundos }
Visita https://github.com/Wruczek/PHP-File-Cache para ver la documentación completa y asegúrate de ver la carpeta de ejemplos.
Se trata de un módulo de permisos que se puede utilizar en tus proyectos si tienes múltiples roles en tu aplicación y cada rol tiene una funcionalidad un poco diferente. Este módulo te permite definir permisos para cada rol y luego verificar si el usuario actual tiene el permiso para acceder a una página específica o realizar una acción determinada.
Haz clic aquí para acceder al repositorio en GitHub.
¡Ejecuta composer require flightphp/permissions y estás listo/a!
composer require flightphp/permissions
Primero necesitas configurar tus permisos, luego le indicas a tu aplicación lo que significan los permisos. Finalmente verificarás tus permisos con $Permissions->has(), ->can(), o is(). has() y can() tienen la misma funcionalidad, pero están nombrados de manera diferente para que tu código sea más legible.
$Permissions->has()
->can()
is()
has()
can()
Imaginemos que tienes una función en tu aplicación que verifica si un usuario ha iniciado sesión. Puedes crear un objeto de permisos de esta manera:
// index.php require 'vendor/autoload.php'; // algún código // luego probablemente tengas algo que te diga cuál es el rol actual de la persona // probablemente tengas algo donde obtienes el rol actual // de una variable de sesión que lo define // después de que alguien inicie sesión, de lo contrario tendrán un rol de 'invitado' o 'público'. $current_role = 'admin'; // configurar permisos $permiso = new \flight\Permiso($current_role); $permiso->defineRegla('iniciadoSesion', function($current_role) { return $current_role !== 'invitado'; }); // Probablemente querrás persistir este objeto en Flight en algún lugar Flight::set('permiso', $permiso);
Luego en un controlador en algún lugar, podrías tener algo como esto.
<?php // algún controlador class AlgunControlador { public function algunaAccion() { $permiso = Flight::get('permiso'); if ($permiso->has('iniciadoSesion')) { // hacer algo } else { // hacer algo más } } }
También puedes usar esto para rastrear si tienen permiso para hacer algo en tu aplicación. Por ejemplo, si tienes una forma en la que los usuarios pueden interactuar con la publicación en tu software, puedes verificar si tienen permiso para realizar ciertas acciones.
$current_role = 'admin'; // configurar permisos $permiso = new \flight\Permiso($current_role); $permiso->defineRegla('publicación', function($current_role) { if($current_role === 'admin') { $permisos = ['crear', 'leer', 'actualizar', 'eliminar']; } else if($current_role === 'editor') { $permisos = ['crear', 'leer', 'actualizar']; } else if($current_role === 'autor') { $permisos = ['crear', 'leer']; } else if($current_role === 'colaborador') { $permisos = ['crear']; } else { $permisos = []; } return $permisos; }); Flight::set('permiso', $permiso);
Luego en un controlador en algún lugar...
class ControladorPublicación { public function crear() { $permiso = Flight::get('permiso'); if ($permiso->can('publicación.crear')) { // hacer algo } else { // hacer algo más } } }
Puedes inyectar dependencias en el cierre que define los permisos. Esto es útil si tienes algún tipo de interruptor, identificación, u otro punto de datos que deseas verificar. Lo mismo funciona para llamadas de tipo Clase->Método, excepto que defines los argumentos en el método.
$Permiso->defineRegla('orden', function(string $current_role, MiDependencia $MiDependencia = null) { // ... código }); // en tu archivo de controlador public function crearOrden() { $MiDependencia = Flight::miDependencia(); $permiso = Flight::get('permiso'); if ($permiso->can('orden.crear', $MiDependencia)) { // hacer algo } else { // hacer algo más } }
namespace MiApp; class Permisos { public function orden(string $current_role, MiDependencia $MiDependencia = null) { // ... código } }
También puedes usar clases para definir tus permisos. Esto es útil si tienes muchos permisos y deseas mantener tu código limpio. Puedes hacer algo como esto:
<?php // código de inicio $Permisos = new \flight\Permiso($current_role); $Permisos->defineRegla('orden', 'MiApp\Permisos->orden'); // myapp/Permisos.php namespace MiApp; class Permisos { public function orden(string $current_role, int $user_id) { // Suponiendo que configuraste esto de antemano /** @var \flight\database\PdoWrapper $db */ $db = Flight::db(); $permisos_permitidos = [ 'leer' ]; // todos pueden ver una orden if($current_role === 'gerente') { $permisos_permitidos[] = 'crear'; // los gerentes pueden crear órdenes } $algún_interruptor_especial_de_db = $db->fetchField('SELECT algún_interruptor_especial FROM ajustes WHERE id = ?', [ $user_id ]); if($algún_interruptor_especial_de_db) { $permisos_permitidos[] = 'actualizar'; // si el usuario tiene un interruptor especial, pueden actualizar órdenes } if($current_role === 'admin') { $permisos_permitidos[] = 'eliminar'; // los administradores pueden eliminar órdenes } return $permisos_permitidos; } }
La parte genial es que también hay un atajo que puedes usar (¡también puede ser almacenado en caché!) donde simplemente indicas a la clase de permisos que mapee todos los métodos de una clase en permisos. Por lo tanto, si tienes un método llamado orden() y un método llamado empresa(), se mapearán automáticamente para que puedas simplemente ejecutar $Permisos->has('orden.leer') o $Permisos->has('empresa.leer') y funcionará. Definir esto es muy complejo, así que quédate conmigo aquí. Solo necesitas hacer esto:
orden()
empresa()
$Permisos->has('orden.leer')
$Permisos->has('empresa.leer')
Crea la clase de permisos que deseas agrupar.
class MisPermisos { public function orden(string $current_role, int $orden_id = 0): array { // código para determinar permisos return $arreglo_permisos; } public function empresa(string $current_role, int $empresa_id): array { // código para determinar permisos return $arreglo_permisos; } }
Luego haz que los permisos sean descubribles usando esta biblioteca.
$Permisos = new \flight\Permiso($current_role); $Permisos->defineReglasDesdeMétodosDeClase(MiApp\Permisos::class); Flight::set('permisos', $Permisos);
Finalmente, llama al permiso en tu base de código para verificar si el usuario tiene permiso para realizar un permiso dado.
class AlgunControlador { public function crearOrden() { if(Flight::get('permisos')->can('orden.crear') === false) { die('¡No puedes crear una orden. ¡Lo siento!'); } } }
Para habilitar la caché, consulta la sencilla biblioteca wruczak/phpfilecache. Un ejemplo de cómo habilitar esto se muestra a continuación.
// esta variable $app puede ser parte de tu código, o // simplemente puedes pasar null y se // obtendrá de Flight::app() en el constructor $app = Flight::app(); // Por ahora solo acepta esto como una caché de archivos. Otros pueden agregarse fácilmente en el futuro. $Caché = new Wruczek\PhpFileCaché\PhpFileCaché; $Permisos = new \flight\Permiso($current_role, $app, $Caché); $Permisos->defineReglasDesdeMétodosDeClase(MiApp\Permisos::class, 3600); // 3600 indica cuántos segundos almacenar en caché. Omite esto para no usar la caché
¡Y listo!
Flight es increíblemente extensible. Hay varios complementos que se pueden utilizar para añadir funcionalidad a tu aplicación Flight. Algunos son oficialmente compatibles con el Equipo de Flight y otros son bibliotecas micro/lite para ayudarte a comenzar.
La caché es una excelente manera de acelerar tu aplicación. Hay varias bibliotecas de caché que se pueden utilizar con Flight.
La depuración es crucial cuando estás desarrollando en tu entorno local. Hay algunos complementos que pueden mejorar tu experiencia de depuración.
Las bases de datos son el núcleo de la mayoría de las aplicaciones. Así es como almacenas y recuperas datos. Algunas bibliotecas de bases de datos son simplemente envoltorios para escribir consultas y otras son ORM completos.
Las sesiones no son realmente útiles para las API, pero para desarrollar una aplicación web, las sesiones pueden ser cruciales para mantener el estado e información de inicio de sesión.
Las plantillas son esenciales para cualquier aplicación web con un UI. Hay varios motores de plantillas que se pueden utilizar con Flight.
¿Tienes un complemento que te gustaría compartir? ¡Envía una solicitud de extracción para añadirlo a la lista!
Flight viene con una clase de ayuda para PDO. Le permite consultar fácilmente su base de datos con toda la locura de preparar/ejecutar/obtenerTodo(). Simplifica en gran medida cómo puede consultar su base de datos. Cada resultado de fila se devuelve como una clase Flight Collection que le permite acceder a sus datos mediante la sintaxis de matriz o la sintaxis de objeto.
// Registrar la clase de ayuda PDO Flight::register('db', \flight\database\PdoWrapper::class, ['mysql:host=localhost;dbname=cool_db_name', 'usuario', 'contraseña', [ PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'utf8mb4\'', PDO::ATTR_EMULATE_PREPARES => false, PDO::ATTR_STRINGIFY_FETCHES => false, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ] ]);
Este objeto extiende PDO, por lo que todos los métodos normales de PDO están disponibles. Los siguientes métodos se agregan para facilitar la consulta a la base de datos:
runQuery(string $sql, array $params = []): PDOStatement
Úselo para INSERTS, UPDATES o si planea usar un SELECT en un bucle while
$db = Flight::db(); $statement = $db->runQuery("SELECT * FROM table WHERE something = ?", [ $algo ]); while($row = $statement->fetch()) { // ... } // O escribir en la base de datos $db->runQuery("INSERT INTO table (nombre) VALUES (?)", [ $nombre ]); $db->runQuery("UPDATE table SET nombre = ? WHERE id = ?", [ $nombre, $id ]);
fetchField(string $sql, array $params = []): mixed
Extrae el primer campo de la consulta
$db = Flight::db(); $count = $db->fetchField("SELECT COUNT(*) FROM table WHERE something = ?", [ $algo ]);
fetchRow(string $sql, array $params = []): array
Extrae una fila de la consulta
$db = Flight::db(); $row = $db->fetchRow("SELECT id, nombre FROM table WHERE id = ?", [ $id ]); echo $row['nombre']; // o echo $row->nombre;
fetchAll(string $sql, array $params = []): array
Extrae todas las filas de la consulta
$db = Flight::db(); $rows = $db->fetchAll("SELECT id, nombre FROM table WHERE something = ?", [ $algo ]); foreach($rows as $row) { echo $row['nombre']; // o echo $row->nombre; }
IN()
Esto también tiene un envoltorio útil para las declaraciones IN(). Simplemente puede pasar un signo de interrogación como marcador de posición para IN() y luego un array de valores. Aquí hay un ejemplo de cómo podría verse eso:
$db = Flight::db(); $nombre = 'Bob'; $ids_compañía = [1,2,3,4,5]; $rows = $db->fetchAll("SELECT id, nombre FROM table WHERE nombre = ? AND company_id IN (?)", [ $nombre, $ids_compañía ]);
// Ruta de ejemplo y cómo usar este envoltorio Flight::route('/usuarios', function () { // Obtener todos los usuarios $usuarios = Flight::db()->fetchAll('SELECT * FROM users'); // Transmitir todos los usuarios $declaración = Flight::db()->runQuery('SELECT * FROM users'); while ($usuario = $declaración->fetch()) { echo $usuario['nombre']; // o echo $usuario->nombre; } // Obtener un usuario único $usuario = Flight::db()->fetchRow('SELECT * FROM users WHERE id = ?', [123]); // Obtener un valor único $count = Flight::db()->fetchField('SELECT COUNT(*) FROM users'); // Sintaxis especial de IN() para ayudar (asegúrese de que IN esté en mayúsculas) $usuarios = Flight::db()->fetchAll('SELECT * FROM users WHERE id IN (?)', [[1,2,3,4,5]]); // también se podría hacer esto $usuarios = Flight::db()->fetchAll('SELECT * FROM users WHERE id IN (?)', [ '1,2,3,4,5']); // Insertar un nuevo usuario Flight::db()->runQuery("INSERT INTO users (nombre, email) VALUES (?, ?)", ['Bob', 'bob@example.com']); $id_insertado = Flight::db()->lastInsertId(); // Actualizar un usuario Flight::db()->runQuery("UPDATE users SET nombre = ? WHERE id = ?", ['Bob', 123]); // Eliminar un usuario Flight::db()->runQuery("DELETE FROM users WHERE id = ?", [123]); // Obtener el número de filas afectadas $declaración = Flight::db()->runQuery("UPDATE users SET nombre = ? WHERE nombre = ?", ['Bob', 'Sally']); $filas_afectadas = $declaración->rowCount(); });
Gestor de Sesiones PHP (no bloqueante, flash, segmento, cifrado de sesiones). Utiliza PHP open_ssl para cifrar/descifrar datos de sesión de forma opcional. Admite Archivo, MySQL, Redis y Memcached.
Instalar con composer.
composer require ghostff/session
No es necesario pasar nada para usar la configuración predeterminada con tu sesión. Puedes leer más sobre la configuración en el README de Github.
use Ghostff\Session\Session; require 'vendor/autoload.php'; $app = Flight::app(); $app->register('session', Session::class); // una cosa a tener en cuenta es que debes confirmar tu sesión en cada carga de página // o necesitarás ejecutar auto_commit en tu configuración.
Aquí tienes un ejemplo simple de cómo podrías usar esto.
Flight::route('POST /login', function() { $session = Flight::session(); // realiza tu lógica de inicio de sesión aquí // valídalo contraseña, etc. // si el inicio de sesión es exitoso $session->set('is_logged_in', true); $session->set('user', $user); // cada vez que escribas en la sesión, debes confirmarla deliberadamente. $session->commit(); }); // Esta verificación podría estar en la lógica de la página restringida, o envuelta con un middleware. Flight::route('/alguna-página-restringida', function() { $session = Flight::session(); if(!$session->get('is_logged_in')) { Flight::redirect('/inicio'); } // realiza tu lógica de página restringida aquí }); // la versión de middleware Flight::route('/alguna-página-restringida', function() { // lógica regular de la página })->addMiddleware(function() { $session = Flight::session(); if(!$session->get('is_logged_in')) { Flight::redirect('/inicio'); } });
Aquí tienes un ejemplo más complejo de cómo podrías usar esto.
use Ghostff\Session\Session; require 'vendor/autoload.php'; $app = Flight::app(); // establece una ruta personalizada a tu archivo de configuración de sesión y dale una cadena aleatoria para la ID de sesión $app->register('session', Session::class, [ 'ruta/a/sesion_config.php', bin2hex(random_bytes(32)) ], function(Session $session) { // o puedes anular manualmente las opciones de configuración $session->updateConfiguration([ // si deseas almacenar tus datos de sesión en una base de datos (bueno si deseas algo como, "cierra la sesión en todos los dispositivos" funcionalidad) Session::CONFIG_DRIVER => Ghostff\Session\Drivers\MySql::class, Session::CONFIG_ENCRYPT_DATA => true, Session::CONFIG_SALT_KEY => hash('sha256', 'mi-super-s3cret-salt'), // por favor cambia esto a algo diferente Session::CONFIG_AUTO_COMMIT => true, // solo hazlo si es necesario y/o es difícil confirmar() tu sesión. // además podrías hacer Flight::after('start', function() { Flight::session()->commit(); }); Session::CONFIG_MYSQL_DS => [ 'driver' => 'mysql', # Controlador de base de datos para dsn de PDO ej.(mysql:host=...;dbname=...) 'host' => '127.0.0.1', # Host de la base de datos 'db_name' => 'mi_base_de_datos_app', # Nombre de la base de datos 'db_table' => 'sesiones', # Tabla de la base de datos 'db_user' => 'root', # Nombre de usuario de la base de datos 'db_pass' => '', # Contraseña de la base de datos 'persistent_conn'=> false, # Evitar la carga de establecer una nueva conexión cada vez que un script necesita hablar con una base de datos, lo que resulta en una aplicación web más rápida. BUSCA LA DESVENTAJA TÚ MISMO ] ]); } );
¿Estás configurando tus datos de sesión y no persisten entre peticiones? Podrías haber olvidado confirmar tus datos de sesión. Puedes hacer esto llamando a $session->commit() después de haber establecido tus datos de sesión.
$session->commit()
Flight::route('POST /login', function() { $session = Flight::session(); // realiza tu lógica de inicio de sesión aquí // valídalo contraseña, etc. // si el inicio de sesión es exitoso $session->set('is_logged_in', true); $session->set('user', $user); // cada vez que escribas en la sesión, debes confirmarla deliberadamente. $session->commit(); });
La otra forma de solucionar esto es cuando configuras tu servicio de sesión, debes establecer auto_commit en true en tu configuración. Esto confirmará automáticamente tus datos de sesión después de cada petición.
auto_commit
$app->register('session', Session::class, [ 'ruta/a/sesion_config.php', bin2hex(random_bytes(32)) ], function(Session $session) { $session->updateConfiguration([ Session::CONFIG_AUTO_COMMIT => true, ]); } );
Además podrías hacer Flight::after('start', function() { Flight::session()->commit(); }); para confirmar tus datos de sesión después de cada petición.
Flight::after('start', function() { Flight::session()->commit(); });
Visita el README de Github para ver la documentación completa. Las opciones de configuración están bien documentadas en el archivo default_config.php en sí. El código es fácil de entender si deseas examinar este paquete por ti mismo.
Pista es una aplicación CLI que te ayuda a gestionar tus aplicaciones Flight. Puede generar controladores, mostrar todas las rutas y más. Está basado en la excelente biblioteca adhocore/php-cli.
Haz clic aquí para ver el código.
Instala con composer.
composer require flightphp/runway
La primera vez que ejecutes Pista, te guiará a través de un proceso de configuración y creará un archivo de configuración .runway.json en la raíz de tu proyecto. Este archivo contendrá algunas configuraciones necesarias para que Pista funcione correctamente.
.runway.json
Pista tiene varios comandos que puedes usar para gestionar tu aplicación Flight. Hay dos formas fáciles de usar Pista.
php runway [comando]
vendor/bin/runway [comando]
Para cualquier comando, puedes agregar la bandera --help para obtener más información sobre cómo usar el comando.
--help
php runway routes --help
Aquí tienes algunos ejemplos:
Basado en la configuración en tu archivo .runway.json, la ubicación predeterminada generará un controlador para ti en el directorio app/controllers/.
app/controllers/
php runway make:controller MiControlador
Basado en la configuración en tu archivo .runway.json, la ubicación predeterminada generará un modelo de Active Record para ti en el directorio app/records/.
app/records/
php runway make:record usuarios
Si por ejemplo tienes la tabla usuarios con el siguiente esquema: id, nombre, correo, creado_en, actualizado_en, se creará un archivo similar al siguiente en el archivo app/records/UserRecord.php:
usuarios
nombre
correo
creado_en
actualizado_en
app/records/UserRecord.php
<?php declare(strict_types=1); namespace app\records; /** * Clase ActiveRecord para la tabla de usuarios. * @link https://docs.flightphp.com/awesome-plugins/active-record * * @property int $id * @property string $nombre * @property string $correo * @property string $creado_en * @property string $actualizado_en * // también puedes añadir relaciones aquí una vez las definas en el array $relations * @property CompanyRecord $company Ejemplo de una relación */ class UserRecord extends \flight\ActiveRecord { /** * @var array $relations Establece las relaciones para el modelo * https://docs.flightphp.com/awesome-plugins/active-record#relationships */ protected array $relations = []; /** * Constructor * @param mixed $conexionBaseDeDatos La conexión a la base de datos */ public function __construct($conexionBaseDeDatos) { parent::__construct($conexionBaseDeDatos, 'usuarios'); } }
Esto mostrará todas las rutas que están actualmente registradas en Flight.
php runway routes
Si deseas ver solo rutas específicas, puedes agregar una bandera para filtrar las rutas.
# Mostrar solo rutas GET php runway routes --get # Mostrar solo rutas POST php runway routes --post # etc.
Si estás creando un paquete para Flight, o deseas añadir tus propios comandos personalizados a tu proyecto, puedes hacerlo creando un directorio src/commands/, flight/commands/, app/commands/ o commands/ para tu proyecto/paquete.
src/commands/
flight/commands/
app/commands/
commands/
Para crear un comando, simplemente extiende la clase AbstractBaseCommand e implementa como mínimo un método __construct y un método execute.
AbstractBaseCommand
__construct
execute
<?php declare(strict_types=1); namespace flight\commands; class ExampleCommand extends AbstractBaseCommand { /** * Constructor * * @param array<string,mixed> $config JSON config de .runway-config.json */ public function __construct(array $config) { parent::__construct('make:example', 'Crear un ejemplo para la documentación', $config); $this->argument('<gif-divertido>', 'El nombre del gif divertido'); } /** * Ejecuta la función * * @return void */ public function execute(string $controlador) { $io = $this->app()->io(); $io->info('Creando ejemplo...'); // Haz algo aquí $io->ok('¡Ejemplo creado!'); } }
Consulta la Documentación de adhocore/php-cli para obtener más información sobre cómo crear tus propios comandos personalizados en tu aplicación Flight.
=====
This is a set of extensions to make working with Flight a little richer.
$_SESSION
This is the Panel
And each panel displays very helpful information about your application!
Click here to view the code.
Run composer require flightphp/tracy-extensions --dev and you're on your way!
composer require flightphp/tracy-extensions --dev
There is very little configuration you need to do to get this started. You will need to initiate the Tracy debugger prior to using this https://tracy.nette.org/en/guide:
<?php use Tracy\Debugger; use flight\debug\tracy\TracyExtensionLoader; // bootstrap code require __DIR__ . '/vendor/autoload.php'; Debugger::enable(); // You may need to specify your environment with Debugger::enable(Debugger::DEVELOPMENT) // if you use database connections in your app, there is a // required PDO wrapper to use ONLY IN DEVELOPMENT (not production please!) // It has the same parameters as a regular PDO connection $pdo = new PdoQueryCapture('sqlite:test.db', 'user', 'pass'); // or if you attach this to the Flight framework Flight::register('db', PdoQueryCapture::class, ['sqlite:test.db', 'user', 'pass']); // now whenever you make a query it will capture the time, query, and parameters // This connects the dots if(Debugger::$showBar === true) { // This needs to be false or Tracy can't actually render :( Flight::set('flight.content_length', false); new TracyExtensionLoader(Flight::app()); } // more code Flight::start();
If you have a custom session handler (such as ghostff/session), you can pass any array of session data to Tracy and it will automatically output it for you. You pass it in with the session_data key in the second parameter of the TracyExtensionLoader constructor.
session_data
TracyExtensionLoader
use Ghostff\Session\Session; require 'vendor/autoload.php'; $app = Flight::app(); $app->register('session', Session::class); if(Debugger::$showBar === true) { // This needs to be false or Tracy can't actually render :( Flight::set('flight.content_length', false); new TracyExtensionLoader(Flight::app(), [ 'session_data' => Flight::session()->getAll() ]); } // routes and other things... Flight::start();
If you have Latte installed in your project, you can use the Latte panel to analyze your templates. You can pass in the Latte instance to the TracyExtensionLoader constructor with the latte key in the second parameter.
latte
use Latte\Engine; require 'vendor/autoload.php'; $app = Flight::app(); $app->register('latte', Engine::class, [], function($latte) { $latte->setTempDirectory(__DIR__ . '/temp'); // this is where you add the Latte Panel to Tracy $latte->addExtension(new Latte\Bridges\Tracy\TracyExtension); }); if(Debugger::$showBar === true) { // This needs to be false or Tracy can't actually render :( Flight::set('flight.content_length', false); new TracyExtensionLoader(Flight::app()); }
Tracy es un increíble manejador de errores que se puede utilizar con Flight. Tiene una serie de paneles que pueden ayudarte a depurar tu aplicación. También es muy fácil de extender y agregar tus propios paneles. El equipo de Flight ha creado algunos paneles específicamente para proyectos de Flight con el complemento flightphp/tracy-extensions.
Instala con composer. Y realmente querrás instalar esto sin la versión de desarrollo ya que Tracy viene con un componente de manejo de errores de producción.
composer require tracy/tracy
Hay algunas opciones de configuración básicas para comenzar. Puedes leer más sobre ellas en la Documentación de Tracy.
require 'vendor/autoload.php'; use Tracy\Debugger; // Habilitar Tracy Debugger::enable(); // Debugger::enable(Debugger::DEVELOPMENT) // a veces tienes que ser explícito (también Debugger::PRODUCTION) // Debugger::enable('23.75.345.200'); // también puedes proporcionar una matriz de direcciones IP // Aquí es donde se registrarán los errores y excepciones. Asegúrate de que este directorio exista y sea escribible. Debugger::$logDirectory = __DIR__ . '/../log/'; Debugger::$strictMode = true; // mostrar todos los errores // Debugger::$strictMode = E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED; // todos los errores excepto avisos obsoletos if (Debugger::$showBar) { $app->set('flight.content_length', false); // si la barra Debugger es visible, entonces la longitud del contenido no puede ser establecida por Flight // Esto es específico para la Extensión de Tracy para Flight si la has incluido // de lo contrario, comenta esto. new TracyExtensionLoader($app); }
Cuando estés depurando tu código, hay algunas funciones muy útiles para mostrar datos para ti.
bdump($var)
dumpe($var)
Un active record es mapear una entidad de base de datos a un objeto PHP. En pocas palabras, si tienes una tabla de usuarios en tu base de datos, puedes "traducir" una fila de esa tabla a una clase User y un objeto $user en tu código. Mira el ejemplo básico.
User
$user
Supongamos que tienes la siguiente tabla:
CREATE TABLE users ( id INTEGER PRIMARY KEY, name TEXT, password TEXT );
Ahora puedes configurar una nueva clase para representar esta tabla:
/** * Una clase ActiveRecord suele ser singular * * Se recomienda encarecidamente agregar las propiedades de la tabla como comentarios aquí * * @property int $id * @property string $name * @property string $password */ class User extends flight\ActiveRecord { public function __construct($database_connection) { // puedes establecerlo de esta manera parent::__construct($database_connection, 'users'); // o de esta manera parent::__construct($database_connection, null, [ 'table' => 'users']); } }
¡Ahora observa la magia suceder!
// para sqlite $database_connection = new PDO('sqlite:test.db'); // esto es solo un ejemplo, probablemente usarías una conexión de base de datos real // para mysql $database_connection = new PDO('mysql:host=localhost;dbname=test_db&charset=utf8bm4', 'nombre_de_usuario', 'contraseña'); // o mysqli $database_connection = new mysqli('localhost', 'nombre_de_usuario', 'contraseña', 'test_db'); // o mysqli con creación no basada en objetos $database_connection = mysqli_connect('localhost', 'nombre_de_usuario', 'contraseña', 'test_db'); $user = new User($database_connection); $user->name = 'Bobby Tables'; $user->password = password_hash('una contraseña genial'); $user->insert(); // o $user->save(); echo $user->id; // 1 $user->name = 'Joseph Mamma'; $user->password = password_hash('¡otra contraseña genial!'); $user->insert(); // ¡no se puede usar $user->save() aquí o pensará que es una actualización! echo $user->id; // 2
¡Y fue tan fácil agregar un nuevo usuario! Ahora que hay una fila de usuario en la base de datos, ¿cómo la sacas?
$user->find(1); // encuentra el id = 1 en la base de datos y devuélvelo. echo $user->name; // 'Bobby Tables'
¿Y si quieres encontrar a todos los usuarios?
$users = $user->findAll();
¿Y con una condición específica?
$users = $user->like('name', '%mamma%')->findAll();
¿Ves lo divertido que es esto? ¡Instalémoslo y empecemos!
Simplemente instala con Composer
composer require flightphp/active-record
Esto se puede usar como una biblioteca independiente o con el Framework PHP Flight. Completamente depende de ti.
Solo asegúrate de pasar una conexión PDO al constructor.
$conexion_pdo = new PDO('sqlite:test.db'); // esto es solo un ejemplo, probablemente usarías una conexión de base de datos real $Usuario = new User($conexion_pdo);
¿No quieres siempre establecer tu conexión de base de datos en el constructor? Consulta Gestión de la conexión de base de datos para más ideas!
Si estás utilizando el Framework PHP Flight, puedes registrar la clase ActiveRecord como un servicio, pero honestamente no es necesario.
Flight::register('usuario', 'User', [ $conexion_pdo ]); // luego puedes usarlo así en un controlador, una función, etc. Flight::usuario()->find(1);
runway
runway es una herramienta CLI para Flight que tiene un comando personalizado para esta biblioteca.
# Uso php runway make:record nombre_tabla_base_datos [nombre_clase] # Ejemplo php runway make:record usuarios
Esto creará una nueva clase en el directorio app/records/ como UserRecord.php con el siguiente contenido:
UserRecord.php
<?php declare(strict_types=1); namespace app\records; /** * Clase ActiveRecord para la tabla de usuarios. * @link https://docs.flightphp.com/awesome-plugins/active-record * * @property int $id * @property string $nombre_usuario * @property string $correo_electrónico * @property string $contraseña_hash * @property string $fecha_creación */ class UserRecord extends \flight\ActiveRecord { /** * @var array $relations Establece las relaciones para el modelo * https://docs.flightphp.com/awesome-plugins/active-record#relationships */ protected array $relations = [ // 'nombre_relación' => [ self::HAS_MANY, 'ClaseRelacionada', 'clave_foránea' ], ]; /** * Constructor * @param mixed $conexionBaseDatos La conexión a la base de datos */ public function __construct($conexionBaseDatos) { parent::__construct($conexionBaseDatos, 'users'); } }
find($id = null) : boolean|ActiveRecord
Encuentra un registro y lo asigna al objeto actual. Si pasas un $id de algún tipo, realizará una búsqueda en la clave principal con ese valor. Si no se pasa nada, simplemente encontrará el primer registro en la tabla.
Además, puedes pasarle otros métodos auxiliares para consultar tu tabla.
// encontrar un registro con algunas condiciones previas $user->notNull('password')->orderBy('id DESC')->find(); // encontrar un registro por un id específico $id = 123; $user->find($id);
findAll(): array<int,ActiveRecord>
Encuentra todos los registros en la tabla que especifiques.
$user->findAll();
isHydrated(): boolean
Devuelve true si el registro actual ha sido recuperado (extraído de la base de datos).
$user->find(1); // si se encuentra un registro con datos... $user->isHydrated(); // true
insert(): boolean|ActiveRecord
Inserta el registro actual en la base de datos.
$user = new User($conexion_pdo); $user->name = 'demo'; $user->password = md5('demo'); $user->insert();
Si tienes una clave primaria basada en texto (como un UUID), puedes establecer el valor de la clave primaria antes de insertar de dos maneras.
$user = new User($conexion_pdo, [ 'primaryKey' => 'uuid' ]); $user->uuid = 'some-uuid'; $user->name = 'demo'; $user->password = md5('demo'); $user->insert(); // o $user->save();
o puedes generar automáticamente la clave primaria para ti a través de eventos.
class User extends flight\ActiveRecord { public function __construct($conexionBaseDatos) { parent::__construct($conexionBaseDatos, 'users', [ 'primaryKey' => 'uuid' ]); // también puedes establecer la clave primaria de esta manera en lugar del arreglo anterior. $this->primaryKey = 'uuid'; } protected function beforeInsert(self $self) { $self->uuid = uniqid(); // o como sea que necesites generar tus identificadores únicos } }
Si no estableces la clave primaria antes de insertar, se establecerá en rowid y la base de datos la generará automáticamente por ti, pero no persistirá porque ese campo puede no existir en tu tabla. Por eso se recomienda usar el evento para manejar esto automáticamente.
rowid
update(): boolean|ActiveRecord
Actualiza el registro actual en la base de datos.
$user->greaterThan('id', 0)->orderBy('id desc')->find(); $user->$email = 'test@example.com'; $user->update();
save(): boolean|ActiveRecord
Inserta o actualiza el registro actual en la base de datos. Si el registro tiene un id, se actualizará; de lo contrario, se insertará.
$user = new User($conexion_pdo); $user->name = 'demo'; $user->password = md5('demo'); $user->save();
Nota: Si tienes relaciones definidas en la clase, se guardarán recursivamente esas relaciones también si se han definido, instanciado y tienen datos sucios para actualizar. (v0.4.0 y superior)
delete(): boolean
Elimina el registro actual de la base de datos.
$user->gt('id', 0)->orderBy('id desc')->find(); $user->delete();
También puedes eliminar varios registros ejecutando una búsqueda antes.
$user->like('name', 'Bob%')->delete();
dirty(array $dirty = []): ActiveRecord
Datos sucios se refiere a los datos que han cambiado en un registro.
$user->greaterThan('id', 0)->orderBy('id desc')->find(); // nada está "sucio" hasta este punto. $user->email = 'test@example.com'; // ahora el email se considera "sucio" ya que ha cambiado. $user->update(); // ahora no hay datos que estén "sucios" porque se han actualizado y persistido en la base de datos $user->password = password_hash()'newpassword'); // ahora esto está sucio $user->dirty(); // pasar nada limpiará todas las entradas sucias. $user->update(); // nada se actualizará porque no se capturó nada como sucio. $user->dirty([ 'name' => 'something', 'password' => password_hash('a different password') ]); $user->update(); // tanto el nombre como la contraseña se actualizarán.
copyFrom(array $data): ActiveRecord
Este es un alias del método dirty(). Es un poco más claro lo que estás haciendo.
dirty()
$user->copyFrom([ 'name' => 'something', 'password' => password_hash('a different password') ]); $user->update(); // tanto el nombre como la contraseña se actualizarán.
isDirty(): boolean
Devuelve true si el registro actual ha cambiado.
$user->greaterThan('id', 0)->orderBy('id desc')->find(); $user->email = 'test@email.com'; $user->isDirty(); // true
reset(bool $include_query_data = true): ActiveRecord
Restablece el registro actual a su estado inicial. Esto es realmente bueno usarlo en comportamientos de bucle. Si pasas true, también restablecerá los datos de la consulta que se utilizaron para encontrar el objeto actual (comportamiento predeterminado).
$users = $user->greaterThan('id', 0)->orderBy('id desc')->find(); $user_company = new UserCompany($conexion_pdo); foreach($users as $user) { $user_company->reset(); // comienza de nuevo $user_company->user_id = $user->id; $user_company->company_id = $some_company_id; $user_company->insert(); }
getBuiltSql(): string
Después de ejecutar un método find(), findAll(), insert(), update() o save(), puedes obtener el SQL que se construyó y usarlo con fines de depuración.
find()
findAll()
insert()
update()
save()
select(string $field1 [, string $field2 ... ])
Puedes seleccionar solo algunos de los campos en una tabla si lo deseas (es más eficiente en tablas muy anchas con muchos campos)
$user->select('id', 'name')->find();
from(string $table)
Técnicamente puedes elegir otra tabla también. ¡¿Por qué no?!
$user->select('id', 'name')->from('user')->find();
join(string $table_name, string $join_condition)
Incluso puedes unirte a otra tabla en la base de datos.
$user->join('contacts', 'contacts.user_id = users.id')->find();
where(string $where_conditions)
Puedes establecer algunas condiciones where personalizadas (no puedes establecer parámetros en esta sentencia where)
$user->where('id=1 AND name="demo"')->find();
Nota de Seguridad - Es posible que te sientas tentado a hacer algo como $user->where("id = '{$id}' AND name = '{$name}'")->find();. ¡Por favor, ¡NO LO HAGAS! Esto es susceptible a lo que se conoce como ataques de inyección de SQL. Hay muchos artículos en línea, por favor busca en Google "ataques de inyección de sql php" y encontrarás muchos artículos sobre este tema. La forma adecuada de manejar esto con esta biblioteca es, en lugar de este método where(), harías algo más como $user->eq('id', $id)->eq('name', $name)->find(); Si realmente necesitas hacer esto, la librería PDO tiene $pdo->quote($var) para escaparlo por ti. Solo después de usar quote() puedes usarlo en una instrucción where().
$user->where("id = '{$id}' AND name = '{$name}'")->find();
where()
$user->eq('id', $id)->eq('name', $name)->find();
$pdo->quote($var)
quote()
group(string $group_by_statement)/groupBy(string $group_by_statement)
Agrupa tus resultados por una condición específica.
$user->select('COUNT(*) as count')->groupBy('name')->findAll();
order(string $order_by_statement)/orderBy(string $order_by_statement)
Ordena la consulta devuelta de una manera específica.
$user->orderBy('name DESC')->find();
limit(string $limit)/limit(int $offset, int $limit)
Limita la cantidad de registros devueltos. Si se da un segundo entero, funcionará como desplazamiento, limit justo como en SQL.
$user->orderby('name DESC')->limit(0, 10)->findAll();
equal(string $field, mixed $value) / eq(string $field, mixed $value)
Donde field = $value
field = $value
$user->eq('id', 1)->find();
notEqual(string $field, mixed $value) / ne(string $field, mixed $value)
Donde field <> $value
field <> $value
$user->ne('id', 1)->find();
isNull(string $field)
Donde field IS NULL
field IS NULL
$user->isNull('id')->find();
isNotNull(string $field) / notNull(string $field)
Donde field IS NOT NULL
field IS NOT NULL
$user->isNotNull('id')->find();
greaterThan(string $field, mixed $value) / gt(string $field, mixed $value)
Donde field > $value
field > $value
$user->gt('id', 1)->find();
lessThan(string $field, mixed $value) / lt(string $field, mixed $value)
Donde field < $value
field < $value
$user->lt('id', 1)->find();
greaterThanOrEqual(string $field, mixed $value) / ge(string $field, mixed $value) / gte(string $field, mixed $value)
Donde field >= $value
field >= $value
$user->ge('id', 1)->find();
lessThanOrEqual(string $field, mixed $value) / le(string $field, mixed $value) / lte(string $field, mixed $value)
Donde field <= $value
field <= $value
$user->le('id', 1)->find();
like(string $field, mixed $value) / notLike(string $field, mixed $value)
Donde field LIKE $value o field NOT LIKE $value
field LIKE $value
field NOT LIKE $value
$user->like('name', 'de')->find();
in(string $field, array $values) / notIn(string $field, array $values)
Donde field IN($value) o field NOT IN($value)
field IN($value)
field NOT IN($value)
$user->in('id', [1, 2])->find();
between(string $field, array $values)
Donde field BETWEEN $value AND $value1
field BETWEEN $value AND $value1
$user->between('id', [1, 2])->find();
Puedes establecer varios tipos de relaciones usando esta biblioteca. Puedes establecer relaciones uno a muchos y uno a uno entre tablas. Esto requiere una configuración adicional en la clase de antemano.
Configurar el array $relations no es difícil, pero adivinar la sintaxis correcta puede ser confuso.
$relations
protected array $relations = [ // puedes nombrar la clave como desees. El nombre del ActiveRecord es probablemente bueno. Ej: usuario, contacto, cliente 'usuario' => [ // necesario // self::HAS_MANY, self::HAS_ONE, self::BELONGS_TO self::HAS_ONE, // este es el tipo de relación // necesario 'Otra_Clase', // esta es la clase de ActiveRecord "otra" a la que se hará referencia // necesario // dependiendo del tipo de relación // self::HAS_ONE = la clave foránea que referencia la unión // self::HAS_MANY = la clave foránea que referencia la unión // self::BELONGS_TO = la clave local que referencia la unión 'clave_local_o_foránea', // solo para que sepas, esto tambiénse unirá a la clave principal del modelo "otro" // opcional [ 'eq' => [ 'id_cliente', 5 ], 'select' => 'COUNT(*) as count', 'limit' 5 ], // condiciones adicionales que deseas al unir la relación // $registro->eq('id_cliente', 5)->select('COUNT(*) as count')->limit(5)) // opcional 'nombre_referencia_inversa' // esto es si quieres referir esta relación de vuelta a sí misma Ej: $usuario->contacto->usuario; ]; ]
class User extends ActiveRecord{ protected array $relations = [ 'contactos' => [ self::HAS_MANY, Contacto::class, 'id_usuario' ], 'contacto' => [ self::HAS_ONE, Contacto::class, 'id_usuario' ], ]; public function __construct($conexionBaseDatos) { parent::__construct($conexionBaseDatos, 'usuarios'); } } class Contacto extends ActiveRecord{ protected array $relations = [ 'usuario' => [ self::BELONGS_TO, Usuario::class, 'id_usuario' ], 'usuario_con_referencia_inversa' => [ self::BELONGS_TO, Usuario::class, 'id_usuario', [], 'contacto' ], ]; public function __construct($conexionBaseDatos) { parent::__construct($conexionBaseDatos, 'contactos'); } }
¡Ahora tenemos las referencias configuradas para que las usemos muy fácilmente!
$usuario = new User($conexion_pdo); // encuentra el usuario más reciente. $usuario->notNull('id')->orderBy('id desc')->find(); // obtener contactos usando una relación: foreach($usuario->contactos as $contacto) { echo $contacto->id; } // o podemos hacerlo al revés. $contacto = new Contacto(); // encuentra un contacto $contacto->find(); // obtener usuario usando una relación: echo $contacto->usuario->nombre; // este es el nombre del usuario
¡Bastante interesante, ¿verdad?
A veces puede que necesites adjuntar algo único a tu ActiveRecord como un cálculo personalizado que podría ser más fácil simplemente adjuntar al objeto y pasar luego a, digamos, una plantilla.
setCustomData(string $field, mixed $value)
Adjuntas los datos personalizados con el método setCustomData().
setCustomData()
$user->setCustomData('recuento_vistas_pagina', $recuento_vistas_pagina);
Y luego simplemente lo referencias como una propiedad normal del objeto.
echo $usuario->recuento_vistas_pagina;
Otra característica súper increíble acerca de esta biblioteca son los eventos. Los eventos se activan en ciertos momentos basados en ciertos métodos que llamas. Son muy útiles para configurar datos automáticamente.
onConstruct(ActiveRecord $ActiveRecord, array &config)
Esto es realmente útil si necesitas establecer una conexión predeterminada o algo así.
// index.php o bootstrap.php Flight::register('db', 'PDO', [ 'sqlite:test.db' ]); // // // // Usuario.php class User extends flight\ActiveRecord { protected function onConstruct(self $self, array &$config) { // no olvides la referencia & // podrías hacer esto para establecer automáticamente la conexión $config['connection'] = Flight::db(); // o esto $self->transformAndPersistConnection(Flight::db()); // También puedes establecer el nombre de la tabla de esta manera. $config['tabla'] = 'usuarios'; } }
beforeFind(ActiveRecord $ActiveRecord)
Probablemente solo sea útil si necesitas una manipulación de consulta cada vez.
class User extends flight\ActiveRecord { public function __construct($conexionBaseDatos) { parent::__construct($conexionBaseDatos, 'usuarios'); } protected function beforeFind(self $self) { // siempre ejecutar id >= 0 si eso es lo tuyo $self->gte('id', 0); } }
afterFind(ActiveRecord $ActiveRecord)
Probablemente sea más útil si necesitas ejecutar alguna lógica cada vez que se recupera este registro. ¿Necesitas descifrar algo? ¿Necesitas ejecutar una consulta de recuento personalizada cada vez (no eficiente pero lo que sea)?
class User extends flight\ActiveRecord { public function __construct($conexionBaseDatos) { parent::__construct($conexionBaseDatos, 'usuarios'); } protected function afterFind(self $self) { // descifrando algo $self->secreto = tuFuncionDescifrar($self->secreto, $alguna_clave); // tal vez almacenar algo personalizado como una consulta??? $self->setCustomData('recuento_vistas', $self->select('COUNT(*) count')->from('vistas_usuario')->eq('id_usuario', $self->id)['count']; } }
beforeFindAll(ActiveRecord $ActiveRecord)
class User extends flight\ActiveRecord { public function __construct($conexionBaseDatos) { parent::__construct($conexionBaseDatos, 'usuarios'); } protected function beforeFindAll(self $self) { // siempre ejecutar id >= 0 si eso es lo tuyo $self->gte('id', 0); } }
afterFindAll(array<int,ActiveRecord> $results)
Similar a afterFind() ¡pero puedes hacerlo a todos los registros en lugar de uno!
afterFind()
class User extends flight\ActiveRecord { public function __construct($conexionBaseDatos) { parent::__construct($conexionBaseDatos, 'usuarios'); } protected function afterFindAll(array $results) { foreach($results as $self) { // haz algo genial como en afterFind() } } }
beforeInsert(ActiveRecord $ActiveRecord)
Realmente útil si necesitas establecer algunos valores predeterminados cada vez.
class User extends flight\ActiveRecord { public function __construct($conexionBaseDatos) { parent::__construct($conexionBaseDatos, 'usuarios'); } protected function beforeInsert(self $self) { // establecer algunos valores predeterminados if(!$self->fecha_creacion) { $self->fecha_creacion = gmdate('Y-m-d'); } if(!$self->contraseña) { $self->contraseña = password_hash((string) microtime(true)); } } }
afterInsert(ActiveRecord $ActiveRecord)
¿Quizás tienes un caso de uso para cambiar datos después de insertarlos?
class User extends flight\ActiveRecord { public function __construct($conexionBaseDatos) { parent::__construct($conexionBaseDatos, 'usuarios'); } protected function afterInsert(self $self) { // ¡haz lo que quieras! Flight::cache()->set('id_mas_reciente_insertado', $self->id); // ¡o lo que sea.... } }
beforeUpdate(ActiveRecord $ActiveRecord)
Realmente útil si necesitas establecer algunos valores predeterminados cada vez que se actualizan.
class User extends flight\ActiveRecord { public function __construct($conexionBaseDatos) { parent::__construct($conexionBaseDatos, 'usuarios'); } protected function beforeInsert(self $self) { // establecer algunos valores predeterminados if(!$self->fecha_actualizacion) { $self->fecha_actualizacion = gmdate('Y-m-d'); } } }
afterUpdate(ActiveRecord $ActiveRecord)
¿Quizás tienes un caso de uso para cambiar datos después de actualizarlos?
class User extends flight\ActiveRecord { public function __construct($conexionBaseDatos) { parent::__construct($conexionBaseDatos, 'usuarios'); } protected function afterInsert(self $self) { // ¡haz lo que quieras! Flight::cache()->set('id_usuario_actualizado_recientemente', $self->id); // ¡o lo que sea.... } }
beforeSave(ActiveRecord $ActiveRecord)/afterSave(ActiveRecord $ActiveRecord)
Esto es útil si deseas que eventos sucedan tanto cuando se realizan inserciones como actualizaciones. Te ahorraré la larga explicación, pero seguro que puedes adivinar qué es.
class User extends flight\ActiveRecord { public function __construct($conexionBaseDatos) { parent::__construct($conexionBaseDatos, 'usuarios'); } protected function beforeSave(self $self) { $self->ultima_actualizacion = gmdate('Y-m-d H:i:s'); } }
beforeDelete(ActiveRecord $ActiveRecord)/afterDelete(ActiveRecord $ActiveRecord)
¡No estoy seguro de qué querrás hacer aquí, pero ¡sin juicios aquí! ¡Adelante!
class User extends flight\ActiveRecord { public function __construct($conexionBaseDatos) { parent::__construct($conexionBaseDatos, 'usuarios'); } protected function beforeDelete(self $self) { echo 'Fue un valiente soldado... :cry-face:'; } }
Cuando estás usando esta biblioteca, puedes configurar la conexión de base de datos de varias maneras. Puedes establecer la conexión en el constructor, puedes establecerla a través de una variable de configuración $config['connection'] o puedes establecerla a través de setDatabaseConnection() (v0.4.1).
$config['connection']
setDatabaseConnection()
$conexion_pdo = new PDO('sqlite:test.db'); // por ejemplo $usuario = new User($conexion_pdo); // o $usuario = new User(null, [ 'connection' => $conexion_pdo ]); // o $usuario = new User(); $usuario->setDatabaseConnection($conexion_pdo);
Si necesitas actualizar la conexión de base de datos, por ejemplo si estás ejecutando un script CLI que tarda mucho tiempo y necesitas actualizar la conexión cada cierto tiempo, puedes restablecer la conexión con $tu_registro->setDatabaseConnection($conexion_pdo).
$tu_registro->setDatabaseConnection($conexion_pdo)
Por favor, hazlo. :D
Cuando contribuyas, asegúrate de ejecutar composer test-coverage para mantener una cobertura de pruebas del 100% (esto no es una cobertura de pruebas unitarias reales, más bien como pruebas de integración).
composer test-coverage
También asegúrate de ejecutar composer beautify y composer phpcs para corregir cualquier error de linting.
composer beautify
composer phpcs
MIT
Latte es un motor de plantillas completo que es muy fácil de usar y se siente más cercano a una sintaxis de PHP que Twig o Smarty. También es muy fácil de extender y agregar tus propios filtros y funciones.
composer require latte/latte
Hay algunas opciones de configuración básicas para comenzar. Puedes leer más sobre ellas en la Documentación de Latte.
use Latte\Engine as LatteEngine; require 'vendor/autoload.php'; $app = Flight::app(); $app->register('latte', LatteEngine::class, [], function(LatteEngine $latte) use ($app) { // Aquí es donde Latte almacenará en caché tus plantillas para acelerar las cosas // ¡Una cosa genial sobre Latte es que actualiza automáticamente tu caché // cuando realizas cambios en tus plantillas! $latte->setTempDirectory(__DIR__ . '/../cache/'); // Indica a Latte dónde estará el directorio raíz de tus vistas. // $app->get('flight.views.path') se establece en el archivo config.php // También podrías hacer algo como `__DIR__ . '/../views/'` $latte->setLoader(new \Latte\Loaders\FileLoader($app->get('flight.views.path'))); });
Aquí tienes un ejemplo simple de un archivo de diseño. Este es el archivo que se utilizará para envolver todas tus otras vistas.
<!-- app/views/layout.latte --> <!doctype html> <html lang="es"> <head> <title>{$title ? $title . ' - '}Mi App</title> <link rel="stylesheet" href="style.css"> </head> <body> <header> <nav> <!-- tus elementos de navegación aquí --> </nav> </header> <div id="content"> <!-- Aquí está la magia --> {block content}{/block} </div> <div id="footer"> © Derechos de Autor </div> </body> </html>
Y ahora tenemos tu archivo que se va a renderizar dentro de ese bloque de contenido:
<!-- app/views/home.latte --> <!-- Esto le dice a Latte que este archivo está "dentro" del archivo layout.latte --> {extends layout.latte} <!-- Este es el contenido que se renderizará dentro del diseño dentro del bloque de contenido --> {block content} <h1>Página de Inicio</h1> <p>¡Bienvenido a mi app!</p> {/block}
Luego, cuando vayas a renderizar esto dentro de tu función o controlador, harías algo así:
// ruta simple Flight::route('/', function () { Flight::latte()->render('home.latte', [ 'title' => 'Página de Inicio' ]); }); // o si estás usando un controlador Flight::route('/', [HomeController::class, 'index']); // HomeController.php class HomeController { public function index() { Flight::latte()->render('home.latte', [ 'title' => 'Página de Inicio' ]); } }
¡Consulta la Documentación de Latte para obtener más información sobre cómo utilizar Latte al máximo!
Flight es increíblemente extensible. Hay varios complementos que se pueden utilizar para agregar funcionalidad a su aplicación de Flight. Algunos son oficialmente compatibles con el Equipo de Flight y otros son bibliotecas micro/lite para ayudarlo a comenzar.
La autenticación y autorización son cruciales para cualquier aplicación que requiera controles para determinar quién puede acceder a qué.
La caché es una excelente manera de acelerar su aplicación. Hay varias bibliotecas de caché que se pueden usar con Flight.
Las aplicaciones de CLI son una excelente manera de interactuar con su aplicación. Puede usarlas para generar controladores, mostrar todas las rutas y más.
Las cookies son una excelente manera de almacenar pequeños fragmentos de datos en el lado del cliente. Se pueden usar para almacenar preferencias de usuario, configuraciones de la aplicación y más.
Las bases de datos son fundamentales para la mayoría de las aplicaciones. Así es como almacena y recupera datos. Algunas bibliotecas de bases de datos son simplemente envoltorios para escribir consultas y otras son ORM completos.
La encriptación es crucial para cualquier aplicación que almacene datos sensibles. Encriptar y desencriptar los datos no es demasiado difícil, pero almacenar adecuadamente la clave de encriptación puede ser difícil. Lo más importante es nunca almacenar su clave de encriptación en un directorio público ni comprometerla en su repositorio de código.
Las plantillas son esenciales para cualquier aplicación web con una interfaz de usuario. Hay varios motores de plantillas que se pueden usar con Flight.
¿Tienes un complemento que te gustaría compartir? ¡Envía un pull request para agregarlo a la lista!
Hemos tratado de rastrear lo que podemos de los diversos tipos de medios en internet sobre Flight. Vea a continuación diferentes recursos que puede utilizar para aprender más sobre Flight.
Tienes dos opciones para comenzar con Flight:
Aunque estos no son patrocinados oficialmente por el equipo de Flight, ¡podrían darte ideas sobre cómo estructurar tus propios proyectos construidos con Flight!
Si tienes un proyecto que quieres compartir, ¡envía una solicitud de extracción para agregarlo a esta lista!
Asegúrate de tener PHP instalado en tu sistema. Si no lo tienes, haz clic aquí para obtener instrucciones sobre cómo instalarlo en tu sistema.
Esta es la forma más sencilla de poner en marcha. Puedes utilizar el servidor integrado para ejecutar tu aplicación e incluso usar SQLite como base de datos (si sqlite3 está instalado en tu sistema) ¡y no requiere mucho más! Simplemente ejecuta el siguiente comando una vez que PHP esté instalado:
php -S localhost:8000
Luego abre tu navegador e ingresa a http://localhost:8000.
http://localhost:8000
Si deseas establecer como directorio raíz de tu proyecto un directorio diferente (por ejemplo, tu proyecto es ~/myproject, pero tu directorio raíz es ~/myproject/public/), puedes ejecutar el siguiente comando una vez que te encuentres en el directorio ~/myproject:
~/myproject
~/myproject/public/
php -S localhost:8000 -t public/
Asegúrate de que Apache ya esté instalado en tu sistema. Si no lo está, busca cómo instalar Apache en tu sistema.
Nota: Si necesitas usar flight en un subdirectorio, agrega la línea RewriteBase /subdir/ justo después de RewriteEngine On. Nota: Si deseas proteger todos los archivos del servidor, como un archivo db o env. Coloca lo siguiente en tu archivo .htaccess:
Nota: Si necesitas usar flight en un subdirectorio, agrega la línea RewriteBase /subdir/ justo después de RewriteEngine On.
Nota: Si deseas proteger todos los archivos del servidor, como un archivo db o env. Coloca lo siguiente en tu archivo .htaccess:
Asegúrate de que Nginx ya esté instalado en tu sistema. Si no lo está, busca cómo instalar Nginx en tu sistema.
<?php // Si estás utilizando Composer, require el autoloader. require 'vendor/autoload.php'; // si no estás utilizando Composer, carga el framework directamente // require 'flight/Flight.php'; // Luego define una ruta y asigna una función para manejar la solicitud. Flight::route('/', function () { echo '¡hola mundo!'; }); // Finalmente, inicia el framework. Flight::start();
Si ya tienes php instalado en tu sistema, puedes saltarte estas instrucciones y pasar a la sección de descarga
php
¡Claro! Aquí tienes las instrucciones para instalar PHP en macOS, Windows 10/11, Ubuntu y Rocky Linux. También incluiré detalles sobre cómo instalar diferentes versiones de PHP.
Instalar Homebrew (si aún no está instalado):
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Instalar PHP:
brew install php
brew tap shivammathur/php brew install shivammathur/php/php@8.1
Cambiar entre versiones de PHP:
brew unlink php brew link --overwrite --force php@8.1
php -v
Descargar PHP:
Extraer PHP:
C:\php
Agregar PHP al PATH del sistema:
Configurar PHP:
php.ini-development
php.ini
extension_dir
Verificar la instalación de PHP:
Repite los pasos anteriores para cada versión, colocando cada una en un directorio separado (por ejemplo, C:\php7, C:\php8).
C:\php7
C:\php8
Cambiar entre versiones ajustando la variable PATH del sistema para que apunte al directorio de la versión deseada.
Actualizar listas de paquetes:
sudo apt update
sudo apt install php
sudo apt install php8.1
Instalar módulos adicionales (opcional):
sudo apt install php8.1-mysql
update-alternatives
sudo update-alternatives --set php /usr/bin/php8.1
Verificar la versión instalada:
Habilitar el repositorio EPEL:
sudo dnf install epel-release
Instalar el repositorio de Remi:
sudo dnf install https://rpms.remirepo.net/enterprise/remi-release-8.rpm sudo dnf module reset php
sudo dnf install php
sudo dnf module install php:remi-7.4
dnf
sudo dnf module reset php sudo dnf module enable php:remi-8.0 sudo dnf install php