Custom CSS Loader: Technische Details
Technik von Custom CSS Loader: Ablauf mit Output-Buffer als Diagramm, Kontext-Erkennung über SCRIPT_NAME, Dateiprüfung, Klassen, Auto-Update und Dateistruktur.
Custom CSS Loader: Technische Details
Ablauf
graph TD
A["Plugin-Datei wird eingebunden"] --> B["ob_start mit Callback"]
C["CustomCssLoaderPlugin.bootstrap()"] --> D{"Option enabled aktiv?"}
D -->|"Nein"| E["Keine Einbindung vorbereitet"]
D -->|"Ja"| F["CssLoaderOrchestrator.prepare()"]
F --> G{"Kontext staff oder client?"}
G -->|"Nein"| E
G -->|"Ja"| H["FilesystemCssDiscovery: Dateien suchen"]
H --> I["HtmlCssRenderer: Link-Tags bauen"]
I --> J["Seite wird gerendert"]
B --> J
J --> K["Callback: CssLoaderOrchestrator.injectIntoBuffer()"]
K --> L["OutputBufferInjectionStrategy.inject(): Tags vor dem ersten head-Ende"]
Der Output-Buffer wird schon beim Einbinden der Plugin-Datei gestartet, also vor bootstrap(). Der Buffer-Callback gibt die Seite unverändert zurück, solange keine Link-Tags vorbereitet wurden, und fängt Fehler ab (Eintrag im PHP-Error-Log, Seite bleibt erhalten).
Kontext-Erkennung
ProductionContextDetector entscheidet anhand der Konstanten und von $_SERVER['SCRIPT_NAME'], nicht anhand des URL-Pfads.
| Kontext | Bedingung |
|---|---|
| staff | Konstante OSTSCPINC definiert oder SCRIPT_NAME enthält /scp/ |
| client | Konstante OSTCLIENTINC definiert oder SCRIPT_NAME enthält weder /scp/ noch /api/ und endet auf .php |
| keiner | alle anderen Fälle, zum Beispiel /api/ oder ein Aufruf ohne Skriptname |
Die Prüfung auf Staff hat Vorrang vor der auf Client.
Dateien finden und prüfen
FilesystemCssDiscovery liest assets/custom/css/*.css per glob() (alphabetisch) und prüft jede Datei:
realpath()muss lösbar sein, sonst wird die Datei (zum Beispiel ein kaputter Symlink) übersprungen.- Der reale Pfad muss unterhalb des realen CSS-Ordners liegen, sonst wird die Datei blockiert und im PHP-Error-Log vermerkt.
- Der Dateiname muss
^[a-zA-Z0-9][a-zA-Z0-9_-]*\.css$entsprechen, sonst wird er blockiert und vermerkt. - Die Muster
/staff/iund/client/iwerden in dieser Reihenfolge geprüft, der erste Treffer gewinnt.
Der HtmlCssRenderer prüft den Dateinamen ein zweites Mal, hängt ?v=<Änderungszeit> an (nur bei Zeit größer 0), maskiert die URL mit htmlspecialchars() und erzeugt <link rel="stylesheet" href="...">. Die Basis-URL ist ROOT_PATH plus assets/custom/css.
Einfügen
OutputBufferInjectionStrategy sucht das erste </head> (ohne Beachtung der Groß-/Kleinschreibung) und fügt den Block genau davor ein. Fehlt </head> oder gibt es keine Link-Tags, bleibt die Seite unverändert.
Klassen
| Klasse | Aufgabe |
|---|---|
CustomCssLoaderPlugin |
Plugin-Klasse: bootstrap(), enable(), Versionsprüfung, Ordner und Demo-Dateien |
CustomCssLoaderConfig |
Konfigurationsfelder (config.php) |
CssLoaderOrchestrator |
prepare() sammelt die Link-Tags, injectIntoBuffer() fügt sie ein |
ProductionContextDetector |
Staff oder Client erkennen |
FilesystemCssDiscovery |
Dateien suchen und prüfen |
HtmlCssRenderer |
Link-Tags erzeugen |
OutputBufferInjectionStrategy |
Tags in den Buffer einfügen |
Außerdem gibt es Test-Hilfen (TestContextDetector) und ältere, als veraltet markierte Methoden in der Plugin-Klasse, die für die Unit-Tests erhalten bleiben.
Auto-Update und Version
Beim bootstrap() ruft das Plugin checkVersion() auf. Fehlt installed_version oder ist sie niedriger als die Version in plugin.php, stellt performUpdate() den CSS-Ordner sicher und speichert die neue Version. Demo-Dateien werden dabei nicht kopiert.
Dateistruktur
custom-css-loader/
├── plugin.php
├── class.CustomCssLoaderPlugin.php
├── config.php
├── composer.json
├── src/
│ ├── CssLoaderOrchestrator.php
│ ├── Context/
│ ├── Discovery/
│ ├── Rendering/
│ └── Injection/
├── assets/demo/
├── vendor/ # nur im Release-ZIP oder nach composer install
└── tests/ # PHPUnit