ข้ามไปยังเนื้อหา

สัญญาเลเยอร์ของเอนจิน HTML (ADR-010)

ระบบย่อย Hypertext Markup Language (HTML) แยกการแจงส่วน Cascading Style Sheets (CSS) สถานะสไตล์ เค้าโครง และการ paint ออกเป็นสี่เลเยอร์ ข้อมูลไหลผ่านเลเยอร์เหล่านี้ในทิศทางเดียว Architecture Decision Record 010 (ADR-010) กำหนดขอบเขตและกฎสำหรับการขยาย

Terminal window
composer require nextpdf/core:^3

Architecture Decision Record 010 (ADR-010) (“Engine Layer Contracts, Hot Path Ownership, and Extension Rules” รับรองเมื่อ 2026-04-12) กำหนดอย่างเป็นทางการว่าระบบย่อย HTML แบ่งเลเยอร์อย่างไร สัญญาการเรนเดอร์หลักมีสี่เลเยอร์ ได้แก่ การแจงส่วน CSS และ applicator สถานะสไตล์ เค้าโครงและการจัดรูปแบบ และการ paint ADR-010 ยังบันทึกเลเยอร์เสริมอีกสองเลเยอร์ คือ paged media และ measurement harness เลเยอร์เสริมเหล่านี้ครอบแกนสี่เลเยอร์ไว้โดยไม่เปลี่ยนทิศทางการไหลของข้อมูล คำศัพท์มาตรฐานในอภิธานศัพท์สำหรับแกนนี้คือ “HTML pipeline” ซึ่งเป็น pipeline สี่เลเยอร์

ข้อมูลไหลไปในทิศทางเดียว ข้อความ CSS กลายเป็นค่าที่มีชนิดข้อมูลในเลเยอร์ 1 เลเยอร์ 1 เขียนค่าเหล่านั้นลงในฟิลด์ HtmlStyleState ของเลเยอร์ 2 เลเยอร์ 3 อ่านฟิลด์สถานะสไตล์และคำนวณเรขาคณิต เลเยอร์ 4 อ่านสแนปช็อต ComputedStyle ที่ไม่เปลี่ยนแปลงพร้อมเรขาคณิต แล้วส่งออกตัวดำเนินการ Portable Document Format (PDF) ไม่มีเลเยอร์ใดอ่านข้อมูลจากเลเยอร์ถัดไป

การแยกสี่เลเยอร์ไม่ได้เป็นเพียงเอกสารประกอบ ADR-010 บันทึกการ refactor แบบมีขอบเขตสองครั้งที่ใช้ใน v1.2.0 ซึ่งย้ายโค้ดไปยังเลเยอร์ที่ถูกต้อง PageBorderPainter ถูกแยกออกจาก HtmlParser ดังนั้นตัวดำเนินการ paint จึงไม่อยู่ใน orchestrator อีกต่อไป docblock ของคลาส HtmlStyleState ตอนนี้เก็บสัญญาเลเยอร์อย่างเป็นทางการ และระบุว่าแต่ละเลเยอร์เขียนหรืออ่านฟิลด์ใดได้บ้าง

มีขอบเขตหนึ่งที่ระบุไว้อย่างชัดเจน FormattingContextFactory::startTable() ยังคงอ่านคีย์ CSS ดิบห้าคีย์โดยตรง ADR-010 บันทึกกรณีนี้ไว้ว่าเป็นหนี้ทางเทคนิคที่ทราบแล้วและเลื่อนไปไว้สำหรับ TableApplicator ในอนาคต ไม่ใช่สัญญาตามที่ตั้งใจไว้ การบันทึกข้อยกเว้นนี้เป็นส่วนหนึ่งของสัญญา

เลเยอร์ไฟล์ (ตัวแทน)เขียนอ่านต้องไม่
1 — การแจงส่วน CSS & applicatorCssValueParser, CssResolver, HtmlCssApplicator, src/Html/Applicator/*HtmlStyleState ฟิลด์ CSSข้อความ CSS ดิบคำนวณเรขาคณิต ส่งออกตัวดำเนินการ
2 — สถานะสไตล์HtmlStyleState, State/ComputedStyle, State/LayoutState— (ถุงค่าแบบเฉื่อย)แจงส่วน CSS ตัดสินใจเค้าโครง ส่งออกตัวดำเนินการ
3 — เค้าโครง & การจัดรูปแบบFormattingContextFactory, HtmlBlockHandler, FlexLayoutEngine, TableParser, FloatContextเรขาคณิตของเคอร์เซอร์HtmlStyleState ฟิลด์อ่าน $css[...] ดิบ ส่งออกตัวดำเนินการ paint
4 — Paint & การเรนเดอร์BorderRenderer, BackgroundImageRenderer, src/Html/Paint/*, src/Html/Gradient/*สตรีมตัวดำเนินการ PDFComputedStyle (ไม่เปลี่ยนแปลง) + เรขาคณิตคำนวณเรขาคณิต แจงส่วน CSS ตัดสินใจเรื่องการแบ่งหน้า
เลเยอร์ไฟล์ (ตัวแทน)บทบาท
5 — Paged mediaPageBreakController, PageBorderPainter, PageRule, PageRuleParser, ParserConfiguratorแก้ไขกฎ @page ประเมินข้อจำกัดการแบ่งหน้าและ orphan/widow แล้วมอบหมายการตกแต่งหน้าให้ paint
6 — การวัด & harnessสคริปต์ตัวจำแนกของ Web Platform Tests (WPT), tests/Support/*จำแนกผลลัพธ์การทดสอบ สร้างสแนปช็อตการถดถอย และให้ตัวช่วยยืนยัน ไม่มีตรรกะการเรนเดอร์ใด ๆ

สัญญานี้บังคับใช้ผ่านตำแหน่งของคลาสและ docblock ของ HtmlStyleState เทียบกับ src/Html/ สำหรับการตรวจสอบ

สัญลักษณ์เลเยอร์บทบาทตามสัญญา
PropertyApplicatorInterface1อินเทอร์เฟซแบบ strategy เป็นจุดเดียวที่เขียนฟิลด์ CSS แบบ computed
ParserConfigurator::buildCssApplicator()1 (การเดินสาย)ลงทะเบียน applicator ทุกตัว พร็อพเพอร์ตี CSS ใหม่ลงทะเบียนที่นี่
HtmlStyleState2ถุงแบบสองกลุ่ม docblock ของคลาสระบุเลเยอร์ที่เป็นเจ้าของของแต่ละฟิลด์
HtmlStyleState::toComputedStyle()2สร้าง ComputedStyle ที่ไม่เปลี่ยนแปลงสำหรับเลเยอร์ paint
FormattingContextFactory::dispatchOpenTag()3จุดกำหนดเส้นทางเดียวสำหรับพฤติกรรมเค้าโครงใหม่
PageBorderPainter::buildStream()4การตกแต่งหน้า เรียกจากเลเยอร์ 5 โดยไม่ฝังไว้ใน HtmlParser โดยตรง

คุณไม่จำเป็นต้องแตะเลเยอร์เหล่านี้โดยตรง การไหลแบบสี่เลเยอร์ทำงานอยู่ภายในการเรียกเพียงครั้งเดียว

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->addPage();
$doc->writeHtml('<p style="color:#1E3A8A;border:1px solid #999;">Layered render.</p>');
$doc->save(__DIR__ . '/output/layers.pdf');

สัญญานี้สำคัญเมื่อคุณมีส่วนร่วมในการพัฒนา ไม่ใช่เมื่อคุณเรียกใช้ไลบรารี หากต้องการเพิ่มพร็อพเพอร์ตี CSS ให้ใช้จุดขยายของเลเยอร์ 1 โดยสร้าง applicator เพิ่มฟิลด์ HtmlStyleState ที่มีชนิดข้อมูลพร้อม docblock ของเลเยอร์ และลงทะเบียน applicator ใน ParserConfigurator ภาพประกอบด้านล่างแสดงรูปแบบของสัญญา applicator ให้ใช้ src/Html/Applicator/ เป็นแบบจำลองสำหรับคลาสที่เป็นรูปธรรม

<?php
declare(strict_types=1);
// Layer 1 extension contract (see ADR-010 §C "New CSS property").
// A new property group ships as a PropertyApplicatorInterface
// implementation registered in ParserConfigurator::buildCssApplicator().
// It writes a typed HtmlStyleState field and never computes geometry
// or emits PDF operators — those belong to Layers 3 and 4.
  • FormattingContextFactory::startTable() อ่าน CSS ดิบ นี่คือข้อยกเว้นเดียวของสัญญาที่บันทึกไว้ และถูกเลื่อนไปยัง TableApplicator ในอนาคต อย่าลอกเลียนแบบกรณีนี้
  • หกเลเยอร์ แกนสี่เลเยอร์ ADR-010 นับรวมเป็นหกเลเยอร์ สัญญาการไหลของข้อมูลคือแกนสี่เลเยอร์ ส่วน paged media และ measurement เป็นเลเยอร์เสริม
  • HtmlStyleState เป็นแบบสองกลุ่ม โครงสร้างนี้บรรจุฟิลด์ CSS แบบ computed และฟิลด์ติดตามเค้าโครง มีเพียง applicator เท่านั้นที่เขียนกลุ่ม CSS Paint อ่าน ComputedStyle ไม่ใช่ฟิลด์ติดตามเค้าโครง
  • HtmlParser ไม่มีเลเยอร์ เป็น orchestrator การแจงส่วน CSS การคำนวณเรขาคณิต และการส่งออก paint ต้องไม่อยู่ในนั้น

สัญญาเลเยอร์เป็นโครงสร้าง จึงไม่เพิ่มต้นทุนระหว่างทำงาน HtmlStyleState::toComputedStyle() สร้างสแนปช็อตที่ไม่เปลี่ยนแปลงหนึ่งชุดสำหรับแต่ละองค์ประกอบที่ต้องการ paint สแนปช็อตนั้นช่วยให้โค้ด paint หลีกเลี่ยงถุงสถานะที่เปลี่ยนแปลงได้ ต้นทุนการเรนเดอร์ถูกควบคุมโดยโมเดลการสตรีม ไม่ใช่โดยการแบ่งเลเยอร์ performance_budget ต่อหน้า (wall_ms: 1500, peak_mb: 64) ยังคงเป็นเพดานการทำงาน

การแยกเลเยอร์สนับสนุนโมเดลความปลอดภัย เลเยอร์ 1 แจงส่วนและกรองค่า CSS ตามนโยบายก่อนที่โค้ดเค้าโครงหรือ paint จะเห็น ดังนั้น DefaultHtmlSecurityPolicy::isCssPropertyAllowed() จึงยังคงเป็นประตูเดียว Paint ไม่เคยอ่าน CSS ดิบที่ผู้โจมตีควบคุม ดูรายละเอียดเพิ่มเติมในโมเดลความปลอดภัยของโมดูล HTML

หน้านี้ไม่ได้อ้างอิงมาตรฐานภายนอกใด ๆ ขอบเขตของเลเยอร์มาจาก ADR-010 และ docblock ของคลาส HtmlStyleState ซึ่งเข้ารหัสสัญญาไว้ในซอร์สโค้ดแล้ว ความสอดคล้องเชิงพฤติกรรมของ CSS บันทึกไว้ในcss-resolver

ความสามารถระดับองค์กร ฟีเจอร์ CSS ของ Premium ใช้สี่เลเยอร์เดียวกันนี้ผ่านจุดขยายที่บันทึกไว้ ไม่มี pipeline ของ Premium ที่แยกต่างหาก ดูเมทริกซ์การรองรับ CSS