CSS 解析器:cascade 與 specificity
CssResolver 會使用選擇器比對 token 串流,將符合的規則依 cascade layer、specificity 與文件順序排序,再於第二輪套用 !important。
composer require nextpdf/core:^3概念總覽
標題為「概念總覽」的區段CssResolver 是 Layer 1 元件(依 ADR-010)。它擁有剖析後的 CSS 規則,負責 resolve(解析)哪些宣告會套用到每個元素。它是為了讓結構更清晰而從 HtmlParser 抽離出的內部類別,不是公開 API。
解析器不需要 document tree 也能運作。選擇器比對會讀取扁平的 token 串流,也會使用 HtmlChildScanner 在 pipeline Stage 3 建立的 Index(索引)對映:子元素數量、同標籤數量與是否為空。結構性 pseudo-class 即由這些對映提供答案。關係型 :has() 選擇器則仰賴 streaming constraints 一節所述的有界限預掃描。
Cascade 解析在 CssResolver::resolveMatchingProperties() 內分兩輪進行。第一輪依 cascade 順序套用一般宣告:先看 cascade layer 權重,再看 specificity,最後看文件順序。第二輪再依 specificity 順序套用 !important 宣告;無論 specificity 高低,!important 宣告都會覆蓋任何一般宣告。這種兩輪拆分是實作策略,產生的已解析屬性集會交由 layout 層使用。
解析器實作的 cascade 順序與 W3C CSS Cascading and Inheritance 規格一致。宣告會先依來源與重要性排序,再依選擇器 specificity 排序。specificity 相等時,文件順序中最後一個宣告勝出(CSS Cascade 5 §6.4;參見「符合性(Conformance)」一節)。CssResolver 原始碼中的內嵌註解也引用同一條款,因此除了規格與詞彙表之外,還為此行為提供第三條佐證路徑。
Specificity 由 ID、class 與 type 元件的數量算出 (A, B, C) 三元組,這些三元組會逐一比較各個元件(Selectors Level 4 §16)。NextPDF 會在 cascade 排序之前,為每條符合的規則計算 specificity。
有一項限制需要明確說明。§6.4.3 的 layer 反轉規則適用於跨 cascade layer 的 !important 宣告,原始碼將它記為 cascade-layer 工作群集中尚待處理的項目。當宣告了 cascade layer,且 !important 跨越 layer 時,解析出的順序可能與完整規格行為不同。CSS support matrix 才是各功能支援狀態的權威來源,本頁不重述各屬性的支援情況。
API 介面
標題為「API 介面」的區段| 符號 | 位置 | 角色 |
|---|---|---|
CssResolver::parseStyleBlock(string $css, bool $nestingEnabled = false): void | src/Html/CssResolver.php | 將單一 <style> 區塊剖析成規則。 |
CssResolver::resolveMatchingProperties(...) | src/Html/CssResolver.php | 比對選擇器並解析兩輪 cascade。 |
CssResolver::resolveHasSelectors(array $tokens): array | src/Html/CssResolver.php | 有界限的 :has() 預掃描(由 gate 控制)。 |
CssResolver::resolveFirstLetterProperties(...) | src/Html/CssResolver.php | 解析 ::first-letter 屬性。 |
CssResolver::resolvePseudoElementProperties(...) | src/Html/CssResolver.php | 解析 ::before / ::after 屬性。 |
CssResolver::getLayerRegistry(): LayerRegistry | src/Html/CssResolver.php | 已宣告的 cascade layer。 |
程式碼範例 — 快速上手
標題為「程式碼範例 — 快速上手」的區段呼叫端不會直接呼叫解析器;他們撰寫 CSS,解析器則在 writeHtml() 內部執行。下面這段 cascade 會將 p 解析為紅色,因為 class 規則的 specificity 高於 type 規則。
<?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');程式碼範例 — 正式環境
標題為「程式碼範例 — 正式環境」的區段示範 !important 的第二輪。即使 class 選擇器的 specificity 較高,等同於 inline 的 class 宣告仍會被帶有 !important 的 type 宣告覆蓋。
<?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');邊界情況與陷阱
標題為「邊界情況與陷阱」的區段!important會忽略 specificity。 第二輪依 specificity 順序套用!important宣告,且它們一律覆蓋一般宣告。- Cascade layer 加上跨 layer 的
!important。 針對重要宣告的 §6.4.3 layer 反轉規則,原始碼將其記為尚待處理。在仰賴此行為之前,請先對照 CSS support matrix 驗證其行為。 - 未宣告任何 layer 是快速路徑。 在沒有
@layer的情況下,排序會簡化為僅依 specificity,且行為會與引入 layer 之前位元層級完全相同。 :has()受 gate 控制。 關係型預掃描只有在啟用css.has這個實驗性功能時才會執行。- 選擇器比對以串流為基礎。 結構性選擇器使用索引對映,而非走訪樹狀結構。若某個選擇器需要超出索引對映能力、任意走訪樹狀結構,在此模型下便無法解析。
選擇器比對在最壞情況下為 O(rules × elements),並受 streaming caps 限制。兩次 cascade 排序對每個元素都是 O(matched rules · log matched rules)。無 layer 的路徑會完全略過 layer 解析。每頁的 performance_budget(wall_ms: 1500、peak_mb: 64)是運作上的上限。效能退化由 HTML render-pipeline 基準測試把關(已合併的工作,PR #564)。
安全性注意事項
標題為「安全性注意事項」的區段解析器只會看到 DefaultHtmlSecurityPolicy::isCssPropertyAllowed() 放行的 CSS。允許清單是安全性上限,而執行時支援表則是另一道能力上限。被原則封鎖的屬性永遠不會抵達 cascade。請參見 HTML 模組安全模型。
符合性(Conformance)
標題為「符合性(Conformance)」的區段| 行為 | 規格 | 條款 | 參考 ID |
|---|---|---|---|
| Cascade 排序:origin/importance → specificity → 出現順序 | W3C CSS Cascading and Inheritance Level 5(CSS 串接與繼承第 5 級規格) | §6.4(css_cascade_5#x1.x7.x1.p21) | |
| 以 ID/class/type 數量算出的 (A,B,C) specificity 三元組 | W3C Selectors Level 4(選擇器第 4 級規格) | §16(selectors_4#x1.x16.p2) | |
| 確定性剖析與剖析錯誤復原 | W3C CSS Syntax Level 3(CSS 語法第 3 級規格) | §4(css_syntax_3#x1.x4.p2) |
W3C 素材採用 CC-BY 4.0 授權。上述聲明皆為改寫。條款與 chunk 識別碼供驗證之用。NextPDF 並未聲稱完全符合這些模組 —— 各模組已驗證的狀態,請參見 CSS support matrix。
商業情境
標題為「商業情境」的區段Enterprise 能力。 Premium 擴大了會被比對與套用的屬性集合。cascade 演算法與兩輪
!important模型,在各版本之間都是一致的。請參見 CSS support matrix。