Pro الإصدار
Stream
لمحة سريعة
قسم بعنوان «لمحة سريعة»تُصيِّر وحدة Stream دفعات من المستندات بدوام وبتزامن، مع التزام محلّي مرّة واحدة بالضبط إلى مخازن دائمة أحادية المضيف (المرّة الواحدة بالضبط عبر مضيفين متعدّدين هي حدّ Enterprise Stream). وهي تقسّم العمل إلى مسؤوليتين مفصولتين بنظافة: محرّك تصيير يحوّل البيانات الوصفية المُتحقَّق منها إلى بايتات (ولا شيء سوى ذلك)، ومجموعة من المخازن الدائمة — المُلتزِم، ونقطة التفتيش، وخاصية العدم التأثيرية، والرسائل الميتة — تنشر تلك البايتات بأمان وتتيح لتشغيلٍ ما الاستئنافَ بعد عطل من دون إعادة نشر المخرَجات الملتزَم بها.
التوفّر والترخيص
قسم بعنوان «التوفّر والترخيص»تُشحَن هذه الإمكانية في NextPDF Pro (nextpdf/pro) وتُفعَّل بمظروف ترخيص من فئة Pro. والنشر من دون ذلك الاستحقاق لا يحمّل أصناف الإمكانية. قارِن الإصدارات واحصل على ترخيص.
لا توجد علامة ترخيص منفصلة لكل ميزة. والتزامن (عدد العمّال)، وحجم الدفعة، وميزانية إعادة المحاولة، والخلفية التخزينية للمخزن (في الذاكرة مقابل نظام ملفات دائم) معامِلات وقت تشغيل، لا مفاتيح ترخيص.
التثبيت
قسم بعنوان «التثبيت»composer require nextpdf/pro:^3تعيش الشيفرة ضمن النطاق الاسمي NextPDF\Pro\Stream.
نظرة مفاهيمية عامة
قسم بعنوان «نظرة مفاهيمية عامة»تُنظَّم Stream حول وصلة مُجمَّدة — NextPDF\Pro\Stream\Engine\RenderEngineInterface — تفصل محرّك الإنتاجية عن دلالات التدفّق:
- يملك محرّك التصيير التزامنَ والذاكرة المحدودة. فهو يُصيِّر نافذة من البيانات الوصفية المُتحقَّق منها والمُزال تكرارها مسبقًا عبر
renderBatch()ويُرجِع نتيجةEngineRenderResultواحدة لكل بيانات وصفية، بترتيب الإدخال. والأهمّ أنّ المحرّك خالٍ من الآثار الجانبية فيما يخصّ المخرَج النهائي: فهو يُرجِع البايتات المُصيَّرة مع بصمة sha-256 الخاصّة بها، ولا يكتب أبدًا إلى مفتاح كائن نهائي. وهذه النقاوة هي ما يجعل التسليم مرّة واحدة بالضبط ممكنًا. - يملك متعاونو التدفّق التسليمَ. فالمُلتزِم، ومخزن نقطة التفتيش، ومخزن خاصية العدم التأثيرية (إزالة التكرار)، ومخزن الرسائل الميتة تقرّر أين تستقرّ البايتات، وكيف يُستأنَف تشغيلٌ ما، وأيُّ عملٍ هو إعادة تشغيل، وما الذي يحدث لحالات الفشل النهائية.
يُبلَّغ عن فشل تصيير لكل بيانات وصفية كنتيجة Failed (أو Timeout) لكل عنصر؛ ولا يُجهِض الدفعة أبدًا. وينجح غلاف الدفعة دائمًا بنتائج لكل عنصر.
المفاهيم الرئيسية
قسم بعنوان «المفاهيم الرئيسية»محرّكات التصيير والمُنفِّذات
قسم بعنوان «محرّكات التصيير والمُنفِّذات»InProcessRenderEngineهو الخطّ المرجعي المتزامن أحادي العملية للصحّة. وهو يتحقّق من كلّ بيانات وصفية تحقُّقًا مُغلَقًا بأمان عبرRenderManifestValidatorالمشحون قبل تصييرها عبرSingleDocumentRendererفي Core، فتصير البيانات الوصفية الفاسدة فشلًا لكل عنصر (رمز الخطأSPEC-MANIFEST-INVALID) بدلًا من وصولها إلى المُصيِّر.- يُوزِّع
ConcurrentRenderEngineدفعةً علىRenderUnitExecutorInterfaceويستعيد ترتيب الدفعة الحتمي بحسب فهرس الوحدة. والمخرَج متطابق بايتيًا مع تصيير متسلسل بصرف النظر عن ترتيب الإكمال؛ وأيُّ إكمال مفقود أو مكرَّر أو مجهول هو فشل صارم، لا إسقاط صامت أبدًا. - المُنفِّذات هي وصلة التزامن.
InlineRenderUnitExecutorهو الخطّ المرجعي الحتمي؛ ويُوزِّعProcessPoolRenderUnitExecutorدفعةً على عدد يصل إلى N من العمليات الفرعية العاملةphpالتي تُصيِّر بالتوازي، ثمّ يجمع نتائجها ويتحقّق من سلامتها.
التزام دائم خالٍ من الآثار الجانبية
قسم بعنوان «التزام دائم خالٍ من الآثار الجانبية»ينشر OutputCommitterInterface::commit() البايتات المُصيَّرة إلى وجهتها النهائية مرّة واحدة بالضبط: ذرّيًّا (لا يُلاحَظ كائن جزئي أبدًا)، وبخاصية العدم التأثيرية (إعادة الالتزام بمحتوى متطابق بايتيًا لا تنفّذ أي كتابة وتُرجِع CommitReceipt بقيمة idempotentReuse = true — إيصال جديد، لا الأصلي)، ومن دون طمس صامت (البايتات المتباعدة إلى مفتاح مشغول من دون overwrite تُثير تعارضًا)، ومُتحقَّقًا من سلامته (يعيد المُلتزِم حساب البصمة قبل الكتابة). ويُنفِّذ LocalFilesystemCommitter هذا لنظام الملفات المحلّي.
الاستعادة من نقاط التفتيش
قسم بعنوان «الاستعادة من نقاط التفتيش»RunCheckpoint هي حاجز دائم يسجّل عدد العناصر التي التزم بها تشغيلٌ ما إضافةً إلى لقطة للحالة المُفتَّحة. وعند الاستعادة يتقدّم المُعالِج سريعًا متجاوزًا الإزاحة الملتزَم بها ويستعيد الحالة المُفتَّحة، فيُستأنَف العطل في منتصف التشغيل من دون إعادة نشر المخرَجات الملتزَم بها. ويُديم FilesystemCheckpointStore كلَّ حاجز ذرّيًّا.
إزالة التكرار بخاصية العدم التأثيرية، وإعادة المحاولة، والرسائل الميتة
قسم بعنوان «إزالة التكرار بخاصية العدم التأثيرية، وإعادة المحاولة، والرسائل الميتة»مخزن خاصية العدم التأثيرية هو المسار السريع الذي يتيح للمُعالِج اختصار الدائرة قبل تصيير بيانات وصفية مُعاد تشغيلها؛ وتبقى مقارنة بصمة المُلتزِم هي ضمانة المرّة الواحدة بالضبط الدائمة، فيتسبّب فقدان سجلّ إزالة تكرار في أسوأ الأحوال بإعادة تصيير مهدورة يُزيل المُلتزِم تكرارها. وتوفّر RetryPolicy تراجعًا أُسّيًّا محدودًا وحتميًّا لحالات الفشل العابرة (انتهاء المهلة)؛ والمهمّة التي تستنفد ميزانيتها تُلتقَط في DeadLetterStoreInterface بدلًا من فقدانها. ويشحن كلُّ مخزن نسخةً في الذاكرة (نطاق تشغيل واحد / اختبار) ونسخةً دائمة على نظام الملفات.
علامة الديمومة
قسم بعنوان «علامة الديمومة»تُنفِّذ المخازن التي تنجو حالتها من إعادة تشغيل عملية ما علامةَ DurableCapability. ويتطلّب التشغيل الآمن عند الأعطال أن يكون كلُّ متعاون دائمًا كي يفشل سريعًا بدلًا من الوعد بدلالات مرّة واحدة بالضبط لا يستطيع مخزن في الذاكرة الحفاظ عليها عبر إعادة تشغيل.
عيّنة شيفرة — بداية سريعة
قسم بعنوان «عيّنة شيفرة — بداية سريعة»صيِّر بيانات وصفية واحدة والتزم ببايتاتها مرّة واحدة بالضبط. يُرجِع المحرّك البايتات إضافةً إلى بصمة؛ ويَنشرها المُلتزِم.
<?php
declare(strict_types=1);
use NextPDF\Manifest\OutputObjectKey;use NextPDF\Manifest\Render\SingleDocumentRenderer;use NextPDF\Manifest\RenderManifestBuilder;use NextPDF\Manifest\TemplateRef;use NextPDF\Pro\Stream\Commit\LocalFilesystemCommitter;use NextPDF\Pro\Stream\Engine\InProcessRenderEngine;
$outputRoot = __DIR__ . '/out';\is_dir($outputRoot) || \mkdir($outputRoot, 0o775, true);
// The engine renders bytes only — it never writes the final object.$engine = new InProcessRenderEngine(SingleDocumentRenderer::standalone());
$target = OutputObjectKey::file('out', 'invoices/1001.pdf');
$manifest = RenderManifestBuilder::create('invoice-1001') ->withInlineInput('<h1>Invoice 1001</h1><p>Amount due: 42.00</p>') ->withTemplate(TemplateRef::html()) ->withOutputKey($target) ->build();
$result = $engine->renderBatch([$manifest])[0];
// A durable committer publishes the rendered bytes exactly once.$committer = new LocalFilesystemCommitter($outputRoot);
if ($result->isRendered()) { $receipt = $committer->commit($result->jobId, $target, $result->bytes, $result->sha256); echo $receipt->target->toUri(), ' (', $receipt->bytesWritten, " bytes)\n";}عيّنة شيفرة — إنتاج
قسم بعنوان «عيّنة شيفرة — إنتاج»صيِّر دفعة، ووجِّه حالات انتهاء المهلة إلى سياسة إعادة المحاولة، وأرسِل حالات الفشل النهائية إلى الرسائل الميتة. ويرفض الالتزام طمس البايتات المتباعدة، فيُلتقَط تصادم المفاتيح ويُحتجَز بدلًا من فقدانه.
<?php
declare(strict_types=1);
use DateTimeImmutable;use NextPDF\Manifest\OutputObjectKey;use NextPDF\Manifest\Render\SingleDocumentRenderer;use NextPDF\Manifest\RenderManifest;use NextPDF\Manifest\RenderManifestBuilder;use NextPDF\Manifest\TemplateRef;use NextPDF\Pro\Stream\Commit\LocalFilesystemCommitter;use NextPDF\Pro\Stream\Engine\EngineRenderStatus;use NextPDF\Pro\Stream\Engine\InProcessRenderEngine;use NextPDF\Pro\Stream\Exception\OutputCommitConflictException;use NextPDF\Pro\Stream\Retry\DeadLetterRecord;use NextPDF\Pro\Stream\Retry\InMemoryDeadLetterStore;use NextPDF\Pro\Stream\Retry\RetryPolicy;
$outputRoot = __DIR__ . '/out';\is_dir($outputRoot) || \mkdir($outputRoot, 0o775, true);
$engine = new InProcessRenderEngine(SingleDocumentRenderer::standalone(), maxBatchSize: 64);$committer = new LocalFilesystemCommitter($outputRoot);$deadLetter = new InMemoryDeadLetterStore();$retry = RetryPolicy::default(); // 3 attempts, 100ms base, 30s cap.
/** * Build one manifest and remember its output target for the commit stage. * * @return array{RenderManifest, OutputObjectKey} */$makeJob = static function (string $jobId, string $html): array { $target = OutputObjectKey::file('out', 'invoices/' . $jobId . '.pdf'); $manifest = RenderManifestBuilder::create($jobId) ->withInlineInput($html) ->withTemplate(TemplateRef::html()) ->withOutputKey($target) ->build();
return [$manifest, $target];};
/** @var array<non-empty-string, OutputObjectKey> $targets */$targets = [];$manifests = [];foreach (['inv-2001' => '<h1>2001</h1>', 'inv-2002' => '<h1>2002</h1>'] as $id => $html) { [$manifest, $target] = $makeJob($id, $html); $manifests[] = $manifest; $targets[$id] = $target;}
foreach ($engine->renderBatch($manifests) as $result) { // A timeout is transient — the policy decides whether to re-enqueue it. if ($result->status === EngineRenderStatus::Timeout && $retry->shouldRetry(1)) { // Re-enqueue on the caller's work queue after delayMsForAttempt(1) ms. continue; }
if (!$result->isRendered()) { $deadLetter->add(new DeadLetterRecord( jobId: $result->jobId, idempotencyKeyValue: $result->jobId, attempts: $retry->maxAttempts, lastErrorCode: $result->errorCode ?? 'SPEC-RENDER-EXCEPTION', lastErrorMessage: $result->errorMessage ?? '', failedAt: new DateTimeImmutable(), ));
continue; }
try { // overwrite=false: identical bytes are an idempotent no-op; divergent // bytes to an occupied key raise SPEC-COMMIT-409 instead of clobbering. $receipt = $committer->commit( $result->jobId, $targets[$result->jobId], $result->bytes, $result->sha256, ); } catch (OutputCommitConflictException $e) { $deadLetter->add(new DeadLetterRecord( jobId: $result->jobId, idempotencyKeyValue: $result->jobId, attempts: 1, lastErrorCode: $e->specCode(), lastErrorMessage: $e->getMessage(), failedAt: new DateTimeImmutable(), ));
continue; }
echo $receipt->idempotentReuse ? "reused {$receipt->target->toUri()}\n" : "committed {$receipt->target->toUri()}\n";}
if ($deadLetter->count() > 0) { \fwrite(\STDERR, $deadLetter->count() . " job(s) dead-lettered\n");}متى تستخدمها
قسم بعنوان «متى تستخدمها»- تصيير دفعات عالية الحجم حيث تستفيد الإنتاجية من التنفيذ المتزامن (مجمَّع العمليات).
- عمليات تشغيل طويلة الأمد يجب أن تنجو من عطل وتُستأنَف من دون نشر مزدوج للمخرَجات.
- خطوط أنابيب يجب أن تضمن تسليم كلِّ مستند مُصيَّر إلى هدفه مرّة واحدة بالضبط.
لمستند واحد عرَضي، صيِّر مباشرةً بوحدة Writer؛ فقيمة Stream في الدفعات الدائمة القابلة للاستئناف والمتزامنة.
الأداء
قسم بعنوان «الأداء»تتوسّع الإنتاجية مع عدد العمّال في ProcessPoolRenderUnitExecutor (محدودةً بـmaxWorkers وmaxBatchSize)، فيما يُبقي المحرّك مخرَج التصيير متطابقًا بايتيًا مع الخطّ المرجعي المتسلسل. وتحدّ مهلة زمن الحائط كلَّ دفعة موازية فلا يستطيع عامل مُعلَّق الحجبَ إلى الأبد. ولا يوجد رقم إنتاجية ثابت منشور؛ فهو يعتمد على تعقيد المستند وتوازي المضيف. قِس بمستندات تمثيلية.
ملاحظات الأمان
قسم بعنوان «ملاحظات الأمان»يُتحقَّق من البيانات الوصفية تحقُّقًا مُغلَقًا بأمان قبل التصيير. ويرفض المُلتزِم اجتياز المسارات، والبايتات الصفرية، ومخطّطات ملفّ التدفّق، والأهداف ذات الروابط الرمزية، ومتّجهات تدفّق البيانات البديل (النقطتان) في NTFS، ويحلّ كلَّ مفتاح تحت جذر واحد مُهيَّأ. وتُعاد بصمة نتائج العمّال عبر العمليات ومطابقتها مع البصمة التي يُبلِّغ بها العامل، فلا يستطيع عامل مُشوَّش إفساد المخرَج بصمت. ولا تُسجِّل هذه الوحدة أيَّ محتوى مستند.
ملاحظة حدّ Enterprise
قسم بعنوان «ملاحظة حدّ Enterprise»مخازن Stream الدائمة هنا مدعومة بنظام الملفات وأحادية المضيف. أمّا المرّة الواحدة بالضبط المتزامنة عبر مضيفين متعدّدين إلى المفتاح نفسه، وإزالة التكرار الدائمة عبر عمليات التشغيل، فهما مهمّة مُلتزِمات ومخازن تخزين الكائنات في Enterprise؛ ومُعالِج تدفّق مهامّ المستندات الذي يقود هؤلاء المتعاونين شأنٌ من شؤون Enterprise. ويوفّر Pro المحرّك، والعقود، والتنفيذات الدائمة المحلّية.
تراجُع النواة / البديل
قسم بعنوان «تراجُع النواة / البديل»من دون Pro، صيِّر المستندات واحدًا تلو الآخر بكاتب NextPDF Core؛ وتدفّق الدفعات الدائم، والتنفيذ المتزامن، والالتزام مرّة واحدة بالضبط إضافاتٌ من Pro. راجِع /modules/writer/.
حدّ النشر
قسم بعنوان «حدّ النشر»تُوثِّق هذه الصفحة السلوك المُلاحَظ خارجيًّا وسطح واجهة البرمجة العامّة المدعومة فقط. أمّا مسارات النطاقات الاسمية الداخلية، والأصناف المساعِدة، وجداول الآليات، وأسماء ملفّات كتيّبات التشغيل، وبادئات التذاكر فخارج النطاق.