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

استكشاف أخطاء compat-legacy وإصلاحها

تندرج معظم مشكلات الترحيل ضمن مجموعة محدودة من الأنماط. يوضّح كل بند أدناه العَرَض والسبب والإصلاح. إذا لم تكن متأكداً من أسلوب معيّن، فراجِع /⁨integrations/tcpdf-compat/method-coverage/⁩ ومصفوفة المرجع المعتمَدة داخل المستودع docs/TCPDF_COVERAGE.md.

كانت العملية تتوقف عند خطأ في ⁨PDF⁩؛ والآن يفلت استثناء

قسم بعنوان «كانت العملية تتوقف عند خطأ في ⁨PDF⁩؛ والآن يفلت استثناء»

العَرَض. الشيفرة التي كانت تتوقف عند عرض معطوب أصبحت ترمي RuntimeException غير ملتقَط، فيُبلِغ الطلب أو المهمة عن خطأ.

السبب. تستدعي استدعاءات Error() في ⁨TCPDF⁩ القديم die(). أمّا المهايئ فيرمي RuntimeException بدلاً من ذلك، بحكم التصميم، حتى تكون الإخفاقات قابلة للرصد.

الإصلاح. غلِّف نقاط دخول العرض داخل try/catch، واربط الاستثناء بعقد معالجة الأخطاء لديك. لا تُعِد سلوك die(). راجِع /⁨integrations/tcpdf-compat/production-usage/⁩ § معالجة الإخفاقات.

new \TCPDF() ما زال يتحلّل إلى مكتبة ⁨TCPDF⁩ الأصلية

قسم بعنوان «new \TCPDF() ما زال يتحلّل إلى مكتبة ⁨TCPDF⁩ الأصلية»

العَرَض. فعّلتَ LegacyBootstrap::enableAliases()، لكنّ الخرج ما زال يبدو مثل ⁨TCPDF⁩ القديم، أو لم يتغيّر السلوك.

السبب. يسجّل enableAliases() اسماً بديلاً فقط عندما لا يكون هناك صنف بهذا الاسم مُعرَّفاً مسبقاً. إذا بقي tecnickcom/tcpdf قابلاً للتحميل التلقائي وحُمِّل صنف \TCPDF الخاص به أولاً، فسيُتخطّى الاسم البديل، وتظلّ شيفرتك تستخدم ⁨TCPDF⁩ القديم.

الإصلاح. أثناء الترحيل، استخدِم استيرادات صريحة في كل ملف (use NextPDF\Compat\Tcpdf\TCPDF;) حتى يكون كل موضع استدعاء واضحاً بلا لبس. أزِل tecnickcom/tcpdf بعد نجاح التدقيق (راجِع /⁨integrations/tcpdf-compat/migration/⁩ المرحلة 5). لا تُشغِّل المكتبتين معاً مع تفعيل الأسماء البديلة العامة في العملية نفسها.

الأسلوب “يعمل” لكنّ المُعامِل الذي مرّرتُه يُتجاهَل

قسم بعنوان «الأسلوب “يعمل” لكنّ المُعامِل الذي مرّرتُه يُتجاهَل»

العَرَض. ينجح الاستدعاء وينتج ملف ⁨Portable Document Format⁩ ‏(⁨PDF⁩)، لكن خياراً مرّرتَه (رابط صورة، محاذاة، نقاط لكل بوصة ‏(⁨DPI⁩)، لون إشارة مرجعية، …) لا يُحدِث أي أثر.

السبب. يقع الأسلوب ضمن مجموعة التجاهل الصامت. يقبل المُعامِل لأجل التوافق على مستوى الشيفرة المصدرية، ثم يُسقِطه. هذا سلوك موثَّق وليس عِلّة؛ راجِع /⁨integrations/tcpdf-compat/method-coverage/⁩ §2.

الإصلاح. شغِّل تدقيقاً بالوضع الصارم للعثور على كل استدعاء من هذا النوع:

examples/troubleshoot-strict.php
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Compat\Tcpdf\Exception\TcpdfNotImplementedException;
use NextPDF\Compat\Tcpdf\TCPDF;
$pdf = new TCPDF();
$pdf->setStrictMode(true);
$pdf->AddPage();
$pdf->SetFont('helvetica', '', 12);
try {
$pdf->Image('logo.png', 10, 10, 50, 0, '', 'https://example.com');
} catch (TcpdfNotImplementedException $e) {
// Message lists every ignored parameter and a migration hint.
echo $e->getMessage(), "\n";
}

ثم أسقِط المُعامِل، أو أعد التعبير عنه عبر الواجهة البرمجية الحديثة ($pdf->getDocument())، كما هو موضّح في /⁨integrations/tcpdf-compat/migration/⁩ المرحلة 4.

القيمة المُعادة من MultiCell() هي دائماً 1

قسم بعنوان «القيمة المُعادة من MultiCell() هي دائماً 1»

العَرَض. الشيفرة التي تتفرّع بناءً على القيمة المُعادة من MultiCell() (على سبيل المثال، لحساب الارتفاع المستخدَم أو عدد الأسطر) تتصرّف بطريقة غير صحيحة.

السبب. يُعيد MultiCell() في المهايئ القيمة النائبة للتوافق 1، لا عدد الخلايا أو عدد الأسطر المعروضة. وكذلك يُعيد Write() القيمة 0.

الإصلاح. لا تتفرّع بناءً على هذه القيم المُعادة. إذا احتجتَ إلى الارتفاع المعروض، فاحسبه من getStringHeight() / getNumLines()، أو انقُل ذلك المنطق إلى الواجهة البرمجية الحديثة.

setPDFVersion('1.4') لم يُنتِج ملف ⁨PDF 1.4⁩

قسم بعنوان «setPDFVersion('1.4') لم يُنتِج ملف ⁨PDF 1.4⁩»

العَرَض. طلبتَ إصدار ⁨PDF⁩ أقدم؛ لكن الخرج ما زال ⁨PDF 2.0.⁩

السبب. يُخرِج المهايئ دائماً ⁨PDF 2.0⁩ ‏(⁨ISO 32000-2⁩). setPDFVersion() يقع ضمن المجموعة غير المنطبقة؛ يُصدِر المهايئ إشعاراً ويتابع.

الإصلاح. أزِل الاستدعاء. إذا اشترط مستهلِك لاحق إصدار ⁨PDF⁩ أقدم، فعالِج ذلك الشرط على حِدة؛ فالمهايئ لا يستطيع استهداف إصدار أدنى.

setSignature() لم يفعل شيئاً — ملف ⁨PDF⁩ غير موقَّع

قسم بعنوان «setSignature() لم يفعل شيئاً — ملف ⁨PDF⁩ غير موقَّع»

العَرَض. استدعيتَ setSignature() بشهادة؛ لكن ملف ⁨PDF⁩ الناتج لا يحمل توقيعاً.

السبب. لا ينفّذ المحرّك الأساسي setSignature() عبر هذا المهايئ. في الوضع الافتراضي يكون الاستدعاء عمليةً لاغية؛ وفي الوضع الصارم يرمي استثناءً.

الإصلاح. يتطلّب التوقيع إصداراً تجارياً من ⁨NextPDF⁩ والواجهة البرمجية الحديثة للتوقيع. راجِع /⁨integrations/tcpdf-compat/security-and-operations/⁩ § التوقيعات الرقمية. لا تتوقّع من استدعاء setSignature() القديم أن يوقِّع أي شيء.

Output() أفسد استجابة ⁨HTTP⁩ أو خرج العامل لديّ

قسم بعنوان «Output() أفسد استجابة ⁨HTTP⁩ أو خرج العامل لديّ»

العَرَض. تظهر بايتات ⁨PDF⁩ في استجابة ⁨Hypertext Transfer Protocol⁩ ‏(⁨HTTP⁩)، أو يتلوّث سجلّ عامل ببايتات ⁨PDF.⁩

السبب. استخدمتَ وجهة خرج تكتب إلى مسار الخرج (I/D) بينما تتحكّم أنت في الاستجابة بنفسك. المهايئ لا يطبع داخل مخزِّنك المؤقّت كما يفعل ⁨TCPDF⁩ القديم، لكنّ I/D ما زالا يوجّهان خرج المحرّك.

الإصلاح. في العوامل والمعالِجات التي تتحكّم فيها، استخدِم Output($path, 'F') لكتابة ملف، أو Output($name, 'S') للحصول على البايتات وإصدارها بنفسك. يؤكِّد tests/Unit/Compat/Tcpdf/Bridge/OutputBridgeTest.php أن تخطيط الوجهة غير حسّاس لحالة الأحرف ومُقتطَع المسافات البيضاء:

الرمزالقيمة المُعادةالأثر الجانبي
Sبايتات ⁨PDF⁩ ‏(%PDF…)لا شيء
Fسلسلة فارغةيكتب ملفاً
Eمتن ⁨MIME⁩ بترميز ⁨base64⁩لا شيء
FI / FDسلسلة فارغةيكتب ملفاً، ثم خرج المحرّك
I / D / غير معروفسلسلة فارغةخرج المحرّك (مضمَّن/تنزيل)

تأكيدات ⁨PDF⁩ على البايتات الدقيقة تفشل بعد التبديل

قسم بعنوان «تأكيدات ⁨PDF⁩ على البايتات الدقيقة تفشل بعد التبديل»

العَرَض. تفشل اختبارات اللقطات التي تقارن بايتات ⁨PDF⁩ الخام في كل مكان.

السبب. يستخدم المحرّك تنفيذاً مستقلاً لـ ⁨PDF 2.0.⁩ تُنتِج الأساليب المُفوَّضة خرجاً مرئياً متوافقاً، لكنّ البايتات تختلف. هذا أمر متوقَّع.

الإصلاح. أعِد ضبط خطوط الأساس لاختباراتك بحيث تؤكّد المحتوى المعروض (النص المستخرَج)، أو البنية (عدد الصفحات، حجم الصفحة)، أو فحص تحقّق سريع (str_starts_with($bytes, '%PDF')). راجِع /⁨integrations/tcpdf-compat/migration/⁩ المرحلة 4.

ثابت K_* / PDF_* قديم له قيمة خاطئة

قسم بعنوان «ثابت K_* / PDF_* قديم له قيمة خاطئة»

العَرَض. مسار مخصّص أو قيمة افتراضية ضبطتَها عبر ثابت لا يأخذ مفعوله.

السبب. لا يعرّف المهايئ ثابتاً تلقائياً إلا إذا لم يكن معرَّفاً مسبقاً، ويفعل ذلك أثناء أول إنشاء. إذا جرى define() لديك بعد إنشاء أول مهايئ، فإن قيمة المهايئ الافتراضية تكون قد أصبحت سارية بالفعل.

الإصلاح. عرِّف كل ثابت K_* / PDF_* مخصّص في طبقة التمهيد لديك، قبل إنشاء أي نسخة من المهايئ. راجِع /⁨integrations/tcpdf-compat/configuration/⁩ § ترتيب تحليل التهيئة.

عدم تطابق إصدار المحرّك عند الإنشاء

قسم بعنوان «عدم تطابق إصدار المحرّك عند الإنشاء»

العَرَض. يفشل الإنشاء أو يتغيّر السلوك على نحو غير متوقَّع بعد تحديث تبعية.

السبب. يتطلّب المهايئ nextpdf/core ^3.0. أي إصدار أساسي يجري حله خارج هذا النطاق غير مدعوم.

الإصلاح. شغِّل composer show nextpdf/core، وثبِّت المحرّك على ^3.0. راجِع /⁨integrations/tcpdf-compat/install/⁩ § التحقّق من إصدار المحرّك.

السؤالأين تبحث
ماذا يفعل الأسلوب ⁨X⁩ فعلياً هنا؟/⁨integrations/tcpdf-compat/method-coverage/⁩، docs/TCPDF_COVERAGE.md
أيٌّ من استدعاءاتي يفقد المُعامِلات؟تدقيق بالوضع الصارم (هذه الصفحة؛ /⁨integrations/tcpdf-compat/migration/⁩)
لماذا لم تتوقّف العملية عند الخطأ؟/⁨integrations/tcpdf-compat/security-and-operations/⁩ § السلوكيات المُحصَّنة
لماذا الخرج غير موقَّع / ليس ⁨PDF/A⁩؟/⁨integrations/tcpdf-compat/security-and-operations/⁩
تعارض الاسم البديل مقابل الاستيراد الصريحهذه الصفحة؛ /⁨integrations/tcpdf-compat/boot-and-discovery/⁩
  • /⁨integrations/tcpdf-compat/migration/⁩ — الترحيل المرحلي الذي يمنع معظم ما سبق
  • /⁨integrations/tcpdf-compat/method-coverage/⁩ — مرجع سلوك كل أسلوب
  • /⁨integrations/tcpdf-compat/boot-and-discovery/⁩ — تسجيل الأسماء البديلة وتجنّب التعارض
  • docs/TCPDF_COVERAGE.md — مصفوفة التغطية المعتمَدة