GLPI 11: O Que Mudou e Quais Plugins Quebraram

Por que plugins quebram no GLPI 11: as mudanças de arquitetura (Symfony/Twig, public/, PHP 8.2, driver sem SQL cru), a matriz de compatibilidade e o checklist de homologação que rodamos antes de migrar.

No GLPI 11, "o plugin quebrou" quase nunca é bug do plugin: é a fundação que mudou embaixo dele. Na sustentação de ambientes de clientes o roteiro se repete - o cliente sobe o core novo, dois ou três plugins somem do menu, um derruba a tela com erro 500 e o php-errors.log fica irritantemente vazio. Este guia separa o que virou nativo do que de fato parou de funcionar, mostra o trecho de código que reprova um plugin no 11 e entrega o checklist que rodamos antes de agendar a janela de migração.

O que mudou no core - e por que isso derruba plugin

O 11 não é um tema novo por cima do 10. É troca de motor. Cada mudança abaixo invalida um padrão que os plugins de GLPI 10 usavam sem cerimônia:

  • Frontend em Symfony + Twig. O antigo fluxo procedural (Html::header() e depois echo de HTML) deu lugar a controllers e templates Twig. Plugin que montava a página "na unha" perde o cabeçalho, o menu, às vezes a tela inteira.
  • A aplicação é servida a partir de public/. O roteamento é do Symfony. Includes relativos do tipo include("../../../inc/includes.php") dependem do diretório de trabalho e deixam de resolver.
  • PHP 8.2 no mínimo, com assinaturas estritas. Um método herdado precisa do return type compatível. Um canCreate() sem : bool lança "Declaration must be compatible" e o plugin sequer carrega.
  • O driver de banco recusa SQL cru. É a que mais dói. O $DB->query() foi removido e o $DB->request() com string literal é bloqueado ("Building and executing raw queries is prohibited"). Qualquer consulta escondida quebra na primeira vez que aquele caminho executa.
  • Sinalizações do hook.php viraram forma canônica. Marcadores como csrf_compliant têm tratamento mais rígido no core; declarado errado, o plugin pode não ser reconhecido como CSRF-compliant e apanhar 403 em POST.

Ou seja: "compatível com o 11" não é sorte, é o autor ter portado esses padrões. Quem não portou quebra - e costuma quebrar em silêncio.

Plugins que viraram core: migre o dado, não reinstale

Boa parte dos plugins mais populares não sumiu por incompatibilidade - sumiu porque a função entrou no core. Aqui o trabalho não é "achar a versão para o 11", é levar o dado para o recurso nativo:

  • FormCreator - virou o módulo de Formulários nativo, um motor novo. Não há importador transparente; existe apenas uma ferramenta de transição, e os formulários não se mudam sozinhos.
  • GenericObject - objetos personalizados agora são nativos. Reavalie cada tipo e mapeie para ativo ou dropdown do core.
  • FusionInventory e dashboards de plugin - já eram substituídos desde o 10 pelo GLPI Agent e pelos dashboards nativos. No 11 não há motivo para insistir.
  • Webhooks de saída - o core passou a notificar sistemas externos em eventos (abertura de chamado, mudança de status), cobrindo parte do que se resolvia com plugin.

Matriz de compatibilidade: por que cada caso cai

Antes de marcar a janela, na sustentação classificamos cada plugin instalado em uma destas linhas. O que decide não é o nome, é o motivo técnico:

Plugin / casoMotivo técnicoAção antes de migrar
FormCreatorFunção virou Formulários nativo (motor novo)Inventariar e migrar os formulários que sustentam o atendimento - não migram sozinhos
GenericObjectObjetos personalizados agora nativosMapear cada tipo para ativo ou dropdown do core
FusionInventorySubstituído pelo GLPI Agent (desde o 10)Migrar a coleta para o GLPI Agent antes
Plugin com SQL cru / saída proceduralUsa $DB->query() ou Html::header() - removidos/quebradosExige release portada; sem ela, desativar
Plugin sem return type nos can*()PHP 8.2 recusa a assinatura; o plugin não carregaAguardar versão do autor ou aposentar
Fields, DataInjection, PDF, TagPossuem release para o 11Atualizar depois do core, um a um, validando entre cada
NexToolCompatível com 10 e 11 (mesmo código portado)Atualizar para a versão do 11
Plugin sem release desde 2023Provavelmente não portadoDecisão de negócio: substituir ou aposentar

Diagnóstico: meça, não decida de memória

Não classifique de cabeça. Levante o estado real dos plugins e varra o fonte deles atrás dos padrões que o core 11 removeu:

# Estado real dos plugins (rode no GLPI 10 antes de migrar; somente leitura)
# state: 0=novo  1=ativo  2=nao instalado  3=a configurar
#        4=nao ativado  5=a limpar  6=nao atualizado
mysql -u glpi -p glpi -e \
  "SELECT name, directory, version, state FROM glpi_plugins ORDER BY state, name;"

# Varra o fonte dos plugins atras dos padroes que o core 11 removeu
cd /var/www/glpi/plugins
grep -rln '$DB->query('   . --include='*.php'   # driver de SQL cru removido no 11
grep -rln 'Html::header'  . --include='*.php'   # saida procedural pre-Twig
grep -rln 'includes.php'  . --include='*.php'   # include relativo fragil no roteamento Symfony

O erro comum é confiar só no selo do marketplace. Uma release marcada como "GLPI 11" ainda pode ter SQL cru num caminho pouco usado - que só estoura quando aquele relatório específico é aberto, semanas depois do go-live.

O código que reprova um plugin no 11

Se você mantém um plugin próprio ou precisa avaliar um de terceiro com o fonte em mãos, estes são os três padrões que mais aparecem quando portamos código de 10 para 11:

// GLPI 10 (funcionava): SQL cru direto no driver
$res = $DB->query("SELECT id FROM glpi_tickets WHERE status = 1");

// GLPI 11: $DB->query() foi REMOVIDO e request() com string crua e bloqueado.
// SELECT vira query builder por criterios:
$rows = $DB->request([
    'SELECT' => 'id',
    'FROM'   => 'glpi_tickets',
    'WHERE'  => ['status' => 1],
]);

// DDL / SQL literal inevitavel (ALTER, SHOW INDEX): use doQuery()
$DB->doQuery("ALTER TABLE glpi_plugin_x ADD COLUMN ativo TINYINT DEFAULT 0");

// PHP 8.2+: metodo herdado exige return type; sem ": bool" o plugin nem carrega
public static function canCreate(): bool
{
    return Session::haveRight('plugin_x', CREATE);
}

Repare que o alias de coluna também mudou: o velho truque com crase inline, que o GLPI 10 tolerava, agora precisa virar 'name AS filho' porque o query builder do 11 escapa as crases corretamente.

Como testar a compatibilidade antes da janela

Nunca descubra a incompatibilidade em produção. O roteiro que aplicamos em homologação:

  1. Clone de produção. Suba o 11 sobre uma cópia do banco real, não sobre uma base de exemplo. Plugin quebra com o seu dado, não com o dado limpo.
  2. Instale e ative pelo console. php bin/console glpi:plugin:install <diretorio> e glpi:plugin:activate <diretorio>, como o usuário do web server. Se o plugin roda migração no install/init, é aqui que ela dispara.
  3. Abra cada página front/ do plugin. Não basta ativar sem erro; o motor Twig só reclama quando a tela é realmente renderizada.
  4. Olhe o log certo. A falha de carga não vira fatal do PHP - vira glpi.ERROR: Error while loading plugin X no log do GLPI. Quem só olha o php-errors.log conclui, errado, que "está tudo bem".
  5. Resete o OPcache entre tentativas. Depois de trocar arquivos, o php-fpm ainda serve o bytecode antigo; recarregue o processo (por exemplo, kill -USR2 no master do php-fpm), não só o Apache.

O que a sustentação nos ensinou

O incidente mais traiçoeiro que vimos não apareceu na migração em si - apareceu meses depois, num simples bump de versão de um plugin administrativo. Ele rodava a migração de schema dentro do plugin_init, um padrão comum e aparentemente inofensivo: o bloco só executa enquanto o schema_version gravado for menor que a versão do setup.php. Um bug latente ali ficou dormindo até o próximo bump - e quando finalmente rodou e lançou uma exceção, o GLPI 11 capturou no Plugin::load e desativou o plugin sozinho. O sintoma para o cliente foi aquele "ativa e desativa sem erro visível", com o plugin oscilando para o estado 4 (não ativado, que parece normal). Desde então a regra é dura: todo bump de plugin com migração-no-init exige um smoke test de ativação depois do bump, e o bloco de migração vai sempre dentro de try/catch, com o schema_version gravado FORA do try - senão ele tenta de novo a cada request e entra em loop. É o tipo de detalhe que só quem opera GLPI de cliente todo dia carrega na memória muscular.

Devo migrar agora?

Se a sua matriz de plugins está verde (release portada para todos os essenciais) e você tem homologação com clone de produção, sim - o 11 é mais moderno, seguro e rápido. Se depende de um plugin sem versão para o 11, a decisão vira de negócio: substituir pela função nativa, trocar de plugin ou adiar. O que não dá é migrar no escuro e descobrir a incompatibilidade com o ambiente no ar.

Se a sua equipe não tem janela para ensaiar a migração e classificar cada plugin com calma, a NexTool conduz o upgrade do seu GLPI com inventário de plugins, homologação sobre clone e rollback ensaiado. Fale com a gente sobre suporte e sustentação de GLPI.


Revisado pela equipe NexTool Solutions.

Perguntas Frequentes

Porque a fundação mudou: frontend em Symfony/Twig, aplicação servida a partir de public/, PHP 8.2+ com return types estritos e um driver que recusa SQL cru (o $DB->query() foi removido). Um plugin de GLPI 10 que dependa de qualquer um desses padrões, carregado sobre o core novo, dá erro 500 ou é desativado no carregamento.

Não confie só no selo do marketplace. Teste em homologação sobre um clone do banco de produção: instale e ative pelo console, abra cada página front/ do plugin e verifique o glpi.ERROR, não só o php-errors.log. Uma release pode esconder SQL cru num caminho pouco usado que só estoura semanas depois do go-live.

Não como plugin de criação. A função virou o módulo de Formulários nativo, que é um motor novo, sem importador transparente. Existe apenas uma ferramenta de transição para os formulários antigos, e eles não se mudam sozinhos. Inventarie quantos formulários você tem antes de agendar a migração.

A falha de carga do plugin não vira erro fatal do PHP. O GLPI captura a exceção no Plugin::load e registra como 'glpi.ERROR: Error while loading plugin X' no log do próprio GLPI. Olhe o log do GLPI, não só o do PHP - é o engano mais comum ao diagnosticar plugin que 'some do menu'.

Provavelmente ele roda a migração de schema no plugin_init, que só executa enquanto o schema_version gravado for menor que a versão do setup.php. Um bug latente ali dorme até o próximo bump; quando dispara e lança exceção, o GLPI 11 desativa o plugin sozinho. Envolva a migração em try/catch e grave o schema_version fora do try, senão ele repete a cada request.

O OPcache do php-fpm ainda serve o bytecode antigo compilado em memória. Recarregue o processo do php-fpm (por exemplo, kill -USR2 no master do php-fpm), não só o Apache. É a causa mais comum de um 'fix que não pega' logo após trocar arquivos de plugin.

Precisa de ajuda?