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

استخدام NextPDF مع Symfony في الإنتاج

استخدم الحزمة في بيئات تشغيل ⁨PHP⁩ طويلة الأمد. فهي تنشئ مستندات غير مشتركة، وتقفل سجل الخطوط بعد الإحماء، وتعيد ضبط ذاكرة الصور المؤقتة بين الطلبات. ابثّ ملفات ⁨Portable Document Format⁩ (⁨PDF⁩) الكبيرة، وأوكِل المهام الثقيلة إلى عمال ⁨Messenger.⁩

دورة حياة آمنة للخدمات مع العمال

قسم بعنوان «دورة حياة آمنة للخدمات مع العمال»

تُبقي بيئات التشغيل طويلة الأمد الحاوية حيّة بين الطلبات، لذا يجب أن تبقى حالة كل طلب معزولة. يتبع هذا النموذج كل من ⁨FrankenPHP⁩ و⁨RoadRunner⁩ وعمال ⁨Messenger.⁩ يحدد ملف services.php الخاص بالحزمة دورة الحياة أدناه، وقد جرى التحقق منها مقابل تعريفات الخدمات:

  • ⁨Document⁩ — غير مشترك. يُحَلّ nextpdf.document (وكذلك الاسمان البديلان PdfDocumentInterface / Document) إلى نسخة جديدة في كل عملية حلّ. بموجب ⁨PSR-11⁩ (⁨PHP Standard Recommendation 11⁩)، يجوز للحاوية بشكل مشروع أن تُرجع قيمة مختلفة في كل استدعاء get() للمعرّف ذاته (⁨PSR-11⁩ §1.1.2). حُلّ المستند لكل طلب على حدة. لا تحتفظ به أبدًا بين الطلبات.
  • ⁨FontRegistry⁩ — مشترك ومقفول. السجل نسخة وحيدة طوال عمر العملية. بعد warmup() (عندما لا يكون preload_fonts فارغًا) يستدعي ممر المُصرِّف lock(). يمنع القفل التعديل أثناء التشغيل ويمنع تلوث حالة الخطوط بين الطلبات.
  • ⁨ImageRegistry⁩ — مشترك، يُعاد ضبطه لكل طلب. ذاكرة الصور المؤقتة المحدودة وفق سياسة الأقل استخدامًا مؤخرًا (⁨LRU⁩) مشتركة، لكنها موسومة بـ kernel.reset مع الأسلوب reset، لذلك يمسحها ⁨Symfony⁩ بين الطلبات في بيئات التشغيل التي تحترم kernel.reset.
  • عقود ⁨EInvoice⁩ — غير مشتركة. عند وجود تطبيقات ⁨Premium⁩، تُسجَّل خدمات المُضمِّن والمُتحقِّق والملف التعريفي و⁨Schematron⁩ بوصفها غير مشتركة. يظل سياق المُحلِّل محصورًا في كل استدعاء ولا يتسرب أبدًا بين الطلبات.

احقن PdfFactory، وهو حامل إعدادات مشترك وعديم الحالة، واستدعِ create() لكل طلب:

public function __construct(private readonly PdfFactory $pdf) {}
public function action(): Response
{
$doc = $this->pdf->create(); // fresh, disposable
// ... build ...
return PdfResponse::inline($doc, 'document.pdf');
}

لا تحقن Document أو nextpdf.document داخل خدمة مشتركة يُحتفظ بها بين الطلبات. حُلّه بدلًا من ذلك داخل الأسلوب المحصور بنطاق الطلب.

يُرجع كل من PdfResponse::streamDownload() وstreamInline() كائن StreamedResponse. تُصدر دالة النداء جسم ⁨PDF⁩ في كتل بحجم 64 ⁨KB⁩ وتُفرّغ بعد كل كتلة، مما يحد من مخزن الاستجابة المؤقت للمستندات الكبيرة. جرى التحقق من السلوكيات أدناه مقابل PdfResponse:

  • تُغفل الصيغ المبثوثة عمدًا ترويسة Content-Length لأن كائن الاستجابة لا يعرف حجم الجسم مسبقًا. تعمل أشرطة تقدّم التنزيل وبعض الوسطاء على نحو أفضل عند توفر طول معروف. استخدم download() أو inline() غير المبثوثَين عندما يكون المستند صغيرًا بما يكفي للاحتفاظ به في الذاكرة ويكون طول المحتوى مطلوبًا.
  • تُصدر الصيغ المبثوثة ترويسات الأمان نفسها وترويسة Cache-Control: private, max-age=0, must-revalidate نفسها مثل الصيغ المخزَّنة مؤقتًا.

اختر البث للتقارير التي يبلغ حجمها عدة ميغابايتات وعمليات التصدير الدُّفعية. اختر الصيغ المخزَّنة مؤقتًا للاستجابات الصغيرة الحساسة للكُمون.

التوليد غير المتزامن على نطاق واسع

قسم بعنوان «التوليد غير المتزامن على نطاق واسع»

أوكِل التوليد إلى ⁨Messenger⁩ عندما يجب أن تستجيب الطلبات بسرعة، أو عندما يكون العرض كثيف الاستهلاك للمعالج.

  1. طبّق PdfBuilderInterface لكل نوع مستند.
  2. سجّل البُناة في container.service_locator ووصّله بوصفه $builderLocator الخاص بـ GeneratePdfHandler.
  3. وجّه GeneratePdfMessage إلى وسيلة نقل دائمة.
  4. شغّل العمال بأعمار محدودة.

أعِد تدوير العمال حتى لا ينمو تخصيص ذاكرة مُسرَّب في اعتمادية طرف ثالث بلا حدود:

Terminal window
php bin/console messenger:consume async \
--limit=200 \
--memory-limit=256M \
--time-limit=3600

يسجّل مفتاحا الإعداد messenger.timeout وmessenger.retries الخاصان بالحزمة المهلة المقصودة لكل رسالة وميزانية إعادة المحاولة. افرض السلوك نفسه عبر استراتيجية إعادة المحاولة في ⁨Symfony⁩ ورايات العامل.

أمان مسار الإخراج في العمال

قسم بعنوان «أمان مسار الإخراج في العمال»

يتحقق GeneratePdfMessage من مسار الإخراج عند الإنشاء. يعيد GeneratePdfHandler التحقق منه وقت التنفيذ، قبل الكتابة إلى القرص. يهم هذا الفحص ذو المرحلتين في العمل غير المتزامن؛ فقد تبقى رسالة في طابور بين الإرسال والاستهلاك، لذلك لا يثق المعالج بالمسار المُدرَج في الطابور ثقة عمياء. قَيّد أذونات نظام ملفات العامل على دليل الإخراج المقصود بوصفه دفاعًا متعدد الطبقات.

تقبل خدمتا FontRegistry وImageRegistry كائن Psr\Log\LoggerInterface اختياريًا (مربوطًا بـ nullOnInvalid()). عندما يوفّر التطبيق مُسجِّلًا، يمكن للسجلات أن تُصدر معلومات تشخيصية من خلاله. المُسجِّل متعاون اختياري قابل للاستبدال بموجب عقد المُسجِّل ⁨PSR-3⁩ (⁨PSR-3⁩). للحصول على رؤية على مستوى الطلب، سجّل حول PdfFactory::create() ومعالج ⁨Messenger⁩ في شيفرة تطبيقك. استخدم messenger:consume -vv أثناء فرز الحوادث.

  • ثبّت إصدارًا رئيسيًا واحدًا من nextpdf/core في ملف composer.json الخاص بالتطبيق (تقبل الحزمة ^3.0 || ^5.2).
  • تأكد من تفعيل ext-mbstring وext-zlib في صورة ⁨PHP⁩ المنشورة (وإلا فستفشل الحزمة سريعًا عند الإقلاع).
  • عَبِّئ preload_fonts مسبقًا بالخطوط التي تستخدمها مستنداتك حتى يُحمى السجل ويُقفل عند الإقلاع بدلًا من أول طلب.
  • وجّه cache_path إلى موقع قابل للكتابة ودائم إذا كنت تعتمد على المُنتجات المخزَّنة مؤقتًا عبر عمليات النشر. وإلا، فالقيمة الافتراضية %kernel.cache_dir% مناسبة.
  • شغّل php bin/console cache:warmup أثناء النشر حتى تُبنى الحاوية المُصرَّفة (بما في ذلك مجسّات الامتدادات الاختيارية) قبل وصول الحركة.
  • استخدم وسيلة نقل ⁨Messenger⁩ دائمة (لا sync) للعمل غير المتزامن في بيئة الإنتاج، وأعِد تدوير العمال باستخدام --limit / --memory-limit / --time-limit.
  • الاستجابات المبثوثة خلف وسيط يخزّن مؤقتًا — الوسيط الذي يخزّن الجسم الكامل مؤقتًا يُلغي فائدة الذاكرة. اضبط الوسيط لبث استجابات ⁨PDF⁩، أو استخدم الاستجابات المخزَّنة مؤقتًا في ذلك الموضع.
  • عدم احترام kernel.reset — في بيئة تشغيل لا تستدعي kernel.reset، تكون ذاكرة الصور المؤقتة محدودة بـ image_cache_mb لكنها لا تُمسح بين الطلبات؛ لذلك حدّد السعة القصوى بناءً على ذلك.
  • الاحتفاظ بمستند بين الطلبات — سيحمل Document المُلتقَط من طلب سابق حالة قديمة. حُلّه دائمًا لكل طلب عبر PdfFactory.

كل صف هو ادعاء معياري في هذه الصفحة، مثبَّت إلى reference_id كامل من 64 خانة ست عشرية من مجموعة منظمة تطوير المعايير (⁨SDO⁩) المحجوبة. يوجد مصدر بيان المجموعة ووسيلة نقل الاسترجاع في _sidecars/rag-citations.yaml.

المواصفةالبند⁨reference_id⁩الادعاء
⁨PSR-11⁩psr_11_container#1.1.2.p3.bخدمة غير مشتركة: قيمة متمايزة لكل عملية حلّ
⁨PSR-3⁩psr_3_logger#x3.p17متعاون مُسجِّل اختياري
  • /⁨integrations/symfony/configuration/⁩ — دورة حياة الخدمات والمعاملات.
  • /⁨integrations/symfony/security-and-operations/⁩ — ترويسات الاستجابة، والتحقق من المسار، ومعالجة المفاتيح.
  • /⁨integrations/symfony/troubleshooting/⁩ — تشخيصات الإقلاع ووقت التشغيل.
  • /⁨integrations/symfony/quickstart/⁩ — الحد الأدنى للإعداد غير المتزامن.