Markus Michalski FAQ

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.

English Version

Zurück zur Übersicht

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:

  1. realpath() muss lösbar sein, sonst wird die Datei (zum Beispiel ein kaputter Symlink) übersprungen.
  2. Der reale Pfad muss unterhalb des realen CSS-Ordners liegen, sonst wird die Datei blockiert und im PHP-Error-Log vermerkt.
  3. Der Dateiname muss ^[a-zA-Z0-9][a-zA-Z0-9_-]*\.css$ entsprechen, sonst wird er blockiert und vermerkt.
  4. Die Muster /staff/i und /client/i werden 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