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

الأنواع الصارمة في كل مكان

Spec: ISO 32000-2, §7.5.5 Evidence: Code-backed PHPStan: Level 10, no src baseline

يُشغّل ⁨NextPDF⁩ أداة ⁨PHPStan⁩ عند ⁨Level 10⁩ على مصدر المحرّك من دون أساس ضبط لكتم الانتهاكات. توضّح هذه الصفحة لماذا يُعدّ “غياب أساس الضبط” قراراً تصميمياً لا تفصيلاً في الأدوات، وما الذي تمنحه هذه الصرامة عملياً لخط معالجة مهمته ألّا يسيء التعامل مع البيانات بصمت.

في معظم التطبيقات، تُعدّ الأنواع الصارمة ممارسةً صحّيةً للشيفرة. أما في محرّك ⁨PDF⁩، فهي أقرب إلى ضمان للصواب، لأن الصيغة لا تتسامح مع الخطأ. يُتوقّع من القارئ أن يحدّد موضع المحتوى بقراءة الملف من نهايته، عبر المُذيّل وجدول الإحالات المتقاطعة، لذلك يجب أن تكون إزاحات البايت لدى الكاتب دقيقةً تماماً. تأمّل نوعاً يتّسع بهدوء إلى mixed، أو قيمة int تتحوّل بصمت إلى string، أو قيمة قابلة لأن تكون فارغة تُفكّ إشارتها من دون فحص. قد تُنتج أيٌّ من هذه الحالات ملفاً يُفتح على نحو سليم في عارض، ثم يفشل في التحقّق في عارض آخر بعد أسابيع، من دون أيّ تتبّع مكدّس يشير إلى السبب.

الإخفاقات المكلِفة في هذا المجال هي الصامتة منها. الأنواع الصارمة إلى جانب محلّل صارم هي الطريقة التي يحوّل بها المحرّك صنفاً من إخفاقات زمن التشغيل الصامتة إلى إخفاقات صاخبة في زمن البناء.

  • يُحلَّل مصدر المحرّك عند ⁨PHPStan Level 10⁩ — أصرم مستوى — كما هو مُتحقَّق منه في phpstan.neon.dist.
  • لا يوجد أساس ضبط لكتم المصدر. يثبّت الإعداد تحليل المصدر عند صفر أخطاء. ويُفشِل أيّ ارتداد عمليةَ البناء بدلاً من امتصاصه داخل ملف تجاهل يتضخّم بمرور الوقت.
  • المُدخلات القليلة الموجودة في ignoreErrors هي ضيّقة النطاق، ومحدّدة بالمعرّف والمسار، ومبرَّرة كلٌّ على حدة في الإعداد (حدود الاعتماد اللين بين الحزم، ومواضع الاختبار الموجّهة إلى الانعكاس) — لا أساس ضبط مُجمَّع.
  • يُشغّل ملف تعريف صارم منفصل level: max ويمنع أيّ مُدخلات تجاهل جديدة، فتُلزَم الشيفرة الجديدة بمعيار أكثر إحكاماً.
  • الأثر المقصود هو ضغط تصميمي: الشيفرة التي لا يمكن التعبير عنها بأمانة نوعية لا تجتاز الفحص، فيُعاد تصميمها بدلاً من كتمها.

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

يسجّل أساس الضبط كلّ انتهاك قائم ويأمر المحلّل بتجاهل تلك الانتهاكات بالتحديد. إنها طريقة عملية لتبنّي التحليل الساكن في قاعدة شيفرة قديمة، لكنها تأتي بكُلفة. إذ يتحوّل أساس الضبط إلى دفتر صامت لدَين اتّفق نظام الأنواع على ألا ينظر إليه. وقد تتسلّل انتهاكات جديدة من النوع نفسه إلى جانب الانتهاكات القائمة. وهكذا يضعف وعد المحلّل من “هذه الشيفرة نظيفة نوعياً” إلى “هذه الشيفرة ليست أسوأ مما كانت عليه.”

لا يقبل ⁨NextPDF⁩ هذه المقايضة في مصدر المحرّك. يثبّت الإعداد تحليل المصدر عند صفر أخطاء ويفعّل reportUnmatchedIgnoredErrors، حتى إنّ الكتم المتقادم — الذي لم يعد يطابق شيئاً — يُفشِل عملية البناء. حالات التجاهل الضيّقة المتبقّية محدّدة بمعرّف خطأ بعينه وبملف بعينه. ويحمل كلٌّ منها تفسيراً مضمّناً يوضّح لماذا يكون ذلك الحدّ مقصوداً (على سبيل المثال، تعامل النواة مع واجهة ⁨Pro/Enterprise⁩ من دون اعتماد عيني عليها عمداً). ويمكن للمراجِع أن يقرأ كلّ واحدة منها ويحكم عليها. ولا توجد قائمة مبهمة يصعب تتبّعها.

المسار الذي يُبقي ذلك أميناً:

  1. Change proposed New or modified engine code.
  2. Level 10 analysis Strictest PHPStan level over src/, treatPhpDocTypesAsCertain on.
  3. Zero-error gate No source baseline; unmatched ignores also fail.
  4. Strict profile level: max; no new ignore entries permitted.
  5. Redesign, not suppress If it cannot be expressed honestly, the design changes.
كيف يصل التغيير إلى مصدر المحرّك: لا يمكن لتغيير غير أمين نوعياً أن يجتاز البوّابة، فيُعاد تصميمه بدلاً من كتمه.

treatPhpDocTypesAsCertain جزء من هذا النهج. تُعامَل تعليقات PHPDoc التوضيحية بوصفها حقيقةً مرجعيةً، فلا تكون @param list<T> أو @return non-empty-string تعليقاً يتجاوزه المحلّل بأدب. إنها وعد مُتحقَّق منه. ويُجبَر التعليق التوضيحي ونوع زمن التشغيل على التطابق.

هذه الصفحة Evidence: Code-backed . الإعداد هو الدليل:

  • يضبط phpstan.neon.dist القيمة level: 10 وphpVersion: 80400، ويحلّل src، ولا يتضمّن أيّ مفتاح baseline: — لذلك لا يوجد phpstan-baseline.neon لتحليل المصدر.
  • يضبط الملف نفسه treatPhpDocTypesAsCertain: true وreportUnmatchedIgnoredErrors: true، مع ملاحظة مضمّنة بأنّ تحليل المصدر عند ⁨L10⁩ مثبَّت عند صفر أخطاء وأنّ أيّ ارتداد يجب أن يُفشِل التكامل المستمرّ.
  • تُحدَّد كلّ من حالات ignoreErrors المتبقّية بالـidentifier وكثيراً ما بالـpath، مع تعليقات تشرح مبرّر الاعتماد اللين والهدف الانعكاسي — وهي ليست أساس ضبط مُولَّداً بالجملة.
  • يرث phpstan-strict.neon.dist ذلك الإعداد، ويرفع المستوى إلى max، ويجمّد قائمة التجاهل بحيث لا يجوز إضافة أيّ مُدخل جديد ضمن الملف الصارم.

من منظور المعايير، المسألة مباشرة. يجب أن يُنتج المحرّك ملفات يستطيع القارئ التنقّل فيها من المُذيّل وجدول الإحالات المتقاطعة وفقاً لـ Spec: ISO 32000-2, §7.5.5 . إزاحات البايت الدقيقة هي مشكلة أنواع قبل أن تكون مشكلة تسلسل. فالإزاحة عدد صحيح يجب ألا يتحوّل بصمت إلى أيّ شيء آخر أبداً. وخط معالجة يكون نظيفاً نوعياً عند ⁨Level 10⁩ يكون قد أزال مسبقاً معظم السبل التي قد تنحرف بها العمليات الحسابية بهدوء.

تتضح الأنواع الصارمة أكثر ما تتضح حين تُرمَّز قاعدة من قواعد المجال بوصفها نوعاً بدلاً من فحص في زمن التشغيل. يجيب مُميِّز المطابقة عن أسئلة على مستوى المواصفة بـmatch شاملة، فتكون أيّ حالة غير معالَجة خطأً في الأنواع، لا ملف ⁨PDF⁩ خاطئاً:

declare(strict_types=1);
enum ConformanceMode: string
{
case Plain = 'plain';
case PdfUa2 = 'pdfua2';
case PdfA4 = 'pdfa4';
/** @return 2|3|4|null */
public function pdfaPart(): ?int
{
return match ($this) {
self::PdfA4 => 4,
default => null,
};
}
}

إنّ @return 2|3|4|null ليست مجرّد توثيق. في ظلّ treatPhpDocTypesAsCertain، تُفحَص. يُبلَّغ المُستدعي الذي يفترض أنّ النتيجة دائماً int بذلك في زمن التحليل، قبل كتابة بايت واحد من رقم جزء ⁨PDF/A⁩ غير مطابق.

المزلق هو قراءة “غياب أساس الضبط” بمعنى “أنّ الشيفرة صادف أنها خالية من الانتهاكات.” والصحيح هو العكس. إنّ غياب أساس الضبط هو السبب، لا نتيجة محظوظة. ولأنه لا يوجد مكان يُركَن فيه انتهاك، يتعيّن كتابة الشيفرة التي قد تُنتج انتهاكاً على نحو مختلف. إنّ ⁨Level 10⁩ من دون أساس ضبط للمصدر قيدٌ يصوغ التصميم، لا بطاقة تقييم تصفه بعد وقوعه.

مفهوم خاطئ ثانٍ: أنّ الحفنة من مُدخلات ignoreErrors هي أساس ضبط باسم آخر. وليست كذلك. أساس الضبط مُولَّد بالجملة ومبهم. أما هذه فمكتوبة كلٌّ على حدة، ومحدّدة بالمعرّف، ومشروحة، ومحميّة بـreportUnmatchedIgnoredErrors بحيث لا يمكن أن تتقادم من دون أن يُلاحَظ ذلك.

تتناول هذه الصفحة تحليل مصدر المحرّك. تُحلَّل حزمة الاختبارات ضمن نطاق وإعداد منفصلين ومتمايزين عمداً؛ و”غياب أساس الضبط” هنا قول عن src/، لا ادّعاء بأنّ كلّ تحليل مساعد في المستودع خالٍ من أساس الضبط. يثبت ⁨PHPStan⁩ سلامة الأنواع، لا صواب السلوك. وهو لا يحلّ محلّ هرم الاختبار، بل يزيل فقط صنفاً من الإخفاقات كان على الاختبارات، لولا ذلك، أن تتعقّبه. المستوى الدقيق والأعلام ومجموعة التجاهل دقيقة اعتباراً من تاريخ مراجعة هذه الصفحة. المصدر المرجعي دائماً هو phpstan.neon.dist وphpstan-strict.neon.dist في مستودع النواة.

لا يغيّر الإصدار هذا الانضباط. يُبنى كلّ إصدار من مصدر ⁨Level 10⁩ نفسه:

Level 10 source analysis — edition availability
Edition Availability
Core يُحلَّل مصدر النواة عند Level 10 من دون أساس ضبط للمصدر.
Pro بُني Pro على انضباط مصدر Level 10 نفسه.
Enterprise بُني Enterprise على انضباط مصدر Level 10 نفسه.
  • ⁨PHPStan Level 10⁩ — أصرم مستوى تحليل، إذ يعامل القيم غير المُنوَّعة والمُنوَّعة على نحو فضفاض بوصفها أخطاءً لا تحذيرات.
  • أساس الضبط — سجلّ مُولَّد للانتهاكات القائمة يُؤمَر المحلّل بتجاهلها. لا يستخدم ⁨NextPDF⁩ أيّاً منه لمصدر المحرّك.
  • treatPhpDocTypesAsCertain — إعداد في ⁨PHPStan⁩ يعامل تعليقات نوع ⁨PHPDoc⁩ التوضيحية بوصفها حقائق مُتحقَّقاً منها، لا تعليقات استرشادية.
  • reportUnmatchedIgnoredErrors — إعداد يُفشِل عملية البناء حين يكفّ مُدخل تجاهل عن مطابقة أيّ شيء، فيمنع حالات الكتم المتقادمة.
  • الضغط التصميمي — أثر قيد يفرض كتابة الشيفرة على نحو معيّن، في مقابل فحص يقتصر على قياسها.