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

الترحيل من TCPDF 6.x إلى NextPDF

رحّل وفق ترتيب واضح. انتقل أولاً إلى محرّك ⁨NextPDF⁩ بأقل قدر ممكن من التغيير. تحقّق مما يعمل بالفعل. دقّق فيما لا يعمل. أصلح كل موضع استدعاء. ثم أزل المحوّل. تدعم طبقة التوافق الخطوات من الثانية إلى الرابعة، لكنها ليست الوجهة النهائية.

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

TCPDF 6.x codebase

Swap dependency: install compat-legacy

Run existing suite unchanged

Strict-mode audit: enumerate behavioral gaps

Fix call sites: drop ignored params or move to modern API

Re-baseline byte-level test assertions

Remove the TCPDF dependency

Incrementally retire the adapter onto Document

Diagram

تُبقي كل مرحلة تطبيقك قابلاً للإصدار. لا تحتاج أبداً إلى انتقال دفعة واحدة.

المرحلة 1 — استبدال التبعية

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

ثبّت nextpdf/compat-legacy (راجع /⁨integrations/tcpdf-compat/install/⁩). لا تُزل tecnickcom/tcpdf بعدُ؛ فالإبقاء على كليهما يتيح لك مقارنة النتائج.

اختر كيفية حلّ مواضع الاستدعاء القديمة للصنف:

  • المُفضّل: غيّر في كل ملف عبارة use/require إلى use NextPDF\Compat\Tcpdf\TCPDF;. هذا أسلوب صريح وسهل البحث عنه.
  • عندما يتعذّر عليك حتى الآن تعديل مواضع الاستدعاء: فعّل الأسماء البديلة العامة الاختيارية مرة واحدة عند الإقلاع باستخدام LegacyBootstrap::enableAliases() (راجع /⁨integrations/tcpdf-compat/boot-and-discovery/⁩). يؤدي ذلك إلى حلّ \TCPDF والأصناف المساعدة الأربعة إلى المحوّل.

الاستراتيجيتان متنافيتان عملياً. إذا ظلّت مكتبة ⁨TCPDF⁩ الفعلية قابلة للتحميل التلقائي وفعّلت الأسماء البديلة العامة، فسيُتجاوز الاسم البديل عند وجود صنف \TCPDF مسبقاً. وقد تستمر عندئذٍ في استخدام ⁨TCPDF⁩ القديمة دون أن تلاحظ ذلك. خلال المرحلة 1، فضّل عمليات الاستيراد على مستوى كل ملف لتعرف بالضبط أي صنف يستخدمه كل موضع استدعاء. راجع /⁨integrations/tcpdf-compat/troubleshooting/.⁩

المرحلة 2 — تشغيل مجموعة الاختبارات القائمة دون تغيير

قسم بعنوان «المرحلة 2 — تشغيل مجموعة الاختبارات القائمة دون تغيير»

شغّل مجموعة اختباراتك الكاملة على المحوّل دون تغيير أي شيء آخر. تعمل معظم الطرق المفوَّضة (94 من نحو 120 طريقة جرى استقصاؤها) بشكل متوافق. توقّع نوعين من الإخفاقات المتوقّعة:

  1. التأكيدات على مستوى البايت. ستفشل الاختبارات التي تقارن بايتات ⁨Portable Document Format⁩ (⁨PDF⁩) بدقّة، لأن المحرّك تطبيق مستقل. هذا أمر متوقّع وليس عيباً. أجّل معالجتها إلى المرحلة 4.
  2. تفرّعات القيمة المُرجَعة. تُرجِع بضع طرق قيماً نائبة للتوافق بدلاً من قيم محسوبة. والأبرز أن MultiCell() تُرجِع 1، وWrite() تُرجِع 0. تحتاج الشفرة التي تتفرّع استناداً إلى تلك القيم المُرجَعة إلى تعديل.

وثّق كل إخفاق. صنّف كل إخفاق على أنه خط أساس بايتي أو قيمة مُرجَعة أو فجوة سلوكية حقيقية.

المرحلة 3 — تدقيق الوضع الصارم

قسم بعنوان «المرحلة 3 — تدقيق الوضع الصارم»

تجعل هذه المرحلة الترحيل آمناً. شغّل مجموعة الاختبارات، أو مساراً إنتاجياً تمثيلياً، مع تفعيل الوضع الصارم:

examples/migration-audit.php
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Compat\Tcpdf\Exception\TcpdfNotImplementedException;
use NextPDF\Compat\Tcpdf\TCPDF;
function renderInvoice(TCPDF $pdf): void
{
// ... your existing rendering code, unchanged ...
}
$pdf = new TCPDF('P', 'mm', 'A4');
$pdf->setStrictMode(true);
try {
renderInvoice($pdf);
$pdf->Output(__DIR__ . '/audit.pdf', 'F');
} catch (TcpdfNotImplementedException $e) {
// Each message names the method, the ignored parameters, and a hint.
fwrite(STDERR, 'MIGRATION GAP: ' . $e->getMessage() . "\n");
}

تعامل مع كل TcpdfNotImplementedException بوصفه بند عمل واحداً. تمنحك الرسالة الطريقة، وقائمة المعامِلات المتجاهَلة بدقّة، وتلميحاً للترحيل. مجموعة الطرق التي تطرح الاستثناءات محدودة ومؤكَّدة بالاختبار في tests/Unit/Compat/Tcpdf/TcpdfStrictModeTest.php. ومسوّغ كل حالة مذكور في docs/TCPDF_COVERAGE.md.

شغّل الوضع الصارم بوصفه مهمة تكامل مستمر (⁨CI⁩) مخصّصة، لا في الإنتاج. الغاية هي إظهار الفجوات، لا أن تجعل الإنتاج يطرح استثناءات.

المرحلة 4 — إصلاح مواضع الاستدعاء

قسم بعنوان «المرحلة 4 — إصلاح مواضع الاستدعاء»

لكل فجوة، اختر الإصلاح الصحيح الأقل كلفة:

نمط الفجوةالإصلاح
معامِل متجاهَل لا أهمية له (⁨e.g.⁩ معامِل $align في ⁨TCPDF⁩ لم تعتمد عليه قط)احذف المعامِل. يصبح الاستدعاء متوافقاً تماماً.
معامِل متجاهَل كان ذا أهمية (⁨e.g.⁩ رابط Image() قابل للنقر)أعِد التعبير عنه باستخدام واجهة ⁨API⁩ الحديثة. ارسم الصورة، ثم أضف Document::link() فوق المستطيل.
الطريقة غير مُنفَّذة (setSignature()، endPage())endPage() / Open(): أزل الاستدعاء. التوقيع: راجع /⁨integrations/tcpdf-compat/security-and-operations/⁩؛ فهو يتطلّب إصداراً تجارياً.
طريقة غير منطبقة (setPDFVersion()، setUserRights())أزل الاستدعاء. المُخرَج دائماً ⁨PDF 2.0⁩؛ وحقوق المستخدم مهملة في ⁨PDF 2.0.⁩
تفرّع القيمة المُرجَعةاحسب القيمة بنفسك، أو انقل ذلك المنطق إلى واجهة ⁨API⁩ الحديثة.

استخدم منفذ الالتفاف عندما يعجز سطح ⁨TCPDF⁩ عن التعبير عمّا تحتاج إليه:

examples/migration-escape-hatch.php
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Compat\Tcpdf\TCPDF;
$pdf = new TCPDF();
$pdf->AddPage();
// Legacy path stays as-is for the parts that work:
$pdf->SetFont('helvetica', '', 12);
$pdf->Cell(0, 10, 'Header line', 0, 1);
// Modern path for what the TCPDF surface cannot express here:
$document = $pdf->getDocument();
$document->image('logo.png', 10, 30, 40, 0);
$document->link(10, 30, 40, 20, 'https://example.com');

إعادة ضبط خط الأساس لاختبارات مستوى البايت

قسم بعنوان «إعادة ضبط خط الأساس لاختبارات مستوى البايت»

استبدل التأكيدات على البايتات الدقيقة بتأكيدات على ما يهمّ فعلاً:

  • يبدأ المُخرَج بـ %PDF ويُحلَّل (مستوى الفحص السريع).
  • محتوى النص المعروض موجود (استخرج النص وأكّد وجوده).
  • الخصائص البنيوية (عدد الصفحات وحجم الصفحة ووجود مخطّط تفصيلي) متطابقة.

تدفع هذه الكلفة مرة واحدة لتحصل على اختبارات تصمد أمام ترقيات المحرّك المستقبلية.

المرحلة 5 — إزالة تبعية ⁨TCPDF⁩

قسم بعنوان «المرحلة 5 — إزالة تبعية ⁨TCPDF⁩»

بعد نجاح تدقيق الوضع الصارم، وإيقاف الوضع الصارم ⁨off⁩ في الإنتاج، ونجاح مجموعة الاختبارات على التأكيدات التي أُعيد ضبط خط أساسها، أزل tecnickcom/tcpdf:

Terminal window
composer remove tecnickcom/tcpdf

شغّل مجموعة الاختبارات مرة أخرى. إذا ظلّ أي شيء يُحلَّل إلى صنف ⁨TCPDF⁩ الفعلي، فقد انطبق تحذير الأسماء البديلة في المرحلة 1؛ أصلح مواضع الاستدعاء المتبقية لتستورد المحوّل صراحةً.

المرحلة 6 — سحب المحوّل من الخدمة

قسم بعنوان «المرحلة 6 — سحب المحوّل من الخدمة»

المحوّل أداة مساعِدة للترحيل، لا طبقة دائمة. بعد التخلّص من ⁨TCPDF⁩ والتحقّق من المحرّك، اسحب المحوّل من الخدمة تدريجياً:

  1. في كل وحدة، استبدل new TCPDF(...) بإنشاء NextPDF\Core\Document الحديث.
  2. استبدل استدعاءات طرق ⁨TCPDF⁩ بمكافئاتها الحديثة (استدعاءات getDocument() التي أضفتها مسبقاً في المرحلة 4 هي القالب).
  3. عندما تتوقّف وحدة عن الإشارة إلى المحوّل، احذف عمليات استيراد التوافق الخاصة بها.
  4. عندما لا تشير أي وحدة إلى المحوّل، أزل nextpdf/compat-legacy من composer.json.

عند تلك النقطة، تكون قد انتقلت إلى واجهة ⁨PDF 2.0⁩ الحديثة دون أي طبقة توافق.

  • تثبيت nextpdf/compat-legacy؛ والتحقّق من ارتباط المحرّك.
  • مواضع الاستدعاء تستورد المحوّل صراحةً (أو تمكين الأسماء البديلة مع إزالة ⁨TCPDF⁩ الفعلية من مسار التحميل التلقائي).
  • تشغيل مجموعة الاختبارات الكاملة على المحوّل؛ وتصنيف الإخفاقات.
  • إضافة مهمة ⁨CI⁩ للوضع الصارم؛ وتوثيق كل فجوة.
  • إصلاح كل فجوة (حذف المعامِل / واجهة ⁨API⁩ الحديثة / إزالة الاستدعاء).
  • إعادة ضبط خط أساس التأكيدات على مستوى البايت ليعتمد على المحتوى والبنية.
  • إزالة tecnickcom/tcpdf؛ ومجموعة الاختبارات ناجحة.
  • سحب المحوّل من الخدمة وحدةً تلو الأخرى؛ وإزالة التبعية.
  • /⁨integrations/tcpdf-compat/method-coverage/⁩ — سلوك كل طريقة وإرشادات الاستبدال
  • docs/TCPDF_COVERAGE.md — مصفوفة مرجعية مُتحقَّق منها بالاختبار
  • /⁨integrations/tcpdf-compat/configuration/⁩ — نقل الإعدادات بعيداً عن الثوابت العامة
  • /⁨integrations/tcpdf-compat/security-and-operations/⁩ — التشفير والتوقيع أثناء الترحيل
  • /⁨integrations/tcpdf-compat/troubleshooting/⁩ — تعارض ⁨alias/real-TCPDF⁩ ومزالق أخرى