قيود التدفّق أحادي المرور في HTML (ADR-001)
لمحة سريعة
قسم بعنوان «لمحة سريعة»يعرض NextPDF مستندات HTML بمرور أمامي واحد، ولا يحتفظ بأي شجرة عناصر في الذاكرة. يوثّق ADR-001 هذا الاختيار والقيود التي يفرضها على كل خاصية من خصائص CSS.
التثبيت
قسم بعنوان «التثبيت»composer require nextpdf/core:^3نظرة مفاهيمية عامة
قسم بعنوان «نظرة مفاهيمية عامة»يعرض نظام HTML الفرعي HTML وCSS إلى PDF عبر مرور تدفّق واحد. إنّ ADR-001 (“Stream-based Rendering Pipeline Retention”، المعتمد 2026-04-06) هو قرار البنية الذي يعرّف هذا النموذج. تشرح هذه الصفحة النموذج وحدوده والقيود التي يجب على المساهمين الالتزام بها.
في هذا النموذج، يقرأ المُجزّئ المعجمي (HtmlTokenizer) المُدخل مرة واحدة وينتج قائمة رموز مسطّحة. يمرّ HtmlParser::processTokens() على تلك القائمة من اليسار إلى اليمين، ويكتب مُعاملات دفق محتوى PDF في مخزّن سلسلة مؤقّت عند بلوغ كلّ عنصر. لا ينشئ المحرّك أي رسم بياني دائم للعناصر بين الاستدعاءات. وأي حالة يجب أن تبقى بعد استدعاء المعالِج تنتقل عبر كائن قيمة لقطة (HtmlBlockCursor)، لا عبر عُقد مشتركة. يستخدم توارث الأنماط مكدّس دفع وإخراج من مثيلات HtmlStyleState المسطّحة، لا شجرة ذات مؤشّرات إلى الأصل.
هذا ليس نموذجًا محتفِظًا بالمستند. لا يحتفظ المحرّك بشجرة مستند، ولا يعيد تخطيط المحتوى الذي كتبه بالفعل، ولا يسمح للمُدخل بأن يتغيّر بعد بدء التحليل. الحدّ واضح: يتدفّق NextPDF من البداية إلى النهاية. يبني العارض المحتفِظ المستند كاملًا في الذاكرة أولًا؛ أمّا NextPDF فلا يفعل ذلك.
تحتاج عمليتان إلى استباق محدود. كلتاهما استثناءان صريحان ومحدودان. يفحص تحديد حجم أعمدة الجدول كلّ صفّ قبل وضع أي خلية. تُخزَّن تلك الصفوف مؤقّتًا في مخزّن جدول زائل داخل TableParser، وهو استثناء يقرّ به ADR-001 بالاسم. يستخدم المُحدِّد العلائقي :has() والمُحدِّدان :last-child و:last-of-type فحصًا مسبقًا محدودًا على قائمة الرموز المسطّحة، لا اجتيازًا للشجرة. يسجّل ADR-001 كلا الاستثناءين وحدودهما.
النموذج آمن للعمل ضمن العُمّال (worker-safe). يُنشأ HtmlParser مرة واحدة لكل طلب، ولا يُستخدم قطّ بوصفه نمطًا مفردًا (singleton). يعيد HtmlParser::parse() تعيين كلّ حقل في بداية كلّ استدعاء. لا توجد حالة ساكنة قابلة للتغيير في مسار العرض، لذلك يستطيع RoadRunner وSwoole وLaravel Octane إعادة استخدام العملية دون أن تتسرّب الحالة بين المستندات.
سطح واجهة برمجة التطبيقات
قسم بعنوان «سطح واجهة برمجة التطبيقات»تفرض الرموز أدناه هذه القيود. تحقّق من كلّ رمز منها مقابل src/Html/.
| الرمز | الموقع | الدور |
|---|---|---|
HtmlParser::parse(string $html): HtmlRenderResult | src/Html/HtmlParser.php | نقطة الدخول. يعيد تعيين كلّ الحالة، ثم يشغّل المرور الواحد. |
HtmlParser::MAX_ELEMENT_COUNT (50_000) | src/Html/HtmlParser.php | حدّ صارم لعدد العناصر المُعالَجة. |
HtmlParser::MAX_NESTING_DEPTH (100) | src/Html/HtmlParser.php | حدّ صارم لعمق التداخل. |
HtmlBlockCursor | src/Html/HtmlBlockCursor.php | لقطة المؤشّر. آلية الحالة المشتركة الوحيدة. |
HtmlStyleState | src/Html/HtmlStyleState.php | إطار نمط مدفوع إلى المكدّس. بلا مؤشّر إلى الأصل. |
TableParser::reset() | src/Html/TableParser.php | إعادة تعيين إلزامية لمخزّن الجدول الزائل بين الجداول. |
مثال برمجي — البدء السريع
قسم بعنوان «مثال برمجي — البدء السريع»لا تُدِر نموذج التدفّق بنفسك مباشرةً. يعرض استدعاء واحد أي مستند مدعوم.
<?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');مثال برمجي — بيئة الإنتاج
قسم بعنوان «مثال برمجي — بيئة الإنتاج»اعرض مستندًا كبيرًا ضمن ميزانية ذاكرة ثابتة. حدّ العناصر هو حدّ الأمان، لذا قدّر حجم المُدخل قبل الاستدعاء.
<?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);}الحالات الحدّية والمزالق
قسم بعنوان «الحالات الحدّية والمزالق»- حدّ العناصر توقُّف صارم. يطرح المحرّك
HtmlParsingExceptionعندMAX_ELEMENT_COUNT = 50_000. قسّم التقارير الكبيرة جدًا إلى استدعاءاتwriteHtml()متعدّدة أو مستندات متعدّدة. - حدّ التداخل توقُّف صارم. يطرح العمق الذي يتجاوز
MAX_NESTING_DEPTH = 100استثناءً. غالبًا ما تتسبّب الأغلفة ذات التداخل العميق في ذلك. - حدّ حجم المُدخل. يرفض
HtmlParser::parse()المُدخل الذي يتجاوز 10 MB قبل التجزئة المعجمية. :has()مُقيّد ببوابة. لا يُشغَّل الفحص المسبق لـ:has()إلا عندما تكون الميزة التجريبيةcss.hasنشطة. من دونها، لا تتطابق مُحدِّدات:has().- التخزين المؤقّت للجداول هو الشجرة الزائلة الوحيدة. يحتفظ جدول واحد عريض جدًا أو طويل جدًا بصفوفه في الذاكرة حتى
render(). يحدّTableParserهذا المخزّن لكل جدول ويعيد تعيينه بين الجداول؛ فهو ليس شجرة على مستوى المستند كاملًا. - لا إعادة تخطيط. لا يُنقَل قطّ المحتوى الذي كُتب بالفعل. لا يستطيع نمط متأخّر أن يغيّر المُخرَج السابق بأثر رجعي.
الأداء
قسم بعنوان «الأداء»يحتفظ نموذج التدفّق بمثيل HtmlStyleState واحد على الأكثر لكل مستوى تداخل، محدودًا بـMAX_NESTING_DEPTH = 100، إضافةً إلى حقول المؤشّر النشطة. ذاكرة حالة النمط والمؤشّر هي O(depth)، لا O(element count). يسجّل ADR-001 قصد التصميم بأن تبقى هذه الذاكرة أدنى بكثير من رسم بياني محتفِظ بالكائنات للمُدخل نفسه. إنّ معيار قياس حجم المجموعة المقيمة الأقصى (RSS) المضبوط عند 50,000 عنصر هو هدف التحقّق التجريبي المسمّى في ADR-001. يتتبّعه معيار قياس أداء مسار عرض HTML ببوابة انحدار 5% (عمل مدموج، PR #564). عامِل performance_budget لكل صفحة (wall_ms: 1500، peak_mb: 64) بوصفه السقف التشغيلي.
ملاحظات أمنية
قسم بعنوان «ملاحظات أمنية»تعمل الحدود في هذه الصفحة أيضًا بوصفها ضوابط لمنع الخدمة (denial-of-service). يفرض DefaultHtmlSecurityPolicy سقفًا للمُدخل قدره 10 MB وسقف تداخل من 100 مستوى بصورة مستقلّة عن المُحلِّل، بحيث لا يستطيع مستند معادٍ استنزاف الذاكرة عبر العمق أو الحجم. ويحدّ نموذج التدفّق نفسه الذاكرة بحكم البناء: لا يوجد رسم بياني للعناصر يمكن لمهاجِم تضخيمه. راجع نموذج أمان وحدة HTML وعقود الطبقات للاطّلاع على سطح السياسة كاملًا.
المطابقة
قسم بعنوان «المطابقة»لا تستشهد هذه الصفحة بأي معيار خارجي. تأتي هذه القيود من ADR-001 ومن الرموز المصدرية المُنفِّذة المُدرَجة ضمن سطح واجهة برمجة التطبيقات. أمّا تعيينات مواصفات CSS السلوكية فموثّقة في css-resolver، لا هنا.
السياق التجاري
قسم بعنوان «السياق التجاري»قدرة المؤسّسات (Enterprise). بنية التدفّق متطابقة في Core وPremium. يوسّع Premium تغطية CSS؛ ولا يغيّر النموذج أحادي المرور ولا يخفّف هذه الحدود. راجع مصفوفة دعم CSS.