İçeriğe geç

HTML'de tek geçişli akış kısıtlamaları (ADR-001)

NextPDF, HyperText Markup Language (HTML) içeriğini tek bir ileri geçişle işler ve bellekte hiçbir öğe ağacı tutmaz. ADR-001, bu kararı ve Cascading Style Sheets (CSS) özellikleri için doğurduğu kısıtlamaları belgeler.

Terminal window
composer require nextpdf/core:^3

HTML alt sistemi, HTML ve CSS içeriğini tek bir akış geçişiyle Portable Document Format (PDF) biçimine işler. ADR-001 (“Stream-based Rendering Pipeline Retention”, kabul tarihi 2026-04-06), bu modeli tanımlayan mimari karardır. Bu sayfa, modeli, sınırlarını ve katkıda bulunanların uyması gereken kısıtlamaları açıklar.

Bu modelde belirteç çözümleyici (HtmlTokenizer), girişi bir kez okur ve düz bir belirteç listesi üretir. HtmlParser::processTokens() bu listeyi soldan sağa dolaşır ve her öğeye ulaştıkça PDF içerik akışı işleçlerini bir dize arabelleğine yazar. Motor, çağrılar arasında kalıcı bir öğe grafiği oluşturmaz. Bir işleyici çağrısından sonra korunması gereken her durum, paylaşılan düğümler üzerinden değil, anlık görüntü değer nesnesi (HtmlBlockCursor) üzerinden taşınır. Stil kalıtımı, üst işaretçi ağacı yerine, düz HtmlStyleState örneklerinden oluşan, öğe ekleme ve çıkarma (push-and-pop) ilkesiyle çalışan bir yığın kullanır.

Bu, bellekte tutulan bir belge modeli değildir. Motor bir belge ağacı tutmaz, halihazırda yazdığı içeriği yeniden yerleştirmez ve ayrıştırma başladıktan sonra girişin değişmesine izin vermez. Sınır açıktır: NextPDF baştan sona akış yapar. Bellekte tutma yaklaşımını kullanan bir işleyici, önce tüm belgeyi bellekte oluşturur; NextPDF bunu yapmaz.

İki işlem sınırlı bir ileri bakış gerektirir. İkisi de açıkça belirtilmiş, sınırları belli istisnalardır. Tablo sütun boyutlandırması, bir hücre yerleştirmeden önce her satırı tarar. Bu satırları TableParser içindeki geçici bir tablo arabelleğinde tamponlar; bu, ADR-001’in açıkça adıyla kabul ettiği bir istisnadır. :has() ilişkisel seçici ile :last-child ve :last-of-type seçicileri, bir ağaç dolaşımı yerine düz belirteç listesi üzerinde sınırlı bir ön tarama kullanır. ADR-001, her iki istisnayı ve bunların sınırlarını belgeler.

Model, worker süreçleri için güvenlidir. HtmlParser, her istek için bir kez oluşturulur; asla tekil (singleton) olarak oluşturulmaz. HtmlParser::parse(), her çağrının başında tüm alanları sıfırlar. İşleme yolunda statik, değiştirilebilir hiçbir durum bulunmaz; bu nedenle RoadRunner, Swoole ve Laravel Octane, belgeler arasında durum sızması olmadan süreci yeniden kullanabilir.

Aşağıdaki semboller bu kısıtlamaları zorunlu kılar. Her birini src/Html/ diziniyle karşılaştırarak doğrulayın.

SembolKonumRol
HtmlParser::parse(string $html): HtmlRenderResultsrc/Html/HtmlParser.phpGiriş noktası. Tüm durumu sıfırlar, ardından tek geçişi çalıştırır.
HtmlParser::MAX_ELEMENT_COUNT (50_000)src/Html/HtmlParser.phpİşlenen öğe sayısı için kesin üst sınır.
HtmlParser::MAX_NESTING_DEPTH (100)src/Html/HtmlParser.phpİç içe geçme derinliği için kesin üst sınır.
HtmlBlockCursorsrc/Html/HtmlBlockCursor.phpİmleç anlık görüntüsü. Tek paylaşılan durum mekanizması.
HtmlStyleStatesrc/Html/HtmlStyleState.phpYığına eklenen stil çerçevesi. Üst işaretçi yoktur.
TableParser::reset()src/Html/TableParser.phpTablolar arasında geçici tablo arabelleğinin zorunlu olarak sıfırlanması.

Akış modelini doğrudan yönetmezsiniz. Tek bir çağrı, desteklenen herhangi bir belgeyi işler.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('Streaming render');
$doc->addPage();
$doc->writeHtml('<h1>One forward pass</h1><p>No retained tree.</p>');
$doc->save(__DIR__ . '/output/streaming.pdf');

Büyük bir belgeyi sabit bir bellek bütçesi içinde işleyin. Öğe üst sınırı bir güvenlik sınırıdır; bu nedenle giriş boyutunu çağrıdan önce sınırlandırın.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
use NextPDF\Exception\HtmlParsingException;
/**
* Render trusted HTML, surfacing the streaming-model limits as typed errors.
*
* @param non-empty-string $html
*/
function renderReport(string $html, string $out): void
{
$doc = Document::createStandalone();
$doc->addPage();
try {
$doc->writeHtml($html);
} catch (HtmlParsingException $e) {
// Thrown on the 10 MB input cap, the 50,000-element cap,
// or the 100-level nesting cap. These are model boundaries,
// not transient faults — do not retry.
throw $e;
}
$doc->save($out);
}
  • Öğe üst sınırı kesin bir durdurma sınırıdır. Motor, HtmlParsingException istisnasını MAX_ELEMENT_COUNT = 50_000 değerinde fırlatır. Çok büyük raporları birden çok writeHtml() çağrısına veya birden çok belgeye bölün.
  • İç içe geçme üst sınırı kesin bir durdurma sınırıdır. MAX_NESTING_DEPTH = 100 değerinin üzerindeki derinlik istisna fırlatır. Buna genellikle çok derin iç içe geçmiş sarmalayıcılar yol açar.
  • Giriş boyutu üst sınırı. HtmlParser::parse(), belirteçlere ayırmadan önce 10 MB’tan büyük girişi reddeder.
  • :has() kısıtlamalıdır. :has() ön taraması, yalnızca css.has deneysel özelliği etkin olduğunda çalışır. Bu etkin değilse :has() seçicileri eşleşmez.
  • Tablo tamponlaması, tek geçici ağaçtır. Çok geniş veya çok uzun tek bir tablo, satırlarını render() çağrısına kadar bellekte tutar. TableParser, bu arabelleği her tablo için sınırlar ve tablolar arasında sıfırlar; bu, belge genelinde bir ağaç değildir.
  • Yeniden yerleştirme yoktur. Halihazırda yazılmış içerik asla taşınmaz. Geç gelen bir stil, daha önceki çıktıyı geriye dönük olarak değiştiremez.

Akış modeli, her iç içe geçme düzeyi için en fazla bir HtmlStyleState tutar; bu sayı MAX_NESTING_DEPTH = 100 ile sınırlıdır. Ayrıca etkin imleç alanları da tutulur. Stil durumu ve imleç belleği O(öğe sayısı) değil, O(derinlik) düzeyindedir. ADR-001, bunun aynı giriş için bellekte tutulan bir nesne grafiğinin oldukça altında kalması yönündeki tasarım amacını belgeler. Tepe yerleşik küme boyutu (RSS) için kontrollü 50,000 öğelik karşılaştırma, ADR-001’de adı geçen ampirik doğrulama hedefidir. HTML işleme hattı performans karşılaştırması, bunu %5’lik bir gerileme eşiğiyle izler (birleştirilmiş çalışma, PR #564). Sayfa başına performance_budget değerini (wall_ms: 1500, peak_mb: 64) operasyonel üst sınır olarak değerlendirin.

Bu sayfadaki üst sınırlar, aynı zamanda hizmet engelleme (denial-of-service) denetimleri görevi de görür. DefaultHtmlSecurityPolicy, 10 MB’lik giriş üst sınırını ve 100 düzeylik iç içe geçme üst sınırını ayrıştırıcıdan bağımsız olarak zorunlu kılar; böylece kötü niyetli bir belge, derinlik veya boyut yoluyla belleği tüketemez. Akış modeli, yapısı gereği belleği sınırlar: bir saldırganın şişirebileceği bir öğe grafiği yoktur. İlkenin kapsadığı yüzeyin tamamı için HTML modülü güvenlik modeli ve katman sözleşmeleri bölümlerine bakın.

Bu sayfa herhangi bir harici standarda atıfta bulunmaz. Bu kısıtlamalar, ADR-001’den ve API yüzeyi altında listelenen, bunları zorunlu kılan kaynak sembollerinden gelir. CSS davranışlarının belirtim eşlemeleri burada değil, css-resolver sayfasında belgelenmiştir.

Kurumsal yetenek. Akış mimarisi, Core ve Premium sürümlerinde aynıdır. Premium, CSS kapsamını genişletir; tek geçişli modeli değiştirmez veya bu üst sınırları gevşetmez. CSS destek matrisi bölümüne bakın.