تخطَّ إلى المحتوى
getnextpdf.com

محرّك واحد، لكل إطار عمل

Spec: PSR-11 Container, §1.1.2Spec: PSR-4 Autoloader, §3

تنتهي أغلب منظومات ⁨PHP⁩ النامية بأكثر من إطار عمل واحد. و⁨NextPDF⁩ محرّك ⁨PDF⁩ واحد يلتقي بكلٍّ منها بشروطه الخاصة: جسور اصطلاحية لـ ⁨Laravel⁩ و⁨Symfony⁩ و⁨CodeIgniter⁩، إضافةً إلى مسار مستقل لشيفرة تعمل في أيٍّ منها. نموذج المستند مشترك. ولا يتغيّر سوى طريقة استدعائه.

مكتبة ⁨PDF⁩ مختلفة لكل بنية ضريبةٌ خفيّة. فلكلٍّ منها غرائبها، ومعالجتها الخاصة للخطوط، وفكرتها الخاصة عمّا يعنيه أن يكون الملف صالحًا. فقد تُعرَض فاتورة بشكل صحيح من خدمة ⁨Laravel⁩ بينما تُعرَض على نحو مختلف خفيًّا من عامل ⁨Symfony⁩، لأنّ مكتبةً مختلفة رسمتها. وعندئذٍ يعتمد هدف الأرشفة لديك، وموضع التوقيع، ووسوم إتاحة الوصول، على أيّ فريقٍ شحن المستند. ويقول تقرير الخلل “ملف ⁨PDF⁩ خاطئ”، وتعتمد الإجابة على أيٍّ من المحرّكات الثلاثة أنتجه.

التوحيد على محرّك واحد يطوي ذلك السطح. فهناك مكان واحد يُقرَّر فيه ملفّ تعريف PDF/A، وأنبوب خطوط واحد يُعتمَد، ومدقّق واحد يُوثَق به. ويكفّ إطار العمل الذي تصادف وجودك فيه عن أن يكون متغيّرًا في ما إذا كان المستند صحيحًا.

  • محرّك النواة محايد إزاء أطر العمل. فـnextpdf/core لا يعرف شيئًا عن ⁨HTTP⁩، أو التوجيه، أو توصيل الحاوية. إنّه محرّك ⁨PDF 2.0⁩ ولا شيء سوى ذلك.
  • يكيّف كل جسر، ولا يعيد التنفيذ. فحزم ⁨Laravel⁩ و⁨Symfony⁩ و ⁨CodeIgniter⁩ تمنحك واجهةً أو مصنعًا، ومساعِد استجابة ⁨HTTP⁩، ومسار توليدٍ في طابور أو غير متزامن — فوق المحرّك نفسه.
  • يتبع الجسر إطار عملك، لا مستندك. فهو يغيّر كيف تستدعي المحرّك، لا ما يستطيع المحرّك إنتاجه أبدًا.
  • المسار المستقل متاح دائمًا. فأداة ⁨CLI⁩، أو خفيٌّ، أو مكتبةٌ ليس لها إطار عمل لتُجسَر منه؛ بل تبني مستندًا مباشرةً.
  • نموذج مستند واحد يسري عبر الأربعة جميعًا. فالكائنات القيمية نفسها، والتعدادات، وعقد المخرَجات، تظهر في كل مكان، فينتقل المستند بين مواضع الاستدعاء من دون تغيير.

البنية انقسامٌ مقصود. فالمحرّك هو الأصل؛ والجسر مكيِّف رقيق يتكلّم اصطلاحات إطار عملٍ واحد. يسجّل الجسر فضاء أسماء صغيرًا فوق النواة المشتركة عبر التحميل التلقائيّ القياسيّ (Spec: PSR-4 Autoloader, §3) ويعيد مستندًا عبر عقد الحاوية (Spec: PSR-11 Container, §1.1.2). وذلك العقد هو البطل الصامت هنا: فهو يتيح لاستجلابين للمعرِّف نفسه أن يعيدا نُسخًا مختلفة، وهو بالضبط كيف يمنحك الجسر مستندًا جديدًا قابلًا للاستهلاك لكل طلب مع إبقاء سجل الخطوط المُحلَّل ومخبأ الصور كائنين أحاديين على مستوى العملية. فتحصل العمالة الطويلة العمر — ⁨Octane⁩ و⁨RoadRunner⁩ و⁨Swoole⁩ و⁨Messenger⁩ — على تحليلِ خطوطٍ مُستهلَك الكلفة دون أيّ تسرّب لحالةٍ بين الطلبات، بحكم البناء.

تختلف الاصطلاحات الأربعة على السطح فقط:

  1. Core enginenextpdf/core — the framework-agnostic PDF 2.0 engine; the single shared document model, value objects, and output contract.
  2. Laravel bridgenextpdf/laravel — auto-discovered provider, a Pdf facade, a PdfResponse helper, and a queued GeneratePdfJob.
  3. Symfony bridgenextpdf/symfony — an auto-registered bundle, an injectable PdfFactory, a PdfResponse, and an optional Messenger handler.
  4. CodeIgniter bridgenextpdf/codeigniter — a service and pdf() helper, a Pdf library over a disposable Document, and a PdfResponse.
  5. StandaloneNo framework to bridge from — construct a Document directly in a CLI tool, daemon, or library.
محرّك نواة واحد محايد إزاء أطر العمل يُبلَغ عبر أربعة أسطح اصطلاحية: واجهة Laravel، أو مصنع Symfony مُحقَن، أو خدمة CodeIgniter، أو مستند مستقل مبنيٌّ مباشرةً — يعيد كلٌّ منها نموذج Document القابل للاستهلاك نفسه.

اقرأ المخطّط من اليسار إلى اليمين فيكون الدرس هو التماثل. فكل سطح يؤول إلى الـDocument نفسه. وواجهة ⁨Laravel⁩، ومصنع ⁨Symfony⁩، وخدمة ⁨CodeIgniter⁩، والبانِي المستقل، أربعة أبواب إلى غرفة واحدة.

النيّة نفسها في ثلاثة أسطر، معبَّرٌ عنها في كل اصطلاح. والجسم الذي يبني المستند — صفحات، وخطوط، وخلايا، وتوقيع، وتوافق — متطابق في الأربعة جميعًا، لأنّه المحرّك نفسه.

<?php
declare(strict_types=1);
// Laravel — resolve a fresh document from the container.
use NextPDF\Contracts\PdfDocumentInterface;
$document = app(PdfDocumentInterface::class);
// Symfony — inject the factory, then ask it for a document.
use NextPDF\Symfony\Service\PdfFactory;
$document = $factory->create(); // PdfFactory injected into your service
// CodeIgniter — pull it from the Services layer.
use NextPDF\CodeIgniter\Config\Services;
$document = Services::pdfDocument();
// Standalone — no framework; construct it directly.
use NextPDF\Core\Document;
$document = Document::createStandalone();
// From here, the code is identical regardless of how $document arrived.
$document->addPage();
$document->cell(0, 10, 'One engine, every framework', newLine: true);
$bytes = $document->getPdfData();

الأسطر الأولى هي الفارق الوحيد. وكل ما بعدها قابل للنقل: انقل خدمة بناء مستند من ⁨Symfony⁩ إلى عامل مستقل ولن تتغيّر شيفرة التصيير، لأنّ العقد الذي تعتمد عليه لم يتغيّر.

الافتراض المتكرّر هو أنّ جسر إطار العمل يُتيح قدرات — أنّ التحقّق طويل الأمد من التوقيع أو الفوترة الإلكترونية المهيكلة يصل لأنّك ثبّتّ nextpdf/laravel بدلًا من استدعاء المحرّك مباشرةً. وهو لا يفعل. فالجسر يغيّر موضع الاستدعاء، لا مدى المحرّك أبدًا. والقدرات الأساسية مثل مخرَجات PDF/A وتوقيع PAdES الأساسي مفتوحة المصدر وتصل إلى كل سطح؛ أمّا القدرات المتقدّمة فيُتيحها إصدارٌ وتصبح حينها متاحة عبر أيّ جسر أو المسار المستقل على حدٍّ سواء. اختيار تكامل إطار العمل ليس اختيارًا لمجموعة ميزات.

والمفهوم الخاطئ المقابل هو أنّ “محرّكًا واحدًا” يجب أن يعني مسار تصيير واحدًا لكل مستند. وهو لا يعني ذلك. فالمحرّك داخل العملية يصيّر ⁨PDF⁩ مباشرةً؛ وحين يحتاج مستندٌ حقًّا إلى محرّك تخطيطٍ من فئة المتصفّح، تتولّى ذلك حزمة تصيير. والتصيير والاستدعاء محوران منفصلان — و دليل قرار التكامل هو المكان الذي يربطهما.

الجسر لا يوسّع ما يستطيع المحرّك تصييره. وذلك هو الحدّ الأمين، وهو المغزى: القدرة تعيش في النواة والفئة، لا في المكيِّف الذي تبلغها عبره.

Framework bridges over one engine — edition availability
EditionAvailability
Core

كل جسر (Laravel وSymfony وCodeIgniter) والمسار المستقل بترخيص Apache-2.0 ويعمل مقابل Core. فهي تكيّف المحرّك أو تعرضه؛ وهي لا تحجب الميزات ولا تغيّر ما يستطيع إنتاجه.

Pro

تُتاح القدرات المتقدّمة مثل التحقّق طويل الأمد من التوقيع (PAdES B-LT وB-LTA) بإصدار، ثم تُبلَغ على نحو متطابق عبر أيّ جسر أو المسار المستقل — لا بتبديل إطار العمل أبدًا. ومخرَجات أرشفة PDF/A وتوقيع PAdES الأساسي (B-B وB-T) موجودة أصلًا في Core، متاحة بالطريقة نفسها عبر كل سطح.

Enterprise

الفوترة الإلكترونية المهيكلة (EN 16931) وأدوات الامتثال الأعمق قدرات إصدارٍ كذلك، وهي كذلك متطابقة أيًّا كان السطح الذي يستدعي المحرّك، بينما يُشحَن التحقّق من التوافق نفسه في Core.

ثمة حدّان آخران جديران بالقول صراحةً. أولًا، يتتبّع كل جسر إصدارًا رئيسيًّا حاليًّا من إطار عمله — فـ⁨Laravel⁩ و⁨Symfony⁩ و⁨CodeIgniter⁩ يثبّت كلٌّ منها نطاقًا مدعومًا، فيعني “لكل إطار عمل” الإصدار المدعوم من كلٍّ منها، لا كل إصدار تاريخيّ؛ وعامِل توثيق كل حزمة بوصفه المرجع لواجهتها البرمجية. ثانيًا، الجسور مكيّفات أطر عمل، لا خلفيات تصيير. فإذا احتاج مستندٌ إلى محرّك تخطيط متصفّح كامل، فذلك اختيار تصيير مستقل عن أيّ إطار عمل استدعى المحرّك.

  • دليل قرار التكامل — خريطة حالة الاستخدام إلى الحزمة، شاملةً المصيّرات وسطح خدمة ⁨Connect⁩، حين تحتاج إلى أن تقرّر بدلًا من أن توحّد.
  • نواة مفتوحة، بلا احتجاز — لماذا المحرّك هو الأصل والجسور رقيقة، فلا يحبسك التوحيد.
  • أنبوب ⁨HTML⁩ — ما الذي يغطّيه المحرّك داخل العملية، لتعرف متى يكون مصيّر المتصفّح سؤالًا منفصلًا.
  • أُسُس ⁨PHP 8.4⁩ — أرضية وقت التشغيل التي يتشاركها كل جسر والمسار المستقل.
  • محرّك النواةnextpdf/core، محرّك ⁨PDF 2.0⁩ المحايد إزاء أطر العمل الذي يبني عليه كل جسر والمسار المستقل.
  • جسر إطار العمل — حزمة تكامل (Laravel وSymfony و CodeIgniter) تكيّف المحرّك مع اصطلاحات إطار عمل — واجهة، ومصنع، واستجابة، ومهمّة في طابور — دون تغيير قدراته.
  • المسار المستقل — استخدام محرّك النواة مباشرةً، دون إطار عمل، ببناء Document بنفسك؛ وهو الطريق لأدوات ⁨CLI⁩، والخفيّات، و المكتبات.
  • المستند القابل للاستهلاك — عقد Document ذو الاستخدام الواحد: ابنِ، وأصدِر، وتخلَّص. ويعيد كل استجلابِ حاويةٍ مستندًا جديدًا، فلا تتسرّب حالة بين الطلبات في عاملٍ طويل العمر.
  • PAdES — التواقيع الإلكترونية المتقدّمة لـPDF، عائلة ملفّات تعريف ⁨ETSI⁩ لتوقيع PDF. والتوقيع الأساسي (B-B وB-T) في Core؛ والتحقّق طويل الأمد (B-LT وB-LTA) قدرة من فئة الإصدار المتقدّم. وأيٌّ منهما يُبلَغ عبر أيّ سطح، ويُتناول بعمق في صفحات التوقيع.