ตัวแก้ไข CSS: ลำดับการแสดงผลและความจำเพาะ
โดยสรุป
หัวข้อที่มีชื่อว่า “โดยสรุป”คลาส CssResolver จับคู่ selector กับสตรีมของโทเค็น จัดลำดับกฎที่จับคู่ได้ตามเลเยอร์การแสดงผล ความจำเพาะ และลำดับในเอกสาร จากนั้นจึงนำ !important มาใช้ในรอบที่สอง
การติดตั้ง
หัวข้อที่มีชื่อว่า “การติดตั้ง”composer require nextpdf/core:^3ภาพรวมเชิงแนวคิด
หัวข้อที่มีชื่อว่า “ภาพรวมเชิงแนวคิด”CssResolver คือคอมโพเนนต์ของ Layer 1 (ตาม ADR-010) คอมโพเนนต์นี้ถือครองกฎ Cascading Style Sheets (CSS) ที่แจงแล้ว และตัดสินว่าการประกาศใดมีผลกับแต่ละเอลิเมนต์ คลาสนี้แยกออกมาจาก HtmlParser เพื่อให้โครงสร้างชัดเจน และเป็นส่วนภายใน ไม่ใช่ application programming interface (API) สาธารณะ
ตัวแก้ไขไม่จำเป็นต้องใช้ทรีของเอกสาร การจับคู่ selector อ่านสตรีมโทเค็นแบบแบนและใช้แมปดัชนีที่ HtmlChildScanner สร้างขึ้นใน pipeline Stage 3 ได้แก่จำนวนเอลิเมนต์ลูก จำนวนแท็กชนิดเดียวกัน และสถานะว่างเปล่า แมปเหล่านี้รองรับ pseudo-class เชิงโครงสร้าง ส่วน selector เชิงสัมพันธ์ :has() ใช้การพรีสแกนแบบมีขอบเขตตามที่ ข้อจำกัดของการสตรีม อธิบายไว้
การแก้ไขลำดับการแสดงผลทำงานเป็นสองรอบภายใน CssResolver::resolveMatchingProperties() รอบที่ 1 นำการประกาศแบบปกติมาใช้ตามลำดับการแสดงผล โดยเรียงตามน้ำหนักของเลเยอร์การแสดงผลก่อน ตามด้วยความจำเพาะ แล้วจึงเป็นลำดับในเอกสาร รอบที่ 2 นำการประกาศ !important มาใช้ตามลำดับความจำเพาะ การประกาศ !important จะแทนที่การประกาศแบบปกติใด ๆ โดยไม่คำนึงถึงความจำเพาะ การแบ่งเป็นสองรอบนี้คือกลยุทธ์ในการนำไปใช้งาน และให้ชุดคุณสมบัติที่แก้ไขแล้วสำหรับเลเยอร์เค้าโครงนำไปใช้
ลำดับการแสดงผลที่ตัวแก้ไขนำไปใช้สอดคล้องกับข้อกำหนด CSS Cascading and Inheritance ของ World Wide Web Consortium (W3C) การประกาศจะถูกจัดลำดับตามแหล่งที่มาและความสำคัญก่อน จากนั้นจึงตามความจำเพาะของ selector เมื่อความจำเพาะเท่ากัน การประกาศลำดับสุดท้ายในเอกสารจะชนะ (CSS Cascade 5 §6.4 ดูที่ความสอดคล้อง) ความคิดเห็นในซอร์สโค้ดของ CssResolver อ้างถึงข้อกำหนดเดียวกัน จึงใช้เป็นวิธีตรวจสอบพฤติกรรมนี้ได้อีกทางหนึ่ง ควบคู่ไปกับข้อกำหนดและอภิธานศัพท์
ความจำเพาะถูกคำนวณเป็นชุดสามค่า (A, B, C) จากจำนวนส่วนประกอบ ID, class และ type แล้วเปรียบเทียบชุดสามค่านั้นทีละส่วนประกอบ (Selectors Level 4 §16) NextPDF คำนวณความจำเพาะของกฎที่จับคู่ได้แต่ละข้อก่อนจัดลำดับการแสดงผล
มีข้อจำกัดสำคัญหนึ่งข้อ กฎการกลับลำดับเลเยอร์ §6.4.3 ใช้กับการประกาศ !important ที่ข้ามเลเยอร์การแสดงผล และซอร์สบันทึกไว้ว่าเป็นงานที่ยังค้างอยู่สำหรับกลุ่มงานเลเยอร์การแสดงผล เมื่อมีการประกาศเลเยอร์การแสดงผลและมี !important ข้ามเลเยอร์ ลำดับที่แก้ไขได้อาจแตกต่างจากพฤติกรรมตามข้อกำหนดฉบับเต็ม เมทริกซ์การรองรับ CSS เป็นแหล่งอ้างอิงสถานะการรองรับของแต่ละฟีเจอร์ และหน้านี้ไม่ได้กล่าวซ้ำถึงการรองรับของแต่ละคุณสมบัติ
พื้นผิว API
หัวข้อที่มีชื่อว่า “พื้นผิว API”| สัญลักษณ์ | ตำแหน่ง | บทบาท |
|---|---|---|
CssResolver::parseStyleBlock(string $css, bool $nestingEnabled = false): void | src/Html/CssResolver.php | แจงบล็อก <style> เป็นกฎ |
CssResolver::resolveMatchingProperties(...) | src/Html/CssResolver.php | จับคู่ selector และแก้ไขการเรียงลำดับการแสดงผลแบบสองรอบ |
CssResolver::resolveHasSelectors(array $tokens): array | src/Html/CssResolver.php | การพรีสแกน :has() แบบมีขอบเขต (เปิดใช้แบบมีเงื่อนไข) |
CssResolver::resolveFirstLetterProperties(...) | src/Html/CssResolver.php | แก้ไขคุณสมบัติของ ::first-letter แบบซูโดเอลิเมนต์ |
CssResolver::resolvePseudoElementProperties(...) | src/Html/CssResolver.php | แก้ไขคุณสมบัติของ ::before / ::after แบบซูโดเอลิเมนต์ |
CssResolver::getLayerRegistry(): LayerRegistry | src/Html/CssResolver.php | เลเยอร์การแสดงผลที่ประกาศไว้ |
ตัวอย่างโค้ด — เริ่มต้นอย่างรวดเร็ว
หัวข้อที่มีชื่อว่า “ตัวอย่างโค้ด — เริ่มต้นอย่างรวดเร็ว”คุณไม่ได้เรียกใช้ตัวแก้ไขโดยตรง แต่เขียน CSS และตัวแก้ไขจะทำงานภายใน writeHtml() ในลำดับการแสดงผลด้านล่าง p ถูกแก้ไขเป็นสีแดง เนื่องจากกฎของ class มีความจำเพาะสูงกว่ากฎของ 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 การประกาศ type ที่เป็น !important จะแทนที่การประกาศ class ที่เทียบเท่าแบบอินไลน์ แม้ว่า selector ของ class จะมีความจำเพาะสูงกว่าก็ตาม
<?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ไม่คำนึงถึงความจำเพาะ รอบที่ 2 นำการประกาศ!importantมาใช้ตามลำดับความจำเพาะ และการประกาศเหล่านั้นจะแทนที่การประกาศแบบปกติเสมอ- เลเยอร์การแสดงผล +
!importantที่ข้ามเลเยอร์ ซอร์สบันทึกกฎการกลับลำดับเลเยอร์ §6.4.3 สำหรับการประกาศ important ไว้ว่ายังค้างอยู่ ตรวจสอบพฤติกรรมกับ เมทริกซ์การรองรับ CSS ก่อนนำไปใช้เป็นเงื่อนไขที่ต้องพึ่งพา - การไม่ประกาศเลเยอร์คือเส้นทางที่รวดเร็ว เมื่อไม่มี
@layerการจัดลำดับจะลดเหลือเพียงพฤติกรรมตามความจำเพาะเท่านั้น และเหมือนกันทุกบิตกับพฤติกรรมก่อนมีเลเยอร์ :has()เปิดใช้แบบมีเงื่อนไข การพรีสแกนเชิงสัมพันธ์จะทำงานเฉพาะเมื่อเปิดใช้ฟีเจอร์ทดลองcss.hasเท่านั้น- การจับคู่ selector อิงตามสตรีม selector เชิงโครงสร้างใช้แมปดัชนี ไม่ใช่การเดินทรี selector ที่ต้องการนำทางในทรีโดยพลการเกินกว่าแมปดัชนีจะรองรับ จะไม่สามารถแก้ไขได้ในโมเดลนี้
ประสิทธิภาพ
หัวข้อที่มีชื่อว่า “ประสิทธิภาพ”ในกรณีที่แย่ที่สุด การจับคู่ selector คือ O(rules × elements) ซึ่งถูกจำกัดด้วย เพดานการสตรีม การจัดลำดับการแสดงผลทั้งสองครั้งคือ O(matched rules · log matched rules) ต่อหนึ่งเอลิเมนต์ เส้นทางแบบไม่มีเลเยอร์จะข้ามการแก้ไขเลเยอร์ทั้งหมด performance_budget ต่อหน้า (wall_ms: 1500, peak_mb: 64) กำหนดเพดานการทำงาน เบนช์มาร์กของไปป์ไลน์การเรนเดอร์ HTML ใช้ป้องกันการถดถอย (งานที่ผสานแล้ว PR #564)
หมายเหตุด้านความปลอดภัย
หัวข้อที่มีชื่อว่า “หมายเหตุด้านความปลอดภัย”ตัวแก้ไขจะเห็นเฉพาะ CSS ที่ DefaultHtmlSecurityPolicy::isCssPropertyAllowed() อนุญาตเท่านั้น allowlist กำหนดเพดานด้านความปลอดภัย และตารางการรองรับในรันไทม์กำหนดเพดานความสามารถแยกต่างหาก คุณสมบัติที่ถูกนโยบายบล็อกจะไม่มีทางเข้าสู่การจัดลำดับการแสดงผล ดู โมเดลความปลอดภัยของโมดูล HTML เพิ่มเติม
ความสอดคล้อง
หัวข้อที่มีชื่อว่า “ความสอดคล้อง”| พฤติกรรม | ข้อกำหนด | ข้อ | ตัวระบุอ้างอิง (reference_id) |
|---|---|---|---|
| การเรียงลำดับการแสดงผล: origin/importance → ความจำเพาะ → ลำดับการปรากฏ | ข้อกำหนด W3C CSS Cascading and Inheritance Level 5 | §6.4 (css_cascade_5#x1.x7.x1.p21) | |
| ความจำเพาะในรูปชุดสามค่า (A,B,C) จากจำนวน ID/class/type | W3C Selectors Level 4 | §16 (selectors_4#x1.x16.p2) | |
| การแจงแบบกำหนดผลแน่นอนและการกู้คืนจากข้อผิดพลาดในการแจง | W3C CSS Syntax Level 3 | §4 (css_syntax_3#x1.x4.p2) |
เนื้อหาของ W3C อยู่ภายใต้สัญญาอนุญาต CC-BY 4.0 ข้อความข้างต้นเป็นการเรียบเรียงใหม่ และระบุตัวระบุข้อกับตัวระบุชังก์ไว้เพื่อการตรวจสอบ NextPDF ไม่ได้อ้างความสอดคล้องอย่างเต็มรูปแบบกับโมดูลเหล่านี้ ดู เมทริกซ์การรองรับ CSS สำหรับสถานะที่ตรวจสอบแล้วของแต่ละโมดูล
บริบทเชิงพาณิชย์
หัวข้อที่มีชื่อว่า “บริบทเชิงพาณิชย์”ความสามารถระดับองค์กร Premium ขยายชุดคุณสมบัติที่จับคู่และนำไปใช้ให้กว้างขึ้น อัลกอริทึมการเรียงลำดับการแสดงผลและโมเดล
!importantแบบสองรอบเหมือนกันในทุกรุ่น ดู เมทริกซ์การรองรับ CSS เพิ่มเติม