Bỏ qua để đến nội dung

Hợp đồng tầng cho bộ máy HTML (ADR-010)

Hệ thống con Hypertext Markup Language (HTML) tách phần phân tích Cascading Style Sheets (CSS), trạng thái kiểu, bố cục và tô vẽ thành bốn tầng. Dữ liệu đi theo một hướng qua các tầng này. Architecture Decision Record 010 (ADR-010) định nghĩa các ranh giới và quy tắc mở rộng.

Terminal window
composer require nextpdf/core:^3

Architecture Decision Record 010 (ADR-010) (“Engine Layer Contracts, Hot Path Ownership, and Extension Rules”, được chấp thuận vào 2026-04-12) chính thức hóa mô hình phân tầng của hệ thống con HTML. Hợp đồng kết xuất cốt lõi gồm bốn tầng: phân tích CSS và applicator, trạng thái kiểu, bố cục và định dạng, và tô vẽ. ADR-010 cũng ghi lại hai tầng phụ trợ: paged media và bộ khung đo lường. Hai tầng này bao quanh lõi bốn tầng mà không làm thay đổi luồng dữ liệu của lõi. Thuật ngữ chuẩn trong bảng chú giải cho lõi là “HTML pipeline”, tức một pipeline bốn tầng.

Dữ liệu chảy theo một hướng. Văn bản CSS trở thành các giá trị có kiểu ở Tầng 1. Tầng 1 ghi các giá trị đó vào các trường HtmlStyleState ở Tầng 2. Tầng 3 đọc các trường trạng thái kiểu và tính toán hình học. Tầng 4 đọc một ảnh chụp ComputedStyle bất biến cùng với hình học, rồi phát ra các toán tử Portable Document Format (PDF). Không tầng nào đọc dữ liệu từ tầng đứng sau nó.

Việc tách bốn tầng không chỉ tồn tại trên tài liệu. ADR-010 ghi lại hai lần tái cấu trúc có giới hạn được áp dụng trong v1.2.0 để đưa mã về đúng tầng. PageBorderPainter đã được tách khỏi HtmlParser, nên các toán tử tô vẽ không còn nằm trong bộ điều phối. Docblock của lớp HtmlStyleState hiện mang hợp đồng tầng chính thức và nêu rõ từng tầng được phép ghi hoặc đọc những trường nào.

Có một ranh giới được nêu rõ. FormattingContextFactory::startTable() vẫn đọc trực tiếp năm khóa CSS thô. ADR-010 ghi nhận đây là nợ kỹ thuật đã biết, được hoãn lại cho một TableApplicator trong tương lai, chứ không phải hợp đồng dự kiến. Việc ghi lại ngoại lệ này là một phần của hợp đồng.

TầngTệp (tiêu biểu)GhiĐọcKhông được
1 — Phân tích CSS & applicatorCssValueParser, CssResolver, HtmlCssApplicator, src/Html/Applicator/*HtmlStyleState — các trường CSSVăn bản CSS thôTính toán hình học; phát ra toán tử
2 — Trạng thái kiểuHtmlStyleState, State/ComputedStyle, State/LayoutState— (túi giá trị thụ động)Phân tích CSS; quyết định bố cục; phát ra toán tử
3 — Bố cục & định dạngFormattingContextFactory, HtmlBlockHandler, FlexLayoutEngine, TableParser, FloatContextHình học con trỏHtmlStyleState — các trườngĐọc $css[...] thô; phát ra toán tử tô vẽ
4 — Tô vẽ & kết xuấtBorderRenderer, BackgroundImageRenderer, src/Html/Paint/*, src/Html/Gradient/*Luồng toán tử PDFComputedStyle (bất biến) + hình họcTính toán hình học; phân tích CSS; quyết định ngắt trang
TầngTệp (tiêu biểu)Vai trò
5 — Paged mediaPageBreakController, PageBorderPainter, PageRule, PageRuleParser, ParserConfiguratorPhân giải các quy tắc @page; đánh giá các ràng buộc ngắt trang và orphan/widow; ủy quyền phần trang trí trang cho tầng tô vẽ.
6 — Đo lường & bộ khungCác script phân loại Web Platform Tests (WPT), tests/Support/*Phân loại kết quả kiểm thử; tạo ảnh chụp hồi quy; cung cấp các hàm hỗ trợ assertion. Không chứa logic kết xuất.

Hợp đồng được thực thi qua cách bố trí lớp và docblock HtmlStyleState. Hãy kiểm chứng hợp đồng dựa trên src/Html/.

Ký hiệuTầngVai trò hợp đồng
PropertyApplicatorInterface1Interface chiến lược; nơi duy nhất ghi các trường CSS đã được tính toán.
ParserConfigurator::buildCssApplicator()1 (đấu nối)Đăng ký mọi applicator. Một thuộc tính CSS mới được đăng ký tại đây.
HtmlStyleState2Túi gồm hai nhóm; docblock của lớp nêu rõ tầng sở hữu của từng trường.
HtmlStyleState::toComputedStyle()2Tạo ra ComputedStyle bất biến cho tầng tô vẽ.
FormattingContextFactory::dispatchOpenTag()3Điểm định tuyến duy nhất cho hành vi bố cục mới.
PageBorderPainter::buildStream()4Trang trí trang; được gọi từ Tầng 5, không được nội tuyến trong HtmlParser.

Bạn không bao giờ chạm trực tiếp vào các tầng. Luồng bốn tầng chạy bên trong một lệnh gọi duy nhất.

<?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');

Hợp đồng này quan trọng khi bạn đóng góp mã, chứ không phải khi bạn gọi thư viện. Để thêm một thuộc tính CSS, hãy dùng điểm mở rộng của Tầng 1: tạo một applicator, thêm một trường HtmlStyleState có kiểu kèm docblock tầng, rồi đăng ký applicator trong ParserConfigurator. Minh họa bên dưới cho thấy hình dạng của hợp đồng applicator. Hãy dùng src/Html/Applicator/ làm mẫu cho một lớp cụ thể.

<?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.

Trường hợp đặc biệt & điểm cần lưu ý

Phần tiêu đề “Trường hợp đặc biệt & điểm cần lưu ý”
  • FormattingContextFactory::startTable() đọc CSS thô. Đây là ngoại lệ hợp đồng duy nhất được ghi lại, được hoãn lại cho một TableApplicator trong tương lai. Đừng sao chép khuôn mẫu này.
  • Sáu tầng, lõi bốn tầng. ADR-010 đánh số sáu tầng. Hợp đồng luồng dữ liệu là lõi bốn tầng; paged media và đo lường là các tầng phụ trợ.
  • HtmlStyleState có hai nhóm. Nó mang các trường CSS đã được tính toán và các trường theo dõi bố cục. Chỉ các applicator mới ghi vào nhóm CSS. Tô vẽ đọc ComputedStyle, không bao giờ đọc các trường theo dõi bố cục.
  • HtmlParser không có tầng. Nó là bộ điều phối. Việc phân tích CSS, tính toán hình học và phát ra tô vẽ không được nằm trong nó.

Hợp đồng tầng mang tính cấu trúc, nên không làm tăng chi phí thời gian chạy. HtmlStyleState::toComputedStyle() tạo ra một ảnh chụp bất biến cho mỗi phần tử cần tô vẽ. Ảnh chụp đó giúp mã tô vẽ tránh túi trạng thái có thể thay đổi. Chi phí kết xuất do mô hình streaming chi phối, chứ không phải do việc phân tầng. performance_budget trên mỗi trang (wall_ms: 1500, peak_mb: 64) vẫn là trần vận hành.

Việc tách tầng hỗ trợ mô hình bảo mật. Tầng 1 phân tích và lọc các giá trị CSS theo chính sách trước khi mã bố cục hay tô vẽ nhìn thấy chúng, nên DefaultHtmlSecurityPolicy::isCssPropertyAllowed() vẫn là cổng duy nhất. Tô vẽ không bao giờ đọc CSS thô do kẻ tấn công kiểm soát. Xem mô hình bảo mật của module HTML.

Trang này không trích dẫn tiêu chuẩn bên ngoài nào. Các ranh giới tầng đến từ ADR-010 và docblock của lớp HtmlStyleState, nơi mã hóa hợp đồng trong mã nguồn. Mức tuân thủ hành vi CSS được ghi lại tại css-resolver.

Khả năng Enterprise. Các tính năng CSS của Premium sử dụng chính bốn tầng này thông qua các điểm mở rộng đã được ghi lại. Không có pipeline Premium riêng biệt. Xem ma trận hỗ trợ CSS.