Lebenszyklus und Befehle
Ein Plugin durchläuft vier Zustände: registriert, installiert, eingeschaltet, entfernt. Wer sie verwechselt, sucht Fehler an der falschen Stelle.
Drei Speicher, drei verschiedene Fragen
Der häufigste Irrtum ist, den Zustand für eine Sache zu halten. Es sind drei, und sie beantworten verschiedene Fragen:
| Frage | Wo die Antwort steht |
|---|---|
| Welche Bundles existieren überhaupt? | config/plugins.php (eingecheckt), dazu die erzeugte Composer-Liste und die Laufzeit-Liste je Instanz |
| Ist das Modul eingeschaltet? | Eine Zeile module.<plugin> in der Datenbank |
| Ist es installiert oder bewusst entfernt worden? | Der Bereitstellungs-Zustand in der Datenbank |
Der dritte Speicher hat einen Zweck, der nicht offensichtlich ist: Er merkt sich ein bewusstes Entfernen. Ohne ihn würde ein Systemupdate ein deinstalliertes Plugin wieder einrichten, weil das Bundle ja weiter registriert ist.
Der Schalter ist fail-closed
Für Plugins gilt die umgekehrte Regel zum Kern:
- Kern: fehlt die Schalter-Zeile, gilt das Modul als eingeschaltet.
- Plugin: die Zeile muss existieren und auf ein stehen. Fehlt sie, oder lässt sich die
Datenbank nicht fragen, antworten alle Routen des Plugins mit 404.
Deshalb legt der Installations-Befehl den Schalter deaktiviert an. Ein frisch installiertes Plugin ist erreichbar, sobald du es einschaltest, nicht vorher.
Der 404 ist Absicht und kein Versehen: Ein 403 verriete, dass die Route existiert.
Es gibt keine Lebenszyklus-Methoden am Bundle
Wer aus anderen Systemen kommt, sucht install(), activate() oder update() an der Bundle-Klasse. Die gibt es hier nicht, und das ist eine Entscheidung, keine Lücke.
Teilnahme am Lebenszyklus läuft über Verträge, die du implementierst:
| Was du willst | Was du implementierst |
|---|---|
| Vor dem Umschalten widersprechen | Auf PluginLifecycleChanging hören und ein Veto einlegen |
| Nach dem Umschalten reagieren | Auf PluginLifecycleChanged hören |
| Stammdaten mitbringen | StandardDataProviderInterface |
| Rechte mitbringen | PermissionProviderInterface |
| Beispieldaten mitbringen | DemoContributorInterface |
| Eigene Daten in Kern-Tabellen beim Entfernen aufräumen | UninstallDataParticipantInterface |
| Reihenfolge und Schutz vor verfrühtem Entfernen | PluginDependencyProviderInterface |
Alle bis auf die beiden Ereignisse liegen unter App\Platform\Plugin\Contract\; der Rechte-Vertrag unter App\Platform\Auth\Contract\.
Der Vorteil: Ein Plugin, das nichts davon braucht, schreibt auch nichts davon. Ein leerer Rumpf ist gültig.
Ein Update ist kein eigener Zustand
Es gibt keinen update()-Schritt. Eine neue Version besteht aus drei gewöhnlichen Dingen: die Versionsnummer hochziehen, Migrationen mitliefern, ausliefern. Die Migrationen laufen beim Einspielen, der Rest ist Code-Austausch.
Was beim Entfernen mit den Daten passiert
Voreinstellung ist behalten. Das Entfernen deaktiviert das Plugin, räumt seine Zeilen aus den gemeinsamen Katalogen (Rechte, Stammdaten, Nachschlagewerte) und nimmt den Schalter weg. Die eigenen Tabellen des Plugins bleiben stehen, damit eine Neuinstallation sie wieder aufnimmt.
Erst --delete-data wirft sie weg. Dann gilt eine Reihenfolge, die leicht zu übersehen ist: Die Migrations-Historie wird zuerst vergessen, danach werden die Tabellen entfernt. Andersherum hielte das System die Migration für angewandt und legte bei der Neuinstallation nichts mehr an.
Solange du behältst, setzt das System außerdem alle Regeln aus, die fremde Daten mitreißen würden. Das ist der Unterschied zwischen einem sauberen Ausbau und einem irreversiblen Datenverlust im Kern.
Vor dem Ernstfall lohnt sich der Trockenlauf:
bin/console octibiz:plugin:uninstall acme.shipping --dry-run
Die Befehle
Alle Befehle nehmen den Plugin-Schlüssel, nicht den Ordnernamen.
Alltag
bin/console octibiz:plugin:list # Inventar mit Version, Vertrauensstufe, Zustand
bin/console octibiz:plugin:install <key> [--activate] # einrichten, Schalter anlegen (deaktiviert)
bin/console octibiz:plugin:activate <key> # einschalten
bin/console octibiz:plugin:deactivate <key> # ausschalten, Daten bleiben, Routen 404
bin/console octibiz:plugin:uninstall <key> # entfernen; --dry-run, --delete-data, --keep-data, --force
Das Ein- und Ausschalten ist ein reiner Schalter-Wechsel. Er wirkt sofort und braucht keinen Neubau des Containers oder der Oberfläche.
Laufzeit-Pakete
bin/console octibiz:plugin:install-package pfad/zum/paket.zip # entpacken, einrichten, Migrationen, Oberfläche
bin/console octibiz:plugin:uninstall-runtime <key> [--purge] # Dateien entfernen, neu bauen
Werkzeuge
bin/console octibiz:plugin:rebuild-container # nach Änderung des Plugin-Satzes zur Laufzeit
bin/console octibiz:plugin:rebuild-spa # Oberfläche neu bauen, atomarer Wechsel
bin/console octibiz:plugin:purge-tables --namespace <ns> # Tabellen entfernen, streng auf den Namensraum begrenzt
bin/console octibiz:plugin:reconcile-ownership # fehlende Eigentums-Nachweise nachziehen
bin/console octibiz:plugin:run-jobs # verwaiste Paket-Uploads aufräumen
Der Neubau der Oberfläche hat ein Sicherheitsnetz: Er baut in ein Nebenverzeichnis, prüft das Ergebnis und tauscht erst dann. Schlägt der Bau fehl, bleibt die alte Fassung live.
Was der Installations-Befehl nicht tut
Er führt keine Migrationen aus. Beim Einrichten aus dem Checkout musst du sie selbst fahren:
bin/console doctrine:migrations:migrate --no-interaction
Der Weg über ein Paket-ZIP macht das mit, dort ist es Teil des Ablaufs.
Wenn etwas schiefgeht
Der Installations-Befehl hält eine Sperre je Plugin und rollt bei einem Fehler selbst zurück. Zwei Prüfungen laufen vor jedem Seiteneffekt und brechen hart ab:
- Das Plugin ist gegen eine andere Hauptversion des Vertrags gebaut.
- Es beansprucht ein Adress-Segment, das dem Kern gehört.
Der zweite Fall ist der unangenehmere, wenn er durchginge: Die Kern-Route hinge danach am fail-closed ausgewerteten Schalter des Plugins. Weil der Schalter deaktiviert angelegt wird, würde allein die Installation eine Kern-Route unerreichbar machen.
Beim Aktivieren prüft das System zusätzlich, ob die Datenmodelle des Plugins überhaupt übersetzen. Alle Plugins teilen sich einen Zugriff auf die Datenbank; ein fehlerhaftes Modell würde die Metadaten für die ganze Anwendung brechen. Deshalb bricht die Aktivierung ab, statt die Plattform mitzureißen.
Weiter
- Verteilung — Paket bauen, Versionen, Auslieferungswege
- Testen — warum eine frische Testdatenbank alle Module ausschaltet
- Backend — was ein Plugin beim Einrichten mitbringt