Resolver CSS: kaskada i swoistość
W skrócie
Dział zatytułowany „W skrócie”Klasa CssResolver dopasowuje selektory do strumienia tokenów, porządkuje dopasowane reguły według warstwy kaskady, swoistości i kolejności w dokumencie, a następnie stosuje !important w drugim przebiegu.
Instalacja
Dział zatytułowany „Instalacja”composer require nextpdf/core:^3Omówienie koncepcyjne
Dział zatytułowany „Omówienie koncepcyjne”CssResolver jest komponentem warstwy 1 (zgodnie z ADR-010). Przechowuje sparsowane reguły kaskadowych arkuszy stylów (CSS) i decyduje, które deklaracje mają zastosowanie do każdego elementu. Klasa została wyodrębniona z HtmlParser, aby zachować przejrzystą strukturę. Jest komponentem wewnętrznym, a nie publicznym interfejsem programowania aplikacji (API).
Resolver nie potrzebuje drzewa dokumentu. Dopasowywanie selektorów odbywa się na płaskim strumieniu tokenów i korzysta z map indeksów, które HtmlChildScanner buduje w etapie 3 potoku: liczby elementów potomnych, liczby tagów tego samego typu oraz informacji o pustości. Te mapy obsługują pseudoklasy strukturalne. Relacyjny selektor :has() korzysta z ograniczonego wstępnego skanowania opisanego w ograniczeniach strumieniowania.
Rozwiązywanie kaskady odbywa się w dwóch przebiegach wewnątrz CssResolver::resolveMatchingProperties(). Przebieg 1 stosuje normalne deklaracje w kolejności kaskady: najpierw waga warstwy kaskady, następnie swoistość, a na końcu kolejność w dokumencie. Przebieg 2 stosuje deklaracje !important w kolejności swoistości. Deklaracja !important nadpisuje każdą normalną deklarację, niezależnie od swoistości. Ten podział na dwa przebiegi jest strategią implementacyjną, która tworzy rozstrzygnięty zestaw właściwości wykorzystywany przez warstwę układu.
Kolejność kaskady zaimplementowana w resolverze jest zgodna ze specyfikacją CSS Cascading and Inheritance organizacji World Wide Web Consortium (W3C). Deklaracje są sortowane najpierw według pochodzenia i ważności, a następnie według swoistości selektora. Przy równej swoistości wygrywa ostatnia deklaracja w kolejności w dokumencie (CSS Cascade 5 §6.4; zobacz Zgodność). Komentarz w kodzie źródłowym w CssResolver przywołuje tę samą klauzulę, więc obok specyfikacji i słownika dostępny jest trzeci sposób weryfikacji tego zachowania.
Swoistość jest obliczana jako trójka (A, B, C) na podstawie liczby składników ID, klasy i typu, a trójki są porównywane składnik po składniku (Selectors Level 4 §16). NextPDF oblicza swoistość dla każdej dopasowanej reguły przed posortowaniem kaskady.
Istotne jest jedno ograniczenie. Reguła inwersji warstw z §6.4.3 dotyczy deklaracji !important obejmujących wiele warstw kaskady, a kod źródłowy odnotowuje ją jako nierozstrzygniętą w klastrze prac nad warstwami kaskady. Gdy warstwy kaskady są zadeklarowane, a !important przekracza warstwy, rozstrzygnięta kolejność może różnić się od pełnego zachowania zgodnego ze specyfikacją. Macierz wsparcia CSS jest źródłem rozstrzygającym dla stanu wsparcia poszczególnych funkcji, a ta strona nie powiela informacji o wsparciu poszczególnych właściwości.
Powierzchnia API
Dział zatytułowany „Powierzchnia API”| Symbol | Lokalizacja | Rola |
|---|---|---|
CssResolver::parseStyleBlock(string $css, bool $nestingEnabled = false): void | src/Html/CssResolver.php | Parsuje blok <style> na reguły. |
CssResolver::resolveMatchingProperties(...) | src/Html/CssResolver.php | Dopasowuje selektory i rozwiązuje dwuprzebiegową kaskadę. |
CssResolver::resolveHasSelectors(array $tokens): array | src/Html/CssResolver.php | Ograniczone wstępne skanowanie :has() (sterowane flagą). |
CssResolver::resolveFirstLetterProperties(...) | src/Html/CssResolver.php | Rozwiązuje właściwości ::first-letter. |
CssResolver::resolvePseudoElementProperties(...) | src/Html/CssResolver.php | Rozwiązuje właściwości ::before / ::after. |
CssResolver::getLayerRegistry(): LayerRegistry | src/Html/CssResolver.php | Zadeklarowane warstwy kaskady. |
Przykład kodu — szybki start
Dział zatytułowany „Przykład kodu — szybki start”Resolver nie jest wywoływany bezpośrednio. CSS definiuje się w treści, a resolver działa wewnątrz writeHtml(). W poniższej kaskadzie kolor elementu p zostaje rozstrzygnięty jako czerwony, ponieważ reguła klasy ma wyższą swoistość niż reguła typu.
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->addPage();$doc->writeHtml( '<style>p { color: blue; } .lead { color: red; }</style>' . '<p class="lead">Higher-specificity class wins.</p>');$doc->save(__DIR__ . '/output/cascade.pdf');Przykład kodu — wdrożenie produkcyjne
Dział zatytułowany „Przykład kodu — wdrożenie produkcyjne”Ten przykład pokazuje drugi przebieg !important. Deklaracja typu z !important nadpisuje odpowiadającą jej deklarację klasy bez takiego oznaczenia, mimo że selektor klasy ma wyższą swoistość.
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->addPage();$doc->writeHtml( '<style>p { color: green !important; } .lead { color: red; }</style>' . '<p class="lead">!important overrides higher specificity.</p>');$doc->save(__DIR__ . '/output/important.pdf');Przypadki brzegowe i pułapki
Dział zatytułowany „Przypadki brzegowe i pułapki”!importantignoruje swoistość. Przebieg 2 stosuje deklaracje!importantw kolejności swoistości, a deklaracje te zawsze nadpisują normalne deklaracje.- Warstwy kaskady +
!importantobejmujące wiele warstw. Kod źródłowy odnotowuje regułę inwersji warstw z §6.4.3 dla deklaracji important jako nierozstrzygniętą. Zweryfikuj zachowanie względem macierzy wsparcia CSS, zanim oprzesz na nim implementację. - Brak zadeklarowanych warstw to szybka ścieżka. Bez
@layerporządkowanie sprowadza się do zachowania opartego wyłącznie na swoistości i jest bitowo identyczne z zachowaniem sprzed warstw. :has()jest sterowane flagą. Relacyjne wstępne skanowanie działa tylko wtedy, gdy włączona jest funkcja eksperymentalnacss.has.- Dopasowywanie selektorów opiera się na strumieniu. Selektory strukturalne korzystają z map indeksów, a nie z przechodzenia po drzewie. Selektor wymagający nawigacji po dowolnym fragmencie drzewa wykraczającej poza mapy indeksów nie jest rozwiązywalny w tym modelu.
Wydajność
Dział zatytułowany „Wydajność”W najgorszym przypadku dopasowywanie selektorów ma złożoność O(reguły × elementy), ograniczoną przez limity strumieniowania. Dwa sortowania kaskady mają złożoność O(dopasowane reguły · log dopasowane reguły) na element. Ścieżka bez warstw całkowicie pomija rozwiązywanie warstw. Przypadający na stronę performance_budget (wall_ms: 1500, peak_mb: 64) wyznacza pułap operacyjny. Benchmark potoku renderowania HTML chroni przed regresjami (praca scalona w PR #564).
Uwagi dotyczące bezpieczeństwa
Dział zatytułowany „Uwagi dotyczące bezpieczeństwa”Resolver widzi tylko ten CSS, który dopuszcza DefaultHtmlSecurityPolicy::isCssPropertyAllowed(). Lista dozwolonych właściwości wyznacza pułap bezpieczeństwa, a tabela wsparcia w środowisku uruchomieniowym wyznacza odrębny pułap możliwości. Właściwość zablokowana przez politykę nigdy nie dociera do kaskady. Zobacz model bezpieczeństwa modułu HTML.
Zgodność
Dział zatytułowany „Zgodność”| Zachowanie | Specyfikacja | Klauzula | reference_id |
|---|---|---|---|
| Sortowanie kaskady: origin/importance → swoistość → kolejność wystąpienia | W3C CSS Cascading and Inheritance Level 5 | §6.4 (css_cascade_5#x1.x7.x1.p21) | |
| Swoistość jako trójka (A,B,C) na podstawie liczby ID/klas/typów | W3C Selectors Level 4 | §16 (selectors_4#x1.x16.p2) | |
| Deterministyczne parsowanie i odzyskiwanie po błędach parsowania | W3C CSS Syntax Level 3 | §4 (css_syntax_3#x1.x4.p2) |
Materiały W3C są objęte licencją CC-BY 4.0. Powyższe stwierdzenia są parafrazą. Identyfikatory klauzul i fragmentów podano w celu weryfikacji. NextPDF nie deklaruje pełnej zgodności z tymi modułami; zweryfikowany stan dla poszczególnych modułów znajdziesz w macierzy wsparcia CSS.
Kontekst komercyjny
Dział zatytułowany „Kontekst komercyjny”Funkcja dla przedsiębiorstw. Premium poszerza zestaw dopasowanych i stosowanych właściwości. Algorytm kaskady i dwuprzebiegowy model
!importantsą identyczne we wszystkich edycjach. Zobacz macierz wsparcia CSS.