Architektur
TailwindPHP ist eine 1:1-Portierung. Die Codebasis spiegelt die Struktur von TailwindCSS wider – dieselben Dateinamen und dieselbe Organisation – und isoliert die Teile, die sich nicht direkt portieren lassen (PHP hat weder Rust noch V8), unter src/_tailwindphp/. Diese Seite führt durch die Kompilierungspipeline, die Quellstruktur, die lightningcss-Neuimplementierung und die Konvention zur Dokumentation von Abweichungen.
Die Pipeline
Das Erzeugen von CSS läuft in mehreren Schritten ab. Es gibt keinen separaten Build-Schritt – jeder Aufruf von tw::generate() führt die vollständige Pipeline aus.
CSS input (@import "tailwindcss"; @theme { … })
│
▼
┌──────────────┐
│ Parse CSS │ css-parser.php → ast.php nodes
└──────┬───────┘
▼ design system (theme, utilities, variants)
┌──────────────┐
│ Scan content │ candidate.php — extract class names from HTML
└──────┬───────┘
▼ candidates
┌──────────────┐
│ Compile │ compile.php — candidate → AST, via design-system.php
└──────┬───────┘
▼ AST
┌──────────────┐
│ Optimize │ _tailwindphp/LightningCss.php — nesting, @media, calc()
└──────┬───────┘
▼
┌──────────────┐
│ Serialize │ ast.php toCss() (minified when requested)
└──────┬───────┘
▼
CSS output
Parsen. css-parser.php liest die CSS-Eingabe – @import, @theme, @utility, @layer, rohe Regeln – in einen abstrakten Syntaxbaum aus ast.php-Knoten ein (Stilregeln, At-Regeln, Deklarationen). @import-Direktiven für virtuelle Module (tailwindcss, tailwindcss/preflight, …) und Dateipfade werden in diesem Schritt aufgelöst und ersetzt.
Designsystem erstellen. theme.php löst Theme-Werte (Farben, Abstände, Breakpoints) aus den geparsten @theme-Blöcken und den Standardwerten auf. design-system.php ist die zentrale Registry, die das Theme mit den in utilities.php und variants.php definierten Utility- und Varianten-Lookups verknüpft.
Inhalte durchsuchen. candidate.php extrahiert aus den angegebenen Inhalten (HTML, Vorlagen) mögliche Klassennamen und zerlegt sie in Bestandteile: Basis-Utility, Varianten, Modifikatoren, beliebige Werte und Wichtigkeitsmarkierung.
Kompilieren. compile.php wandelt jeden Kandidaten in AST-Knoten um, indem es ihn im Designsystem nachschlägt und seine Varianten und Modifikatoren anwendet. Kandidaten, die keiner echten Utility entsprechen, werden verworfen.
Serialisieren und optimieren. Der toCss()-Prozess von ast.php durchläuft den Baum (mit Hilfsfunktionen aus walk.php) und gibt den CSS-String aus. Die Transformationen in _tailwindphp/LightningCss.php, die lightningcss entsprechen, werden angewendet, damit die Ausgabe dem Ergebnis von TailwindCSS nach der Rust-Nachverarbeitung entspricht.
Minifizieren. Wenn minify aktiviert ist, gibt toCss() während der Serialisierung minifiziertes CSS aus. _tailwindphp/CssMinifier.php übernimmt den eigenständigen tw::minify()-Durchlauf. Siehe Caching und Minifizierung.
Quellstruktur
src/ spiegelt TailwindCSS wider. Die einzige strukturelle Änderung: TailwindCSSs utilities.ts umfasst mehr als 6,000 Zeilen und ist daher zur besseren Wartbarkeit in src/utilities/ aufgeteilt, mit einer Datei pro Kategorie.
src/
├── _tailwindphp/ # PHP-specific — NOT part of the TailwindCSS port
│ ├── LightningCss.php # CSS transforms (lightningcss Rust library equivalent)
│ ├── CssMinifier.php # CSS minification
│ └── lib/ # Companion library ports (TailwindPHP\Lib\*)
│ ├── clsx/ # clsx port
│ ├── tailwind-merge/ # tailwind-merge port
│ └── cva/ # CVA port
│
├── plugin/ # Plugin system
│ └── plugins/
│ ├── typography-plugin.php # @tailwindcss/typography port
│ └── forms-plugin.php # @tailwindcss/forms port
│
├── utilities/ # Split from TailwindCSS utilities.ts (one file per category)
│ ├── accessibility.php # sr-only, forced-colors
│ ├── backgrounds.php # bg-*, gradient-*, from-*, via-*, to-*
│ ├── borders.php # border-*, rounded-*, divide-*, outline-*
│ ├── effects.php # shadow-*, opacity-*, mix-blend-*
│ ├── filters.php # blur-*, brightness-*, contrast-*, …
│ ├── flexbox.php # flex-*, grid-*, gap-*, justify-*, align-*
│ ├── interactivity.php # cursor-*, scroll-*, touch-*, select-*
│ ├── layout.php # display, position, z-*, overflow-*, …
│ ├── masks.php # mask-linear-*, mask-radial-*, mask-conic-*
│ ├── sizing.php # w-*, h-*, min-*, max-*, size-*
│ ├── spacing.php # m-*, p-*, space-*
│ ├── svg.php # fill-*, stroke-*
│ ├── tables.php # border-collapse, table-layout
│ ├── transforms.php # translate-*, rotate-*, scale-*, skew-*
│ ├── transitions.php # transition-*, duration-*, ease-*, delay-*
│ └── typography.php # font-*, text-*, leading-*, tracking-*
│
├── utils/ # Helper functions (ported from utils/)
│
├── index.php # Main entry — compile(), cn(), variants(), merge(), join()
├── ast.php # AST nodes and toCss()
├── candidate.php # Candidate parsing (class name → parts)
├── compile.php # Candidate to CSS compilation
├── css-functions.php # theme(), --theme(), --spacing(), --alpha()
├── css-parser.php # CSS parsing
├── design-system.php # Central registry for utilities/variants
├── plugin.php # Plugin system (PluginInterface, PluginAPI, PluginManager)
├── theme.php # Theme value resolution
├── utilities.php # Utility registration and lookup
├── value-parser.php # CSS value parsing
├── variants.php # Variant handling (hover, focus, responsive, …)
└── walk.php # AST traversal
Das Gegenstück zu lightningcss
TailwindCSS überlässt seine CSS-Nachverarbeitung – Verschachtelung, Media-Query-Verarbeitung und Vereinfachung von Werten – lightningcss, einem in Rust geschriebenen Parser, Transformer und Minifier. PHP kann keine Rust-Bibliothek laden, daher implementiert src/_tailwindphp/LightningCss.php die Transformationen neu, auf die TailwindPHP tatsächlich angewiesen ist. Für die vom Testsatz abgedeckten Fälle entsteht dabei eine byteidentische Ausgabe.
Bestätigte Transformationen umfassen:
| Transformation | Wirkung |
|---|---|
| Verschachtelung auflösen | Löst &-Selektoren mit den :is(…)-Regeln der Referenz auf und flacht verschachtelte Regeln ab (handleNesting() in ast.php, eine Portierung des TailwindCSS-Durchlaufs); transformNesting stuft die Verschachtelung unterhalb der ersten Ebene mit Deklarationen herab |
| Bedingte At-Regeln abflachen | Flacht verschachtelte @media, @supports, @container und @starting-style an Ort und Stelle ab, erhält die Quellreihenfolge und führt nur direkt benachbarte Geschwister mit identischen Bedingungen zusammen |
| Reihenfolge der Deklarationen | Ordnet Deklarationen gemäß der Handler-Kette von lightningcss neu, sodass Eigenschaften in der Referenzreihenfolge ausgegeben werden (reorderDeclarations) |
calc()-Vereinfachung |
Vereinfacht calc()-Ausdrücke (simplifyCalcExpressions) |
| Führende Null entfernen | 0.5 → .5 (normalizeLeadingZeros) |
| Abstände in Transform-Funktionen | Vereinheitlicht Abstände innerhalb von Transform-Funktionen (normalizeTransformFunctions) |
| Grid-Werte normalisieren | Fügt in Span-Werten Leerzeichen um / ein, wandelt reine Ganzzahlen für grid-template-* in px um (normalizeGridValues) |
| Farben normalisieren | Vereinheitlicht die Farbsyntax und wertet color-mix() zu oklab() aus (normalizeColors, evaluateColorMix) |
| URL-Anführungszeichen | url(./x.jpg) → url("./x.jpg") (normalizeUrlQuoting) |
| Herstellerpräfixe | Präfixe wie bei Autoprefixer (addVendorPrefixes) |
Diese Datei ist absichtlich unter _tailwindphp/ isoliert und als @port-deviation:replacement markiert – sie steht stellvertretend für eine externe Abhängigkeit, statt TailwindCSS-Quellcode zu portieren.
Markierungen für Abweichungen bei der Portierung
Jede Implementierungsdatei enthält in ihrem Docblock @port-deviation:*-Markierungen, die angeben, wo und warum die PHP-Implementierung vom TypeScript-Original abweicht. So muss sich niemand beim Vergleich von PHP mit dem TailwindCSS-Quellcode fragen, ob ein Unterschied beabsichtigt ist.
| Markierung | Bedeutung |
|---|---|
@port-deviation:none |
Direkte 1:1-Portierung ohne wesentliche Abweichungen |
@port-deviation:async |
PHP verwendet synchronen Code statt async/await |
@port-deviation:storage |
Andere Datenstrukturen (PHP-Array statt JS Map/Set) |
@port-deviation:types |
PHPDoc-Annotationen statt TypeScript-Typen |
@port-deviation:sourcemaps |
Source-Map-Verfolgung ausgelassen |
@port-deviation:enum |
PHP-Konstanten statt TypeScript-Enums |
@port-deviation:errors |
Anderer Ansatz zur Fehlerbehandlung |
@port-deviation:replacement |
PHP-Implementierung ersetzt eine externe Bibliothek (e.g. lightningcss) |
@port-deviation:helper |
Hilfsfunktion speziell für PHP, die im Original fehlt |
@port-deviation:performance |
Für PHP spezifische Optimierung, die identische Ausgabe beibehält |
@port-deviation:omitted |
Gesamtes Modul/Feature nicht portiert (außerhalb des Umfangs) |
Zu den @port-deviation:performance-Markierungen und dazu, wie die Parität sichergestellt wird, siehe Leistung und Tests.