Learn
Aprende sobre Flight
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. Se construyó con la simplicidad en mente y está escrito de una manera que es fácil de entender y usar.
Conceptos Importantes del Marco
¿Por qué un Marco?
Aquí hay un artículo breve sobre por qué deberías usar un marco. Es una buena idea comprender 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 muchos detalles sobre Flight específicamente, esta guía te ayudará a entender algunos de los conceptos principales que rodean a un marco y por qué son beneficiosos de usar. Puedes encontrar el tutorial aquí.
Flight en Comparación con Otros Marcos
Si estás migrando de otro marco como Laravel, Slim, Fat-Free o Symfony a Flight, esta página te ayudará a entender las diferencias entre los dos.
Temas Principales
Autocarga
Aprende cómo cargar automáticamente tus propias clases en tu aplicación.
Enrutamiento
Aprende cómo gestionar rutas para tu aplicación web. Esto también incluye agrupar rutas, parámetros de ruta y middleware.
Middleware
Aprende cómo usar middleware para filtrar solicitudes y respuestas en tu aplicación.
Solicitudes
Aprende cómo manejar solicitudes y respuestas en tu aplicación.
Respuestas
Aprende cómo enviar respuestas a tus usuarios.
Eventos
Aprende cómo usar el sistema de eventos para agregar eventos personalizados a tu aplicación.
Plantillas HTML
Aprende cómo usar el motor de vistas incorporado para renderizar tus plantillas HTML.
Seguridad
Aprende cómo asegurar tu aplicación de amenazas de seguridad comunes.
Configuración
Aprende cómo configurar el marco para tu aplicación.
Extendiendo Flight
Aprende cómo extender el marco agregando tus propios métodos y clases.
Eventos y Filtrado
Aprende cómo usar el sistema de eventos para agregar ganchos a tus métodos y métodos internos del marco.
Contenedor de Inyección de Dependencias
Aprende cómo usar contenedores de inyección de dependencias (DIC) para gestionar las dependencias de tu aplicación.
API del Marco
Aprende sobre los métodos principales del marco.
Migrando a v3
La compatibilidad hacia atrás se ha mantenido en su mayor parte, pero hay algunos cambios de los que deberías estar al tanto al migrar de v2 a v3.
Resolución de Problemas
Hay algunos problemas comunes con los que puedes encontrarte al usar Flight. Esta página te ayudará a solucionar esos problemas.
Learn/stopping
Detener
Puedes detener el marco de trabajo en cualquier momento llamando al método halt
:
Flight::halt();
También puedes especificar un código de estado HTTP
opcional y un mensaje:
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
:
Flight::stop();
Learn/errorhandling
Manejo de Errores
Errores y Excepciones
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.
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);
No Encontrado
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.
Puede anular este comportamiento según sus necesidades:
Flight::map('notFound', function () {
// Manejar no encontrado
});
Learn/flight_vs_laravel
Vuelo vs Laravel
¿Qué es Laravel?
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.
Pros en comparación con Vuelo
- Laravel tiene un enorme ecosistema de desarrolladores y módulos que se pueden usar para resolver problemas comunes.
- Laravel tiene un ORM completo que se puede utilizar para interactuar con tu base de datos.
- Laravel tiene una cantidad increíble de documentación y tutoriales que se pueden utilizar para aprender el marco de trabajo.
- Laravel tiene un sistema de autenticación integrado que se puede utilizar para asegurar tu aplicación.
- Laravel tiene podcasts, conferencias, reuniones, videos y otros recursos que se pueden utilizar para aprender el marco de trabajo.
- Laravel está dirigido hacia un desarrollador experimentado que busca construir una aplicación web empresarial completa.
Contras en comparación con Vuelo
- Laravel tiene mucho más en marcha bajo el capó que Vuelo. Esto conlleva un costo dramático en términos de rendimiento. Consulta los índices de TechEmpower para obtener más información.
- Vuelo está dirigido hacia un desarrollador que busca construir una aplicación web liviana, rápida y fácil de usar.
- Vuelo está orientado hacia la simplicidad y facilidad de uso.
- Una de las características principales de Vuelo es que hace lo mejor posible para mantener la compatibilidad con versiones anteriores. Laravel causa mucha frustración entre las principales versiones.
- Vuelo está destinado a desarrolladores que se adentran en el mundo de los marcos de trabajo por primera vez.
- Vuelo no tiene dependencias, mientras que Laravel tiene una cantidad atroz de dependencias
- Vuelo también puede manejar aplicaciones a nivel empresarial, pero no tiene tanto código espagueti como Laravel. También requerirá más disciplina por parte del desarrollador para mantener las cosas organizadas y bien estructuradas.
- Vuelo le brinda al desarrollador más control sobre la aplicación, mientras que Laravel tiene mucha magia detrás de escena que puede resultar frustrante.
Learn/migrating_to_v3
Migrating to v3
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.
Comportamiento del almacenamiento en búfer de salida (3.5.0)
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.
Donde podrías tener problemas
// 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>';
});
Activando el Comportamiento de Renderizado v2
¿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.
// 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
Cambios en el Despachador (3.7.0)
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.
Cambios en halt()
stop()
redirect()
y error()
(3.10.0)
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()
.
Learn/configuration
Configuración
Puede personalizar ciertos comportamientos de Flight configurando valores de configuración a través del método 'set'.
Flight::set('flight.log_errors', true);
Configuraciones Disponibles
La siguiente es una lista de todas las configuraciones disponibles:
- flight.base_url
?string
- Anular la URL base de la solicitud. (por defecto: null) - flight.case_sensitive
bool
- Coincidencia sensible a mayúsculas y minúsculas para las URL. (por defecto: false) - flight.handle_errors
bool
- Permitir que Flight maneje todos los errores internamente. (por defecto: true) - flight.log_errors
bool
- Registrar errores en el archivo de registro de errores del servidor web. (por defecto: false) - flight.views.path
string
- Directorio que contiene archivos de plantillas de vista. (por defecto: ./views) - flight.views.extension
string
- Extensión del archivo de plantilla de vista. (por defecto: .php) - flight.content_length
bool
- Establecer la cabeceraContent-Length
. (por defecto: true) - flight.v2.output_buffering
bool
- Usar el almacenamiento en búfer de salida heredado. Consulte migrating to v3. (por defecto: false)
Configuración del Loader
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;
Variables
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.
Flight::set('flight.log_errors', true);
Manejo de Errores
Errores y Excepciones
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.
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);
No Encontrado
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.
Puede anular este comportamiento según sus necesidades:
Flight::map('notFound', function () {
// Manejar no encontrado
});
Learn/security
Seguridad
La seguridad es un gran problema cuando se trata de aplicaciones web. Quieres asegurarte de que tu aplicación sea segura y que los datos de tus usuarios estén a salvo. Flight proporciona una serie de características para ayudarte a asegurar tus aplicaciones web.
Encabezados
Los encabezados HTTP son una de las formas más fáciles de asegurar tus aplicaciones web. Puedes usar encabezados para prevenir clickjacking, XSS y otros ataques. Hay varias maneras de agregar estos encabezados a tu aplicación.
Dos excelentes sitios web para verificar la seguridad de tus encabezados son securityheaders.com y observatory.mozilla.org.
Agregar a Mano
Puedes agregar manualmente estos encabezados utilizando el método header
en el objeto Flight\Response
.
// Establece el encabezado X-Frame-Options para prevenir clickjacking
Flight::response()->header('X-Frame-Options', 'SAMEORIGIN');
// Establece el encabezado Content-Security-Policy para prevenir XSS
// Nota: este encabezado puede volverse muy complejo, así que querrás
// consultar ejemplos en internet para tu aplicación
Flight::response()->header("Content-Security-Policy", "default-src 'self'");
// Establece el encabezado X-XSS-Protection para prevenir XSS
Flight::response()->header('X-XSS-Protection', '1; mode=block');
// Establece el encabezado X-Content-Type-Options para prevenir MIME sniffing
Flight::response()->header('X-Content-Type-Options', 'nosniff');
// Establece el encabezado Referrer-Policy para controlar cuánta información de referencia se envía
Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade');
// Establece el encabezado Strict-Transport-Security para forzar HTTPS
Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
// Establece el encabezado Permissions-Policy para controlar qué características y APIs pueden usarse
Flight::response()->header('Permissions-Policy', 'geolocation=()');
Estos se pueden agregar al principio de tus archivos bootstrap.php
o index.php
.
Agregar como un Filtro
También puedes agregarlos en un filtro/gatillo como el siguiente:
// Agrega los encabezados 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=()');
});
Agregar como Middleware
También puedes agregarlos como una clase 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
// FYI, este grupo de cadena vacía actúa como un middleware global para
// todas las rutas. Por supuesto, podrías hacer lo mismo y solo agregar
// esto solo a rutas específicas.
Flight::group('', function(Router $router) {
$router->get('/users', [ 'UserController', 'getUsers' ]);
// más rutas
}, [ new SecurityHeadersMiddleware() ]);
Cross Site Request Forgery (CSRF)
Cross Site Request Forgery (CSRF) es un tipo de ataque donde un sitio web malicioso puede hacer que el navegador de un usuario envíe una solicitud a tu sitio web. Esto puede usarse para realizar acciones en tu sitio web sin el conocimiento del usuario. Flight no proporciona un mecanismo de protección CSRF incorporado, pero puedes implementar fácilmente el tuyo utilizando middleware.
Configuración
Primero, necesitas 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.
// Genera un token CSRF y almacénalo en la sesión del usuario
// (suponiendo que has creado un objeto de sesión y lo has adjuntado a Flight)
// consulta la documentación de sesión para más información
Flight::register('session', \Ghostff\Session\Session::class);
// Solo necesitas generar un único 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)) );
}
<!-- Usa 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>
Usando Latte
También puedes establecer una función personalizada para mostrar el token CSRF en tus plantillas Latte.
// Establece una función personalizada para mostrar el token CSRF
// Nota: La vista ha sido configurada 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 Latte puedes usar la función csrf()
para mostrar el token CSRF.
<form method="post">
{csrf()}
<!-- otros campos del formulario -->
</form>
¿Corto y sencillo, verdad?
Verificar el Token CSRF
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') {
// captura 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 inválido');
// o para una respuesta JSON
Flight::jsonHalt(['error' => 'Token CSRF inválido'], 403);
}
}
});
O puedes usar una clase 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 inválido');
}
}
}
}
// index.php o donde tengas tus rutas
Flight::group('', function(Router $router) {
$router->get('/users', [ 'UserController', 'getUsers' ]);
// más rutas
}, [ new CsrfMiddleware() ]);
Cross Site Scripting (XSS)
Cross Site Scripting (XSS) es un tipo de ataque donde un sitio web malicioso puede inyectar código en tu sitio web. La mayoría de estas oportunidades provienen de valores de formularios que tus usuarios finales llenarán. ¡Nunca debes confiar en la salida de tus usuarios! Siempre asume que todos ellos son los mejores hackers del mundo. Pueden inyectar JavaScript o HTML malicioso en tu página. Este código puede usarse para robar información de tus usuarios o realizar acciones en tu sitio web. Usando la clase de vista de Flight, puedes escapar fácilmente la salida para prevenir ataques XSS.
// Supongamos que el usuario es astuto y trata de usar esto como su nombre
$name = '<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 escapará esto automáticamente.
Flight::view()->render('template', ['name' => $name]);
Inyección de SQL
La inyección de SQL es un tipo de ataque donde un usuario malicioso puede inyectar código SQL en tu base de datos. Esto puede usarse para robar información de tu base de datos o realizar acciones en tu base de datos. Nuevamente, jamás debes confiar en la entrada de tus usuarios. Siempre asume que están en busca de venganza. Puedes usar declaraciones preparadas en tus objetos PDO
para prevenir la inyección de SQL.
// Suponiendo que tienes Flight::db() registrado como tu objeto PDO
$statement = Flight::db()->prepare('SELECT * FROM users WHERE username = :username');
$statement->execute([':username' => $username]);
$users = $statement->fetchAll();
// Si usas la clase PdoWrapper, esto se puede hacer fácilmente en una línea
$users = Flight::db()->fetchAll('SELECT * FROM users WHERE username = :username', [ 'username' => $username ]);
// Puedes hacer lo mismo con un objeto PDO con marcadores de posición ?
$statement = Flight::db()->fetchAll('SELECT * FROM users WHERE username = ?', [ $username ]);
// Solo prométeme que nunca, NUNCA harás algo como esto...
$users = Flight::db()->fetchAll("SELECT * FROM users WHERE username = '{$username}' LIMIT 5");
// porque ¿qué pasa si $username = "' OR 1=1; -- ";
// Después de que se construye la consulta, se ve así
// SELECT * FROM users WHERE username = '' OR 1=1; -- LIMIT 5
// Se ve extraño, pero es una consulta válida que funcionará. De hecho,
// es un ataque de inyección de SQL muy común que devolverá todos los usuarios.
CORS
El intercambio de recursos de origen cruzado (CORS) es un mecanismo que permite que muchos recursos (por ejemplo, fuentes, JavaScript, etc.) en una página web sean solicitados desde otro dominio fuera del dominio del que se originó el recurso. Flight no tiene funcionalidad incorporada, pero esto se puede manejar fácilmente con un gancho que se ejecute antes de que se llame al método Flight::start()
.
// app/utils/CorsUtil.php
namespace app\utils;
class CorsUtil
{
public function set(array $params): void
{
$request = Flight::request();
$response = 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í.
$allowed = [
'capacitor://localhost',
'ionic://localhost',
'http://localhost',
'http://localhost:4200',
'http://localhost:8080',
'http://localhost:8100',
];
$request = Flight::request();
if (in_array($request->getVar('HTTP_ORIGIN'), $allowed, true) === true) {
$response = Flight::response();
$response->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 se inicie el arranque.
Flight::before('start', [ $CorsUtil, 'setupCors' ]);
Manejo de Errores
Oculta detalles sensibles de los errores en producción para evitar filtrar información a los atacantes.
// En tu bootstrap.php o index.php
// en flightphp/skeleton, esto está en app/config/config.php
$environment = ENVIRONMENT;
if ($environment === 'production') {
ini_set('display_errors', 0); // Desactivar la visualización de errores
ini_set('log_errors', 1); // Registrar errores en su lugar
ini_set('error_log', '/path/to/error.log');
}
// En tus rutas o controladores
// Usa Flight::halt() para respuestas de error controladas
Flight::halt(403, 'Acceso denegado');
Sanitización de Entrada
Nunca confíes en la entrada del usuario. Sanitiza antes de procesar para evitar que datos maliciosos se cuelen.
// Supongamos que hay una solicitud $_POST con $_POST['input'] y $_POST['email']
// Sanitiza una entrada de cadena
$clean_input = filter_var(Flight::request()->data->input, FILTER_SANITIZE_STRING);
// Sanitiza un email
$clean_email = filter_var(Flight::request()->data->email, FILTER_SANITIZE_EMAIL);
Hashing de Contraseña
Almacena las contraseñas de forma segura y verifícalas de manera segura utilizando las funciones integradas de PHP.
$password = Flight::request()->data->password;
// Hashea una contraseña al almacenarla (por ejemplo, durante el registro)
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
// Verifica una contraseña (por ejemplo, durante el inicio de sesión)
if (password_verify($password, $stored_hash)) {
// La contraseña coincide
}
Limitación de Tasa
Protege contra ataques de fuerza bruta limitando las tasas de solicitud con un caché.
// Suponiendo que tienes flightphp/cache instalado y registrado
// Usando flightphp/cache en un middleware
Flight::before('start', function() {
$cache = Flight::cache();
$ip = Flight::request()->ip;
$key = "rate_limit_{$ip}";
$attempts = (int) $cache->retrieve($key);
if ($attempts >= 10) {
Flight::halt(429, 'Demasiadas solicitudes');
}
$cache->set($key, $attempts + 1, 60); // Reiniciar después de 60 segundos
});
Conclusión
La seguridad es un gran problema y es importante asegurarte de que tus aplicaciones web sean seguras. Flight proporciona una serie de características para ayudarte a asegurar tus aplicaciones web, pero es importante siempre estar alerta y asegurarte de que estás haciendo 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 sentencias preparadas para prevenir la inyección de SQL. Siempre usa middleware para proteger tus rutas de ataques CSRF y CORS. Si haces todas estas cosas, estarás bien encaminado para construir aplicaciones web seguras.
Learn/overriding
Anulando
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
:
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.
Learn/routing
Enrutamiento
Nota: ¿Quieres entender más sobre el enrutamiento? Consulta la página de "¿por qué un framework?" para una explicación más detallada.
El enrutamiento básico en Flight se realiza haciendo coincidir un patrón de URL con una función de devolución de llamada o un arreglo 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 coincida con una solicitud será invocada.
Devoluciones de llamada/Funciones
La devolución de llamada puede ser cualquier objeto que sea invocable. Así que puedes usar una función normal:
function hello() {
echo '¡hola mundo!';
}
Flight::route('/', 'hello');
Clases
También puedes usar un método estático de una clase:
class Greeting {
public static function hello() {
echo '¡hola mundo!';
}
}
Flight::route('/', [ 'Greeting','hello' ]);
O creando primero un objeto y luego llamando al método:
// Greeting.php
class Greeting
{
public function __construct() {
$this->name = 'John Doe';
}
public function hello() {
echo "¡Hola, {$this->name}!";
}
}
// index.php
$greeting = new Greeting();
Flight::route('/', [ $greeting, 'hello' ]);
// También puedes hacer esto sin crear el objeto primero
// Nota: No se inyectarán argumentos en el constructor
Flight::route('/', [ 'Greeting', 'hello' ]);
// Además, puedes usar esta sintaxis más corta
Flight::route('/', 'Greeting->hello');
// o
Flight::route('/', Greeting::class.'->hello');
Inyección de Dependencias a través de DIC (Contenedor de Inyección de Dependencias)
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 eso está disponible es creando directamente el objeto tú mismo y utilizando el contenedor para crear tu objeto o puedes usar cadenas para definir la clase y el método a llamar. Puedes consultar la página de Inyección de Dependencias para obtener más información.
Aquí hay un ejemplo rápido:
use flight\database\PdoWrapper;
// Greeting.php
class Greeting
{
protected PdoWrapper $pdoWrapper;
public function __construct(PdoWrapper $pdoWrapper) {
$this->pdoWrapper = $pdoWrapper;
}
public function hello(int $id) {
// hacer algo con $this->pdoWrapper
$name = $this->pdoWrapper->fetchField("SELECT name FROM users WHERE id = ?", [ $id ]);
echo "¡Hola, mundo! ¡Mi nombre es {$name}!";
}
}
// 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',
'contraseña'
]
]);
// Registra el manejador del contenedor
Flight::registerContainerHandler(function($class, $params) use ($dice) {
return $dice->create($class, $params);
});
// Rutas como de costumbre
Flight::route('/hello/@id', [ 'Greeting', 'hello' ]);
// o
Flight::route('/hello/@id', 'Greeting->hello');
// o
Flight::route('/hello/@id', 'Greeting::hello');
Flight::start();
Enrutamiento por Método
Por defecto, los patrones de ruta se comparan 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 para 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 múltiples métodos a una sola devolución de llamada usando un delimitador |
:
Flight::route('GET|POST /', function () {
echo 'He recibido ya sea 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('/users', function() {
echo 'usuarios';
});
// $router->post();
// $router->put();
// $router->delete();
// $router->patch();
Expresiones Regulares
Puedes usar expresiones regulares en tus rutas:
Flight::route('/user/[0-9]+', function () {
// Esto coincidirá con /user/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.
Parámetros Nombrados
Puedes especificar parámetros nombrados en tus rutas que se pasarán a tu función de devolución de llamada. Esto es más por la legibilidad de la ruta que por cualquier otra cosa. Por favor, consulta la sección siguiente sobre una advertencia importante.
Flight::route('/@name/@id', function (string $name, string $id) {
echo "¡hola, $name ($id)!";
});
También puedes incluir expresiones regulares en tus parámetros nombrados usando el delimitador :
:
Flight::route('/@name/@id:[0-9]{3}', function (string $name, string $id) {
// Esto coincidirá con /bob/123
// Pero no coincidirá con /bob/12345
});
Nota: No se admiten grupos de coincidencias regex
()
con parámetros posicionales. :'(
Advertencia Importante
Aunque en el ejemplo anterior, parece que @name
está directamente vinculado a la variable $name
, no lo está. El orden de los parámetros en la función de devolución de llamada es lo que determina qué se pasa a ella. Así que, si cambiaras el orden de los parámetros en la función de devolución de llamada, las variables también se cambiarían. Aquí hay un ejemplo:
Flight::route('/@name/@id', function (string $id, string $name) {
echo "¡hola, $name ($id)!";
});
Y si fueras a la siguiente URL: /bob/123
, la salida sería ¡hola, 123 (bob)!
.
Por favor, ten cuidado al configurar tus rutas y tus funciones de devolución de llamada.
Parámetros Opcionales
Puedes especificar parámetros nombrados que son opcionales para la coincidencia envolviendo segmentos entre paréntesis.
Flight::route(
'/blog(/@year(/@month(/@day)))',
function(?string $year, ?string $month, ?string $day) {
// 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
.
Comodines
La coincidencia se realiza solo en segmentos individuales de la URL. Si deseas hacer 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 () {
// Hacer algo
});
Pasar
Puedes pasar la ejecución a la siguiente ruta coincidente devolviendo true
desde tu función de devolución de llamada.
Flight::route('/user/@name', function (string $name) {
// Verifica alguna condición
if ($name !== "Bob") {
// Continuar a la siguiente ruta
return true;
}
});
Flight::route('/user/*', function () {
// Esto se llamará
});
Alias de Ruta
Puedes asignar un alias a una ruta, para que la URL pueda generarse de forma dinámica más tarde en tu código (como una plantilla, por ejemplo).
Flight::route('/users/@id', function($id) { echo 'usuario:'.$id; }, false, 'user_view');
// más tarde en el código en alguna parte
Flight::getUrl('user_view', [ 'id' => 5 ]); // devolverá '/users/5'
Esto es especialmente útil si tu URL cambia. En el ejemplo anterior, digamos que los usuarios se movieron a /admin/users/@id
en su lugar.
Con el alias en su lugar, no tienes que cambiar en ningún lugar donde referencias el alias porque el alias ahora devolverá /admin/users/5
como en el ejemplo anterior.
El alias de ruta también funciona en grupos:
Flight::group('/users', function() {
Flight::route('/@id', function($id) { echo 'usuario:'.$id; }, false, 'user_view');
});
// más tarde en el código en alguna parte
Flight::getUrl('user_view', [ 'id' => 5 ]); // devolverá '/users/5'
Información de Ruta
Si deseas inspeccionar la información de la ruta coincidente, puedes solicitar que el objeto de ruta se pase a tu función de 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 $route) {
// Array de métodos HTTP coincidentes
$route->methods;
// Array de parámetros nombrados
$route->params;
// Expresión regular coincidente
$route->regex;
// Contiene el contenido de cualquier '*' usado en el patrón de URL
$route->splat;
// Muestra la ruta de la URL....si realmente lo necesitas
$route->pattern;
// Muestra qué middleware está asignado a esto
$route->middleware;
// Muestra el alias asignado a esta ruta
$route->alias;
}, true);
Agrupación de Rutas
Puede haber momentos en los que desees agrupar rutas relacionadas (como /api/v1
).
Puedes hacer esto usando el método group
:
Flight::group('/api/v1', function () {
Flight::route('/users', function () {
// Coincide con /api/v1/users
});
Flight::route('/posts', function () {
// Coincide con /api/v1/posts
});
});
Incluso puedes anidar grupos de grupos:
Flight::group('/api', function () {
Flight::group('/v1', function () {
// Flight::get() obtiene variables, ¡no establece una ruta! Consulta el contexto del objeto a continuación
Flight::route('GET /users', function () {
// Coincide con GET /api/v1/users
});
Flight::post('/posts', function () {
// Coincide con POST /api/v1/posts
});
Flight::put('/posts/1', function () {
// Coincide con PUT /api/v1/posts
});
});
Flight::group('/v2', function () {
// Flight::get() obtiene variables, ¡no establece una ruta! Consulta el contexto del objeto a continuación
Flight::route('GET /users', function () {
// Coincide con GET /api/v2/users
});
});
});
Agrupación con Contexto de Objeto
Aún puedes utilizar la agrupación de rutas con el objeto Engine
de la siguiente manera:
$app = new \flight\Engine();
$app->group('/api/v1', function (Router $router) {
// usa la variable $router
$router->get('/users', function () {
// Coincide con GET /api/v1/users
});
$router->post('/posts', function () {
// Coincide con POST /api/v1/posts
});
});
Enrutamiento de Recursos
Puedes crear un conjunto de rutas para un recurso usando el método resource
. Esto creará un conjunto de rutas para un recurso que sigue las convenciones RESTful.
Para crear un recurso, haz lo siguiente:
Flight::resource('/users', UsersController::class);
Y lo que sucederá en segundo plano es que se crearán las siguientes rutas:
[
'index' => 'GET ',
'create' => 'GET /create',
'store' => 'POST ',
'show' => 'GET /@id',
'edit' => 'GET /@id/edit',
'update' => 'PUT /@id',
'destroy' => 'DELETE /@id'
]
Y tu controlador se verá así:
class UsersController
{
public function index(): void
{
}
public function show(string $id): void
{
}
public function create(): void
{
}
public function store(): void
{
}
public function edit(string $id): void
{
}
public function update(string $id): void
{
}
public function destroy(string $id): void
{
}
}
Nota: Puedes ver las rutas recién añadidas con
runway
al ejecutarphp runway routes
.
Personalizando Rutas de Recursos
Hay algunas opciones para configurar las rutas de recursos.
Alias Base
Puedes configurar el aliasBase
. Por defecto, el alias es la última parte de la URL especificada.
Por ejemplo, /users/
daría como resultado un aliasBase
de users
. Cuando se crean estas rutas,
los alias son users.index
, users.create
, etc. Si deseas cambiar el alias, establece el aliasBase
al valor que quieras.
Flight::resource('/users', UsersController::class, [ 'aliasBase' => 'user' ]);
Solo y Excepto
También puedes especificar qué rutas deseas crear utilizando las opciones only
y except
.
Flight::resource('/users', UsersController::class, [ 'only' => [ 'index', 'show' ] ]);
Flight::resource('/users', UsersController::class, [ 'except' => [ 'create', 'store', 'edit', 'update', 'destroy' ] ]);
Estas son opciones básicamente de listas blancas y negras para que puedas especificar qué rutas deseas crear.
Middleware
También puedes especificar middleware que se ejecute en cada una de las rutas creadas por el método resource
.
Flight::resource('/users', UsersController::class, [ 'middleware' => [ MyAuthMiddleware::class ] ]);
Transmisión
Ahora puedes transmitir respuestas al cliente utilizando el método streamWithHeaders()
.
Esto es útil para enviar archivos grandes, procesos de larga duración o para generar grandes respuestas.
Transmitir una ruta se maneja de manera un poco diferente a una ruta regular.
Nota: La transmisión de respuestas solo está disponible si tienes
flight.v2.output_buffering
configurado en falso.
Transmitir con Encabezados Manuales
Puedes transmitir una respuesta al cliente utilizando el método stream()
en una ruta. Si haces esto, debes establecer todos los métodos a mano antes de enviar cualquier cosa al cliente.
Esto se hace con la función header()
de php o con el método Flight::response()->setRealHeader()
.
Flight::route('/@filename', function($filename) {
// obviamente sanitizarías la ruta y demás.
$fileNameSafe = basename($filename);
// Si tienes encabezados adicionales que establecer aquí después de que se haya ejecutado la ruta
// debes definirlos antes de que se imprima cualquier cosa.
// Todos deben ser una llamada sin procesar a la función header() o
// una llamada a Flight::response()->setRealHeader()
header('Content-Disposition: attachment; filename="'.$fileNameSafe.'"');
// o
Flight::response()->setRealHeader('Content-Disposition', 'attachment; filename="'.$fileNameSafe.'"');
$fileData = file_get_contents('/some/path/to/files/'.$fileNameSafe);
// Captura de errores y demás
if(empty($fileData)) {
Flight::halt(404, 'Archivo no encontrado');
}
// establece manualmente la longitud del contenido si lo deseas
header('Content-Length: '.filesize($filename));
// Transmite los datos al cliente
echo $fileData;
// Esta es la línea mágica aquí
})->stream();
Transmitir con Encabezados
También puedes usar el método streamWithHeaders()
para establecer los encabezados antes de comenzar a transmitir.
Flight::route('/stream-users', function() {
// puedes agregar cualquier encabezado adicional que desees aquí
// solo debes usar header() o Flight::response()->setRealHeader()
// sin importar cómo obtengas tus datos, solo como ejemplo...
$users_stmt = Flight::db()->query("SELECT id, first_name, last_name FROM users");
echo '{';
$user_count = count($users);
while($user = $users_stmt->fetch(PDO::FETCH_ASSOC)) {
echo json_encode($user);
if(--$user_count > 0) {
echo ',';
}
// Esto es necesario para enviar los datos al cliente
ob_flush();
}
echo '}';
// Así es como establecerás los encabezados antes de comenzar a transmitir.
})->streamWithHeaders([
'Content-Type' => 'application/json',
'Content-Disposition' => 'attachment; filename="users.json"',
// código de estado opcional, predeterminado es 200
'status' => 200
]);
Learn/flight_vs_symfony
Vuelo vs Symfony
¿Qué es Symfony?
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.
Pros en comparación con Vuelo
- Symfony tiene un enorme ecosistema de desarrolladores y módulos que se pueden utilizar para resolver problemas comunes.
- Symfony tiene un ORM completo (Doctrine) que se puede utilizar para interactuar con su base de datos.
- Symfony tiene una gran cantidad de documentación y tutoriales que se pueden utilizar para aprender el framework.
- Symfony tiene podcasts, conferencias, reuniones, videos y otros recursos que se pueden utilizar para aprender el framework.
- Symfony está orientado hacia un desarrollador experimentado que busca construir una aplicación web empresarial con todas las funciones.
Contras en comparación con Vuelo
- Symfony tiene mucho más en marcha bajo el capó que Vuelo. Esto conlleva un costo dramático en términos de rendimiento. Consulte los benchmarks de TechEmpower para obtener más información.
- Vuelo está orientado hacia un desarrollador que busca construir una aplicación web ligera, rápida y fácil de usar.
- Vuelo está orientado hacia la simplicidad y facilidad de uso.
- Una de las características principales de Vuelo es que hace todo lo posible para mantener la compatibilidad hacia atrás.
- Vuelo no tiene dependencias, mientras que Symfony tiene una serie de dependencias
- Vuelo está destinado a desarrolladores que se aventuran en el mundo de los frameworks por primera vez.
- Vuelo también puede realizar aplicaciones a nivel empresarial, pero no tiene tantos ejemplos y tutoriales como Symfony. También requerirá más disciplina por parte del desarrollador para mantener las cosas organizadas y bien estructuradas.
- Vuelo le da al desarrollador más control sobre la aplicación, mientras que Symfony puede introducir algo de magia entre bastidores.
Learn/flight_vs_another_framework
Comparación de Flight con Otro Framework
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
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
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
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
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.
Learn/variables
Variables
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.
Flight::set('flight.log_errors', true);
Learn/dependency_injection_container
Contenedor de Inyección de Dependencias
Introducció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.
Ejemplo Básico
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.
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!
PSR-11
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.
Controlador DIC Personalizado
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.
Instancia del Motor
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');
}
}
Añadiendo Otras Clases
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');
Learn/middleware
Middleware de Ruta
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.
Middleware Básico
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:
- Las funciones de middleware se ejecutan en el orden en que se agregan a la ruta. La ejecución es similar a cómo Slim Framework maneja esto.
- Los Antes se ejecutan en el orden agregado, y los Después se ejecutan en orden inverso.
- Si su función de middleware devuelve false, se detiene toda la ejecución y se lanza un error 403 Prohibido. Probablemente querrás manejar esto de manera más elegante con un
Flight::redirect()
o algo similar. - Si necesita parámetros de su ruta, se pasarán en una sola matriz a su función de middleware. (
function($params) { ... }
opublic function before($params) {}
). La razón de esto es que puedes estructurar tus parámetros en grupos y en algunos de esos grupos, tus parámetros pueden aparecer en un orden diferente que rompería la función de middleware al hacer referencia al parámetro incorrecto. De esta manera, puedes acceder a ellos por nombre en lugar de por posición. - Si solo pasa el nombre del middleware, se ejecutará automáticamente por el contenedor de inyección de dependencias y el middleware se ejecutará con los parámetros que necesita. Si no tiene un contenedor de inyección de dependencias registrado, pasará la instancia de
flight\Engine
en__construct()
.
Clases de Middleware
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!"
Manejo de Errores de Middleware
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:
- Puedes devolver false desde la función de middleware y Flight devolverá automáticamente un error 403 Prohibido, pero sin personalización.
- Puedes redirigir al usuario a una página de inicio de sesión usando
Flight::redirect()
. - Puedes crear un error personalizado dentro del middleware y detener la ejecución de la ruta.
Ejemplo Básico
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
}
}
Ejemplo de Redirección
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;
}
}
}
Ejemplo de Error Personalizado
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.']);
}
}
}
Agrupación de Middleware
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() ]);
Learn/filtering
# 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:
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.
Learn/requests
Solicitudes
Flight encapsula la solicitud HTTP en un solo objeto, que puede ser accedido haciendo:
$request = Flight::request();
Casos de Uso Típicos
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.
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
});
Propiedades del Objeto Solicitud
El objeto solicitud proporciona las siguientes propiedades:
- body - El cuerpo de la solicitud HTTP en bruto
- url - La URL solicitada
- base - El subdirectorio padre de la URL
- method - El método de solicitud (GET, POST, PUT, DELETE)
- referrer - La URL de referencia
- ip - Dirección IP del cliente
- ajax - Si la solicitud es una solicitud AJAX
- scheme - El protocolo del servidor (http, https)
- user_agent - Información del navegador
- type - El tipo de contenido
- length - La longitud del contenido
- query - Parámetros de la cadena de consulta
- data - Datos de la publicación o datos JSON
- cookies - Datos de cookies
- files - Archivos subidos
- secure - Si la conexión es segura
- accept - Parámetros de aceptación HTTP
- proxy_ip - Dirección IP del proxy del cliente. Escanea el arreglo
$_SERVER
en busca deHTTP_CLIENT_IP
,HTTP_X_FORWARDED_FOR
,HTTP_X_FORWARDED
,HTTP_X_CLUSTER_CLIENT_IP
,HTTP_FORWARDED_FOR
,HTTP_FORWARDED
en ese orden. - host - El nombre de host de la solicitud
Puedes acceder a las propiedades query
, data
, cookies
y files
como matrices u objetos.
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;
Cuerpo de Solicitud RAW
Para obtener el cuerpo de la solicitud HTTP en bruto, por ejemplo al manejar solicitudes PUT, puedes hacer:
$body = Flight::request()->getBody();
Entrada JSON
Si envías una solicitud con el tipo application/json
y los datos {"id": 123}
estará disponible desde la propiedad data
:
$id = Flight::request()->data->id;
$_GET
Puedes acceder al arreglo $_GET
a través de la propiedad query
:
$id = Flight::request()->query['id'];
$_POST
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'];
$_SERVER
Hay un acceso directo disponible para acceder al arreglo $_SERVER
a través del método getVar()
:
$host = Flight::request()->getVar['HTTP_HOST'];
Accediendo a Archivos Subidos a través de $_FILES
Puedes acceder a archivos subidos a través de la propiedad files
:
$uploadedFile = Flight::request()->files['myFile'];
Procesamiento de Cargas de Archivos
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.
Encabezados de Solicitud
Puedes acceder a los encabezados de solicitud usando el método getHeader()
o 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();
Cuerpo de Solicitud
Puedes acceder al cuerpo de la solicitud en bruto utilizando el método getBody()
:
$body = Flight::request()->getBody();
Método de Solicitud
Puedes acceder al método de solicitud utilizando la propiedad method
o el método 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.
URLs de Solicitud
Hay un par de métodos auxiliares para ensamblar partes de una URL para tu conveniencia.
URL Completa
Puedes acceder a la URL completa de la solicitud usando el método getFullUrl()
:
$url = Flight::request()->getFullUrl();
// https://example.com/some/path?foo=bar
URL Base
Puedes acceder a la URL base usando el método getBaseUrl()
:
$url = Flight::request()->getBaseUrl();
// Nota, sin barra final.
// https://example.com
Análisis de Consultas
Puedes pasar una URL al método parseQuery()
para analizar la cadena de consulta en un arreglo asociativo:
$query = Flight::request()->parseQuery('https://example.com/some/path?foo=bar');
// ['foo' => 'bar']
Learn/frameworkmethods
Métodos del Framework
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.
Métodos Centrales
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
Métodos Extensibles
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.
Learn/api
Métodos de la API del Framework
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 principales, que son métodos estáticos regulares, y métodos extensibles, que son métodos mapeados que pueden ser filtrados o sobrescritos.
Métodos Principales
Estos métodos son fundamentales para el framework y no pueden ser sobrescritos.
Flight::map(string $name, callable $callback, bool $pass_route = false) // Crea un método personalizado del 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 a un método del framework.
Flight::before(string $name, callable $callback) // Agrega un filtro antes de un método del framework.
Flight::after(string $name, callable $callback) // Agrega un filtro después de un método del framework.
Flight::path(string $path) // Agrega una ruta para la carga automática de clases.
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) // Verifica si una variable está establecida.
Flight::clear(array|string $key = []) // Limpia una variable.
Flight::init() // Inicializa el framework con su configuración predeterminada.
Flight::app() // Obtiene la instancia del objeto de la aplicación.
Flight::request() // Obtiene la instancia del objeto de solicitud.
Flight::response() // Obtiene la instancia del objeto de respuesta.
Flight::router() // Obtiene la instancia del objeto de enrutador.
Flight::view() // Obtiene la instancia del objeto de vista.
Métodos Extensibles
Flight::start() // Inicia el framework.
Flight::stop() // Detiene el framework y envía una respuesta.
Flight::halt(int $code = 200, string $message = '') // Detiene el framework con un código de estado y mensaje opcionales.
Flight::route(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Mapea un patrón de URL a un callback.
Flight::post(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Mapea un patrón de URL de solicitud POST a un callback.
Flight::put(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Mapea un patrón de URL de solicitud PUT a un callback.
Flight::patch(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Mapea un patrón de URL de solicitud PATCH a un callback.
Flight::delete(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Mapea un patrón de URL de solicitud DELETE a un callback.
Flight::group(string $pattern, callable $callback) // Crea agrupación 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 $file, array $data, ?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 caché HTTP ETag.
Flight::lastModified(int $time) // Realiza 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.
Flight::onEvent(string $event, callable $callback) // Registra un oyente de eventos.
Flight::triggerEvent(string $event, ...$args) // Dispara un evento.
Cualquier método personalizado agregado con map
y register
también puede ser filtrado. Para ejemplos sobre cómo mapear estos métodos, consulte la guía Extending Flight.
Learn/why_frameworks
¿Por qué un Framework?
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.
Razones para usar un Framework
Aquí hay algunas razones por las cuales podrías considerar utilizar un framework:
- Desarrollo Rápido: Los frameworks proporcionan mucha funcionalidad de serie. Esto significa que puedes construir aplicaciones web más rápidamente. No necesitas escribir tanto código porque el framework proporciona mucha de la funcionalidad que necesitas.
- Consistencia: Los frameworks ofrecen una forma consistente de hacer las cosas. Esto facilita tu comprensión de cómo funciona el código y también facilita que otros desarrolladores entiendan tu código. Si lo tienes guion por guion, podrías perder consistencia entre guiones, especialmente si estás trabajando con un equipo de desarrolladores.
- Seguridad: Los frameworks ofrecen funciones de seguridad que ayudan a proteger tus aplicaciones web de amenazas de seguridad comunes. Esto significa que no tienes que preocuparte tanto por la seguridad porque el framework se encarga de gran parte de ello.
- Comunidad: Los frameworks cuentan con grandes comunidades de desarrolladores que contribuyen al framework. Esto significa que puedes obtener ayuda de otros desarrolladores cuando tengas preguntas o problemas. También significa que hay muchos recursos disponibles para ayudarte a aprender cómo utilizar el framework.
- Mejores Prácticas: Los frameworks están construidos utilizando mejores prácticas. Esto significa que puedes aprender del framework y usar las mismas mejores prácticas en tu propio código. Esto puede ayudarte a ser un mejor programador. A veces no sabes lo que no sabes y eso puede perjudicarte al final.
- Extensibilidad: Los frameworks están diseñados para ser extendidos. Esto significa que puedes agregar tu propia funcionalidad al framework. Esto te permite construir aplicaciones web adaptadas a tus necesidades específicas.
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.
¿Qué es el Enrutamiento?
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.
Podría funcionar algo así:
- Un usuario va a tu navegador y escribe
http://ejemplo.com/usuario/1234
. - El servidor recibe la solicitud y mira la URL y la pasa a tu código de aplicación de Flight.
- Digamos que en tu código de Flight tienes algo así como
Flight::route('/usuario/@id', [ 'ControladorUsuario', 'verPerfilUsuario' ]);
. Tu código de la aplicación de Flight mira la URL y ve que coincide con una ruta que has definido, y luego ejecuta el código que has definido para esa ruta. - El enrutador de Flight luego ejecutará y llamará el método
verPerfilUsuario($id)
en la claseControladorUsuario
, pasando el1234
como el argumento$id
en el método. - El código en tu método
verPerfilUsuario()
se ejecutará y hará lo que le hayas indicado. Podrías terminar imprimiendo algo de HTML para la página del perfil del usuario, o si se trata de una API RESTful, podrías imprimir una respuesta JSON con la información del usuario. - Flight envuelve esto en un bonito lazo, genera los encabezados de respuesta y lo envía de vuelta al navegador del usuario.
- ¡El usuario se llena de alegría y se da un cálido abrazo a sí mismo!
¿Y por qué es importante?
¡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:
- Enrutamiento Centralizado: Puedes mantener todas tus rutas en un solo lugar. Esto facilita ver qué rutas tienes y qué hacen. También facilita cambiarlas si es necesario.
- Parámetros de Ruta: Puedes usar parámetros de ruta para pasar datos a tus métodos de ruta. Esta es una excelente manera de mantener tu código limpio y organizado.
- Grupos de Rutas: Puedes agrupar rutas juntas. Esto es excelente para mantener tu código organizado y para aplicar middleware a un grupo de rutas.
- Alias de Ruta: Puedes asignar un alias a una ruta, para que la URL pueda generarse dinámicamente más tarde en tu código (como una plantilla, por ejemplo). Ej: en lugar de codificar
/usuario/1234
en tu código, podrías en su lugar hacer referencia al aliasvista_usuario
y pasar elid
como parámetro. Esto es útil en caso de que decidas cambiarlo a/admin/usuario/1234
más adelante. No tendrías que cambiar todas tus URLs codificadas, solo la URL vinculada a la ruta. - Middleware de Ruta: Puedes agregar middleware a tus rutas. El middleware es increíblemente potente para agregar comportamientos específicos a tu aplicación como autenticar que cierto usuario pueda acceder a una ruta o grupo de rutas.
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.
¿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!
Solicitudes y Respuestas
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.
Solicitudes
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.
Respuestas
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.
Learn/httpcaching
Caché HTTP
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.
Última modificación
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.
Flight::route('/noticias', function () {
Flight::lastModified(1234567890);
echo 'Este contenido se almacenará en caché.';
});
ETag
La caché ETag
es similar a Última modificación
, excepto que puedes especificar cualquier identificación que desees para el recurso:
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.
Learn/responses
Respuestas
Flight ayuda a generar parte de los encabezados de respuesta por ti, pero tú mantienes la mayor parte del control sobre lo que envías de vuelta al usuario. A veces puedes acceder al objeto Response
directamente, pero la mayor parte del tiempo usarás la instancia de Flight
para enviar una respuesta.
Enviando una Respuesta Básica
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 los encabezados apropiados.
// 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 agregar al cuerpo también.
// Esto enviará "¡Hola, Mundo!" al navegador del usuario
Flight::route('/', function() {
// Verboso, pero a veces hace el trabajo cuando lo necesitas
Flight::response()->write("¡Hola, Mundo!");
// Si deseas recuperar el cuerpo que has establecido en este punto
// puedes hacerlo de esta manera
$body = Flight::response()->getBody();
});
Códigos de Estado
Puedes establecer el código de estado de la respuesta utilizando el método status
:
Flight::route('/@id', function($id) {
if($id == 123) {
Flight::response()->status(200);
echo "¡Hola, Mundo!";
} else {
Flight::response()->status(403);
echo "Prohibido";
}
});
Si deseas obtener el código de estado actual, puedes utilizar el método status
sin ningún argumento:
Flight::response()->status(); // 200
Estableciendo un Cuerpo de Respuesta
Puedes establecer el cuerpo de la respuesta utilizando el método write
, sin embargo, si haces echo o print de algo,
será capturado y enviado como el cuerpo de respuesta a través del almacenamiento en búfer.
Flight::route('/', function() {
Flight::response()->write("¡Hola, Mundo!");
});
// igual que
Flight::route('/', function() {
echo "¡Hola, Mundo!";
});
Limpiando un Cuerpo de Respuesta
Si deseas limpiar el cuerpo de la respuesta, puedes usar el método clearBody
:
Flight::route('/', function() {
if($someCondition) {
Flight::response()->write("¡Hola, Mundo!");
} else {
Flight::response()->clearBody();
}
});
Ejecutando un Callback en el Cuerpo de Respuesta
Puedes ejecutar un callback en el cuerpo de la respuesta utilizando el método addResponseBodyCallback
:
Flight::route('/users', function() {
$db = Flight::db();
$users = $db->fetchAll("SELECT * FROM users");
Flight::render('users_table', ['users' => $users]);
});
// Esto comprimirá todas las respuestas para cualquier ruta
Flight::response()->addResponseBodyCallback(function($body) {
return gzencode($body, 9);
});
Puedes agregar múltiples callbacks y se ejecutarán en el orden en que fueron agregados. Debido a que esto puede aceptar cualquier callable, puede aceptar un array de clase [ $class, 'method' ]
, un closure $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.
Nota: Los callbacks de ruta no funcionarán si estás utilizando la opción de configuración flight.v2.output_buffering
.
Callback de Ruta Específica
Si quieres que esto aplique solo a una ruta específica, podrías agregar el callback en la ruta misma:
Flight::route('/users', function() {
$db = Flight::db();
$users = $db->fetchAll("SELECT * FROM users");
Flight::render('users_table', ['users' => $users]);
// Esto comprimirá solo la respuesta para esta ruta
Flight::response()->addResponseBodyCallback(function($body) {
return gzencode($body, 9);
});
});
Opción de Middleware
También puedes usar middleware para aplicar el callback a todas las rutas a través de middleware:
// MinifyMiddleware.php
class MinifyMiddleware {
public function before() {
// Aplica el callback 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 manera
return $body;
}
}
// index.php
Flight::group('/users', function() {
Flight::route('', function() { /* ... */ });
Flight::route('/@id', function($id) { /* ... */ });
}, [ new MinifyMiddleware() ]);
Estableciendo un Encabezado de Respuesta
Puedes establecer un encabezado como el tipo de contenido de la respuesta utilizando 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!";
});
JSON
Flight proporciona soporte para enviar respuestas JSON y JSONP. Para enviar una respuesta JSON pasas algunos datos para que sean codificados en JSON:
Flight::json(['id' => 123]);
Nota: Por defecto, Flight enviará un encabezado
Content-Type: application/json
con la respuesta. También utilizará las constantesJSON_THROW_ON_ERROR
yJSON_UNESCAPED_SLASHES
al codificar el JSON.
JSON con Código de Estado
También puedes pasar un código de estado como el segundo argumento:
Flight::json(['id' => 123], 201);
JSON con Formato Bonito
También puedes pasar un argumento en la última posición para habilitar el formato bonito:
Flight::json(['id' => 123], 200, true, 'utf-8', JSON_PRETTY_PRINT);
Si estás cambiando las opciones pasadas a Flight::json()
y deseas una sintaxis más simple, simplemente puedes volver a mapear el método JSON:
Flight::map('json', function($data, $code = 200, $options = 0) {
Flight::_json($data, $code, true, 'utf-8', $options);
}
// Y ahora se puede usar así
Flight::json(['id' => 123], 200, JSON_PRETTY_PRINT);
JSON y Detener la Ejecución (v3.10.0)
Si deseas enviar una respuesta JSON y detener la ejecución, puedes usar el método jsonHalt
. Esto es útil en casos donde estás verificando tal vez algún tipo de autorización y si el usuario no está autorizado, puedes enviar una respuesta JSON de inmediato, limpiar el contenido del cuerpo existente y detener la ejecución.
Flight::route('/users', function() {
$authorized = someAuthorizationCheck();
// Verifica si el usuario está autorizado
if($authorized === 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('/users', function() {
$authorized = someAuthorizationCheck();
// Verifica si el usuario está autorizado
if($authorized === false) {
Flight::halt(401, json_encode(['error' => 'No autorizado']));
}
// Continúa con el resto de la ruta
});
JSONP
Para las solicitudes JSONP, puedes opcionalmente pasar el nombre del parámetro de consulta que estás utilizando para definir tu función de callback:
Flight::jsonp(['id' => 123], 'q');
Entonces, al hacer una solicitud GET usando ?q=my_func
, deberías recibir la salida:
my_func({"id":123});
Si no pasas un nombre de parámetro de consulta, predeterminará a jsonp
.
Redirigir a otra URL
Puedes redirigir la solicitud actual utilizando el método redirect()
y pasando una nueva URL:
Flight::redirect('/new/location');
Por defecto, Flight envía un código de estado HTTP 303 ("Ver Otro"). Puedes opcionalmente establecer un código personalizado:
Flight::redirect('/new/location', 401);
Deteniendo
Puedes detener el framework en cualquier momento llamando al método halt
:
Flight::halt();
También puedes especificar un código de estado HTTP y un mensaje opcional:
Flight::halt(200, 'Regresaré pronto...');
Llamar a halt
descartará cualquier contenido de respuesta hasta ese punto. Si deseas detener el framework y outputar la respuesta actual, usa el método stop
:
Flight::stop();
Limpiando Datos de Respuesta
Puedes limpiar el cuerpo y los encabezados de la respuesta utilizando el método clear()
. Esto limpiará
cualquier encabezado asignado a la respuesta, limpiará el cuerpo de la respuesta y establecerá el código de estado en 200
.
Flight::response()->clear();
Limpiando Solo el Cuerpo de Respuesta
Si solo deseas limpiar el cuerpo de la respuesta, puedes usar el método clearBody()
:
// Esto aún mantendrá cualquier encabezado establecido en el objeto response().
Flight::response()->clearBody();
Caché HTTP
Flight proporciona soporte integrado para 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 recomendará utilizar su versión en caché local.
Caché a Nivel de Ruta
Si deseas caché tu respuesta completa, puedes utilizar el método cache()
y pasar el tiempo para caché.
// Esto almacenará en caché la respuesta durante 5 minutos
Flight::route('/news', function () {
Flight::response()->cache(time() + 300);
echo 'Este contenido será almacenado en caché.';
});
// Alternativamente, puedes utilizar una cadena que pasarías
// al método strtotime()
Flight::route('/news', function () {
Flight::response()->cache('+5 minutes');
echo 'Este contenido será almacenado en caché.';
});
Última Modificación
Puedes usar el método lastModified
y pasar un timestamp 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 el valor de última modificación cambie.
Flight::route('/news', function () {
Flight::lastModified(1234567890);
echo 'Este contenido será almacenado en caché.';
});
ETag
El almacenamiento en caché de ETag
es similar a Última Modificación
, excepto que puedes especificar cualquier id que quieras para el recurso:
Flight::route('/news', function () {
Flight::etag('mi-id-único');
echo 'Este contenido será almacenado en caché.';
});
Ten en cuenta que llamar a lastModified
o etag
establecerá y comprobará el
valor de la caché. Si el valor de la caché es el mismo entre solicitudes, Flight enviará inmediatamente
una respuesta HTTP 304
y detendrá el procesamiento.
Descargar un Archivo (v3.12.0)
Hay un método auxiliar para descargar un archivo. Puedes usar el método download
y pasar la ruta.
Flight::route('/download', function () {
Flight::download('/path/to/file.txt');
});
Learn/frameworkinstance
Instancia del Framework
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.
Learn/redirects
Redirecciones
Puedes redirigir la solicitud actual utilizando el método redirect
y pasando
una nueva URL:
Flight::redirect('/nueva/ubicacion');
Por defecto, Flight envía un código de estado HTTP 303. Opcionalmente, puedes establecer un código personalizado:
Flight::redirect('/nueva/ubicacion', 401);
Learn/events
Sistema de Eventos en Flight PHP (v3.15.0+)
Flight PHP introduce un sistema de eventos ligero e intuitivo que te permite registrar y activar eventos personalizados en tu aplicación. Con la adición de Flight::onEvent()
y Flight::triggerEvent()
, ahora puedes engancharte a momentos clave del ciclo de vida de tu aplicación o definir tus propios eventos para hacer tu código más modular y extensible. Estos métodos son parte de los métodos mapeables de Flight, lo que significa que puedes sobrescribir su comportamiento para adaptarlo a tus necesidades.
Esta guía cubre todo lo que necesitas saber para comenzar con eventos, incluyendo por qué son valiosos, cómo usarlos y ejemplos prácticos para ayudar a los principiantes a entender su poder.
¿Por qué usar eventos?
Los eventos te permiten separar diferentes partes de tu aplicación para que no dependan demasiado entre sí. Esta separación—frecuentemente llamada desacoplamiento—hace que tu código sea más fácil de actualizar, extender o depurar. En lugar de escribir todo en un solo bloque grande, puedes dividir tu lógica en piezas más pequeñas e independientes que responden a acciones específicas (eventos).
Imagina que estás construyendo una aplicación de blog:
- Cuando un usuario publica un comentario, podrías querer:
- Guardar el comentario en la base de datos.
- Enviar un correo electrónico al propietario del blog.
- Registrar la acción por seguridad.
Sin eventos, meterías todo esto en una sola función. Con eventos, puedes dividirlo: una parte guarda el comentario, otra activa un evento como 'comment.posted'
, y oyentes separados manejan el correo electrónico y el registro. Esto mantiene tu código más limpio y te permite agregar o eliminar características (como notificaciones) sin tocar la lógica central.
Usos Comunes
- Registro: Registra acciones como inicios de sesión o errores sin desordenar tu código principal.
- Notificaciones: Envía correos electrónicos o alertas cuando algo sucede.
- Actualizaciones: Actualiza cachés o notifica a otros sistemas sobre cambios.
Registro de Oyentes de Eventos
Para escuchar un evento, utiliza Flight::onEvent()
. Este método te permite definir lo que debería suceder cuando un evento ocurre.
Sintaxis
Flight::onEvent(string $event, callable $callback): void
$event
: Un nombre para tu evento (por ejemplo,'user.login'
).$callback
: La función que se ejecutará cuando se active el evento.
Cómo Funciona
"Te suscribes" a un evento diciéndole a Flight qué hacer cuando sucede. El callback puede aceptar argumentos que se pasan desde el disparador del evento.
El sistema de eventos de Flight es sincrónico, lo que significa que cada oyente de eventos se ejecuta en secuencia, uno tras otro. Cuando activas un evento, todos los oyentes registrados para ese evento se ejecutarán completamente antes de que tu código continúe. Esto es importante de entender, ya que difiere de los sistemas de eventos asincrónicos donde los oyentes pueden ejecutarse en paralelo o en un momento posterior.
Ejemplo Sencillo
Flight::onEvent('user.login', function ($username) {
echo "¡Bienvenido de nuevo, $username!";
});
Aquí, cuando se activa el evento 'user.login'
, saludará al usuario por su nombre.
Puntos Clave
- Puedes agregar múltiples oyentes al mismo evento: se ejecutarán en el orden en que los registraste.
- El callback puede ser una función, una función anónima o un método de una clase.
Activación de Eventos
Para hacer que un evento suceda, utiliza Flight::triggerEvent()
. Esto le dice a Flight que ejecute todos los oyentes registrados para ese evento, pasando cualquier dato que proporciones.
Sintaxis
Flight::triggerEvent(string $event, ...$args): void
$event
: El nombre del evento que estás activando (debe coincidir con un evento registrado)....$args
: Argumentos opcionales para enviar a los oyentes (puede ser cualquier número de argumentos).
Ejemplo Sencillo
$username = 'alice';
Flight::triggerEvent('user.login', $username);
Esto activa el evento 'user.login'
y envía 'alice'
al oyente que definimos anteriormente, que mostrará: ¡Bienvenido de nuevo, alice!
.
Puntos Clave
- Si no hay oyentes registrados, no sucede nada: tu aplicación no se romperá.
- Utiliza el operador de expansión (
...
) para pasar múltiples argumentos de manera flexible.
Registro de Oyentes de Eventos
...
Deteniendo Oyentes Adicionales:
Si un oyente devuelve false
, no se ejecutarán oyentes adicionales para ese evento. Esto te permite detener la cadena de eventos según condiciones específicas. Recuerda, el orden de los oyentes es importante, ya que el primero que devuelva false
detendrá el resto.
Ejemplo:
Flight::onEvent('user.login', function ($username) {
if (isBanned($username)) {
logoutUser($username);
return false; // Detiene oyentes subsiguientes
}
});
Flight::onEvent('user.login', function ($username) {
sendWelcomeEmail($username); // esto nunca se envía
});
Sobrescribiendo Métodos de Eventos
Flight::onEvent()
y Flight::triggerEvent()
están disponibles para ser extendidos, lo que significa que puedes redefinir cómo funcionan. Esto es excelente para usuarios avanzados que desean personalizar el sistema de eventos, como agregar registro o cambiar la forma en que se envían los eventos.
Ejemplo: Personalizando onEvent
Flight::map('onEvent', function (string $event, callable $callback) {
// Registra cada registro de evento
error_log("Nuevo oyente de evento agregado para: $event");
// Llama al comportamiento por defecto (asumiendo un sistema de eventos interno)
Flight::_onEvent($event, $callback);
});
Ahora, cada vez que registras un evento, se registra antes de continuar.
¿Por qué sobrescribir?
- Agregar depuración o monitoreo.
- Restringir eventos en ciertos entornos (por ejemplo, deshabilitar en pruebas).
- Integrar con una biblioteca de eventos diferente.
Dónde Colocar Tus Eventos
Como principiante, podrías preguntarte: ¿dónde registro todos estos eventos en mi aplicación? La simplicidad de Flight significa que no hay reglas estrictas: puedes colocarlos donde tenga sentido para tu proyecto. Sin embargo, mantenerlos organizados te ayuda a mantener tu código a medida que tu aplicación crece. Aquí hay algunas opciones prácticas y mejores prácticas, adaptadas a la naturaleza ligera de Flight:
Opción 1: En tu index.php
Principal
Para aplicaciones pequeñas o prototipos rápidos, puedes registrar eventos directamente en tu archivo index.php
junto a tus rutas. Esto mantiene todo en un solo lugar, lo cual está bien cuando la simplicidad es tu prioridad.
require 'vendor/autoload.php';
// Registrar eventos
Flight::onEvent('user.login', function ($username) {
error_log("$username inició sesión en " . date('Y-m-d H:i:s'));
});
// Definir rutas
Flight::route('/login', function () {
$username = 'bob';
Flight::triggerEvent('user.login', $username);
echo "¡Iniciado sesión!";
});
Flight::start();
- Pros: Simple, sin archivos extra, excelente para proyectos pequeños.
- Contras: Puede volverse desordenado a medida que tu aplicación crece con más eventos y rutas.
Opción 2: Un Archivo events.php
Separado
Para una aplicación ligeramente más grande, considera mover registros de eventos a un archivo dedicado como app/config/events.php
. Incluye este archivo en tu index.php
antes de tus rutas. Esto imita cómo a menudo se organizan las rutas en app/config/routes.php
en proyectos de Flight.
// app/config/events.php
Flight::onEvent('user.login', function ($username) {
error_log("$username inició sesión en " . date('Y-m-d H:i:s'));
});
Flight::onEvent('user.registered', function ($email, $name) {
echo "Correo enviado a $email: ¡Bienvenido, $name!";
});
// index.php
require 'vendor/autoload.php';
require 'app/config/events.php';
Flight::route('/login', function () {
$username = 'bob';
Flight::triggerEvent('user.login', $username);
echo "¡Iniciado sesión!";
});
Flight::start();
- Pros: Mantiene
index.php
enfocado en el enrutamiento, organiza los eventos lógicamente, fácil de encontrar y editar. - Contras: Agrega un poco de estructura, lo que podría sentirse como excesivo para aplicaciones muy pequeñas.
Opción 3: Cerca de Donde Son Activados
Otro enfoque es registrar eventos cerca de donde son activados, como dentro de un controlador o definición de ruta. Esto funciona bien si un evento es específico de una parte de tu aplicación.
Flight::route('/signup', function () {
// Registrar evento aquí
Flight::onEvent('user.registered', function ($email) {
echo "Correo de bienvenida enviado a $email!";
});
$email = 'jane@example.com';
Flight::triggerEvent('user.registered', $email);
echo "¡Registrado!";
});
- Pros: Mantiene el código relacionado junto, bueno para características aisladas.
- Contras: Dispersa los registros de eventos, dificultando ver todos los eventos a la vez; riesgo de registros duplicados si no se tiene cuidado.
Mejores Prácticas para Flight
- Comienza Simple: Para aplicaciones pequeñas, coloca eventos en
index.php
. Es rápido y se alinea con el minimalismo de Flight. - Crecimiento Inteligente: A medida que tu aplicación se expande (por ejemplo, más de 5-10 eventos), usa un archivo
app/config/events.php
. Es un paso natural hacia arriba, como organizar rutas, y mantiene tu código ordenado sin añadir marcos complejos. - Evita la Sobrecarga de Ingeniería: No crees una clase o directorio de "gestor de eventos" a menos que tu aplicación crezca enormemente: Flight prospera en la simplicidad, así que manténlo ligero.
Consejo: Agrupar por Propósito
En events.php
, agrupa eventos relacionados (por ejemplo, todos los eventos relacionados con usuarios juntos) con comentarios para claridad:
// app/config/events.php
// Eventos de Usuario
Flight::onEvent('user.login', function ($username) {
error_log("$username inició sesión");
});
Flight::onEvent('user.registered', function ($email) {
echo "¡Bienvenido a $email!";
});
// Eventos de Página
Flight::onEvent('page.updated', function ($pageId) {
unset($_SESSION['pages'][$pageId]);
});
Esta estructura escala bien y se mantiene amigable para principiantes.
Ejemplos para Principiantes
Veamos algunos escenarios reales para mostrar cómo funcionan los eventos y por qué son útiles.
Ejemplo 1: Registrando un Inicio de Sesión de Usuario
// Paso 1: Registrar un oyente
Flight::onEvent('user.login', function ($username) {
$time = date('Y-m-d H:i:s');
error_log("$username inició sesión en $time");
});
// Paso 2: Activarlo en tu aplicación
Flight::route('/login', function () {
$username = 'bob'; // Suponiendo que esto proviene de un formulario
Flight::triggerEvent('user.login', $username);
echo "¡Hola, $username!";
});
Por qué es útil: El código de inicio de sesión no necesita saber sobre el registro: simplemente activa el evento. Podrías agregar más oyentes más tarde (por ejemplo, enviar un correo de bienvenida) sin cambiar la ruta.
Ejemplo 2: Notificando sobre Nuevos Usuarios
// Oyente para nuevos registros
Flight::onEvent('user.registered', function ($email, $name) {
// Simular el envío de un correo electrónico
echo "Correo enviado a $email: ¡Bienvenido, $name!";
});
// Activarlo cuando alguien se registre
Flight::route('/signup', function () {
$email = 'jane@example.com';
$name = 'Jane';
Flight::triggerEvent('user.registered', $email, $name);
echo "¡Gracias por registrarte!";
});
Por qué es útil: La lógica de registro se centra en crear al usuario, mientras que el evento maneja las notificaciones. Podrías agregar más oyentes (por ejemplo, registrar el registro) más tarde.
Ejemplo 3: Limpiando una Caché
// Oyente para limpiar una caché
Flight::onEvent('page.updated', function ($pageId) {
unset($_SESSION['pages'][$pageId]); // Limpiar caché de sesión si corresponde
echo "Caché limpiada para la página $pageId.";
});
// Activarlo cuando se edita una página
Flight::route('/edit-page/(@id)', function ($pageId) {
// Suponiendo que actualizamos la página
Flight::triggerEvent('page.updated', $pageId);
echo "Página $pageId actualizada.";
});
Por qué es útil: El código de edición no se preocupa por la caché: simplemente señala la actualización. Otras partes de la aplicación pueden reaccionar según sea necesario.
Mejores Prácticas
- Nombra los eventos claramente: Usa nombres específicos como
'user.login'
o'page.updated'
para que sea evidente lo que hacen. - Mantén los oyentes simples: No pongas tareas lentas o complejas en los oyentes: mantén tu aplicación rápida.
- Prueba tus eventos: Actívalos manualmente para asegurar que los oyentes funcionen como se espera.
- Usa eventos sabiamente: Son excelentes para desacoplar, pero demasiados pueden complicar tu código: úsalos cuando tenga sentido.
El sistema de eventos en Flight PHP, con Flight::onEvent()
y Flight::triggerEvent()
, te ofrece una forma simple pero poderosa de construir aplicaciones flexibles. Al permitir que diferentes partes de tu aplicación se comuniquen entre sí a través de eventos, puedes mantener tu código organizado, reutilizable y fácil de expandir. Ya sea que estés registrando acciones, enviando notificaciones o gestionando actualizaciones, los eventos te ayudan a hacerlo sin enredar tu lógica. Además, con la capacidad de sobrescribir estos métodos, tienes la libertad de adaptar el sistema a tus necesidades. Comienza con un solo evento y observa cómo transforma la estructura de tu aplicación.
Eventos Incorporados
Flight PHP viene con algunos eventos incorporados que puedes usar para engancharte al ciclo de vida del marco. Estos eventos se activan en puntos específicos del ciclo de solicitud/respuesta, permitiéndote ejecutar lógica personalizada cuando ocurren ciertas acciones.
Lista de Eventos Incorporados
flight.request.received
: Activado cuando se recibe, analiza y procesa una solicitud.flight.route.middleware.before
: Activado después de que se ejecute el middleware anterior.flight.route.middleware.after
: Activado después de que se ejecute el middleware posterior.flight.route.executed
: Activado después de que se ejecute y procese una ruta.flight.response.sent
: Activado después de que se envíe una respuesta al cliente.
Learn/views
Vistas
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:
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:
¡Hola, <?= $name ?>!
La salida sería:
¡Hola, Bob!
También puede configurar manualmente variables de vista utilizando el método set
:
Flight::view()->set('name', 'Bob');
La variable name
ahora está disponible en todas sus vistas. Entonces simplemente puede hacer:
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
.
Por defecto, Flight buscará un directorio views
para los archivos de plantilla. Puede establecer una ruta alternativa para sus plantillas configurando lo siguiente:
Flight::set('flight.views.path', '/ruta/a/vistas');
Diseños
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:
Flight::render('layout', ['title' => 'Página de inicio']);
Si los archivos de plantilla se ven así:
header.php
:
<h1><?= $heading ?></h1>
body.php
:
<div><?= $body ?></div>
layout.php
:
<html>
<head>
<title><?= $title ?></title>
</head>
<body>
<?= $headerContent ?>
<?= $bodyContent ?>
</body>
</html>
La salida sería:
<html>
<head>
<title>Página de inicio</title>
</head>
<body>
<h1>Hola</h1>
<div>Mundo</div>
</body>
</html>
Vistas Personalizadas
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);
});
Learn/templates
Vistas HTML y Plantillas
Flight proporciona una funcionalidad básica de plantillas por defecto.
Flight te permite cambiar el motor de vista predeterminado simplemente registrando tu propia clase de vista. Desplázate hacia abajo para ver ejemplos de cómo usar Smarty, Latte, Blade y más.
Motor de Vista Integrado
Para mostrar una plantilla de vista, llama al método render
con el nombre del archivo de plantilla y datos de plantilla opcionales:
Flight::render('hello.php', ['name' => 'Bob']);
Los datos de plantilla que pases se inyectan automáticamente en la plantilla y se pueden referenciar como una variable local. Los archivos de plantilla son simplemente archivos PHP. Si el contenido del archivo de plantilla hello.php
es:
¡Hola, <?= $name ?>!
La salida sería:
¡Hola, Bob!
También puedes establecer manualmente variables de vista utilizando el método set:
Flight::view()->set('name', 'Bob');
La variable name
ahora está disponible en todas tus vistas. Así que puedes hacerlo simplemente:
Flight::render('hello');
Ten en cuenta que al especificar el nombre de la plantilla en el método render, puedes omitir la extensión .php
.
Por defecto, Flight buscará un directorio views
para archivos de plantilla. Puedes establecer una ruta alternativa para tus plantillas configurando lo siguiente:
Flight::set('flight.views.path', '/path/to/views');
Diseños
Es común que los sitios web tengan un único archivo de plantilla de diseño con contenido intercambiable. Para renderizar contenido que se utilizará en un diseño, puedes pasar un parámetro opcional al método render
.
Flight::render('header', ['heading' => 'Hola'], 'headerContent');
Flight::render('body', ['body' => 'Mundo'], 'bodyContent');
Tu vista tendrá variables guardadas llamadas headerContent
y bodyContent
. Luego puedes renderizar tu diseño haciendo:
Flight::render('layout', ['title' => 'Página Principal']);
Si los archivos de plantilla se ven así:
header.php
:
<h1><?= $heading ?></h1>
body.php
:
<div><?= $body ?></div>
layout.php
:
<html>
<head>
<title><?= $title ?></title>
</head>
<body>
<?= $headerContent ?>
<?= $bodyContent ?>
</body>
</html>
La salida sería:
<html>
<head>
<title>Página Principal</title>
</head>
<body>
<h1>Hola</h1>
<div>Mundo</div>
</body>
</html>
Smarty
Aquí tienes cómo usar el motor de plantillas Smarty para tus vistas:
// Cargar la biblioteca Smarty
require './Smarty/libs/Smarty.class.php';
// Registrar Smarty como la clase de vista
// También pasar una función de callback 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 a la plantilla
Flight::view()->assign('name', 'Bob');
// Mostrar la plantilla
Flight::view()->display('hello.tpl');
Para ser completo, también deberías sobrescribir el método render predeterminado de Flight:
Flight::map('render', function(string $template, array $data): void {
Flight::view()->assign($data);
Flight::view()->display($template);
});
Latte
Aquí tienes cómo usar el motor de plantillas Latte para tus vistas:
// Registrar Latte como la clase de vista
// También pasar una función de callback para configurar Latte al cargar
Flight::register('view', Latte\Engine::class, [], function (Latte\Engine $latte) {
// Aquí es donde Latte almacenará en caché tus plantillas para acelerar las cosas
// Una característica interesante de Latte es que automáticamente actualiza tu
// caché cuando haces cambios en tus plantillas.
$latte->setTempDirectory(__DIR__ . '/../cache/');
// Indicar a Latte dónde estará el directorio raíz para tus vistas.
$latte->setLoader(new \Latte\Loaders\FileLoader(__DIR__ . '/../views/'));
});
// Y rematarlo para que puedas 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);
});
Blade
Aquí tienes cómo usar el motor de plantillas Blade para tus vistas:
Primero, necesitas instalar la biblioteca BladeOne a través de Composer:
composer require eftec/bladeone
Luego, puedes configurar BladeOne como la clase de vista en Flight:
<?php
// Cargar la biblioteca BladeOne
use eftec\bladeone\BladeOne;
// Registrar BladeOne como la clase de vista
// También pasar una función de callback para configurar BladeOne al cargar
Flight::register('view', BladeOne::class, [], function (BladeOne $blade) {
$views = __DIR__ . '/../views';
$cache = __DIR__ . '/../cache';
$blade->setPath($views);
$blade->setCompiledPath($cache);
});
// Asignar datos a la plantilla
Flight::view()->share('name', 'Bob');
// Mostrar la plantilla
echo Flight::view()->run('hello', []);
Para ser completo, también deberías sobrescribir el método render predeterminado de Flight:
<?php
Flight::map('render', function(string $template, array $data): void {
echo Flight::view()->run($template, $data);
});
En este ejemplo, el archivo de plantilla hello.blade.php podría verse así:
<?php
¡Hola, {{ $name }}!
La salida sería:
¡Hola, Bob!
Al seguir estos pasos, puedes integrar el motor de plantillas Blade con Flight y usarlo para renderizar tus vistas.
Learn/flight_vs_fat_free
Flight vs Fat-Free
¿Qué es Fat-Free?
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.
Pros en comparación con Flight
- Fat-Free tiene algunas estrellas más en GitHub que Flight.
- Fat-Free tiene cierta documentación decente, pero carece en algunas áreas en cuanto a claridad.
- Fat-Free tiene algunos recursos dispersos como tutoriales en YouTube y artículos en línea que se pueden utilizar para aprender el marco.
- Fat-Free tiene algunos complementos útiles incorporados que a veces son útiles.
- Fat-Free tiene un ORM incorporado llamado Mapper que se puede utilizar para interactuar con tu base de datos. Flight tiene active-record.
- Fat-Free tiene Sesiones, Caché y localización incorporadas. Flight requiere que uses bibliotecas de terceros, pero está cubierto en la documentación.
- Fat-Free tiene un pequeño grupo de complementos creados por la comunidad que se pueden utilizar para ampliar el marco. Flight tiene algunos cubiertos en las páginas de documentación y ejemplos.
- Fat-Free al igual que Flight no tiene dependencias.
- Fat-Free al igual que Flight está orientado a dar al desarrollador control sobre su aplicación y una experiencia de desarrollo simple.
- Fat-Free mantiene la compatibilidad hacia atrás al igual que Flight (parcialmente porque las actualizaciones están siendo menos frecuentes).
- Fat-Free al igual que Flight está destinado a desarrolladores que se adentran por primera vez en el mundo de los marcos.
- Fat-Free tiene un motor de plantillas incorporado que es más robusto que el motor de plantillas de Flight. Flight recomienda Latte para lograr esto.
- Fat-Free tiene un comando de tipo CLI único "route" donde puedes construir aplicaciones CLI dentro de Fat-Free mismo y tratarlo de manera similar a una solicitud
GET
. Flight logra esto con runway.
Contras en comparación con Flight
- Fat-Free tiene algunas pruebas de implementación e incluso tiene su propia clase de prueba que es muy básica. Sin embargo, no está probado al 100% como lo está Flight.
- Tienes que utilizar un motor de búsqueda como Google para buscar realmente en el sitio de documentación.
- Flight tiene modo oscuro en su sitio de documentación. (dejar caer el micrófono)
- Fat-Free tiene algunos módulos que están terriblemente descuidados.
- Flight tiene un PdoWrapper simple que es un poco más simple que la clase
DB\SQL
incorporada de Fat-Free. - Flight tiene un complemento de permisos que se puede utilizar para asegurar tu aplicación. Slim requiere que utilices una biblioteca de terceros.
- Flight tiene un ORM llamado active-record que se siente más como un ORM que el Mapper de Fat-Free.
El beneficio añadido de
active-record
es que puedes definir relaciones entre registros para uniones automáticas donde el Mapper de Fat-Free requiere que crees vistas SQL. - Sorprendentemente, Fat-Free no tiene un espacio de nombres raíz. Flight tiene espacios de nombres hasta el final para no chocar con tu propio código.
la clase
Cache
es el mayor infractor aquí. - Fat-Free no tiene middleware. En su lugar, hay ganchos
beforeroute
yafterroute
que se pueden utilizar para filtrar solicitudes y respuestas en controladores. - Fat-Free no puede agrupar rutas.
- Fat-Free tiene un manejador de contenedores de inyección de dependencias, pero la documentación es increíblemente escasa sobre cómo usarlo.
- El depurado puede complicarse un poco ya que básicamente todo se almacena en lo que se llama el
HIVE
Learn/extending
Extendiendo
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 sobrescribir clases y métodos existentes.
Si estás buscando un DIC (Contenedor de Inyección de Dependencias), dirígete a la página del Contenedor de Inyección de Dependencias.
Mapeo de Métodos
Para mapear tu propio método personalizado simple, usas 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');
Si bien es posible crear métodos personalizados simples, se recomienda crear funciones estándar en PHP. Esto tiene autocompletado en IDEs 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. Usar el método register()
como se muestra a continuación es más
para pasar configuraciones y luego llamar a tu clase preconfigurada.
Registro de Clases
Para registrar tu propia clase y configurarla, utilizas 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 cargas 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 la clase con parámetros de constructor
Flight::register('db', PDO::class, ['mysql:host=localhost;dbname=test', 'user', 'pass']);
// Obtén una instancia de tu clase
// Esto creará un objeto con los parámetros definidos
//
// new PDO('mysql:host=localhost;dbname=test','user','pass');
//
$db = Flight::db();
// y si lo necesitas más tarde en tu código, solo llama al mismo método nuevamente
class SomeController {
public function __construct() {
$this->db = Flight::db();
}
}
Si pasas un parámetro de callback 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 callback toma un parámetro, una instancia del nuevo objeto.
// El callback recibirá el objeto que fue construido
Flight::register(
'db',
PDO::class,
['mysql:host=localhost;dbname=test', 'user', 'pass'],
function (PDO $db) {
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
);
Por defecto, cada vez que cargues 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
$shared = Flight::db();
// Nueva instancia de la clase
$new = Flight::db(false);
Ten en cuenta que los métodos mapeados tienen prioridad sobre las clases registradas. Si declara ambos usando el mismo nombre, solo se invocará el método mapeado.
Registro
Flight no tiene un sistema de registro integrado, sin embargo, es muy fácil utilizar una biblioteca de registro con Flight. Aquí hay un ejemplo usando la biblioteca Monolog:
// index.php o bootstrap.php
// Registra el logger con Flight
Flight::register('log', Monolog\Logger::class, [ 'name' ], function(Monolog\Logger $log) {
$log->pushHandler(new Monolog\Handler\StreamHandler('path/to/your.log', Monolog\Logger::WARNING));
});
Ahora que está registrado, puedes usarlo en tu aplicación:
// En tu controlador o ruta
Flight::log()->warning('Este es un mensaje de advertencia');
Esto registrará un mensaje en el archivo de registro que especificaste. ¿Qué pasa si
quieres registrar algo cuando ocurre un error? Puedes usar el método error
:
// En tu controlador o ruta
Flight::map('error', function(Throwable $ex) {
Flight::log()->error($ex->getMessage());
// Muestra tu página de error personalizada
include 'errors/500.html';
});
También podrías crear un sistema básico de APM (Monitoreo del Rendimiento de la Aplicación)
usando los métodos before
y after
:
// En tu archivo de bootstrap
Flight::before('start', function() {
Flight::set('start_time', microtime(true));
});
Flight::after('start', function() {
$end = microtime(true);
$start = Flight::get('start_time');
Flight::log()->info('La solicitud '.Flight::request()->url.' tomó ' . round($end - $start, 4) . ' segundos');
// También podrías agregar tus encabezados de solicitud o respuesta
// para registrarlos también (ten cuidado ya que esto sería un
// gran volumen de datos si tienes muchas solicitudes)
Flight::log()->info('Encabezados de Solicitud: ' . json_encode(Flight::request()->headers));
Flight::log()->info('Encabezados de Respuesta: ' . json_encode(Flight::response()->headers));
});
Sobrescribiendo Métodos del Marco
Flight te permite sobrescribir su funcionalidad predeterminada para adaptarla a tus propias necesidades, sin necesidad de modificar ningún código. Puedes ver todos los métodos que puedes sobrescribir 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 HTTP 404
. Puedes sobrescribir
este comportamiento utilizando el método map
:
Flight::map('notFound', function() {
// Muestra la página personalizada 404
include 'errors/404.html';
});
Flight también te permite reemplazar componentes centrales del marco. 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 Router, cargará tu clase
$myrouter = Flight::router();
Sin embargo, los métodos del marco como map
y register
no se pueden sobrescribir.
Recibirás un error si intentas hacerlo.
Learn/json
JSON
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]);
Para solicitudes JSONP, opcionalmente puedes pasar el nombre del parámetro de consulta que estás utilizando para definir tu función de callback:
Flight::jsonp(['id' => 123], 'q');
Entonces, al hacer una solicitud GET usando ?q=my_func
, deberías recibir la salida:
my_func({"id":123});
Si no pasas un nombre de parámetro de consulta, se utilizará por defecto jsonp
.
Learn/flight_vs_slim
Comparación entre Flight y Slim
¿Qué es Slim?
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.
Ventajas en comparación con Flight
- Slim tiene una comunidad más grande de desarrolladores, quienes a su vez crean módulos útiles para ayudarte a no reinventar la rueda.
- Slim sigue muchas interfaces y estándares comunes en la comunidad de PHP, lo que aumenta la interoperabilidad.
- Slim tiene una documentación decente y tutoriales que se pueden utilizar para aprender el marco de trabajo (aunque no tanto como Laravel o Symfony).
- Slim tiene varios recursos como tutoriales en YouTube y artículos en línea que se pueden utilizar para aprender el marco de trabajo.
- Slim te permite usar los componentes que desees para manejar las características básicas de enrutamiento, ya que cumple con PSR-7.
Desventajas en comparación con Flight
- Sorprendentemente, Slim no es tan rápido como podrías pensar que sería para un micro marco de trabajo. Consulta las pruebas de TechEmpower para más información.
- Flight está enfocado hacia un desarrollador que busca construir una aplicación web ligera, rápida y fácil de usar.
- Flight no tiene dependencias, mientras que Slim tiene algunas dependencias que debes instalar.
- Flight está enfocado hacia la simplicidad y la facilidad de uso.
- Una de las características principales de Flight es que hace todo lo posible por mantener la compatibilidad con versiones anteriores. Slim v3 a v4 fue un cambio rupturista.
- Flight está destinado a desarrolladores que se adentran en el mundo de los marcos de trabajo por primera vez.
- Flight también puede manejar aplicaciones a nivel empresarial, pero no tiene tantos ejemplos y tutoriales como Slim. También requerirá más disciplina por parte del desarrollador para mantener las cosas organizadas y bien estructuradas.
- Flight da al desarrollador más control sobre la aplicación, mientras que Slim puede incluir algo de magia detrás de escena.
- Flight tiene un sencillo PdoWrapper que se puede usar para interactuar con tu base de datos. Slim requiere que uses una biblioteca de terceros.
- Flight tiene un plugin de permisos que se puede utilizar para asegurar tu aplicación. Slim requiere que uses una biblioteca de terceros.
- Flight tiene un ORM llamado active-record que se puede utilizar para interactuar con tu base de datos. Slim requiere que uses una biblioteca de terceros.
- Flight tiene una aplicación CLI llamada runway que se puede utilizar para ejecutar tu aplicación desde la línea de comandos. Slim no lo tiene.
Learn/autoloading
Carga automática
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.
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.
Ejemplo básico
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
}
}
Espacios de nombres
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/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
}
}
Guiones bajos en los nombres de las clases
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.
/**
* 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
}
}
Learn/troubleshooting
Resolución de Problemas
Esta página te ayudará a solucionar problemas comunes que puedes encontrar al usar Flight.
Problemas Comunes
404 No Encontrado o Comportamiento de Ruta Inesperado
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.
Clase No Encontrada (la carga automática no funciona)
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.
Nombre de Archivo Incorrecto
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.
Namespace Incorrecto
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()
no definido
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.
// Agregar una ruta al cargador automático
Flight::path(__DIR__.'/../');
Install
Instalación
Descargar los archivos.
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.
Configurar tu Servidor Web.
Apache
Para Apache, edita tu archivo .htaccess
con lo siguiente:
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 deRewriteEngine 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
Nginx
Para Nginx, agrega lo siguiente a la declaración de tu servidor:
server {
location / {
try_files $uri $uri/ /index.php;
}
}
Crea tu archivo 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();
Guides/blog
Construyendo un Blog Simple con Flight PHP
Esta guía te guía a través de la creación de un blog básico utilizando el framework Flight PHP. Configurarás un proyecto, definirás rutas, administrarás publicaciones con JSON y las renderizarás con el motor de plantillas Latte, todo mostrando la simplicidad y flexibilidad de Flight. Al final, tendrás un blog funcional con una página de inicio, páginas de publicaciones individuales y un formulario de creación.
Requisitos Previos
- PHP 7.4+: Instalado en tu sistema.
- Composer: Para la gestión de dependencias.
- Editor de Texto: Cualquier editor como VS Code o PHPStorm.
- Conocimientos básicos de PHP y desarrollo web.
Paso 1: Configura Tu Proyecto
Comienza creando un nuevo directorio de proyecto e instalando Flight a través de Composer.
-
Crear un Directorio:
mkdir flight-blog cd flight-blog
-
Instalar Flight:
composer require flightphp/core
-
Crear un Directorio Público: Flight utiliza un único punto de entrada (
index.php
). Crea una carpetapublic/
para ello:mkdir public
-
Básico
index.php
: Creapublic/index.php
con una ruta simple de “hola mundo”:<?php require '../vendor/autoload.php'; Flight::route('/', function () { echo '¡Hola, Flight!'; }); Flight::start();
-
Ejecutar el Servidor Integrado: Prueba tu configuración con el servidor de desarrollo de PHP:
php -S localhost:8000 -t public/
Visita
http://localhost:8000
para ver “¡Hola, Flight!”.
Paso 2: Organizar la Estructura de Tu Proyecto
Para una configuración limpia, estructura tu proyecto así:
flight-blog/
├── app/
│ ├── config/
│ └── views/
├── data/
├── public/
│ └── index.php
├── vendor/
└── composer.json
app/config/
: Archivos de configuración (por ejemplo, eventos, rutas).app/views/
: Plantillas para renderizar páginas.data/
: Archivo JSON para almacenar publicaciones de blog.public/
: Raíz web conindex.php
.
Paso 3: Instalar y Configurar Latte
Latte es un motor de plantillas ligero que se integra bien con Flight.
-
Instalar Latte:
composer require latte/latte
-
Configurar Latte en Flight: Actualiza
public/index.php
para registrar Latte como el motor de vista:<?php require '../vendor/autoload.php'; use Latte\Engine; Flight::register('view', Engine::class, [], function ($latte) { $latte->setTempDirectory(__DIR__ . '/../cache/'); $latte->setLoader(new \Latte\Loaders\FileLoader(__DIR__ . '/../app/views/')); }); Flight::route('/', function () { Flight::view()->render('home.latte', ['title' => 'Mi Blog']); }); Flight::start();
-
Crear una Plantilla de Diseño: En
app/views/layout.latte
:<!DOCTYPE html> <html> <head> <title>{$title}</title> </head> <body> <header> <h1>Mi Blog</h1> <nav> <a href="/">Inicio</a> | <a href="/create">Crear una Publicación</a> </nav> </header> <main> {block content}{/block} </main> <footer> <p>© {date('Y')} Blog Flight</p> </footer> </body> </html>
-
Crear una Plantilla de Inicio: En
app/views/home.latte
:{extends 'layout.latte'} {block content} <h2>{$title}</h2> <ul> {foreach $posts as $post} <li><a href="/post/{$post['slug']}">{$post['title']}</a></li> {/foreach} </ul> {/block}
Reinicia el servidor si te has salido de él y visita
http://localhost:8000
para ver la página renderizada. -
Crear un Archivo de Datos:
Utiliza un archivo JSON para simular una base de datos por simplicidad.
En
data/posts.json
:[ { "slug": "primer-post", "title": "Mi Primer Post", "content": "¡Esta es mi primera publicación de blog con Flight PHP!" } ]
Paso 4: Definir Rutas
Separa tus rutas en un archivo de configuración para mejor organización.
-
Crear
routes.php
: Enapp/config/routes.php
:<?php Flight::route('/', function () { Flight::view()->render('home.latte', ['title' => 'Mi Blog']); }); Flight::route('/post/@slug', function ($slug) { Flight::view()->render('post.latte', ['title' => 'Publicación: ' . $slug, 'slug' => $slug]); }); Flight::route('GET /create', function () { Flight::view()->render('create.latte', ['title' => 'Crear una Publicación']); });
-
Actualizar
index.php
: Incluye el archivo de rutas:<?php require '../vendor/autoload.php'; use Latte\Engine; Flight::register('view', Engine::class, [], function ($latte) { $latte->setTempDirectory(__DIR__ . '/../cache/'); $latte->setLoader(new \Latte\Loaders\FileLoader(__DIR__ . '/../app/views/')); }); require '../app/config/routes.php'; Flight::start();
Paso 5: Almacenar y Recuperar Publicaciones de Blog
Agrega los métodos para cargar y guardar publicaciones.
-
Agregar un Método de Publicaciones: En
index.php
, agrega un método para cargar publicaciones:Flight::map('posts', function () { $file = __DIR__ . '/../data/posts.json'; return json_decode(file_get_contents($file), true); });
-
Actualizar Rutas: Modifica
app/config/routes.php
para usar publicaciones:<?php Flight::route('/', function () { $posts = Flight::posts(); Flight::view()->render('home.latte', [ 'title' => 'Mi Blog', 'posts' => $posts ]); }); Flight::route('/post/@slug', function ($slug) { $posts = Flight::posts(); $post = array_filter($posts, fn($p) => $p['slug'] === $slug); $post = reset($post) ?: null; if (!$post) { Flight::notFound(); return; } Flight::view()->render('post.latte', [ 'title' => $post['title'], 'post' => $post ]); }); Flight::route('GET /create', function () { Flight::view()->render('create.latte', ['title' => 'Crear una Publicación']); });
Paso 6: Crear Plantillas
Actualiza tus plantillas para mostrar publicaciones.
-
Página de Publicación (
app/views/post.latte
):{extends 'layout.latte'} {block content} <h2>{$post['title']}</h2> <div class="post-content"> <p>{$post['content']}</p> </div> {/block}
Paso 7: Agregar Creación de Publicación
Manejar la presentación del formulario para agregar nuevas publicaciones.
-
Crear Formulario (
app/views/create.latte
):{extends 'layout.latte'} {block content} <h2>{$title}</h2> <form method="POST" action="/create"> <div class="form-group"> <label for="title">Título:</label> <input type="text" name="title" id="title" required> </div> <div class="form-group"> <label for="content">Contenido:</label> <textarea name="content" id="content" required></textarea> </div> <button type="submit">Guardar Publicación</button> </form> {/block}
-
Agregar Ruta POST: En
app/config/routes.php
:Flight::route('POST /create', function () { $request = Flight::request(); $title = $request->data['title']; $content = $request->data['content']; $slug = strtolower(str_replace(' ', '-', $title)); $posts = Flight::posts(); $posts[] = ['slug' => $slug, 'title' => $title, 'content' => $content]; file_put_contents(__DIR__ . '/../../data/posts.json', json_encode($posts, JSON_PRETTY_PRINT)); Flight::redirect('/'); });
-
Prueba:
- Visita
http://localhost:8000/create
. - Envía una nueva publicación (por ejemplo, “Segundo Post” con algo de contenido).
- Verifica la página de inicio para verlo listado.
- Visita
Paso 8: Mejorar con Manejo de Errores
Sobrescribe el método notFound
para una mejor experiencia 404.
En index.php
:
Flight::map('notFound', function () {
Flight::view()->render('404.latte', ['title' => 'Página No Encontrada']);
});
Crea app/views/404.latte
:
{extends 'layout.latte'}
{block content}
<h2>404 - {$title}</h2>
<p>¡Lo siento, esa página no existe!</p>
{/block}
Próximos Pasos
- Agregar Estilo: Utiliza CSS en tus plantillas para un mejor aspecto.
- Base de Datos: Reemplaza
posts.json
con una base de datos como SQLite utilizandoPdoWrapper
. - Validación: Agrega verificaciones para slugs duplicados o entradas vacías.
- Middleware: Implementa autenticación para la creación de publicaciones.
Conclusión
¡Has construido un blog simple con Flight PHP! Esta guía demuestra características básicas como enrutamiento, templating con Latte y manejo de envíos de formularios, todo mientras mantienes las cosas ligeras. ¡Explora la documentación de Flight para más características avanzadas que lleven tu blog más lejos!
License
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.
About
¿Qué es Flight?
Flight es un marco rápido, simple y extensible para PHP. Es bastante versátil y se puede usar 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 gran marco para principiantes que son nuevos en PHP y quieren aprender a construir aplicaciones web. También es un gran marco para desarrolladores experimentados que desean 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.
Comenzar Rápido
Primero instálalo con Composer
composer require flightphp/core
o puedes descargar un zip del repositorio aquí. Luego tendrías un archivo básico index.php
como el siguiente:
<?php
// si se instaló con composer
require 'vendor/autoload.php';
// o si se instaló manualmente mediante un archivo zip
// require 'flight/Flight.php';
Flight::route('/', function() {
echo '¡hola mundo!';
});
Flight::route('/json', function() {
Flight::json(['hello' => 'world']);
});
Flight::start();
¡Eso es todo! Tienes una aplicación básica de Flight. Ahora puedes ejecutar este archivo con php -S localhost:8000
y visitar http://localhost:8000
en tu navegador para ver la salida.
¿Es rápido?
¡Sí! Flight es rápido. Es uno de los marcos PHP más rápidos disponibles. Puedes ver todos los benchmarks en TechEmpower
Ve el benchmark a continuación con algunos otros marcos PHP populares.
Marco | Reqs de texto plano/sec | Reqs de JSON/sec |
---|---|---|
Flight | 190,421 | 182,491 |
Yii | 145,749 | 131,434 |
Fat-Free | 139,238 | 133,952 |
Slim | 89,588 | 87,348 |
Phalcon | 95,911 | 87,675 |
Symfony | 65,053 | 63,237 |
Lumen | 40,572 | 39,700 |
Laravel | 26,657 | 26,901 |
CodeIgniter | 20,628 | 19,901 |
Aplicación Esqueleto/Plantilla
Hay una aplicación de ejemplo que puede ayudarte a comenzar con el marco 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.
Comunidad
Estamos en Matrix Chat
Y en Discord
Contribuciones
Hay dos formas en que puedes contribuir a Flight:
- Puedes contribuir al marco central visitando el repositorio principal.
- Puedes contribuir a la documentación. Este sitio web de documentación está alojado en Github. Si notas un error o quieres mejorar algo, siéntete libre de corregirlo y enviar una solicitud de extracción. Intentamos mantenernos al día, pero las actualizaciones y traducciones de idiomas son bienvenidas.
Requisitos
Flight requiere PHP 7.4 o superior.
Nota: PHP 7.4 es compatible porque en el momento actual de escribir (2024) PHP 7.4 es la versión predeterminada para algunas distribuciones Linux LTS. Forzar un cambio a PHP >8 causaría muchos inconvenientes para esos usuarios. El marco también es compatible con PHP >8.
Licencia
Flight se publica bajo la licencia MIT.
Awesome-plugins/php_cookie
Cookies
overclokk/cookie es una biblioteca sencilla para administrar cookies dentro de su aplicación.
Instalación
La instalación es sencilla con composer.
composer require overclokk/cookie
Uso
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');
}
}
}
Awesome-plugins/php_encryption
Cifrado en PHP
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.
Instalación
La instalación es sencilla con composer.
composer require defuse/php-encryption
Configuración
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.
Uso
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;
});
Awesome-plugins/php_file_cache
flightphp/cache
Clase de caché de archivo PHP ligera, simple y autónoma
Ventajas
- Ligera, autónoma y simple
- Todo el código en un solo archivo - sin controladores innecesarios.
- Seguro - cada archivo de caché generado tiene un encabezado php con die, haciendo imposible el acceso directo incluso si alguien conoce la ruta y tu servidor no está configurado correctamente
- Bien documentado y probado
- Maneja la concurrencia correctamente a través de flock
- Soporta PHP 7.4+
- Gratis bajo una licencia MIT
¡Este sitio de documentación está usando esta biblioteca para almacenar en caché cada una de las páginas!
Haz clic aquí para ver el código.
Instalación
Instalar a través de composer:
composer require flightphp/cache
Uso
El uso es bastante sencillo. Esto guarda un archivo de caché en el directorio de caché.
use flight\Cache;
$app = Flight::app();
// Pasas el directorio donde se almacenará la caché en el constructor
$app->register('cache', Cache::class, [ __DIR__ . '/../cache/' ], function(Cache $cache) {
// Esto asegura que la caché solo se use cuando esté en modo de producción
// ENVIRONMENT es una constante que se establece en tu archivo de inicialización o en otro lugar de tu aplicación
$cache->setDevMode(ENVIRONMENT === 'development');
});
Luego 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"); // devolver datos a ser almacenados en caché
}, 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
}
Documentación
Visita https://github.com/flightphp/cache para la documentación completa y asegúrate de ver la carpeta de ejemplos.
Awesome-plugins/permissions
FlightPHP/Permisos
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.
Instalación
¡Ejecuta composer require flightphp/permissions
y estás listo/a!
Uso
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.
Ejemplo Básico
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
}
}
}
Inyectar dependencias
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.
Cierres
$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
}
}
Clases
namespace MiApp;
class Permisos {
public function orden(string $current_role, MiDependencia $MiDependencia = null) {
// ... código
}
}
Atajo para establecer permisos con clases
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:
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!');
}
}
}
Caché
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!
Awesome-plugins/simple_job_queue
Cola de Trabajos Simple
La Cola de Trabajos Simple es una biblioteca que se puede utilizar para procesar trabajos de forma asíncrona. Se puede usar con beanstalkd, MySQL/MariaDB, SQLite y PostgreSQL.
Instalar
composer require n0nag0n/simple-job-queue
Uso
Para que esto funcione, necesitas una manera de agregar trabajos a la cola y una manera de procesar los trabajos (un trabajador). A continuación se presentan ejemplos de cómo agregar un trabajo a la cola y cómo procesar el trabajo.
Agregando a Flight
Agregar esto a Flight es simple y se hace utilizando el método register()
. A continuación se muestra un ejemplo de cómo agregar esto a Flight.
<?php
require 'vendor/autoload.php';
// Cambia ['mysql'] a ['beanstalkd'] si deseas usar beanstalkd
Flight::register('queue', n0nag0n\Job_Queue::class, ['mysql'], function($Job_Queue) {
// si ya tienes una conexión PDO en Flight::db();
$Job_Queue->addQueueConnection(Flight::db());
// o si estás usando beanstalkd/Pheanstalk
$pheanstalk = Pheanstalk\Pheanstalk::create('127.0.0.1');
$Job_Queue->addQueueConnection($pheanstalk);
});
Agregando un nuevo trabajo
Cuando agregas un trabajo, necesitas especificar un pipeline (cola). Esto es comparable a un canal en RabbitMQ o un tubo en beanstalkd.
<?php
Flight::queue()->selectPipeline('send_important_emails');
Flight::queue()->addJob(json_encode([ 'something' => 'that', 'ends' => 'up', 'a' => 'string' ]));
Ejecutando un trabajador
Aquí hay un archivo de ejemplo de cómo ejecutar un trabajador.
<?php
require 'vendor/autoload.php';
$Job_Queue = new n0nag0n\Job_Queue('mysql');
// Conexión PDO
$PDO = new PDO('mysql:dbname=testdb;host=127.0.0.1', 'user', 'pass');
$Job_Queue->addQueueConnection($PDO);
// o si estás usando beanstalkd/Pheanstalk
$pheanstalk = Pheanstalk\Pheanstalk::create('127.0.0.1');
$Job_Queue->addQueueConnection($pheanstalk);
$Job_Queue->watchPipeline('send_important_emails');
while(true) {
$job = $Job_Queue->getNextJobAndReserve();
// ajusta lo que te haga dormir mejor por la noche (solo para colas de base de datos, beanstalkd no necesita esta declaración if)
if(empty($job)) {
usleep(500000);
continue;
}
echo "Procesando {$job['id']}\n";
$payload = json_decode($job['payload'], true);
try {
$result = doSomethingThatDoesSomething($payload);
if($result === true) {
$Job_Queue->deleteJob($job);
} else {
// esto lo saca de la cola lista y lo coloca en otra cola que puede ser recogida y "patada" más tarde.
$Job_Queue->buryJob($job);
}
} catch(Exception $e) {
$Job_Queue->buryJob($job);
}
}
Manejo de Procesos Largos con Supervisord
Supervisord es un sistema de control de procesos que garantiza que tus procesos de trabajo sigan funcionando continuamente. Aquí hay una guía más completa sobre cómo configurarlo con tu trabajador de Cola de Trabajos Simple:
Instalando Supervisord
# En Ubuntu/Debian
sudo apt-get install supervisor
# En CentOS/RHEL
sudo yum install supervisor
# En macOS con Homebrew
brew install supervisor
Creando un Script de Trabajador
Primero, guarda tu código de trabajador en un archivo PHP dedicado:
<?php
require 'vendor/autoload.php';
$Job_Queue = new n0nag0n\Job_Queue('mysql');
// Conexión PDO
$PDO = new PDO('mysql:dbname=your_database;host=127.0.0.1', 'username', 'password');
$Job_Queue->addQueueConnection($PDO);
// Establecer el pipeline a observar
$Job_Queue->watchPipeline('send_important_emails');
// Registrar inicio del trabajador
echo date('Y-m-d H:i:s') . " - Trabajador iniciado\n";
while(true) {
$job = $Job_Queue->getNextJobAndReserve();
if(empty($job)) {
usleep(500000); // Dormir durante 0.5 segundos
continue;
}
echo date('Y-m-d H:i:s') . " - Procesando trabajo {$job['id']}\n";
$payload = json_decode($job['payload'], true);
try {
$result = doSomethingThatDoesSomething($payload);
if($result === true) {
$Job_Queue->deleteJob($job);
echo date('Y-m-d H:i:s') . " - Trabajo {$job['id']} completado con éxito\n";
} else {
$Job_Queue->buryJob($job);
echo date('Y-m-d H:i:s') . " - Trabajo {$job['id']} falló, enterrado\n";
}
} catch(Exception $e) {
$Job_Queue->buryJob($job);
echo date('Y-m-d H:i:s') . " - Excepción al procesar el trabajo {$job['id']}: {$e->getMessage()}\n";
}
}
Configurando Supervisord
Crea un archivo de configuración para tu trabajador:
[program:email_worker]
command=php /path/to/worker.php
directory=/path/to/project
autostart=true
autorestart=true
startretries=3
stderr_logfile=/var/log/simple_job_queue_err.log
stdout_logfile=/var/log/simple_job_queue.log
user=www-data
numprocs=2
process_name=%(program_name)s_%(process_num)02d
Opciones Clave de Configuración:
command
: El comando para ejecutar tu trabajadordirectory
: Directorio de trabajo para el trabajadorautostart
: Iniciar automáticamente cuando supervisord iniciaautorestart
: Reiniciar automáticamente si el proceso salestartretries
: Número de veces para intentar iniciar si fallastderr_logfile
/stdout_logfile
: Ubicaciones de los archivos de registrouser
: Usuario del sistema para ejecutar el procesonumprocs
: Número de instancias de trabajador a ejecutarprocess_name
: Formato de nombre para múltiples procesos de trabajador
Gestionando Trabajadores con Supervisorctl
Después de crear o modificar la configuración:
# Recargar configuración de supervisor
sudo supervisorctl reread
sudo supervisorctl update
# Controlar procesos de trabajadores específicos
sudo supervisorctl start email_worker:*
sudo supervisorctl stop email_worker:*
sudo supervisorctl restart email_worker:*
sudo supervisorctl status email_worker:*
Ejecutando Múltiples Pipelines
Para múltiples pipelines, crea archivos y configuraciones de trabajador separados:
[program:email_worker]
command=php /path/to/email_worker.php
# ... otras configuraciones ...
[program:notification_worker]
command=php /path/to/notification_worker.php
# ... otras configuraciones ...
Monitoreo y Registros
Verifica los registros para monitorear la actividad del trabajador:
# Ver registros
sudo tail -f /var/log/simple_job_queue.log
# Verificar estado
sudo supervisorctl status
Esta configuración asegura que tus trabajadores de trabajos continúen funcionando incluso después de fallos, reinicios del servidor u otros problemas, haciendo que tu sistema de colas sea confiable para entornos de producción.
Awesome-plugins/index
Increíbles complementos
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.
Caché
La caché es una excelente manera de acelerar tu aplicación. Hay varias bibliotecas de caché que se pueden utilizar con Flight.
- Wruczek/PHP-File-Cache - Clase de caché en archivo PHP ligera, simple y autónoma
Depuración
La depuración es crucial cuando estás desarrollando en tu entorno local. Hay algunos complementos que pueden mejorar tu experiencia de depuración.
- tracy/tracy - Este es un manejador de errores con todas las funciones que se puede utilizar con Flight. Tiene varios paneles que pueden ayudarte a depurar tu aplicación. También es muy fácil de ampliar y agregar tus propios paneles.
- flightphp/tracy-extensions - Utilizado con el manejador de errores Tracy, este complemento añade algunos paneles adicionales para ayudar con la depuración específicamente para proyectos de Flight.
Bases de datos
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.
- flightphp/core PdoWrapper - Envoltorio oficial Flight PDO que forma parte del núcleo. Este es un envoltorio simple para ayudar a simplificar el proceso de escribir y ejecutar consultas. No es un ORM.
- flightphp/active-record - ORM/Mapper oficial de Flight ActiveRecord. Excelente pequeña biblioteca para recuperar y almacenar datos fácilmente en tu base de datos.
Sesión
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.
- Ghostff/Session - Administrador de sesiones de PHP (no bloqueante, flash, segmento, cifrado de sesiones). Utiliza PHP open_ssl para cifrar/descifrar opcionalmente los datos de sesión.
Plantillas
Las plantillas son esenciales para cualquier aplicación web con un UI. Hay varios motores de plantillas que se pueden utilizar con Flight.
- flightphp/core View - Este es un motor de plantillas muy básico que forma parte del núcleo. No se recomienda su uso si tienes más de un par de páginas en tu proyecto.
- latte/latte - Latte es un motor de plantillas completo que es muy fácil de usar y se siente más cercano a una sintaxis PHP que Twig o Smarty. También es muy fácil de ampliar y agregar tus propios filtros y funciones.
Contribuir
¿Tienes un complemento que te gustaría compartir? ¡Envía una solicitud de extracción para añadirlo a la lista!
Awesome-plugins/ghost_session
Ghostff/Session
Gestor de Sesiones PHP (no bloqueante, flash, segmento, cifrado de sesión). Utiliza PHP open_ssl para la cifrado/descifrado opcional de los datos de la sesión. Soporta Archivos, MySQL, Redis y Memcached.
Haz clic aquí para ver el código.
Instalación
Instala con composer.
composer require ghostff/session
Configuración Básica
No se requiere pasar nada para usar la configuración predeterminada con tu sesión. Puedes leer sobre más configuraciones en el Github Readme.
use Ghostff\Session\Session;
require 'vendor/autoload.php';
$app = Flight::app();
$app->register('session', Session::class);
// una cosa a recordar es que debes confirmar tu sesión en cada carga de página
// o necesitarás ejecutar auto_commit en tu configuración.
Ejemplo Sencillo
Aquí hay 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í
// valida la 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 confirmarlo deliberadamente.
$session->commit();
});
// Esta verificación podría estar en la lógica de la página restringida, o envuelta en middleware.
Flight::route('/some-restricted-page', function() {
$session = Flight::session();
if(!$session->get('is_logged_in')) {
Flight::redirect('/login');
}
// realiza tu lógica de página restringida aquí
});
// la versión con middleware
Flight::route('/some-restricted-page', function() {
// lógica de página regular
})->addMiddleware(function() {
$session = Flight::session();
if(!$session->get('is_logged_in')) {
Flight::redirect('/login');
}
});
Ejemplo Más Complejo
Aquí hay un ejemplo más complejo de cómo podrías usar esto.
use Ghostff\Session\Session;
require 'vendor/autoload.php';
$app = Flight::app();
// establecer una ruta personalizada para tu archivo de configuración de sesión y darle una cadena aleatoria para el id de sesión
$app->register('session', Session::class, [ 'path/to/session_config.php', bin2hex(random_bytes(32)) ], function(Session $session) {
// o puedes sobreescribir manualmente las opciones de configuración
$session->updateConfiguration([
// si deseas almacenar tus datos de sesión en una base de datos (bueno si quieres algo como, "desconéctame de todos los dispositivos" funcionalidad)
Session::CONFIG_DRIVER => Ghostff\Session\Drivers\MySql::class,
Session::CONFIG_ENCRYPT_DATA => true,
Session::CONFIG_SALT_KEY => hash('sha256', 'my-super-S3CR3T-salt'), // por favor cambia esto a algo diferente
Session::CONFIG_AUTO_COMMIT => true, // haz esto solo si lo requiere y/o es difícil confirmarlo
// adicionalmente podrías hacer Flight::after('start', function() { Flight::session()->commit(); });
Session::CONFIG_MYSQL_DS => [
'driver' => 'mysql', # Controlador de base de datos para dns de PDO eg(mysql:host=...;dbname=...)
'host' => '127.0.0.1', # Host de base de datos
'db_name' => 'my_app_database', # Nombre de la base de datos
'db_table' => 'sessions', # Tabla de 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, # Evita sobrecostos de establecer una nueva conexión cada vez que un script necesita comunicarse con una base de datos, resultando en una aplicación web más rápida. ENCUENTRA EL REVERSO TÚ MISMO
]
]);
}
);
¡Ayuda! ¡Mis Datos de Sesión No Están Persistiendo!
¿Estás estableciendo tus datos de sesión y no están persistiendo entre solicitudes? Puede que hayas olvidado confirmar tus datos de sesión. Puedes hacerlo llamando a $session->commit()
después de haber establecido tus datos de sesión.
Flight::route('POST /login', function() {
$session = Flight::session();
// realiza tu lógica de inicio de sesión aquí
// valida la 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 confirmarlo 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 solicitud.
$app->register('session', Session::class, [ 'path/to/session_config.php', bin2hex(random_bytes(32)) ], function(Session $session) {
$session->updateConfiguration([
Session::CONFIG_AUTO_COMMIT => true,
]);
}
);
Adicionalmente podrías hacer Flight::after('start', function() { Flight::session()->commit(); });
para confirmar tus datos de sesión después de cada solicitud.
Documentación
Visita el Github Readme para la documentación completa. Las opciones de configuración están bien documentadas en el archivo default_config.php. El código es simple de entender si deseas revisar este paquete por ti mismo.
Awesome-plugins/pdo_wrapper
Clase de ayuda PdoWrapper PDO
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.
Registrando la Clase de Ayuda PDO
// 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
]
]);
Uso
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;
}
Nota para la sintaxis de 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 ]);
Ejemplo Completo
// 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();
});
Awesome-plugins/migrations
Migraciones
Una migración para tu proyecto es el seguimiento de todos los cambios de base de datos involucrados en tu proyecto. byjg/php-migration es una biblioteca central muy útil para comenzar.
Instalación
Biblioteca PHP
Si deseas usar solo la Biblioteca PHP en tu proyecto:
composer require "byjg/migration"
Interfaz de Línea de Comando
La interfaz de línea de comando es independiente y no requiere que la instales con tu proyecto.
Puedes instalarlo globalmente y crear un enlace simbólico.
composer require "byjg/migration-cli"
Por favor visita byjg/migration-cli para obtener más información sobre Migration CLI.
Bases de datos soportadas
Base de datos | Controlador | Cadena de conexión |
---|---|---|
Sqlite | pdo_sqlite | sqlite:///path/to/file |
MySql/MariaDb | pdo_mysql | mysql://username:password@hostname:port/database |
Postgres | pdo_pgsql | pgsql://username:password@hostname:port/database |
Sql Server | pdo_dblib, pdo_sysbase Linux | dblib://username:password@hostname:port/database |
Sql Server | pdo_sqlsrv Windows | sqlsrv://username:password@hostname:port/database |
¿Cómo funciona?
La Migración de Base de Datos utiliza SQL PURO para gestionar la versión de la base de datos. Para que funcione, necesitas:
- Crear los Scripts SQL
- Gestionar usando la Línea de Comando o la API.
Los Scripts SQL
Los scripts se dividen en tres conjuntos de scripts:
- El script BASE contiene TODOS los comandos SQL para crear una nueva base de datos;
- Los scripts UP contienen todos los comandos de migración SQL para "subir" la versión de la base de datos;
- Los scripts DOWN contienen todos los comandos de migración SQL para "bajar" o revertir la versión de la base de datos;
El directorio de scripts es:
<root dir>
|
+-- base.sql
|
+-- /migrations
|
+-- /up
|
+-- 00001.sql
+-- 00002.sql
+-- /down
|
+-- 00000.sql
+-- 00001.sql
- "base.sql" es el script base
- La carpeta "up" contiene los scripts para migrar la versión hacia arriba. Por ejemplo: 00002.sql es el script para mover la base de datos de la versión '1' a '2'.
- La carpeta "down" contiene los scripts para migrar la versión hacia abajo. Por ejemplo: 00001.sql es el script para mover la base de datos de la versión '2' a '1'. La carpeta "down" es opcional.
Entorno de Desarrollo Múltiple
Si trabajas con múltiples desarrolladores y múltiples ramas, es difícil determinar cuál es el siguiente número.
En ese caso, tienes el sufijo "-dev" después del número de versión.
Veamos el escenario:
- El desarrollador 1 crea una rama y la versión más reciente es, por ejemplo, 42.
- El desarrollador 2 crea una rama al mismo tiempo y tiene el mismo número de versión de base de datos.
En ambos casos, los desarrolladores crearán un archivo llamado 43-dev.sql. Ambos desarrolladores migrarán HACIA ARRIBA y HACIA ABAJO sin problemas y tu versión local será 43.
Pero el desarrollador 1 fusionó tus cambios y creó una versión final 43.sql (git mv 43-dev.sql 43.sql
). Si el desarrollador 2 actualiza su rama local, tendrá un archivo 43.sql (del dev 1) y su archivo 43-dev.sql. Si intenta migrar HACIA ARRIBA o HACIA ABAJO, el script de migración fallará y le alertará que hay DOS versiones 43. En ese caso, el desarrollador 2 tendrá que actualizar su archivo a 44-dev.sql y continuar trabajando hasta fusionar tus cambios y generar una versión final.
Usando la API PHP e integrándola en tus proyectos
El uso básico es
- Crear una conexión con un objeto ConnectionManagement. Para más información, consulta el componente "byjg/anydataset".
- Crear un objeto de Migración con esta conexión y la carpeta donde se encuentran los scripts SQL.
- Usar el comando apropiado para "resetear", "subir" o "bajar" los scripts de migración.
Veamos un ejemplo:
<?php
// Crear la URI de conexión
// Ver más: https://github.com/byjg/anydataset#connection-based-on-uri
$connectionUri = new \ByJG\Util\Uri('mysql://migrateuser:migratepwd@localhost/migratedatabase');
// Registrar la base de datos o bases de datos que pueden manejar esa URI:
\ByJG\DbMigration\Migration::registerDatabase(\ByJG\DbMigration\Database\MySqlDatabase::class);
// Crear la instancia de Migración
$migration = new \ByJG\DbMigration\Migration($connectionUri, '.');
// Agregar una función de progreso de devolución de llamada para recibir información de la ejecución
$migration->addCallbackProgress(function ($action, $currentVersion, $fileInfo) {
echo "$action, $currentVersion, ${fileInfo['description']}\n";
});
// Restaurar la base de datos usando el script "base.sql"
// y ejecutar TODOS los scripts existentes para subir la versión de la base de datos a la última versión
$migration->reset();
// Ejecutar TODOS los scripts existentes para subir o bajar la versión de la base de datos
// desde la versión actual hasta el número $version;
// Si el número de versión no está especificado, migrar hasta la última versión de la base de datos
$migration->update($version = null);
El objeto de Migración controla la versión de la base de datos.
Creando un control de versión en tu proyecto
<?php
// Registrar la base de datos o bases de datos que pueden manejar esa URI:
\ByJG\DbMigration\Migration::registerDatabase(\ByJG\DbMigration\Database\MySqlDatabase::class);
// Crear la instancia de Migración
$migration = new \ByJG\DbMigration\Migration($connectionUri, '.');
// Este comando creará la tabla de versiones en tu base de datos
$migration->createVersion();
Obteniendo la versión actual
<?php
$migration->getCurrentVersion();
Agregar Callback para controlar el progreso
<?php
$migration->addCallbackProgress(function ($command, $version, $fileInfo) {
echo "Ejecutando Comando: $command en la versión $version - ${fileInfo['description']}, ${fileInfo['exists']}, ${fileInfo['file']}, ${fileInfo['checksum']}\n";
});
Obteniendo la instancia del controlador de base de datos
<?php
$migration->getDbDriver();
Para usarlo, por favor visita: https://github.com/byjg/anydataset-db
Evitando Migraciones Parciales (no disponible para MySQL)
Una migración parcial es cuando el script de migración se interrumpe en medio del proceso debido a un error o una interrupción manual.
La tabla de migración tendrá el estado partial up
o partial down
y debe ser corregido manualmente antes de poder migrar nuevamente.
Para evitar esta situación, puedes especificar que la migración se ejecute en un contexto transaccional.
Si el script de migración falla, la transacción se revertirá y la tabla de migración se marcará como complete
y
la versión será la versión anterior inmediata antes del script que causó el error.
Para habilitar esta función, debes llamar al método withTransactionEnabled
pasando true
como parámetro:
<?php
$migration->withTransactionEnabled(true);
NOTA: Esta característica no está disponible para MySQL, ya que no admite comandos DDL dentro de una transacción. Si utilizas este método con MySQL, la Migración lo ignorará en silencio. Más info: https://dev.mysql.com/doc/refman/8.0/en/cannot-roll-back.html
Consejos sobre cómo escribir migraciones SQL para Postgres
Al crear triggers y funciones SQL
-- HACER
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- Comprobar que empname y salary están dados
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname no puede ser nulo'; -- no importa si estos comentarios están vacíos o no
END IF; --
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% no puede tener salary nulo', NEW.empname; --
END IF; --
-- ¿Quién trabaja para nosotros cuando tienen que pagarlo?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% no puede tener un salary negativo', NEW.empname; --
END IF; --
-- Recuerda quién cambió la nómina y cuándo
NEW.last_date := current_timestamp; --
NEW.last_user := current_user; --
RETURN NEW; --
END; --
$emp_stamp$ LANGUAGE plpgsql;
-- NO HACER
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- Comprobar que empname y salary están dados
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname no puede ser nulo';
END IF;
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% no puede tener salary nulo', NEW.empname;
END IF;
-- ¿Quién trabaja para nosotros cuando tienen que pagarlo?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% no puede tener un salary negativo', NEW.empname;
END IF;
-- Recuerda quién cambió la nómina y cuándo
NEW.last_date := current_timestamp;
NEW.last_user := current_user;
RETURN NEW;
END;
$emp_stamp$ LANGUAGE plpgsql;
Dado que la capa de abstracción de base de datos PDO
no puede ejecutar lotes de declaraciones SQL, al leer un archivo de migración, byjg/migration
tiene que dividir todo el contenido del archivo SQL en los puntos y comas, y ejecutar las declaraciones una por una. Sin embargo, hay un tipo de declaración que puede tener múltiples puntos y comas en su interior: funciones.
Con el fin de poder analizar correctamente las funciones, byjg/migration
2.1.0 comenzó a dividir los archivos de migración en la secuencia de punto y coma + EOL
en lugar de solo el punto y coma. De esta manera, si agregas un comentario vacío después de cada punto y coma interno de una definición de función, byjg/migration
podrá analizarlo.
Desafortunadamente, si olvidas agregar alguno de estos comentarios, la biblioteca dividirá la declaración CREATE FUNCTION
en múltiples partes y la migración fallará.
Evitar el carácter de dos puntos (:
)
-- HACER
CREATE TABLE bookings (
booking_id UUID PRIMARY KEY,
booked_at TIMESTAMPTZ NOT NULL CHECK (CAST(booked_at AS DATE) <= check_in),
check_in DATE NOT NULL
);
-- NO HACER
CREATE TABLE bookings (
booking_id UUID PRIMARY KEY,
booked_at TIMESTAMPTZ NOT NULL CHECK (booked_at::DATE <= check_in),
check_in DATE NOT NULL
);
Dado que PDO
utiliza el carácter de dos puntos para prefijar parámetros nombrados en declaraciones preparadas, su uso puede causar problemas en otros contextos.
Por ejemplo, las declaraciones de PostgreSQL pueden usar ::
para convertir valores entre tipos. Por otro lado, PDO
leerá esto como un parámetro nombrado inválido en un contexto inválido y fallará cuando intente ejecutarlo.
La única forma de solucionar esta inconsistencia es evitar los dos puntos por completo (en este caso, PostgreSQL también tiene una sintaxis alternativa: CAST(value AS type)
).
Usar un editor SQL
Finalmente, escribir migraciones SQL manuales puede ser agotador, pero es significativamente más fácil si usas un editor capaz de entender la sintaxis SQL, proporcionando autocompletado, introspección de tu esquema de base de datos actual y/o autoformateo de tu código.
Manejo de diferentes migraciones dentro de un esquema
Si necesitas crear diferentes scripts de migración y versiones dentro del mismo esquema, es posible, pero es demasiado arriesgado y no lo recomiendo en absoluto.
Para hacerlo, necesitas crear diferentes "tablas de migración" pasando el parámetro al constructor.
<?php
$migration = new \ByJG\DbMigration\Migration("db:/uri", "/path", true, "NEW_MIGRATION_TABLE_NAME");
Por razones de seguridad, esta función no está disponible en la línea de comandos, pero puedes usar la variable de entorno MIGRATION_VERSION
para almacenar el nombre.
Recomendamos encarecidamente no utilizar esta función. La recomendación es una migración para un esquema.
Ejecución de pruebas unitarias
Las pruebas unitarias básicas se pueden ejecutar con:
vendor/bin/phpunit
Ejecución de pruebas de base de datos
Ejecutar pruebas de integración requiere que las bases de datos estén activas y en funcionamiento. Proporcionamos un docker-compose.yml
básico que puedes usar para iniciar las bases de datos para pruebas.
Ejecutando las bases de datos
docker-compose up -d postgres mysql mssql
Ejecutar las pruebas
vendor/bin/phpunit
vendor/bin/phpunit tests/SqliteDatabase*
vendor/bin/phpunit tests/MysqlDatabase*
vendor/bin/phpunit tests/PostgresDatabase*
vendor/bin/phpunit tests/SqlServerDblibDatabase*
vendor/bin/phpunit tests/SqlServerSqlsrvDatabase*
Opcionalmente puedes establecer el host y la contraseña utilizados por las pruebas unitarias
export MYSQL_TEST_HOST=localhost # predeterminado a localhost
export MYSQL_PASSWORD=newpassword # usa '.' si quieres tener una contraseña nula
export PSQL_TEST_HOST=localhost # predeterminado a localhost
export PSQL_PASSWORD=newpassword # usa '.' si quieres tener una contraseña nula
export MSSQL_TEST_HOST=localhost # predeterminado a localhost
export MSSQL_PASSWORD=Pa55word
export SQLITE_TEST_HOST=/tmp/test.db # predeterminado a /tmp/test.db
Awesome-plugins/session
FlightPHP Sesión - Controlador de Sesiones Basado en Archivos Liviano
Este es un plugin liviano de controlador de sesiones basado en archivos para el Flight PHP Framework. Proporciona una solución simple pero poderosa para gestionar sesiones, con características como lecturas de sesiones no bloqueantes, cifrado opcional, funcionalidad de auto-confirmación y un modo de prueba para el desarrollo. Los datos de la sesión se almacenan en archivos, lo que lo hace ideal para aplicaciones que no requieren una base de datos.
Si deseas usar una base de datos, consulta el plugin ghostff/session que tiene muchas de estas mismas características pero con un backend de base de datos.
Visita el repositorio de Github para el código fuente completo y detalles.
Instalación
Instala el plugin a través de Composer:
composer require flightphp/session
Uso Básico
Aquí tienes un ejemplo simple de cómo usar el plugin flightphp/session
en tu aplicación Flight:
require 'vendor/autoload.php';
use flight\Session;
$app = Flight::app();
// Registrar el servicio de sesión
$app->register('session', Session::class);
// Ruta de ejemplo con uso de sesión
Flight::route('/login', function() {
$session = Flight::session();
$session->set('user_id', 123);
$session->set('username', 'johndoe');
$session->set('is_admin', false);
echo $session->get('username'); // Salida: johndoe
echo $session->get('preferences', 'default_theme'); // Salida: default_theme
if ($session->get('user_id')) {
Flight::json(['message' => '¡El usuario ha iniciado sesión!', 'user_id' => $session->get('user_id')]);
}
});
Flight::route('/logout', function() {
$session = Flight::session();
$session->clear(); // Limpiar todos los datos de la sesión
Flight::json(['message' => '¡Salida exitosa!']);
});
Flight::start();
Puntos Clave
- No Bloqueante: Utiliza
read_and_close
para iniciar la sesión de manera predeterminada, evitando problemas de bloqueo de sesión. - Auto-Confirmar: Habilitado por defecto, por lo que los cambios se guardan automáticamente al apagar, a menos que se desactive.
- Almacenamiento en Archivos: Las sesiones se almacenan en el directorio temporal del sistema bajo
/flight_sessions
por defecto.
Configuración
Puedes personalizar el controlador de sesiones pasando un array de opciones al registrarte:
$app->register('session', Session::class, [
'save_path' => '/custom/path/to/sessions', // Directorio para archivos de sesión
'encryption_key' => 'a-secure-32-byte-key-here', // Habilitar cifrado (32 bytes recomendados para AES-256-CBC)
'auto_commit' => false, // Desactivar auto-confirmación para control manual
'start_session' => true, // Iniciar sesión automáticamente (predeterminado: true)
'test_mode' => false // Habilitar modo de prueba para el desarrollo
]);
Opciones de Configuración
Opción | Descripción | Valor Predeterminado |
---|---|---|
save_path |
Directorio donde se almacenan los archivos de sesión | sys_get_temp_dir() . '/flight_sessions' |
encryption_key |
Clave para cifrado AES-256-CBC (opcional) | null (sin cifrado) |
auto_commit |
Guardar automáticamente los datos de la sesión al apagarse | true |
start_session |
Iniciar la sesión automáticamente | true |
test_mode |
Ejecutar en modo de prueba sin afectar las sesiones de PHP | false |
test_session_id |
ID de sesión personalizado para modo de prueba (opcional) | Generado aleatoriamente si no está configurado |
Uso Avanzado
Confirmación Manual
Si desactivas la auto-confirmación, debes confirmar manualmente los cambios:
$app->register('session', Session::class, ['auto_commit' => false]);
Flight::route('/update', function() {
$session = Flight::session();
$session->set('key', 'value');
$session->commit(); // Guardar cambios explícitamente
});
Seguridad de la Sesión con Cifrado
Habilitar el cifrado para datos sensibles:
$app->register('session', Session::class, [
'encryption_key' => 'your-32-byte-secret-key-here'
]);
Flight::route('/secure', function() {
$session = Flight::session();
$session->set('credit_card', '4111-1111-1111-1111'); // Cifrado automáticamente
echo $session->get('credit_card'); // Desencriptado al recuperar
});
Regeneración de Sesión
Regenerar el ID de sesión por seguridad (por ejemplo, después de iniciar sesión):
Flight::route('/post-login', function() {
$session = Flight::session();
$session->regenerate(); // Nuevo ID, conservar datos
// O
$session->regenerate(true); // Nuevo ID, eliminar datos antiguos
});
Ejemplo de Middleware
Proteger rutas con autenticación basada en sesión:
Flight::route('/admin', function() {
Flight::json(['message' => 'Bienvenido al panel de administración']);
})->addMiddleware(function() {
$session = Flight::session();
if (!$session->get('is_admin')) {
Flight::halt(403, 'Acceso denegado');
}
});
Este es solo un ejemplo simple de cómo usar esto en middleware. Para un ejemplo más detallado, consulta la documentación de middleware.
Métodos
La clase Session
proporciona estos métodos:
set(string $key, $value)
: Almacena un valor en la sesión.get(string $key, $default = null)
: Recupera un valor, con un valor predeterminado opcional si la clave no existe.delete(string $key)
: Elimina una clave específica de la sesión.clear()
: Elimina todos los datos de la sesión.commit()
: Guarda los datos actuales de la sesión en el sistema de archivos.id()
: Devuelve el ID de sesión actual.regenerate(bool $deleteOld = false)
: Regenera el ID de sesión, opcionalmente eliminando datos antiguos.
Todos los métodos excepto get()
y id()
devuelven la instancia de Session
para la encadenación.
¿Por Qué Usar Este Plugin?
- Liviano: Sin dependencias externas, solo archivos.
- No Bloqueante: Evita el bloqueo de sesiones con
read_and_close
por defecto. - Seguro: Soporta cifrado AES-256-CBC para datos sensibles.
- Flexible: Opciones de auto-confirmación, modo de prueba y control manual.
- Nativo de Flight: Construido específicamente para el framework Flight.
Detalles Técnicos
- Formato de Almacenamiento: Los archivos de sesión están prefijados con
sess_
y almacenados en elsave_path
configurado. Los datos cifrados utilizan un prefijoE
, los datos en texto plano utilizanP
. - Cifrado: Utiliza AES-256-CBC con un IV aleatorio por cada escritura de sesión cuando se proporciona una
encryption_key
. - Recolección de Basura: Implementa el
SessionHandlerInterface::gc()
de PHP para limpiar sesiones caducadas.
Contribuyendo
¡Las contribuciones son bienvenidas! Haz un fork del repositorio, realiza tus cambios y envía una solicitud de extracción. Informa sobre errores o sugiere funcionalidades a través del rastreador de problemas de Github.
Licencia
Este plugin está bajo la Licencia MIT. Consulta el repositorio de Github para detalles.
Awesome-plugins/runway
Pista
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.
Instalación
Instala con composer.
composer require flightphp/runway
Configuración Básica
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.
Uso
Pista tiene varios comandos que puedes usar para gestionar tu aplicación Flight. Hay dos formas fáciles de usar Pista.
- Si estás usando el proyecto esqueleto, puedes ejecutar
php runway [comando]
desde la raíz de tu proyecto. - Si estás usando Pista como un paquete instalado a través de composer, puedes ejecutar
vendor/bin/runway [comando]
desde la raíz de tu proyecto.
Para cualquier comando, puedes agregar la bandera --help
para obtener más información sobre cómo usar el comando.
php runway routes --help
Aquí tienes algunos ejemplos:
Generar un Controlador
Basado en la configuración en tu archivo .runway.json
, la ubicación predeterminada generará un controlador para ti en el directorio app/controllers/
.
php runway make:controller MiControlador
Generar un Modelo de Active Record
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/
.
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
:
<?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');
}
}
Mostrar Todas las Rutas
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.
Personalización de Pista
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.
Para crear un comando, simplemente extiende la clase AbstractBaseCommand
e implementa como mínimo un método __construct
y un método 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.
Awesome-plugins/tracy_extensions
Tracy Flight Panel Extensions
=====
This is a set of extensions to make working with Flight a little richer.
- Flight - Analyze all Flight variables.
- Database - Analyze all queries that have run on the page (if you correctly initiate the database connection)
- Request - Analyze all
$_SERVER
variables and examine all global payloads ($_GET
,$_POST
,$_FILES
) - Session - Analyze all
$_SESSION
variables if sessions are active.
This is the Panel
And each panel displays very helpful information about your application!
Click here to view the code.
Installation
Run composer require flightphp/tracy-extensions --dev
and you're on your way!
Configuration
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();
Additional Configuration
Session Data
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.
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();
Latte
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.
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());
}
Awesome-plugins/tracy
Tracy
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.
Instalación
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
Configuración Básica
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);
}
Consejos Útiles
Cuando estés depurando tu código, hay algunas funciones muy útiles para mostrar datos para ti.
bdump($var)
- Esto volcará la variable en la Barra de Tracy en un panel separado.dumpe($var)
- Esto volcará la variable y luego morirá inmediatamente.
Awesome-plugins/active_record
Flight Active Record
Un registro activo es el mapeo de una entidad de base de datos a un objeto PHP. Hablando claramente, si tienes una tabla de usuarios en tu base de datos, puedes "traducir" una fila en esa tabla a una clase User
y un objeto $user
en tu base de código. Consulta ejemplo básico.
Haz clic aquí para el repositorio en GitHub.
Ejemplo Básico
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 configurarlo de esta manera
parent::__construct($database_connection, 'users');
// o de esta manera
parent::__construct($database_connection, null, [ 'table' => 'users']);
}
}
¡Ahora mira cómo sucede la magia!
// para sqlite
$database_connection = new PDO('sqlite:test.db'); // esto es solo un ejemplo, probablemente uses una conexión a base de datos real
// para mysql
$database_connection = new PDO('mysql:host=localhost;dbname=test_db&charset=utf8bm4', 'username', 'password');
// o mysqli
$database_connection = new mysqli('localhost', 'username', 'password', 'test_db');
// o mysqli con creación no basada en objetos
$database_connection = mysqli_connect('localhost', 'username', 'password', 'test_db');
$user = new User($database_connection);
$user->name = 'Bobby Tables';
$user->password = password_hash('some cool password');
$user->insert();
// o $user->save();
echo $user->id; // 1
$user->name = 'Joseph Mamma';
$user->password = password_hash('some cool password again!!!');
$user->insert();
// ¡no se puede usar $user->save() aquí o pensará que es una actualización!
echo $user->id; // 2
¡Y así de fácil fue agregar un nuevo usuario! Ahora que hay una fila de usuario en la base de datos, ¿cómo la extraes?
$user->find(1); // encuentra id = 1 en la base de datos y lo retorna.
echo $user->name; // 'Bobby Tables'
¿Y si quieres encontrar todos los usuarios?
$users = $user->findAll();
¿Qué tal con una cierta condición?
$users = $user->like('name', '%mamma%')->findAll();
¿Ves cuánto puede ser divertido esto? ¡Instalémoslo y comencemos!
Instalación
Simplemente instala con Composer
composer require flightphp/active-record
Uso
Esto puede ser utilizado como una biblioteca independiente o junto con el Flight PHP Framework. Depende completamente de ti.
Independiente
Solo asegúrate de pasar una conexión PDO al constructor.
$pdo_connection = new PDO('sqlite:test.db'); // esto es solo un ejemplo, probablemente uses una conexión a base de datos real
$User = new User($pdo_connection);
¿No quieres siempre establecer tu conexión a la base de datos en el constructor? Consulta Gestión de Conexiones a la Base de Datos para más ideas.
Registrar como un método en Flight
Si estás utilizando el Flight PHP Framework, puedes registrar la clase ActiveRecord como un servicio, pero honestamente no tienes que hacerlo.
Flight::register('user', 'User', [ $pdo_connection ]);
// entonces puedes usarlo así en un controlador, una función, etc.
Flight::user()->find(1);
Métodos runway
runway es una herramienta CLI para Flight que tiene un comando personalizado para esta biblioteca.
# Uso
php runway make:record database_table_name [class_name]
# Ejemplo
php runway make:record users
Esto creará una nueva clase en el directorio app/records/
como UserRecord.php
con el siguiente contenido:
<?php
declare(strict_types=1);
namespace app\records;
/**
* Clase ActiveRecord para la tabla users.
* @link https://docs.flightphp.com/awesome-plugins/active-record
*
* @property int $id
* @property string $username
* @property string $email
* @property string $password_hash
* @property string $created_dt
*/
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 = [
// 'relation_name' => [ self::HAS_MANY, 'RelatedClass', 'foreign_key' ],
];
/**
* Constructor
* @param mixed $databaseConnection La conexión a la base de datos
*/
public function __construct($databaseConnection)
{
parent::__construct($databaseConnection, 'users');
}
}
Funciones CRUD
find($id = null) : boolean|ActiveRecord
Encuentra un registro y asígnalo al objeto actual. Si pasas un $id
de algún tipo, realizará una búsqueda en la clave primaria con ese valor. Si no se pasa nada, simplemente encontrará el primer registro en la tabla.
Además, puedes pasarlo otros métodos auxiliares para consultar tu tabla.
// encontrar un registro con algunas condiciones previamente
$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
(v0.4.0)
Retorna true
si el registro actual ha sido hidratado (obtenido 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($pdo_connection);
$user->name = 'demo';
$user->password = md5('demo');
$user->insert();
Claves Primarias Basadas en Texto
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($pdo_connection, [ 'primaryKey' => 'uuid' ]);
$user->uuid = 'some-uuid';
$user->name = 'demo';
$user->password = md5('demo');
$user->insert(); // o $user->save();
o puedes hacer que la clave primaria se genere automáticamente para ti a través de eventos.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users', [ 'primaryKey' => 'uuid' ]);
// también puedes establecer la primaryKey de esta manera en lugar de la matriz anterior.
$this->primaryKey = 'uuid';
}
protected function beforeInsert(self $self) {
$self->uuid = uniqid(); // o como sea necesario para generar tus ids únicos
}
}
Si no estableces la clave primaria antes de insertar, se establecerá en rowid
y la base de datos la generará para ti, pero no persistirá porque ese campo puede no existir en tu tabla. Por eso se recomienda utilizar el evento para manejar esto automáticamente.
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, actualizará, de lo contrario, insertará.
$user = new User($pdo_connection);
$user->name = 'demo';
$user->password = md5('demo');
$user->save();
Nota: Si tienes relaciones definidas en la clase, también se guardarán recursivamente esas relaciones si han sido definidas, instanciadas y tienen datos modificados para actualizar. (v0.4.0 y superiores)
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 múltiples registros ejecutando una búsqueda previamente.
$user->like('name', 'Bob%')->delete();
dirty(array $dirty = []): ActiveRecord
Los datos "sucios" se refieren a los datos que han sido cambiados 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 sucios porque han sido actualizados y persistidos 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(); // no se actualizará nada porque nada fue capturado como sucio.
$user->dirty([ 'name' => 'something', 'password' => password_hash('a different password') ]);
$user->update(); // tanto el nombre como la contraseña son actualizados.
copyFrom(array $data): ActiveRecord
(v0.4.0)
Este es un alias para el método dirty()
. Es un poco más claro lo que estás haciendo.
$user->copyFrom([ 'name' => 'something', 'password' => password_hash('a different password') ]);
$user->update(); // tanto el nombre como la contraseña son actualizados.
isDirty(): boolean
(v0.4.0)
Retorna true
si el registro actual ha sido 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 para usar en comportamientos de tipo bucle. Si pasas true
, también restablecerá los datos de consulta que se utilizaron para encontrar el objeto actual (comportamiento predeterminado).
$users = $user->greaterThan('id', 0)->orderBy('id desc')->find();
$user_company = new UserCompany($pdo_connection);
foreach($users as $user) {
$user_company->reset(); // empezar con un lienzo limpio
$user_company->user_id = $user->id;
$user_company->company_id = $some_company_id;
$user_company->insert();
}
getBuiltSql(): string
(v0.4.1)
Después de ejecutar un método find()
, findAll()
, insert()
, update()
o save()
, puedes obtener el SQL que se construyó y usarlo para fines de depuración.
Métodos de Consulta SQL
select(string $field1 [, string $field2 ... ])
Puedes seleccionar solo unas pocas de las columnas en una tabla si lo deseas (es más eficiente en tablas muy amplias con muchas columnas).
$user->select('id', 'name')->find();
from(string $table)
¡Técnicamente puedes elegir otra tabla también! ¿Por qué no hacerlo?!
$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 algunos argumentos where personalizados (no puedes establecer parámetros en esta declaración where).
$user->where('id=1 AND name="demo"')->find();
Nota de Seguridad - Podrías sentirte tentado a hacer algo como $user->where("id = '{$id}' AND name = '{$name}'")->find();
. ¡Por favor, NO HAGAS ESTO! Esto es susceptible a lo que se conoce como ataques de inyección SQL. Hay muchos artículos en línea, por favor busca "sql injection attacks php" y encontrarás muchos artículos sobre este tema. La manera correcta 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 absolutamente tienes que hacer esto, la biblioteca PDO
tiene $pdo->quote($var)
para escapar de ello por ti. Solo después de usar quote()
, puedes usarlo en una declaración where()
.
group(string $group_by_statement)/groupBy(string $group_by_statement)
Agrupa tus resultados por una condición particular.
$user->select('COUNT(*) as count')->groupBy('name')->findAll();
order(string $order_by_statement)/orderBy(string $order_by_statement)
Ordena la consulta devuelta de cierta manera.
$user->orderBy('name DESC')->find();
limit(string $limit)/limit(int $offset, int $limit)
Limita la cantidad de registros devueltos. Si se da un segundo int, se desplazará, limitará justo como en SQL.
$user->orderby('name DESC')->limit(0, 10)->findAll();
Condiciones WHERE
equal(string $field, mixed $value) / eq(string $field, mixed $value)
Donde field = $value
$user->eq('id', 1)->find();
notEqual(string $field, mixed $value) / ne(string $field, mixed $value)
Donde field <> $value
$user->ne('id', 1)->find();
isNull(string $field)
Donde field IS NULL
$user->isNull('id')->find();
isNotNull(string $field) / notNull(string $field)
Donde field IS NOT NULL
$user->isNotNull('id')->find();
greaterThan(string $field, mixed $value) / gt(string $field, mixed $value)
Donde field > $value
$user->gt('id', 1)->find();
lessThan(string $field, mixed $value) / lt(string $field, mixed $value)
Donde 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
$user->ge('id', 1)->find();
lessThanOrEqual(string $field, mixed $value) / le(string $field, mixed $value) / lte(string $field, mixed $value)
Donde 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
$user->like('name', 'de')->find();
in(string $field, array $values) / notIn(string $field, array $values)
Donde field IN($value)
o field NOT IN($value)
$user->in('id', [1, 2])->find();
between(string $field, array $values)
Donde field BETWEEN $value AND $value1
$user->between('id', [1, 2])->find();
Condiciones OR
Es posible envolver tus condiciones en una declaración OR. Esto se realiza con el método startWrap()
y endWrap()
o completando el tercer parámetro de la condición después del campo y el valor.
// Método 1
$user->eq('id', 1)->startWrap()->eq('name', 'demo')->or()->eq('name', 'test')->endWrap('OR')->find();
// Esto evaluará a `id = 1 AND (name = 'demo' OR name = 'test')`
// Método 2
$user->eq('id', 1)->eq('name', 'demo', 'OR')->find();
// Esto evaluará a `id = 1 OR name = 'demo'`
Relaciones
Puedes establecer varios tipos de relaciones utilizando esta biblioteca. Puedes establecer relaciones uno->muchos y uno->uno entre tablas. Esto requiere un poco de configuración adicional en la clase de antemano.
Establecer la matriz $relations
no es difícil, pero adivinar la sintaxis correcta puede ser confuso.
protected array $relations = [
// puedes nombrar la clave como desees. El nombre del ActiveRecord es probablemente bueno. Ej: user, contact, client
'user' => [
// requerido
// self::HAS_MANY, self::HAS_ONE, self::BELONGS_TO
self::HAS_ONE, // este es el tipo de relación
// requerido
'Some_Class', // esta es la clase ActiveRecord "otra" a la que hará referencia
// requerido
// dependiendo del tipo de relación
// self::HAS_ONE = la clave externa que referencia la unión
// self::HAS_MANY = la clave externa que referencia la unión
// self::BELONGS_TO = la clave local que referencia la unión
'local_or_foreign_key',
// solo FYI, esto también se une solo a la clave primaria del "otro" modelo
// opcional
[ 'eq' => [ 'client_id', 5 ], 'select' => 'COUNT(*) as count', 'limit' 5 ], // condiciones adicionales que deseas al unir la relación
// $record->eq('client_id', 5)->select('COUNT(*) as count')->limit(5))
// opcional
'back_reference_name' // esto es si deseas referenciar esta relación de vuelta a sí misma Ej: $user->contact->user;
];
]
class User extends ActiveRecord{
protected array $relations = [
'contacts' => [ self::HAS_MANY, Contact::class, 'user_id' ],
'contact' => [ self::HAS_ONE, Contact::class, 'user_id' ],
];
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
}
class Contact extends ActiveRecord{
protected array $relations = [
'user' => [ self::BELONGS_TO, User::class, 'user_id' ],
'user_with_backref' => [ self::BELONGS_TO, User::class, 'user_id', [], 'contact' ],
];
public function __construct($database_connection)
{
parent::__construct($database_connection, 'contacts');
}
}
¡Ahora tenemos las referencias configuradas para que podamos usarlas muy fácilmente!
$user = new User($pdo_connection);
// encuentra el usuario más reciente.
$user->notNull('id')->orderBy('id desc')->find();
// obtener contactos usando la relación:
foreach($user->contacts as $contact) {
echo $contact->id;
}
// o podemos ir en la otra dirección.
$contact = new Contact();
// encuentra un contacto
$contact->find();
// obtener usuario usando la relación:
echo $contact->user->name; // este es el nombre del usuario
¿Bastante genial, eh?
Estableciendo Datos Personalizados
A veces puedes necesitar adjuntar algo único a tu ActiveRecord, como un cálculo personalizado que podría ser más fácil simplemente adjuntar al objeto que luego se pasaría a decir un template.
setCustomData(string $field, mixed $value)
Adjuntas los datos personalizados con el método setCustomData()
.
$user->setCustomData('page_view_count', $page_view_count);
Y luego simplemente lo referencias como una propiedad normal del objeto.
echo $user->page_view_count;
Eventos
Una característica más super increíble de esta biblioteca es sobre los eventos. Los eventos se activan en ciertos momentos basados en ciertos métodos que llamas. Son muy útiles para configurar datos automáticamente para ti.
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' ]);
//
//
//
// User.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['table'] = 'users';
}
}
beforeFind(ActiveRecord $ActiveRecord)
Esto probablemente solo sea útil si necesitas manipular la consulta cada vez.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeFind(self $self) {
// siempre ejecutar id >= 0 si eso es lo que prefieres
$self->gte('id', 0);
}
}
afterFind(ActiveRecord $ActiveRecord)
Este probablemente sea más útil si siempre necesitas ejecutar alguna lógica cada vez que se recoge este registro. ¿Necesitas desencriptar algo? ¿Necesitas ejecutar una consulta personalizada cada vez (no muy eficiente, pero bueno)?
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function afterFind(self $self) {
// desencriptando algo
$self->secret = yourDecryptFunction($self->secret, $some_key);
// tal vez almacenando algo personalizado como una consulta???
$self->setCustomData('view_count', $self->select('COUNT(*) count')->from('user_views')->eq('user_id', $self->id)['count']);
}
}
beforeFindAll(ActiveRecord $ActiveRecord)
Esto probablemente solo sea útil si necesitas manipular la consulta cada vez.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeFindAll(self $self) {
// siempre ejecutar id >= 0 si eso es lo que prefieres
$self->gte('id', 0);
}
}
afterFindAll(array<int,ActiveRecord> $results)
Similar a afterFind()
, pero ¡puedes hacerlo a todos los registros en su lugar!
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function afterFindAll(array $results) {
foreach($results as $self) {
// hacer algo genial como afterFind()
}
}
}
beforeInsert(ActiveRecord $ActiveRecord)
Realmente útil si necesitas establecer algunos valores predeterminados cada vez.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeInsert(self $self) {
// establecer algunos valores predeterminados
if(!$self->created_date) {
$self->created_date = gmdate('Y-m-d');
}
if(!$self->password) {
$self->password = password_hash((string) microtime(true));
}
}
}
afterInsert(ActiveRecord $ActiveRecord)
Tal vez tengas un caso de uso para cambiar datos después de que se inserte.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function afterInsert(self $self) {
// tú decides
Flight::cache()->set('most_recent_insert_id', $self->id);
// o lo que sea....
}
}
beforeUpdate(ActiveRecord $ActiveRecord)
Realmente útil si necesitas establecer algunos valores predeterminados cada vez en una actualización.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeInsert(self $self) {
// establecer algunos valores predeterminados
if(!$self->updated_date) {
$self->updated_date = gmdate('Y-m-d');
}
}
}
afterUpdate(ActiveRecord $ActiveRecord)
Tal vez tengas un caso de uso para cambiar datos después de que se actualice.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function afterInsert(self $self) {
// tú decides
Flight::cache()->set('most_recently_updated_user_id', $self->id);
// o lo que sea....
}
}
beforeSave(ActiveRecord $ActiveRecord)/afterSave(ActiveRecord $ActiveRecord)
Esto es útil si deseas que se produzcan eventos tanto cuando se inserten como cuando se actualicen. Te ahorraré la larga explicación, pero estoy seguro de que puedes adivinar qué es.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeSave(self $self) {
$self->last_updated = gmdate('Y-m-d H:i:s');
}
}
beforeDelete(ActiveRecord $ActiveRecord)/afterDelete(ActiveRecord $ActiveRecord)
¡No estoy seguro de lo que querrías hacer aquí, pero no hago juicios! ¡Adelante!
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeDelete(self $self) {
echo 'Él fue un valiente soldado... :cry-face:';
}
}
Gestión de Conexiones a la Base de Datos
Al utilizar esta biblioteca, puedes establecer la conexión a la base de datos de varias maneras. Puedes establecer la conexión en el constructor, puedes configurarla a través de una variable de configuración $config['connection']
o puedes establecerla a través de setDatabaseConnection()
(v0.4.1).
$pdo_connection = new PDO('sqlite:test.db'); // por ejemplo
$user = new User($pdo_connection);
// o
$user = new User(null, [ 'connection' => $pdo_connection ]);
// o
$user = new User();
$user->setDatabaseConnection($pdo_connection);
Si deseas evitar establecer siempre una $database_connection
cada vez que llamas a un registro activo, ¡hay formas de hacerlo!
// index.php o bootstrap.php
// Establecer esto como una clase registrada en Flight
Flight::register('db', 'PDO', [ 'sqlite:test.db' ]);
// User.php
class User extends flight\ActiveRecord {
public function __construct(array $config = [])
{
$database_connection = $config['connection'] ?? Flight::db();
parent::__construct($database_connection, 'users', $config);
}
}
// ¡Y ahora, no se requieren argumentos!
$user = new User();
Nota: Si planeas hacer pruebas unitarias, hacerlo de esta manera puede agregar algunos desafíos a las pruebas unitarias, pero en general debido a que puedes inyectar tu conexión con
setDatabaseConnection()
o$config['connection']
, no es tan malo.
Si necesitas refrescar la conexión a la base de datos, por ejemplo, si estás ejecutando un script CLI de larga duración y necesitas refrescar la conexión de vez en cuando, puedes restablecer la conexión con $your_record->setDatabaseConnection($pdo_connection)
.
Contribuyendo
Por favor, hazlo. :D
Configuración
Cuando contribuyas, asegúrate de ejecutar composer test-coverage
para mantener un 100% de cobertura de pruebas (esto no es una cobertura de pruebas unitarias verdadera, más bien pruebas de integración).
Además, asegúrate de ejecutar composer beautify
y composer phpcs
para corregir cualquier error de sintaxis.
Licencia
MIT
Awesome-plugins/latte
Latte
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.
Instalación
Instalar con composer.
composer require latte/latte
Configuración Básica
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')));
});
Ejemplo de Diseño Simple
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!
Awesome-plugins/awesome_plugins
Plugins Asombrosos
Flight es increíblemente extensible. Hay una serie de plugins que se pueden usar para agregar 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.
Documentación de la API
La documentación de la API es crucial para cualquier API. Ayuda a los desarrolladores a entender cómo interactuar con tu API y qué esperar a cambio. Hay un par de herramientas disponibles para ayudarte a generar documentación de la API para tus proyectos Flight.
- FlightPHP OpenAPI Generator - Publicación de blog escrita por Daniel Schreiber sobre cómo usar la especificación OpenAPI con FlightPHP para construir tu API utilizando un enfoque primero de API.
- SwaggerUI - Swagger UI es una gran herramienta para ayudarte a generar documentación de API para tus proyectos Flight. Es muy fácil de usar y se puede personalizar para adaptarse a tus necesidades. Esta es la biblioteca PHP para ayudarte a generar la documentación Swagger.
Autenticación/Autorización
La autenticación y la autorización son cruciales para cualquier aplicación que requiera controles sobre quién puede acceder a qué.
- oficial flightphp/permissions - Biblioteca de permisos de Flight oficial. Esta biblioteca es una forma simple de agregar permisos a nivel de usuario y a nivel de aplicación a tu aplicación.
Caching
El caching es una excelente manera de acelerar tu aplicación. Hay varias bibliotecas de caching que se pueden utilizar con Flight.
- oficial flightphp/cache - Clase de caching en archivo PHP ligera, simple y autónoma.
CLI
Las aplicaciones CLI son una excelente manera de interactuar con tu aplicación. Puedes usarlas para generar controladores, mostrar todas las rutas y más.
- oficial flightphp/runway - Runway es una aplicación CLI que te ayuda a gestionar tus aplicaciones Flight.
Cookies
Las cookies son una excelente manera de almacenar pequeñas cantidades de datos en el lado del cliente. Pueden usarse para almacenar preferencias del usuario, configuraciones de la aplicación y más.
- overclokk/cookie - PHP Cookie es una biblioteca PHP que proporciona una forma simple y efectiva de gestionar cookies.
Depuración
La depuración es crucial cuando estás desarrollando en tu entorno local. Hay algunos plugins que pueden elevar tu experiencia de depuración.
- tracy/tracy - Este es un manejador de errores completo que se puede usar con Flight. Tiene varios paneles que pueden ayudarte a depurar tu aplicación. También es muy fácil de extender y agregar tus propios paneles.
- flightphp/tracy-extensions - Usado con el manejador de errores Tracy, este plugin agrega algunos paneles adicionales para ayudar con la depuración específicamente para proyectos Flight.
Bases de Datos
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 envolturas para escribir consultas y algunas son ORM completamente desarrolladas.
- oficial flightphp/core PdoWrapper - Envoltura PDO oficial de Flight que es parte del núcleo. Esta es una envoltura simple para ayudar a simplificar el proceso de escritura de consultas y su ejecución. No es un ORM.
- oficial flightphp/active-record - ORM/Mapper ActiveRecord oficial de Flight. Gran pequeña biblioteca para recuperar y almacenar datos fácilmente en tu base de datos.
- byjg/php-migration - Plugin para realizar un seguimiento de todos los cambios en la base de datos para tu proyecto.
Cifrado
El cifrado es crucial para cualquier aplicación que almacene datos sensibles. Cifrar y descifrar los datos no es terriblemente difícil, pero almacenar adecuadamente la clave de cifrado puede ser difícil. Lo más importante es nunca almacenar tu clave de cifrado en un directorio público o comprometerla en tu repositorio de código.
- defuse/php-encryption - Esta es una biblioteca que se puede usar para cifrar y descifrar datos. Comenzar a usarla es bastante simple para comenzar a cifrar y descifrar datos.
Cola de Trabajo
Las colas de trabajo son realmente útiles para procesar tareas de manera asíncrona. Esto puede ser enviar correos electrónicos, procesar imágenes o cualquier cosa que no necesite hacerse en tiempo real.
- n0nag0n/simple-job-queue - Simple Job Queue es una biblioteca que se puede usar para procesar trabajos de manera asíncrona. Se puede usar con beanstalkd, MySQL/MariaDB, SQLite y PostgreSQL.
Sesión
Las sesiones realmente no son útiles para APIs, pero para construir una aplicación web, las sesiones pueden ser cruciales para mantener el estado y la información de inicio de sesión.
- oficial flightphp/session - Biblioteca de sesión oficial de Flight. Esta es una biblioteca de sesión simple que se puede usar para almacenar y recuperar datos de sesión. Utiliza el manejo de sesiones incorporado de PHP.
- Ghostff/Session - Administrador de sesiones PHP (sin bloqueo, flash, segmento, cifrado de sesión). Utiliza PHP open_ssl para cifrado/descifrado opcional de datos de sesión.
Plantillas
Las plantillas son fundamentales para cualquier aplicación web con una UI. Hay una serie de motores de plantillas que se pueden utilizar con Flight.
- deprecated flightphp/core View - Este es un motor de plantillas muy básico que es parte del núcleo. No se recomienda su uso si tienes más de un par de páginas en tu proyecto.
- latte/latte - 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.
Contribuyendo
¿Tienes un plugin que te gustaría compartir? ¡Envía una solicitud de extracción para agregarlo a la lista!
Media
Media
Hemos intentado rastrear lo que podemos de los diversos tipos de medios en Internet alrededor de Flight. Consulta a continuación diferentes recursos que puedes utilizar para aprender más sobre Flight.
Artículos y Escrituras
- Define, Generate, and Implement: An API-First Approach with OpenAPI Generator and FlightPHP por Daniel Schreiber (2025)
- Best PHP Micro Frameworks for 2024 por n0nag0n (2024)
- Creating a RESTful API with Flight Framework por n0nag0n (2024)
- Building a Simple Blog with Flight Part 2 por n0nag0n (2024)
- Building a Simple Blog with Flight Part 1 por n0nag0n (2024)
- 🚀 Build a Simple CRUD API in PHP with the Flight Framework por soheil-khaledabadi (2024)
- Building a PHP Web Application with the Flight Micro-framework por Arthur C. Codex (2023)
- Best PHP Frameworks for Web Development in 2024 por Ravikiran A S (2023)
- Top 12 PHP Frameworks: A Comprehensive Guide for 2023 por marketing kbk (2023)
- 5 PHP Frameworks You've (Probably) Never Heard of por n0nag0n (2022)
- 12 top PHP frameworks for web developers to consider in 2023 por Anna Monus (2022)
- The Best PHP Microframeworks on a Cloud Server por Shahzeb Ahmed (2021)
- PHP framework: Top 15 powerful ones for your web development por AHT Tech (2020)
- Easy PHP Routing with FlightPHP por Lucas Conceição (2019)
- Trying Out New PHP Framework (Flight) por Leon (2017)
- Setting up FlightPHP to work with Backbonejs por Timothy Tocci (2015)
Videos y Tutoriales
- Create a REST API for IoT Devices Using PHP & FlightPHP - ESP32 API por IoT Craft Hub (2024)
- PHP Flight Framework Simple Introductory Video por n0nag0n (2024)
- Set header HTTP code in Flightphp (3 Solutions!!) por Roel Van de Paar (2024)
- PHP Flight Framework Tutorial. Super easy API Project! por n0nag0n (2022)
- Aplicación web CRUD con php y mysql y bootstrap usando flight por Devlopteca - Oscar Uh (2021)
- DevOps & SysAdmins: Lighttpd rewrite rule for Flight PHP microframework por Roel Van de Paar (2021)
- Tutorial REST API Flight PHP #PART2 INSERT TABLE Info #Code (Tagalog) por Info Singkat Official (2020)
- Tutorial REST API Flight PHP #PART1 Info #Code (Tagalog) por Info Singkat Official (2020)
- How To Create JSON REST API IN PHP - Part 2 por Codewife (2018)
- How To Create JSON REST API IN PHP - Part 1 por Codewife (2018)
- Teste Micro Frameworks PHP - Flight PHP, Lumen, Slim 3 e Laravel por Codemarket (2016)
- Tutorial 1 Flight PHP - Instalación por absagg (2014)
- Tutorial 2 Flight PHP - Route parte 1 por absagg (2014)
Examples
¿Necesitas un comienzo rápido?
Tienes dos opciones para comenzar con un nuevo proyecto de Flight:
- Plantilla Completa Skeleton: Un ejemplo más completo con controladores y vistas.
- Plantilla Skeleton en un Solo Archivo: Un solo archivo que incluye todo lo que necesitas para ejecutar tu aplicación en un solo archivo sencillo.
Ejemplos contribuidos por la comunidad:
- flightravel: FlightPHP con directorios de Laravel, con herramientas PHP + GH Actions
- fleact - Un kit de inicio de FlightPHP con integración de ReactJS.
- flastro - Un kit de inicio de FlightPHP con integración de Astro.
- velt - Velt es una plantilla de inicio rápida y fácil de Svelte con un backend de FlightPHP.
¿Necesitas algo de inspiración?
Aunque estos no están oficialmente patrocinados por el equipo de Flight, ¡podrían darte ideas sobre cómo estructurar tus propios proyectos construidos con Flight!
- Decay - Flight v3 con HTMX y SleekDB ¡todo sobre zombis! (Demo)
- Blog de Ejemplo de Flight - Flight v3 con Middleware, Controladores, Active Record y Latte.
- API RESTful CRUD de Flight - Proyecto simple de API CRUD utilizando el marco Flight, que proporciona una estructura básica para que los nuevos usuarios configuren rápidamente una aplicación PHP con operaciones CRUD y conectividad a la base de datos. El proyecto demuestra cómo usar Flight para el desarrollo de API RESTful, convirtiéndolo en una herramienta de aprendizaje ideal para principiantes y un útil kit de inicio para desarrolladores más experimentados.
- Sistema de Gestión Escolar de Flight - Flight v3
- Paste Bin con Comentarios - Flight v3
- Aplicación Básica Skeleton
- Ejemplo de Wiki
- La Aplicación del Marco PHP IT-Innovator
- LittleEducationalCMS (Español)
- API de Páginas Amarillas Italianas
- Sistema de Gestión de Contenidos Genérico (con....muy poca documentación)
- Un pequeño marco PHP basado en Flight y medoo.
- Ejemplo de Aplicación MVC
¿Quieres compartir tu propio ejemplo?
Si tienes un proyecto que deseas compartir, ¡envía una solicitud de extracción para agregarlo a esta lista!
Install/install
Instalación
Descargar los archivos
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.
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.
Configurar tu Servidor Web
Servidor de Desarrollo PHP Integrado
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
.
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
:
php -S localhost:8000 -t public/
Luego abre tu navegador e ingresa a http://localhost:8000
.
Apache
Asegúrate de que Apache ya esté instalado en tu sistema. Si no lo está, busca cómo instalar Apache en tu sistema.
Para Apache, edita tu archivo .htaccess
con lo siguiente:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
Nota: Si necesitas usar flight en un subdirectorio, agrega la línea
RewriteBase /subdir/
justo después deRewriteEngine On
.Nota: Si deseas proteger todos los archivos del servidor, como un archivo db o env. Coloca lo siguiente en tu archivo
.htaccess
:
RewriteEngine On
RewriteRule ^(.*)$ index.php
Nginx
Asegúrate de que Nginx ya esté instalado en tu sistema. Si no lo está, busca cómo instalar Nginx en tu sistema.
Para Nginx, agrega lo siguiente a la declaración de tu servidor:
server {
location / {
try_files $uri $uri/ /index.php;
}
}
Crea tu archivo index.php
<?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();
Instalación de PHP
Si ya tienes php
instalado en tu sistema, puedes saltarte estas instrucciones y pasar a la sección de descarga
¡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.
macOS
Instalar PHP usando Homebrew
-
Instalar Homebrew (si aún no está instalado):
- Abre Terminal y ejecuta:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
- Abre Terminal y ejecuta:
-
Instalar PHP:
- Instalar la última versión:
brew install php
- Para instalar una versión específica, por ejemplo, PHP 8.1:
brew tap shivammathur/php brew install shivammathur/php/php@8.1
- Instalar la última versión:
-
Cambiar entre versiones de PHP:
- Desvincula la versión actual y enlaza la versión deseada:
brew unlink php brew link --overwrite --force php@8.1
- Verifica la versión instalada:
php -v
- Desvincula la versión actual y enlaza la versión deseada:
Windows 10/11
Instalar PHP manualmente
-
Descargar PHP:
- Visita PHP for Windows y descarga la última versión o una específica (por ejemplo, 7.4, 8.0) como un archivo zip no seguro para subprocesos.
-
Extraer PHP:
- Extrae el archivo zip descargado en
C:\php
.
- Extrae el archivo zip descargado en
-
Agregar PHP al PATH del sistema:
- Ve a Propiedades del Sistema > Variables de Entorno.
- En Variables del sistema, encuentra Path y haz clic en Editar.
- Agrega la ruta
C:\php
(o donde hayas extraído PHP). - Haz clic en Aceptar para cerrar todas las ventanas.
-
Configurar PHP:
- Copia
php.ini-development
aphp.ini
. - Edita
php.ini
para configurar PHP según sea necesario (por ejemplo, configurarextension_dir
, habilitar extensiones).
- Copia
-
Verificar la instalación de PHP:
- Abre el Símbolo del sistema y ejecuta:
php -v
- Abre el Símbolo del sistema y ejecuta:
Instalar Múltiples Versiones de PHP
-
Repite los pasos anteriores para cada versión, colocando cada una en un directorio separado (por ejemplo,
C:\php7
,C:\php8
). -
Cambiar entre versiones ajustando la variable PATH del sistema para que apunte al directorio de la versión deseada.
Ubuntu (20.04, 22.04, etc.)
Instalar PHP usando apt
-
Actualizar listas de paquetes:
- Abre Terminal y ejecuta:
sudo apt update
- Abre Terminal y ejecuta:
-
Instalar PHP:
- Instalar la última versión de PHP:
sudo apt install php
- Para instalar una versión específica, por ejemplo, PHP 8.1:
sudo apt install php8.1
- Instalar la última versión de PHP:
-
Instalar módulos adicionales (opcional):
- Por ejemplo, para instalar soporte para MySQL:
sudo apt install php8.1-mysql
- Por ejemplo, para instalar soporte para MySQL:
-
Cambiar entre versiones de PHP:
- Usa
update-alternatives
:sudo update-alternatives --set php /usr/bin/php8.1
- Usa
-
Verificar la versión instalada:
- Ejecuta:
php -v
- Ejecuta:
Rocky Linux
Instalar PHP usando yum/dnf
-
Habilitar el repositorio EPEL:
- Abre Terminal y ejecuta:
sudo dnf install epel-release
- Abre Terminal y ejecuta:
-
Instalar el repositorio de Remi:
- Ejecuta:
sudo dnf install https://rpms.remirepo.net/enterprise/remi-release-8.rpm sudo dnf module reset php
- Ejecuta:
-
Instalar PHP:
- Para instalar la versión predeterminada:
sudo dnf install php
- Para instalar una versión específica, por ejemplo, PHP 7.4:
sudo dnf module install php:remi-7.4
- Para instalar la versión predeterminada:
-
Cambiar entre versiones de PHP:
- Usa el comando de módulo
dnf
:sudo dnf module reset php sudo dnf module enable php:remi-8.0 sudo dnf install php
- Usa el comando de módulo
-
Verificar la versión instalada:
- Ejecuta:
php -v
- Ejecuta:
Notas Generales
- Para entornos de desarrollo, es importante configurar los ajustes de PHP según los requerimientos de tu proyecto.
- Al cambiar entre versiones de PHP, asegúrate de que todas las extensiones relevantes de PHP estén instaladas para la versión específica que deseas utilizar.
- Reinicia tu servidor web (Apache, Nginx, etc.) después de cambiar de versión de PHP o actualizar configuraciones para aplicar los cambios.
Guides
Guías
Flight PHP está diseñado para ser simple pero poderoso, y nuestras guías te ayudarán a construir aplicaciones del mundo real paso a paso. Estos tutoriales prácticos te guían a través de proyectos completos para demostrar cómo Flight se puede utilizar de manera efectiva.
Guías Oficiales
Construyendo un Blog
Aprende cómo crear una aplicación de blog funcional con Flight PHP. Esta guía te guía a través de:
- Configurar una estructura de proyecto
- Trabajar con plantillas utilizando Latte
- Implementar rutas para publicaciones
- Almacenar y recuperar datos
- Manejar envíos de formularios
- Manejo básico de errores
Este tutorial es perfecto para principiantes que quieren ver cómo todas las piezas encajan en una aplicación real.
Guías No Oficiales
Aunque estas guías no están oficialmente mantenidas por el equipo de Flight, son recursos valiosos creados por la comunidad. Cubren varios temas y casos de uso, proporcionando información adicional sobre el uso de Flight PHP.
Creando una API RESTful con Flight Framework
Esta guía te guía a través de la creación de una API RESTful utilizando el marco de Flight PHP. Cubre los conceptos básicos para configurar una API, definir rutas y devolver respuestas JSON.
Construyendo un Blog Simple
Esta guía te guía a través de la creación de un blog básico utilizando el marco de Flight PHP. De hecho, tiene 2 partes: una para cubrir los conceptos básicos y la otra para cubrir temas más avanzados y refinamientos para un blog listo para producción.
- Construyendo un Blog Simple con Flight - Parte 1 - Empezando con un blog simple.
- Construyendo un Blog Simple con Flight - Parte 2 - Refinando el blog para producción.
Construyendo una API de Pokémon en PHP: Guía para Principiantes
Esta divertida guía te guía a través de la creación de una API simple de Pokémon utilizando Flight PHP. Cubre los conceptos básicos de configuración de una API, definición de rutas y devolución de respuestas JSON.
Contribuyendo
¿Tienes una idea para una guía? ¿Encontraste un error? ¡Aceptamos contribuciones! Nuestras guías se mantienen en el repositorio de documentación de FlightPHP.
Si has construido algo interesante con Flight y quieres compartirlo como una guía, envía una solicitud de extracción. Compartir tu conocimiento ayuda a la comunidad de Flight a crecer.
¿Buscando Documentación de API?
Si estás buscando información específica sobre las características y métodos principales de Flight, consulta la sección Aprender de nuestra documentación.