GLPI 11: Was sich geändert hat und welche Plugins nicht mehr funktionieren

Warum Plugins in GLPI 11 ausfallen: die Architekturänderungen (Symfony/Twig, public/, PHP 8.2, Treiber ohne rohes SQL), die Kompatibilitätsmatrix und die Abnahme-Checkliste, die wir vor einer Migration durchgehen.

In diesem Artikel8 Abschnitte

In GLPI 11 ist „das Plugin ist kaputt“ fast nie ein Fehler des Plugins: Es ist sein Fundament, das sich geändert hat. Im Betrieb von Kundenumgebungen wiederholt sich dieses Muster – der Kunde spielt den neuen Core ein, zwei oder drei Plugins verschwinden aus dem Menü, eines legt die Oberfläche mit einem Fehler 500 lahm, und im php-errors.log steht ärgerlich nichts. Dieser Leitfaden trennt, was nativ geworden ist, von dem, was tatsächlich nicht mehr funktioniert, zeigt den Codeabschnitt, an dem ein Plugin in 11 scheitert, und liefert die Checkliste, die wir durchgehen, bevor wir ein Migrationsfenster planen.

Was sich im Core geändert hat – und warum dies Plugins zu Fall bringt

Version 11 ist kein neues Theme über Version 10. Es ist ein Motorwechsel. Jede der folgenden Änderungen macht ein Muster ungültig, welches Plugins für GLPI 10 ohne Weiteres verwendet haben:

  • Frontend in Symfony + Twig. Der alte prozedurale Ablauf (Html::header() und danach echo von HTML) wurde durch Controller und Twig-Templates ersetzt. Ein Plugin, welches die Seite „von Hand“ aufgebaut hat, verliert Kopfbereich, Menü, manchmal die ganze Seite.
  • Die Anwendung wird aus public/ ausgeliefert. Für Routing sorgt Symfony. Relative Includes wie include("../../../inc/includes.php") hängen vom Arbeitsverzeichnis ab und werden nicht mehr aufgelöst.
  • Mindestens PHP 8.2, mit strikten Signaturen. Eine geerbte Methode braucht einen kompatiblen Return Type. Ein canCreate() ohne : bool löst „Declaration must be compatible“ aus, und ein solches Plugin wird gar nicht erst geladen.
  • Der Datenbanktreiber verweigert rohes SQL. Dies schmerzt am meisten. $DB->query() wurde entfernt, und $DB->request() mit einem String-Literal wird blockiert („Building and executing raw queries is prohibited“). Jede versteckte Abfrage scheitert beim ersten Mal, wenn dieser Pfad ausgeführt wird.
  • Kennzeichnungen in der hook.php haben jetzt eine kanonische Form. Markierungen wie csrf_compliant werden im Core strenger behandelt; falsch deklariert, wird ein Plugin womöglich nicht als CSRF-konform erkannt und erhält bei POST einen 403.

Das heißt: „Kompatibel mit 11“ ist kein Glück, sondern bedeutet, dass der Autor diese Muster portiert hat. Wer nicht portiert hat, fällt aus – meist lautlos.

Plugins, die Teil des Core wurden: migrieren Sie die Daten, nicht die Installation

Viele der beliebtesten Plugins sind nicht wegen Inkompatibilität verschwunden – sondern weil die Funktion in den Core gewandert ist. Hier besteht die Arbeit nicht darin, „die Version für 11 zu finden“, sondern die Daten in die native Funktion zu überführen:

  • FormCreator – wurde zum nativen Formular-Modul, einer neuen Engine. Es gibt keinen transparenten Importer; es existiert nur ein Übergangswerkzeug, und die Formulare ziehen nicht von selbst um.
  • GenericObject – benutzerdefinierte Objekte sind jetzt nativ. Bewerten Sie jeden Typ neu und ordnen Sie ihn einem Asset oder einem Dropdown des Core zu.
  • FusionInventory und Plugin-Dashboards – wurden schon seit Version 10 durch den GLPI Agent und die nativen Dashboards ersetzt. In 11 gibt es keinen Grund, daran festzuhalten.
  • Ausgehende Webhooks – der Core benachrichtigt externe Systeme jetzt bei Ereignissen (Ticket erstellt, Statusänderung) und deckt damit einen Teil dessen ab, was früher per Plugin gelöst wurde.

Kompatibilitätsmatrix: warum jeder Fall ausfällt

Bevor wir ein Fenster festlegen, ordnen wir im Betrieb jedes installierte Plugin einer dieser Zeilen zu. Entscheidend ist nicht der Name, sondern der technische Grund:

Plugin / FallTechnischer GrundMaßnahme vor der Migration
FormCreatorFunktion wurde zu nativen Formularen (neue Engine)Die Formulare, die den Support tragen, inventarisieren und migrieren – sie migrieren nicht von selbst
GenericObjectBenutzerdefinierte Objekte jetzt nativJeden Typ einem Asset oder Dropdown des Core zuordnen
FusionInventoryDurch den GLPI Agent ersetzt (seit 10)Die Erfassung vorher auf den GLPI Agent umstellen
Plugin mit rohem SQL / prozeduraler AusgabeVerwendet $DB->query() oder Html::header() – entfernt/defektErfordert ein portiertes Release; ohne dieses deaktivieren
Plugin ohne Return Type in den can*()PHP 8.2 lehnt die Signatur ab; Plugin lädt nichtAuf eine Version des Autors warten oder ausmustern
Fields, DataInjection, PDF, TagHaben ein Release für 11Nach dem Core einzeln aktualisieren und dazwischen jeweils prüfen
NexToolKompatibel mit 10 und 11 (derselbe portierte Code)Auf die Version für 11 aktualisieren
Plugin ohne Release seit 2023Wahrscheinlich nicht portiertGeschäftsentscheidung: ersetzen oder ausmustern

Diagnose: messen, nicht aus dem Gedächtnis entscheiden

Klassifizieren Sie nicht aus dem Kopf. Erheben Sie den tatsächlichen Zustand der Plugins und durchsuchen Sie deren Quellcode nach den Mustern, die der Core 11 entfernt hat:

# 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

Der häufige Fehler ist, sich nur auf ein Siegel im Marketplace zu verlassen. Ein als „GLPI 11“ markiertes Release kann in einem selten genutzten Pfad noch rohes SQL enthalten – welches erst knallt, wenn genau dieser Bericht geöffnet wird, Wochen nach dem Go-live.

Der Code, an dem ein Plugin in 11 scheitert

Wenn Sie ein eigenes Plugin pflegen oder eines von Dritten mit vorliegendem Quellcode bewerten müssen, sind dies die drei Muster, die beim Portieren von Code von 10 auf 11 am häufigsten auftauchen:

// 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);
}

Beachten Sie, dass sich auch der Spaltenalias geändert hat: Der alte Trick mit Inline-Backticks, den GLPI 10 tolerierte, muss jetzt zu 'name AS filho' werden, weil der Query Builder von 11 Backticks korrekt maskiert.

So testen Sie die Kompatibilität vor dem Fenster

Entdecken Sie die Inkompatibilität nie in der Produktion. Der Ablauf, den wir in der Abnahmeumgebung anwenden:

  1. Klon der Produktion. Spielen Sie 11 auf eine Kopie der echten Datenbank ein, nicht auf eine Beispieldatenbank. Plugins scheitern an Ihren Daten, nicht an sauberen Daten.
  2. Über die Konsole installieren und aktivieren. php bin/console glpi:plugin:install <diretorio> und glpi:plugin:activate <diretorio>, als Benutzer des Webservers. Wenn ein Plugin bei install/init eine Migration ausführt, wird sie hier ausgelöst.
  3. Öffnen Sie jede front/-Seite des Plugins. Es reicht nicht, fehlerfrei zu aktivieren; die Twig-Engine beschwert sich erst, wenn die Seite tatsächlich gerendert wird.
  4. Schauen Sie ins richtige Log. Der Ladefehler wird nicht zu einem PHP-Fatal – er wird zu glpi.ERROR: Error while loading plugin X im GLPI-Log. Wer nur ins php-errors.log schaut, schließt fälschlich, dass „alles in Ordnung ist“.
  5. Setzen Sie den OPcache zwischen den Versuchen zurück. Nach dem Austausch von Dateien liefert php-fpm noch den alten Bytecode aus; laden Sie den Prozess neu (zum Beispiel kill -USR2 an den Master von php-fpm), nicht nur den Apache.

Was uns der Betrieb gelehrt hat

Der tückischste Vorfall, den wir gesehen haben, trat nicht bei der Migration selbst auf – sondern Monate später, bei einem einfachen Versions-Bump eines administrativen Plugins. Es führte die Schema-Migration innerhalb von plugin_init aus, ein verbreitetes und scheinbar harmloses Muster: Der Block läuft nur, solange die gespeicherte schema_version kleiner ist als die Version in der setup.php. Ein latenter Fehler darin schlummerte bis zum nächsten Bump – und als er schließlich lief und eine Exception warf, fing GLPI 11 sie in Plugin::load ab und deaktivierte es von selbst. Als Symptom sah der Kunde jenes „aktiviert und deaktiviert sich ohne sichtbaren Fehler“, wobei es in den Zustand 4 pendelte (nicht aktiviert, was normal aussieht). Seitdem gilt eine harte Regel: Jeder Bump eines Plugins mit Migration-im-Init erfordert danach einen Smoke-Test der Aktivierung, und der Migrationsblock kommt immer in ein try/catch, wobei die schema_version AUSSERHALB des try gespeichert wird – sonst versucht er es bei jedem Request erneut und gerät in eine Schleife. Solche Details verinnerlicht nur, wer täglich GLPI-Umgebungen von Kunden betreibt.

Sollte ich jetzt migrieren?

Wenn Ihre Plugin-Matrix grün ist (portiertes Release für alle wesentlichen Plugins) und Sie eine Abnahmeumgebung mit einem Klon der Produktion haben, ja – Version 11 ist moderner, sicherer und schneller. Wenn Sie von einem Plugin ohne Version für 11 abhängen, wird es zu einer Geschäftsentscheidung: durch die native Funktion ersetzen, auf ein anderes Plugin wechseln oder verschieben. Was nicht geht, ist blind zu migrieren und die Inkompatibilität bei laufender Umgebung zu entdecken.

Wenn Ihr Team kein Zeitfenster hat, um die Migration zu proben und jedes Plugin in Ruhe zu klassifizieren, führt NexTool ein Upgrade Ihres GLPI durch – mit Plugin-Inventar, Abnahme auf einem Klon und geprobtem Rollback. Sprechen Sie mit uns über Support und Betrieb für GLPI.


Geprüft vom Team der NexTool Solutions.

Häufig gestellte Fragen

Weil sich sein Fundament geändert hat: Frontend in Symfony/Twig, Auslieferung der Anwendung aus public/, PHP 8.2+ mit strikten Return Types und ein Treiber, der rohes SQL verweigert ($DB->query() wurde entfernt). Ein Plugin für GLPI 10, welches von einem dieser Muster abhängt, liefert auf dem neuen Core einen Fehler 500 oder wird beim Laden deaktiviert.

Verlassen Sie sich nicht nur auf ein Siegel im Marketplace. Testen Sie in einer Abnahmeumgebung auf einem Klon der Produktionsdatenbank: über die Konsole installieren und aktivieren, jede front/-Seite des Plugins öffnen und auf glpi.ERROR prüfen, nicht nur im php-errors.log. Ein Release kann in einem selten genutzten Pfad rohes SQL verstecken, welches erst Wochen nach dem Go-live knallt.

Nicht als Plugin zum Erstellen. Die Funktion wurde zum nativen Formular-Modul, einer neuen Engine ohne transparenten Importer. Es gibt nur ein Übergangswerkzeug für die alten Formulare, und sie ziehen nicht von selbst um. Inventarisieren Sie, wie viele Formulare Sie haben, bevor Sie die Migration planen.

Der Ladefehler des Plugins wird nicht zu einem fatalen PHP-Fehler. GLPI fängt die Exception in Plugin::load ab und protokolliert sie als „glpi.ERROR: Error while loading plugin X“ im eigenen GLPI-Log. Schauen Sie ins GLPI-Log, nicht nur ins PHP-Log – dies ist der häufigste Irrtum bei der Diagnose eines Plugins, welches „aus dem Menü verschwindet“.

Wahrscheinlich führt es die Schema-Migration in plugin_init aus, die nur läuft, solange die gespeicherte schema_version kleiner ist als die Version in der setup.php. Ein latenter Fehler darin schlummert bis zum nächsten Bump; wenn er auslöst und eine Exception wirft, deaktiviert GLPI 11 es von selbst. Packen Sie die Migration in ein try/catch und speichern Sie die schema_version außerhalb des try, sonst wiederholt sie sich bei jedem Request.

Der OPcache von php-fpm liefert noch den alten, im Speicher kompilierten Bytecode aus. Laden Sie den php-fpm-Prozess neu (zum Beispiel kill -USR2 an den Master von php-fpm), nicht nur den Apache. Dies ist die häufigste Ursache für einen „Fix, der nicht greift“, direkt nach dem Austausch von Plugin-Dateien.

Brauchen Sie Hilfe?