Guía de migración: 9.8.5 → 9.9.0
La versión 9.9.0 sustituye el runtime de Filters por un sistema de Middlewares por fases e introduce el primer motor de migraciones versionadas de aplicaciones de Osumi Framework.
Es una versión con cambios incompatibles para aplicaciones que utilicen Filters.
El framework incluye una migración automática que transforma los usos compatibles de Filters al nuevo sistema de Middlewares conservando la lógica de negocio original de los Filters.
Resumen
Añadido
- Pipeline nativo de Middlewares con tres fases:
beforeafterRenderafterResponse
- Middlewares globales, de grupo y de ruta.
src/Middleware/Middlewares.phppara la configuración global.ORequest::getMiddleware().ORequest::getMiddlewareValue().- Orígenes Middleware en
ODTOField:middlewaremiddlewareProperty
- Respuestas de error tipadas mediante
stop. - Motor de migraciones del framework.
vendor/bin/ofw-migrate.- Copias transaccionales de migración.
- Estado de migración en
ofw/tmp/state.json. - Integración con Composer mediante
osumionline/plugin-updater.
Modificado
- Los Filters de ruta se sustituyen por definiciones de Middlewares agrupadas por fase.
- La salida de los Filters antiguos se expone como contexto de Middleware después de la migración automática.
ODTOField(filter: ..., filterProperty: ...)pasa amiddleware/middlewareProperty.$req->getFilter('Name')pasa a$req->getMiddleware('Name').- La autenticación, autorización e interceptación de peticiones deben implementarse mediante Middlewares nativos.
Eliminado del runtime 9.9
- El pipeline de Filters.
- La creación de nuevos Filters mediante CLI.
- Las APIs de
ORequestorientadas a Filters.
Las clases Filter antiguas pueden permanecer temporalmente en proyectos migrados porque la migración automática genera adaptadores Middleware de compatibilidad sobre ellas.
1. Requisitos antes de actualizar
Osumi Framework 9.9 requiere:
- PHP 8.5 o superior.
- Composer 2.x.
osumionline/plugin-updater.- Permiso de Composer para ejecutar el plugin updater.
Antes de actualizar un proyecto existente, añade esto al composer.json raíz si aún no existe:
{
"config": {
"allow-plugins": {
"osumionline/plugin-updater": true
}
}
}
Composer también puede preguntar de forma interactiva si se confía en el plugin cuando se instala por primera vez.
2. Procedimiento de actualización recomendado
Antes de actualizar:
- Haz commit o stash de los cambios de la aplicación.
- Comprueba que el proyecto está en un estado funcional conocido.
- Haz copia de los datos persistentes si se trata de producción.
- Actualiza el framework mediante Composer.
Ejemplo:
composer update osumionline/framework --with-dependencies
Cuando Composer haya regenerado el autoloader, osumionline/plugin-updater comprueba la versión instalada del framework y ejecuta todas las migraciones pendientes.
Para la actualización 9.8.5 → 9.9.0 se ejecuta el paso de migración 9.9.0.
3. Migración automática mediante Composer
La integración sigue este flujo:
Composer update
↓
osumionline/plugin-updater
↓
post-autoload-dump
↓
Runner::runPending()
↓
preflight de la migración 9.9.0
↓
cambios transaccionales del proyecto
↓
actualización de state.json
El estado de migración es la fuente autoritativa. Esto permite ejecutar correctamente las migraciones aunque:
- se salten varias versiones del framework;
- el plugin updater se instale durante la misma operación de Composer;
- Composer emita más de un evento de autoload.
Durante una migración lanzada por Composer, la comprobación de Git ignora únicamente:
composer.json
composer.lock
Los cambios en el código de la aplicación u otros archivos del proyecto siguen provocando que la migración se detenga, salvo que se habilite explícitamente la ejecución forzada.
4. Previsualizar migraciones lanzadas por Composer
El updater admite variables de entorno.
Dry-run
OFW_DRY_RUN=1 composer update osumionline/framework --with-dependencies
Esto ejecuta en dry-run la migración del framework.
Importante: Composer sigue realizando su operación de paquetes. OFW_DRY_RUN solo evita que la migración de Osumi Framework persista cambios de migración en el proyecto.
Salida detallada
OFW_VERBOSE=1 composer update osumionline/framework --with-dependencies
Forzar comprobaciones de seguridad
OFW_FORCE=1 composer update osumionline/framework --with-dependencies
Úsalo con precaución. Omite las comprobaciones de seguridad de migración que admiten ejecución forzada.
Valores booleanos aceptados:
1
true
yes
y
on
y:
0
false
no
n
off
5. CLI manual de migraciones
El paquete del framework expone:
php vendor/bin/ofw-migrate --help
Ejecución normal de migraciones pendientes:
php vendor/bin/ofw-migrate
Comando recomendado para inspección:
php vendor/bin/ofw-migrate --dry-run --verbose
Opciones disponibles:
--from=VERSION
--to=VERSION
--dry-run
--force
--verbose
--no-interaction
--help
--from
Sustituye la versión de origen obtenida del estado de migración.
Ejemplo:
php vendor/bin/ofw-migrate --from=9.8.5 --to=9.9.0
Úsalo solo cuando necesites intencionadamente un rango explícito.
--to
Sobrescribe la versión objetivo. Si no se indica, se detecta automáticamente la versión instalada.
--dry-run
Muestra las operaciones previstas sin modificar archivos del proyecto.
--force
Omite las comprobaciones de seguridad que admiten ejecución forzada, incluida la comprobación de árbol Git limpio.
--verbose
Muestra detalles de las migraciones y operaciones de archivos.
--no-interaction
Deshabilita el comportamiento interactivo.
6. Protección del árbol Git
En una migración manual normal, un proyecto gestionado por Git debe tener el árbol de trabajo limpio.
Si existen cambios inesperados, la migración se detiene en lugar de modificar el proyecto.
Los proyectos sin .git también son válidos; siguen protegidos mediante las copias transaccionales de migración.
El modo dry-run no requiere un árbol Git limpio porque no persiste cambios del proyecto.
En la migración automática de Composer solo se excluyen de esta comprobación composer.json y composer.lock.
Es preferible hacer commit o stash antes que utilizar --force.
7. Qué hace la migración 9.9.0
La migración realiza un preflight completo antes de escribir archivos de aplicación.
Busca Filters antiguos bajo:
src/Filter/
Los Filters compatibles deben seguir las convenciones normales de namespace PSR-4 y nombres de archivo de Osumi Framework.
Los archivos Filter originales no se eliminan ni se reescriben.
Para cada Filter compatible se genera un Middleware de compatibilidad en:
src/Middleware/
Ejemplo:
src/Filter/LoginFilter.php
↓ se conserva
src/Middleware/LoginMiddleware.php
↓ adaptador de compatibilidad generado
Los adaptadores generados ejecutan el Filter original únicamente durante:
OMiddleware::PHASE_BEFORE
8. Conversión del resultado de un Filter antiguo
El adaptador transforma el resultado del Filter al contrato de Middleware 9.9.
Filter correcto
Resultado antiguo:
[
'status' => 'ok',
'id' => 42
]
se convierte en contexto:
[
'context' => [
'status' => 'ok',
'id' => 42
]
]
Puede leerse mediante:
$login = $req->getMiddleware(
'Login'
);
o:
$id = $req->getMiddlewareValue(
'Login',
'id'
);
Filter fallido
Un Filter cuyo resultado no sea correcto se convierte en stop con HTTP 403.
Redirección antigua
Si el Filter devuelve una URL válida en return, el adaptador produce:
- HTTP 302
- cabecera
Location stop
De esta forma se conserva el comportamiento legacy durante la migración.
9. Transformaciones automáticas de código
La migración transforma el código compatible fuera de src/Filter/ y src/Middleware/.
ORequest
Antes:
$login = $req->getFilter(
'Login'
);
Después:
$login = $req->getMiddleware(
'Login'
);
DTOs
Antes:
#[ODTOField(
filter: 'Login',
filterProperty: 'id'
)]
public ?int $idUser = null;
Después:
#[ODTOField(
middleware: 'Login',
middlewareProperty: 'id'
)]
public ?int $idUser = null;
Rutas
Antes:
ORoute::get(
'/profile',
ProfileComponent::class,
[
LoginFilter::class
]
);
Después:
ORoute::get(
'/profile',
ProfileComponent::class,
[
'before' => [
\Osumi\OsumiFramework\App\Middleware\LoginMiddleware::class
]
]
);
Los arrays literales de Filters en llamadas ORoute compatibles se convierten en listas de Middlewares before.
10. Configuración global de Middlewares
Si no existe:
src/Middleware/Middlewares.php
la migración crea:
<?php
declare(strict_types=1);
namespace Osumi\OsumiFramework\App\Middleware;
use Osumi\OsumiFramework\Core\OMiddleware;
OMiddleware::setGlobal([
OMiddleware::PHASE_BEFORE => [],
OMiddleware::PHASE_AFTER_RENDER => [],
OMiddleware::PHASE_AFTER_RESPONSE => []
]);
Si ya existe, se conserva exactamente.
11. Casos que no se migran automáticamente
La migración se detiene deliberadamente cuando no puede transformar el código con seguridad.
Expresiones dinámicas de Filters en rutas
No compatible:
$filters = [
LoginFilter::class
];
ORoute::get(
'/profile',
ProfileComponent::class,
$filters
);
Para la migración automática, las expresiones de Filters de ruta deben ser arrays literales de clases.
Métodos legacy de ORequest
Requieren cambios manuales:
$req->getFilters()
$req->setFilter(...)
$req->setFilters(...)
La migración se detiene si encuentra estas llamadas.
Referencias de Filter no resolubles
Una ruta que haga referencia a un Filter que no pueda asociarse a una definición migrable se rechaza.
Conflictos con Middlewares existentes
Si el destino del Middleware generado ya existe con contenido diferente, la migración se detiene en lugar de sobrescribirlo.
Estructuras de archivos inseguras
Se rechazan recorridos mediante enlaces simbólicos y rutas inseguras.
La migración es deliberadamente conservadora: el código ambiguo debe migrarse manualmente en lugar de intentar adivinar su intención.
12. Seguridad transaccional y rollback
Los cambios se escriben mediante un sistema transaccional.
Las copias se almacenan bajo:
ofw/tmp/migrations/
Cada transacción tiene su propio directorio con timestamp/identificador aleatorio y un manifest de archivos modificados.
El estado de migración se escribe dentro de la misma transacción que los cambios de aplicación.
Si una operación falla:
- se restauran los archivos modificados;
- se eliminan los archivos creados por la migración fallida;
- el estado se revierte junto con el proyecto;
- la migración finaliza con error.
Las copias de una transacción completada correctamente se conservan para inspección.
13. Estado de migración
El estado se guarda en:
ofw/tmp/state.json
Después de completar 9.9.0:
{
"last_migrated": "9.9.0"
}
Si no existe estado, el runner considera 0.0.0 como versión inicial de migración y selecciona todos los pasos pendientes aplicables hasta la versión instalada.
Esto permite migrar proyectos creados antes de que existiera el motor de migraciones.
Una ejecución normal posterior no vuelve a aplicar 9.9.0.
14. Idempotencia
El sistema está diseñado para ser idempotente.
Por ejemplo:
- El estado evita repetir pasos ya completados en una ejecución normal.
- Un adaptador generado previamente se acepta si su contenido coincide exactamente.
- Un
Middlewares.phpexistente se conserva. - Una segunda ejecución normal no encuentra la migración 9.9.0 pendiente.
No utilices --from para volver a seleccionar deliberadamente una migración ya completada salvo que conozcas las consecuencias.
15. Verificación posterior
Después de migrar:
- Revisa los archivos generados en
src/Middleware/. - Revisa las rutas transformadas.
- Revisa los DTOs que usan
middleware/middlewareProperty. - Busca usos legacy del runtime.
- Ejecuta la batería completa de tests.
- Realiza pruebas funcionales de autenticación/autorización.
- Haz commit del proyecto migrado.
Búsquedas útiles:
getFilter(
getFilters(
setFilter(
setFilters(
filterProperty
Es normal que las clases legacy de src/Filter/ sigan existiendo mientras se usen los adaptadores de compatibilidad.
16. Convertir adaptadores generados en Middlewares nativos
Los adaptadores generados son un puente de compatibilidad, no la arquitectura final recomendada.
Después de comprobar el proyecto migrado puedes trasladar progresivamente la lógica del Filter al propio Middleware.
Un Middleware nativo utiliza:
public static function handle(
string $phase,
array $data
): array
y devuelve resultados explícitos como:
return [
'context' => [
'id' => 42
]
];
o:
return [
'stop' => true,
'status_code' => 401,
'message' => 'Unauthorized'
];
Proceso recomendado:
Filter legacy
↓
Middleware de compatibilidad generado
↓
verificación funcional
↓
mover lógica al Middleware nativo
↓
eliminar el Filter sin uso
Mantén, cuando sea posible, el nombre de la clase Middleware generada para no tener que cambiar las rutas ya migradas ni los nombres públicos del contexto.
Cuando todos los adaptadores se hayan convertido, src/Filter/ puede eliminarse si ya no contiene lógica legacy necesaria.
17. Nuevas capacidades de Middleware
Los Middlewares nativos no están limitados al comportamiento de los antiguos Filters.
Pueden ejecutarse en:
before
afterRender
afterResponse
Pueden publicar o modificar:
- contexto
- cuerpo
- cabeceras
- estado HTTP
- respuestas de error mediante
stop
Los Middlewares globales, de grupos anidados y de ruta se acumulan en cada fase en este orden:
global
↓
grupo exterior
↓
grupo interior
↓
ruta
Consulta:
docs/es/concepts/middlewares.md
para la API completa.
18. Acción requerida por el desarrollador
Para aplicaciones que actualicen desde 9.8.5:
- Asegura PHP 8.5+.
- Autoriza
osumionline/plugin-updateren Composer. - Haz commit o stash de cambios locales.
- Actualiza Osumi Framework con Composer.
- Revisa la salida de la migración automática.
- Resuelve manualmente cualquier construcción legacy no soportada que indique el migrador.
- Ejecuta los tests completos y pruebas funcionales.
- Haz commit del código migrado.
- Opcionalmente sustituye los adaptadores de compatibilidad por Middlewares nativos.
En proyectos que no utilicen Filters, la migración 9.9 también crea src/Middleware/Middlewares.php cuando sea necesario y registra el estado de migración.
19. Compatibilidad con versiones anteriores
9.9.0 elimina intencionadamente la arquitectura runtime de Filters.
Los adaptadores automáticos ofrecen una vía de actualización para proyectos legacy compatibles, pero el código nuevo de 9.9 debe utilizar Middlewares.
No añadas nuevos Filters después de migrar.