Архітектура
TailwindPHP — це порт 1:1. Структура кодової бази відтворює оригінальну структуру TailwindCSS: ті самі назви файлів і та сама організація. Частини, які неможливо перенести безпосередньо (у PHP немає Rust і V8), ізольовано в src/_tailwindphp/. На цій сторінці описано конвеєр компіляції, структуру вихідного коду, повторну реалізацію lightningcss і домовленість щодо документування відмінностей.
Конвеєр
Генерування CSS складається з послідовних етапів. Окремого етапу збирання немає — кожен виклик tw::generate() запускає весь конвеєр.
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
Розбір. css-parser.php читає вхідні дані CSS — @import, @theme, @utility, @layer та необроблені правила — і створює абстрактне синтаксичне дерево з вузлів ast.php (правила стилів, at-правила, оголошення). На цьому етапі обробляються директиви @import для віртуальних модулів (tailwindcss, tailwindcss/preflight, …) і шляхи до файлів.
Побудова дизайн-системи. theme.php визначає значення теми (кольори, відступи, контрольні точки) з розібраних блоків @theme і значень за замовчуванням. design-system.php — це центральний реєстр, що пов’язує тему з пошуком утиліт і варіантів, визначеним у utilities.php та variants.php.
Сканування вмісту. candidate.php витягує кандидатні назви класів із переданого вмісту (HTML, шаблони) та розбирає кожну на частини: базову утиліту, варіанти, модифікатори, довільні значення й прапорець важливості.
Компіляція. compile.php перетворює кожного кандидата на вузли AST, шукаючи його в дизайн-системі та застосовуючи його варіанти й модифікатори. Кандидатів, які не відповідають жодній реальній утиліті, відкинуто.
Серіалізація й оптимізація. ast.php проходить деревом за допомогою toCss() (допоміжні функції в walk.php) і формує рядок CSS. Застосовуються перетворення, еквівалентні lightningcss, з _tailwindphp/LightningCss.php, щоб результат відповідав тому, що створює TailwindCSS після етапу післяобробки Rust.
Мініфікація. Коли ввімкнено minify, під час серіалізації toCss() формує мініфікований CSS. Окремий етап обробки рядка tw::minify() забезпечує _tailwindphp/CssMinifier.php. Див. розділ Кешування та мініфікація.
Структура вихідного коду
src/ відтворює TailwindCSS. Єдина структурна зміна: utilities.ts TailwindCSS займає 6,000+ рядків, тому для зручності супроводу його розділено на src/utilities/ з окремим файлом для кожної категорії.
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
Відповідник lightningcss
TailwindCSS делегує післяобробку CSS — вкладеність, обробку медіазапитів і спрощення значень — бібліотеці lightningcss, парсеру, трансформеру й мініфікатору, написаному на Rust. PHP не може завантажувати бібліотеку Rust, тому src/_tailwindphp/LightningCss.php повторно реалізує перетворення, на які справді покладається TailwindPHP, і створює побайтно ідентичний результат для випадків, охоплених тестовим набором.
Підтверджені перетворення:
| Перетворення | Що воно робить |
|---|---|
| Розгортання вкладених правил | Розв’язує селектори & за правилами :is(…) з еталонної реалізації та розгортає вкладені правила (handleNesting() у ast.php, порт етапу TailwindCSS); transformNesting розгортає вкладеність, що залишається нижче першого рівня з оголошеннями |
| Розгортання умовних at-правил | На місці розгортає вкладені @media, @supports, @container та @starting-style, зберігаючи порядок у вихідному коді, і об’єднує лише безпосередньо сусідні правила з однаковими умовами |
| Упорядкування оголошень | Переупорядковує оголошення відповідно до ланцюжка обробників оголошень lightningcss, тож властивості виводяться в еталонному порядку (reorderDeclarations) |
Спрощення calc() |
Спрощує вирази calc() (simplifyCalcExpressions) |
| Видалення нуля на початку | 0.5 → .5 (normalizeLeadingZeros) |
| Інтервали у функціях перетворення | Нормалізує інтервали всередині функцій перетворення (normalizeTransformFunctions) |
| Нормалізація значень сітки | Додає пробіли навколо / у значеннях span; цілі числа без одиниць перетворює на px для grid-template-* (normalizeGridValues) |
| Нормалізація кольорів | Нормалізує синтаксис кольорів і обчислює color-mix() до oklab() (normalizeColors, evaluateColorMix) |
| Лапки URL | url(./x.jpg) → url("./x.jpg") (normalizeUrlQuoting) |
| Префікси постачальників | Додає префікси за логікою Autoprefixer (addVendorPrefixes) |
Цей файл навмисно ізольовано в _tailwindphp/ і позначено як @port-deviation:replacement — він замінює зовнішню залежність, а не переносить вихідний код TailwindCSS.
Маркери відмінностей порту
У docblock кожного файлу реалізації є маркери @port-deviation:*, які вказують, де й чому реалізація PHP відрізняється від оригіналу TypeScript. Завдяки цьому читач, який порівнює PHP із вихідним кодом TailwindCSS, завжди розуміє, чи є відмінність навмисною.
| Маркер | Значення |
|---|---|
@port-deviation:none |
Безпосередній порт 1:1 без істотних відмінностей |
@port-deviation:async |
PHP використовує синхронний код замість async/await |
@port-deviation:storage |
Інші структури даних (масив PHP замість Map/Set JS) |
@port-deviation:types |
Анотації PHPDoc замість типів TypeScript |
@port-deviation:sourcemaps |
Відстеження source map не реалізовано |
@port-deviation:enum |
Константи PHP замість переліків TypeScript |
@port-deviation:errors |
Інший підхід до обробки помилок |
@port-deviation:replacement |
Реалізація PHP замість зовнішньої бібліотеки (e.g. lightningcss) |
@port-deviation:helper |
Допоміжна функція для PHP, якої немає в оригіналі |
@port-deviation:performance |
Оптимізація для PHP, що зберігає ідентичний результат |
@port-deviation:omitted |
Модуль або функцію не перенесено (поза межами завдання) |
Про маркери @port-deviation:performance і забезпечення відповідності див. розділ Продуктивність і тестування.