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 danachechovon 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 wieinclude("../../../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: boollö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_compliantwerden 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 / Fall | Technischer Grund | Maßnahme vor der Migration |
|---|---|---|
| FormCreator | Funktion wurde zu nativen Formularen (neue Engine) | Die Formulare, die den Support tragen, inventarisieren und migrieren – sie migrieren nicht von selbst |
| GenericObject | Benutzerdefinierte Objekte jetzt nativ | Jeden Typ einem Asset oder Dropdown des Core zuordnen |
| FusionInventory | Durch den GLPI Agent ersetzt (seit 10) | Die Erfassung vorher auf den GLPI Agent umstellen |
| Plugin mit rohem SQL / prozeduraler Ausgabe | Verwendet $DB->query() oder Html::header() – entfernt/defekt | Erfordert ein portiertes Release; ohne dieses deaktivieren |
Plugin ohne Return Type in den can*() | PHP 8.2 lehnt die Signatur ab; Plugin lädt nicht | Auf eine Version des Autors warten oder ausmustern |
| Fields, DataInjection, PDF, Tag | Haben ein Release für 11 | Nach dem Core einzeln aktualisieren und dazwischen jeweils prüfen |
| NexTool | Kompatibel mit 10 und 11 (derselbe portierte Code) | Auf die Version für 11 aktualisieren |
| Plugin ohne Release seit 2023 | Wahrscheinlich nicht portiert | Geschä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:
- 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.
- Über die Konsole installieren und aktivieren.
php bin/console glpi:plugin:install <diretorio>undglpi:plugin:activate <diretorio>, als Benutzer des Webservers. Wenn ein Plugin bei install/init eine Migration ausführt, wird sie hier ausgelöst. - Ö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. - Schauen Sie ins richtige Log. Der Ladefehler wird nicht zu einem PHP-Fatal – er wird zu
glpi.ERROR: Error while loading plugin Xim GLPI-Log. Wer nur ins php-errors.log schaut, schließt fälschlich, dass „alles in Ordnung ist“. - 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 -USR2an 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.