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

إعداد مُصيِّر Chrome في NextPDF Artisan

يُشغّل الجسر عملية ⁨Chrome/Chromium⁩ محلية ويديرها عبر chrome-php/chrome. استخدم هذه الصفحة لإعداد بيئة التشغيل هذه كي تنجح عمليات تصيير تنسيق المستندات المحمولة (⁨PDF⁩)، ولاختيار إعدادات الحاوية وصندوق العزل المناسبة.

كيف يتواصل الجسر مع ⁨Chrome⁩

قسم بعنوان «كيف يتواصل الجسر مع ⁨Chrome⁩»

يُنشئ BrowserPool كائن chrome-php/chromeBrowserFactory (مع مسار ثنائي صريح اختياريًا) ويُشغّل ⁨Chrome⁩ بمجموعة رايات ثابتة: headless: true، وkeepAlive: true، وwindowSize: [1200, 800]، وsendSyncDefaultTimeout: renderTimeout * 1000، إضافة إلى الرايات المخصّصة المُدرجة في صفحة /⁨integrations/artisan/configuration/.⁩ بعد ذلك يدير الجسر العملية المُشغّلة عبر بروتوكول ⁨Chrome DevTools⁩‏ (⁨CDP⁩). ولا يتصل بعملية ⁨Chrome⁩ منفصلة عبر منفذ تنقيح بعيد، لذلك لا توجد نقطة نهاية شبكية يجب تعريضها أو مصادقتها. يعمل ⁨Chrome⁩ بوصفه عملية فرعية لعامل ⁨PHP.⁩ ويتحقّق الاختبار tests/Unit/Artisan/BrowserPoolTest.php::getBrowserCreatesAndReusesInstanceWithExpectedOptions من خيارات التشغيل هذه بدقة.

ثبّت إصدارًا من ⁨Chrome⁩ أو ⁨Chromium⁩ يستطيع مستخدم العامل تنفيذه:

Terminal window
# Debian / Ubuntu
apt-get install -y chromium
# RHEL / Fedora
dnf install -y chromium
# Alpine (containers)
apk add --no-cache chromium nss freetype harfbuzz ttf-freefont

تحقّق من أنه يعمل بلا واجهة بصفتك مستخدم العامل:

Terminal window
chromium --headless --dump-dom about:blank

يعني رمز خروج 0 مع نموذج كائن مستند (⁨DOM⁩) فارغ أن الملف الثنائي ومكتباته المشتركة موجودة. أما رمز الخروج غير الصفري فهو الإخفاق نفسه الذي يُظهره الجسر على هيئة ChromeRenderException. عالجه هنا أولًا.

توجيه الجسر إلى الملف الثنائي

قسم بعنوان «توجيه الجسر إلى الملف الثنائي»

يعمل الاكتشاف التلقائي (الافتراضي في chrome-php/chrome) عندما يكون الملف الثنائي على مسار قياسي. وللحصول على سلوك إنتاجي حتمي، حدّده صراحةً:

$config = new ChromeRendererConfig(
chromeBinaryPath: '/usr/bin/chromium',
);

أو عبر إعداد المصفوفة:

$config = ChromeRendererConfig::fromArray([
'chrome_binary' => '/usr/bin/chromium',
]);

توفير الحاوية وقرار صندوق العزل

قسم بعنوان «توفير الحاوية وقرار صندوق العزل»

داخل الحاوية، غالبًا ما يتعذّر على صندوق عزل نظام التشغيل الخاص بـ ⁨Chrome⁩ أن يتهيأ عند التشغيل بصفة الجذر / معرّف العملية (⁨PID⁩) 1 دون قدرات نواة إضافية. لديك مساران:

  1. أبقِ صندوق العزل (المفضّل). شغّل العامل بمستخدم غير الجذر، وامنح الحاوية القدرات التي يحتاجها صندوق عزل ⁨Chrome⁩ (عادةً SYS_ADMIN، أو ملف تعريف ⁨seccomp⁩ يسمح بإنشاء فضاء أسماء المستخدم). يُبقي ذلك عزل عمليات ⁨Chrome⁩ سليمًا.
  2. عطّل صندوق العزل. عيّن no_sandbox: true. عندئذٍ يُشغَّل ⁨Chrome⁩ بالراية --no-sandbox. يزيل هذا صندوق عزل عمليات ⁨Chrome⁩: وهو خفض فعلي في الاحتواء، وليس راية شكلية. استخدمه فقط عندما يتعذّر تمكين صندوق العزل، وشغّل ⁨Chrome⁩ بمستخدم غير الجذر داخل حاوية مقيّدة، وتعامل مع النشر على أنه يتطلب ثقة أعلى في المُدخل. تبقى الحواجز الشبكية للجسر، أي سياسة أمان المحتوى (⁨CSP⁩) وحجب ⁨CDP⁩، سارية في كلتا الحالتين، لكنها لا تُغني عن عزل العمليات. يتوافق هذا مع إرشادات ⁨OWASP ASVS⁩ بشأن أقل امتياز والعزل عند تصيير محتوى غير موثوق.

تجد بيان الحدود الكامل، بما في ذلك ما يحميه صندوق العزل وما لا يحميه، في صفحة /⁨integrations/artisan/security-and-operations/.⁩ لا تزعم هذه الصفحة أن تعطيل صندوق العزل آمن.

FROM php:8.4-cli
RUN apt-get update && apt-get install -y --no-install-recommends \
chromium fonts-liberation \
&& rm -rf /var/lib/apt/lists/*
RUN useradd -m -u 10001 worker
USER worker
ENV CHROME_BINARY=/usr/bin/chromium
# Set CHROME_NO_SANDBOX=1 only if the sandbox cannot be enabled in your runtime.

شغّل العامل بصفته worker (معرّف المستخدم 10001)، لا الجذر. يطبّق الجسر الراية --disable-dev-shm-usage أصلًا، وهي تتجنّب التعطّل المرتبط بصِغَر /dev/shm الشائع في الحاويات دون ضبط إضافي.

يحجب الجسر جلب الخطوط البعيدة (--disable-remote-fonts و⁨CSP⁩). ثبّت الخطوط التي تحتاجها في طبقة نظام التشغيل، أو ضمّنها بصفتها مصادر @font-face بمعرّف موارد موحّد (⁨URI⁩) من نوع data: داخل defaultCss أو لغة ترميز النص التشعّبي (⁨HTML⁩). يتطلّب الإخراج بالصينية واليابانية والكورية (⁨CJK⁩) حزمة خطوط ⁨CJK⁩ (مثل fonts-noto-cjk) مثبّتة في الصورة.

استخدم هذا المِسبار المستقل لاختبار مسار الجسر الكامل دون تطبيق المُضيف:

chrome-health.php
<?php
declare(strict_types=1);
use NextPDF\Artisan\ChromeHtmlRenderer;
use NextPDF\Artisan\ChromeRendererConfig;
require __DIR__ . '/vendor/autoload.php';
$renderer = new ChromeHtmlRenderer(
ChromeRendererConfig::fromArray([
'chrome_binary' => getenv('CHROME_BINARY') ?: null,
'no_sandbox' => (bool) getenv('CHROME_NO_SANDBOX'),
]),
);
$result = $renderer->render('<p>ok</p>', 200.0, 0.0);
fwrite(STDOUT, strlen($result->getPdfData()) > 0 ? "CHROME_OK\n" : "CHROME_EMPTY\n");
$renderer->close();

يؤكّد CHROME_OK نجاح التشغيل والتصيير والاستيراد. والاستثناء المُطلَق هو الإخفاق الدقيق. طابِقه مع صفحة /⁨integrations/artisan/troubleshooting/.⁩ اربط هذا المِسبار بوصفه فحص جاهزية في عمليات النشر المُنسّقة.

  • شغّل ⁨Chrome⁩ بمستخدم مخصّص غير الجذر.
  • طبّق حدًّا للذاكرة على المُضيف؛ يحدّ الجسر من النمو بإعادة التشغيل كل 100 عملية تصيير، لكن سقف المُضيف يبقى مطلوبًا.
  • اقرِن render_timeout بميزانية طلب في المسار الأعلى على أي مسار يمكن بلوغه عبر مُدخل غير موثوق.
  • لا تُعرّض منفذ تنقيح بعيد في ⁨Chrome.⁩ لا يستخدم الجسر أي منفذ من هذا القبيل، ومنفذ ⁨CDP⁩ المفتوح هو قناة تحكّم غير مُصادَق عليها.

أنماط الإخفاق واستكشاف الأخطاء وإصلاحها

قسم بعنوان «أنماط الإخفاق واستكشاف الأخطاء وإصلاحها»
العَرَضالسبب المُرجَّحأين تبحث
ChromeNotAvailableExceptionchrome-php/chrome غير مثبّت/⁨integrations/artisan/install/⁩
ChromeRenderException عند أول تصييرالملف الثنائي مفقود / يتعذّر تهيئة صندوق العزلهذه الصفحة؛ /⁨integrations/artisan/troubleshooting/⁩
ملف ⁨PDF⁩ فارغلا صندوق عرض مرئي / تعطّل ⁨Chrome⁩/⁨integrations/artisan/troubleshooting/⁩
صور بعيدة فارغةالشبكة محجوبة بحكم التصميم/⁨integrations/artisan/security-and-operations/⁩
ارتفاع دوري في زمن الاستجابةإعادة التشغيل كل 100 عملية تصيير/⁨integrations/artisan/production-usage/⁩
  • /integrations/artisan/install/
  • /integrations/artisan/configuration/
  • /integrations/artisan/security-and-operations/
  • /integrations/artisan/troubleshooting/
  • /integrations/artisan/production-usage/