En GLPI 11, "el plugin se rompió" casi nunca es un bug del plugin: es la base la que cambió debajo de él. En el mantenimiento de entornos de clientes el guion se repite - el cliente levanta el core nuevo, dos o tres plugins desaparecen del menú, uno tira la pantalla con error 500 y el php-errors.log queda irritantemente vacío. Esta guía separa lo que pasó a ser nativo de lo que de verdad dejó de funcionar, muestra el fragmento de código que reprueba un plugin en 11 y entrega el checklist que ejecutamos antes de agendar la ventana de migración.
Qué cambió en el core - y por qué eso tira los plugins
11 no es un tema nuevo sobre 10. Es un cambio de motor. Cada cambio de abajo invalida un patrón que los plugins de GLPI 10 usaban sin ceremonia:
- Frontend en Symfony + Twig. El antiguo flujo procedural (
Html::header()y luegoechode HTML) dio paso a controladores y plantillas Twig. Un plugin que armaba la página "a mano" pierde la cabecera, el menú, a veces la pantalla entera. - La aplicación se sirve desde
public/. El enrutado es de Symfony ahora. Los includes relativos del tipoinclude("../../../inc/includes.php")dependen del directorio de trabajo y dejan de resolver. - PHP 8.2 como mínimo, con firmas estrictas. Un método heredado necesita el return type compatible. Un
canCreate()sin: boollanza "Declaration must be compatible" y el plugin ni siquiera carga. - El driver de base de datos rechaza SQL crudo. Este es el que más duele.
$DB->query()fue eliminado y$DB->request()con string literal está bloqueado ("Building and executing raw queries is prohibited"). Cualquier consulta escondida se rompe la primera vez que ese camino se ejecuta. - Las señales del hook.php pasaron a forma canónica. Marcadores como
csrf_compliantse tratan de forma más estricta en el core; mal declarado, el plugin puede no reconocerse como CSRF-compliant y recibir un 403 en POST.
Es decir: "compatible con 11" no es suerte, es que el autor haya portado esos patrones. Quien no portó se rompe - y suele romperse en silencio.
Plugins que pasaron al core: migre el dato, no reinstale
Buena parte de los plugins más populares no desapareció por incompatibilidad - desapareció porque la función entró en el core. Aquí el trabajo no es "encontrar la versión para 11", es llevar el dato al recurso nativo:
- FormCreator - pasó a ser el módulo de Formularios nativo, un motor nuevo. No hay importador transparente; solo existe una herramienta de transición, y los formularios no se mudan solos.
- GenericObject - los objetos personalizados ahora son nativos. Reevalúe cada tipo y mapéelo a un activo o dropdown del core.
- FusionInventory y dashboards de plugin - ya estaban sustituidos desde 10 por el GLPI Agent y los dashboards nativos. En 11 no hay motivo para insistir.
- Webhooks de salida - el core ahora notifica a sistemas externos en eventos (apertura de ticket, cambio de estado), cubriendo parte de lo que se resolvía con plugin.
Matriz de compatibilidad: por qué cae cada caso
Antes de reservar la ventana, en mantenimiento clasificamos cada plugin instalado en una de estas filas. Lo que decide no es el nombre, es el motivo técnico:
| Plugin / caso | Motivo técnico | Acción antes de migrar |
|---|---|---|
| FormCreator | La función pasó a Formularios nativo (motor nuevo) | Inventariar y migrar los formularios que sostienen la mesa de servicio - no migran solos |
| GenericObject | Objetos personalizados ahora nativos | Mapear cada tipo a un activo o dropdown del core |
| FusionInventory | Sustituido por el GLPI Agent (desde 10) | Mover la recolección al GLPI Agent antes |
| Plugin con SQL crudo / salida procedural | Usa $DB->query() o Html::header() - eliminados/rotos | Exige release portada; sin ella, desactivar |
Plugin sin return type en can*() | PHP 8.2 rechaza la firma; el plugin no carga | Esperar la versión del autor o retirarlo |
| Fields, DataInjection, PDF, Tag | Tienen release para 11 | Actualizar tras el core, uno a uno, validando entre cada uno |
| NexTool | Compatible con 10 y 11 (mismo código portado) | Actualizar a la versión de 11 |
| Plugin sin release desde 2023 | Probablemente no portado | Decisión de negocio: sustituir o retirar |
Diagnóstico: mida, no decida de memoria
No clasifique de cabeza. Levante el estado real de los plugins y rastree su código buscando los patrones que el core 11 eliminó:
# Estado real de los plugins (ejecute en GLPI 10 antes de migrar; solo lectura)
# state: 0=nuevo 1=activo 2=no instalado 3=a configurar
# 4=no activado 5=a limpiar 6=no actualizado
mysql -u glpi -p glpi -e \
"SELECT name, directory, version, state FROM glpi_plugins ORDER BY state, name;"
# Rastree el codigo de los plugins buscando los patrones que el core 11 elimino
cd /var/www/glpi/plugins
grep -rln '$DB->query(' . --include='*.php' # driver de SQL crudo eliminado en 11
grep -rln 'Html::header' . --include='*.php' # salida procedural, pre-Twig
grep -rln 'includes.php' . --include='*.php' # include relativo fragil bajo el enrutado Symfony
El error común es confiar solo en el sello del marketplace. Una release marcada como "GLPI 11" todavía puede esconder SQL crudo en un camino poco usado - que solo estalla cuando se abre ese informe específico, semanas después del go-live.
El código que reprueba un plugin en 11
Si mantiene un plugin propio, o necesita evaluar uno de terceros con el fuente en mano, estos son los tres patrones que más aparecen cuando portamos código de 10 a 11:
// GLPI 10 (funcionaba): SQL crudo directo en el driver
$res = $DB->query("SELECT id FROM glpi_tickets WHERE status = 1");
// GLPI 11: $DB->query() fue ELIMINADO y request() con cadena cruda esta bloqueado.
// SELECT pasa al query builder por criterios:
$rows = $DB->request([
'SELECT' => 'id',
'FROM' => 'glpi_tickets',
'WHERE' => ['status' => 1],
]);
// DDL / SQL literal inevitable (ALTER, SHOW INDEX): use doQuery()
$DB->doQuery("ALTER TABLE glpi_plugin_x ADD COLUMN activo TINYINT DEFAULT 0");
// PHP 8.2+: el metodo heredado exige return type; sin ": bool" el plugin ni carga
public static function canCreate(): bool
{
return Session::haveRight('plugin_x', CREATE);
}
Fíjese en que el alias de columna también cambió: el viejo truco con comilla invertida inline que GLPI 10 toleraba ahora tiene que ser 'name AS hijo', porque el query builder de 11 escapa las comillas invertidas correctamente.
Cómo probar la compatibilidad antes de la ventana
Nunca descubra la incompatibilidad en producción. La rutina que aplicamos en homologación:
- Clon de producción. Levante 11 sobre una copia de la base real, no sobre una de ejemplo. Un plugin se rompe con sus datos, no con datos limpios.
- Instale y active por consola.
php bin/console glpi:plugin:install <directorio>yglpi:plugin:activate <directorio>, como el usuario del servidor web. Si el plugin corre una migración en install/init, es aquí donde se dispara. - Abra cada página
front/del plugin. No basta con activar sin error; el motor Twig solo se queja cuando la pantalla se renderiza de verdad. - Mire el log correcto. El fallo de carga no se vuelve fatal de PHP - se vuelve
glpi.ERROR: Error while loading plugin Xen el log de GLPI. Quien solo mira el php-errors.log concluye, mal, que "todo está bien". - Resetee el OPcache entre intentos. Tras cambiar archivos, php-fpm todavía sirve el bytecode viejo; recargue el proceso (por ejemplo,
kill -USR2en el master de php-fpm), no solo Apache.
Lo que nos enseñó el mantenimiento
El incidente más traicionero que vimos no apareció en la migración en sí - apareció meses después, en un simple bump de versión de un plugin administrativo. Corría su migración de schema dentro de plugin_init, un patrón común y aparentemente inofensivo: el bloque solo se ejecuta mientras el schema_version guardado sea menor que la versión de setup.php. Un bug latente allí durmió hasta el siguiente bump - y cuando por fin corrió y lanzó una excepción, GLPI 11 la capturó en Plugin::load y desactivó el plugin solo. El síntoma para el cliente fue ese "se activa y se desactiva sin error visible", con el plugin oscilando al estado 4 (no activado, que parece normal). Desde entonces la regla es dura: todo bump de un plugin con migración-en-init exige un smoke test de activación tras el bump, y el bloque de migración va siempre dentro de try/catch, con el schema_version escrito FUERA del try - si no, reintenta en cada request y entra en bucle. Es el tipo de detalle que solo quien opera GLPI de cliente todos los días lleva en la memoria muscular.
¿Debo migrar ahora?
Si su matriz de plugins está verde (release portada para todos los esenciales) y tiene homologación con clon de producción, sí - 11 es más moderno, seguro y rápido. Si depende de un plugin sin versión para 11, la decisión pasa a ser de negocio: sustituirlo por la función nativa, cambiar de plugin o posponer. Lo que no se puede es migrar a ciegas y descubrir la incompatibilidad con el entorno en el aire.
Si su equipo no tiene ventana para ensayar la migración y clasificar cada plugin con calma, NexTool conduce el upgrade de su GLPI con inventario de plugins, homologación sobre clon y rollback ensayado. Hable con nosotros sobre soporte y mantenimiento de GLPI.
Revisado por el equipo de NexTool Solutions.