لو عندك إضافة ووردبريس شغالة عند عملاء كتير، الاستضافة هتنقلك لـ PHP 8.3 أو 8.4 قريب، سواء الكود جاهز أو لأ. والفخ الحقيقي مش في اللي بيكسر على الإصدار الجديد بس، لكن في الإصلاح اللي يكسر الإصدار القديم عند عميل تاني. المقال ده للمطور أو المستقل اللي بيصون إضافات لعملائه، وعايز يعدّي الترقية من غير مفاجآت.
لماذا الآن؟ مواعيد الدعم الرسمية
ينتهي الدعم الأمني لـ PHP 8.2 يوم 31 ديسمبر 2026، أي بعد شهرين تقريباً من نشر هذا المقال. بعدها لن يصدر لهذا الفرع أي إصلاح أمني، وستبدأ الاستضافات في نقل المواقع تلقائياً.
| الإصدار | نهاية الدعم النشط | نهاية الدعم الأمني |
|---|---|---|
| PHP 8.2 | 31 ديسمبر 2024 | 31 ديسمبر 2026 |
| PHP 8.3 | 31 ديسمبر 2025 | 31 ديسمبر 2027 |
| PHP 8.4 | 31 ديسمبر 2026 | 31 ديسمبر 2028 |
| PHP 8.5 | 31 ديسمبر 2027 | 31 ديسمبر 2029 |
المواعيد من صفحة الإصدارات المدعومة في موقع PHP الرسمي (بتاريخ نوفمبر 2026). إذا كنت صاحب موقع لا مطور إضافات، فابدأ بمقالنا عن ترتيب ترقية PHP الآمن على الموقع نفسه؛ هذا المقال يكمله من جهة الكود.
القاعدة الأولى: الإصلاح لا يتجاوز الإصدار الأدنى
تعلن كل إضافة عن أقدم إصدار PHP تدعمه في سطر Requires PHP داخل ترويسة الملف الرئيسي أو في readme.txt، وأحياناً في composer.json. هذا الرقم هو سقف أي إصلاح تكتبه: إذا كانت الإضافة تعلن Requires PHP: 7.2، فكل سطر جديد يجب أن يعمل على 7.2 كما يعمل على 8.4.
هنا تقع أدوات الترقية الآلية في المشكلة. أداة Rector مثلاً ممتازة في إعادة كتابة الكود، لكن مجموعات قواعدها الخاصة بكل إصدار لا تكتفي بإزالة ما ينكسر؛ إنها تحدّث الكود أيضاً إلى match وreadonly وstr_contains(). هذا مناسب لتطبيق يعمل على PHP 8 فقط، لكنه يحوّل إضافة تدعم 7.2 إلى إضافة تتعطل عند كل عميل ما زال على 7.x.
في ووردبريس: إذا رفعت قيمة
Requires PHPفي ترويسة الإضافة، يمنع ووردبريس تثبيتها أو تفعيلها على السيرفرات الأقدم بدلاً من أن يتعطل الموقع. رفع الحد الأدنى قرار مشروع، بشرط أن يكون قراراً واعياً ومكتوباً في سجل التغييرات، لا نتيجة جانبية لأداة.
المشكلة الأولى: DateTime::modify أصبح يرمي استثناء
منذ PHP 8.3، إذا مررت إلى DateTime::modify() أو DateTimeImmutable::modify() نصاً لا يستطيع تحليله، يرمي استثناء (Exception) من نوع DateMalformedStringException. قبل 8.3 كانت الدالة ترجع false مع تحذير فقط.
لاحظ أن الدالة الإجرائية date_modify() لم يتغير سلوكها، والتغيير يخص صيغة الكائنات فقط. هذا النمط الشائع يعمل بلا أخطاء صياغة، ولا يستخدم أي دالة محذوفة:
function legacy_next_date($when)
{
$date = new DateTime();
if (false === $date->modify($when)) {
return null;
}
return $date;
}
على PHP 8.3 وما بعده، لن يصل الكود إلى فحص false أصلاً. أول مرة تأتي $when فارغة من رابط أو إعداد محفوظ، يتوقف التنفيذ باستثناء غير ملتقط. والحل أن تلتقط الاستثناء وتقرر أنت القيمة البديلة:
function legacy_safe_date($when)
{
try {
return (new DateTimeImmutable())->modify($when);
} catch (Exception $e) {
return null;
}
}
لماذا تفوّتها أدوات الفحص؟ لأنها تبحث عن دوال محذوفة أو مهملة، وهذه الدالة ليست كذلك. ابحث عنها بنفسك في كود إضافتك، وركّز على الاستدعاءات التي تأخذ قيمة متغيرة لا نصاً ثابتاً مثل '+1 day'؛ فالنص الثابت إما يعمل دائماً أو يفشل في أول اختبار.
grep -rn -- "->modify(" --include=*.php .
تنبيه: التقاط
Exceptionالعام يعمل على كل الإصدارات، لأنDateMalformedStringExceptionيرث منه. وتذكّر أن الاستثناء يعالج 8.3 وما بعده فقط؛ على الإصدارات الأقدم ما زالت الدالة ترجعfalse، فاحتفظ بفحصfalseأيضاً إذا كانت إضافتك تدعمها.
المشكلة الثانية: الخصائص الديناميكية والأصناف التي لا تملكها
أصبحت الخاصية الديناميكية (Dynamic Property) مهملة منذ PHP 8.2، وشرحنا أساسها في مقال سابق. الإصلاح المعتاد أن تعرّف الخاصية داخل الصنف، لكن هذا آمن فقط حين يكون الصنف الأب من كودك.
إذا ظهر لك تحذير الخصائص الديناميكية على موقع قائم، فالشرح العملي وخطوات الحل من جهة صاحب الموقع في مقالنا تحذير Dynamic Property في PHP 8.2.
خذ صنفاً يرث من WP_Widget، وهو صنف من نواة ووردبريس لا تتحكم فيه:
class Legacy_Widget extends WP_Widget
{
public function __construct()
{
$this->widget_title = 'Legacy';
}
}
تعريف widget_title هنا قد يتعارض مع خاصية في الصنف الأب لا تراها. الأسلم في هذه الحالة إضافة السمة (Attribute) #[AllowDynamicProperties] فوق الصنف. وينطبق الشيء نفسه إذا كان الصنف أو أحد آبائه يعرّف __get أو __isset أو __unset، لأن تعريف الخاصية صراحة يجعل PHP يتجاوز هذه الدوال السحرية لذلك الاسم، فيتغير السلوك بصمت.
ملاحظة: صيغة
#[...]تُقرأ تعليقاً عادياً في PHP 7، لذلك تضيفها دون أن تكسر الإصدارات القديمة.
المشكلة الثالثة: المعامل nullable الضمني في PHP 8.4
يهمل PHP 8.4 هذه الصيغة القديمة، حيث يصبح المعامل قابلاً لـ null ضمنياً فقط لأن قيمته الافتراضية null:
public function save(array $data = null)
الإصلاح البديهي هو النوع القابل للقيمة الفارغة (Nullable Type) صراحة:
public function save(?array $data = null)
لكن صيغة ?Type لم تظهر إلا في PHP 7.1. فإذا كانت إضافتك تعلن Requires PHP: 7.0، يتحول تحذير الإهمال على 8.4 إلى خطأ صياغة يُسقط الموقع على 7.0. أمامك خياران فقط: ارفع الحد الأدنى إلى 7.1 وأعلن ذلك، أو اترك التحذير مؤقتاً حتى تقرر.
بصراحة: هذا القسم والذي قبله قراءة في توثيق PHP وفي تقرير كاتب المصدر الأصلي على إضافتين حقيقيتين، لا تجربة على إضافات ArabCMS. لكن المنطق نفسه ينطبق على أي إضافة: الأداة تعرض المشكلة، والقرار لصاحب الإضافة.
ترقية إضافة إلى PHP 8.4 دون كسر القديم
الترتيب الآمن قبل أن تنقلك الاستضافة
اقرأ Requires PHP
من ترويسة الإضافة وreadme.txt وcomposer.json. هذا سقف كل إصلاح.
git نظيف
لا تعديلات غير محفوظة، حتى تراجع كل تغيير وتتراجع عنه.
فحص ثابت
PHPCompatibility على الإصدار المستهدف لكشف المحذوف والمهمل.
بحث يدوي
استدعاءات ->modify() والأصناف التي ترث من أصناف ووردبريس.
php -l مرتين
على الإصدار المستهدف وعلى الإصدار الأدنى بعد كل إصلاح.
تجربة حقيقية
نسخة تجريبية على PHP 8.4 ببيانات فعلية قبل النشر.
خطة الترقية خطوة بخطوة
- اقرأ الحد الأدنى من ترويسة الإضافة و
readme.txtوcomposer.json، واكتبه أمامك. - ابدأ من مستودع git نظيف بلا تعديلات غير محفوظة، حتى تراجع كل تغيير بـ
git diffوتتراجع عنه. - افحص فحصاً ثابتاً بـ PHPCompatibility مع PHP_CodeSniffer على الإصدار المستهدف.
- ابحث يدوياً عن
->modify(وعن الأصناف التي ترث من أصناف ووردبريس. - طبّق الإصلاحات الآمنة فقط، ثم شغّل
php -lعلى كل ملف تغيّر، مرة على الإصدار المستهدف ومرة على الإصدار الأدنى. - لا تلمس المكتبات المضمّنة مثل
vendor/وAction Scheduler وCMB2؛ الحل تحديث المكتبة نفسها. - جرّب الإضافة يدوياً على نسخة تجريبية تعمل بالإصدار المستهدف.
التحليل الثابت (Static Analysis) يقرأ الكود دون تشغيله، فلا يرى المشكلات التي تعتمد على البيانات، مثل تمرير null إلى دالة نصية، وهو تحذير بدأ مع PHP 8.1. لهذا تبقى الخطوة السابعة إلزامية مهما كانت نتيجة الفحص نظيفة.
ملاحظة: معظم الاستضافات المشتركة الشائعة في مصر والخليج تتيح تغيير إصدار PHP لكل موقع من لوحة التحكم (MultiPHP Manager في cPanel أو ما يقابله). أنشئ نطاقاً فرعياً للتجربة، وانسخ الموقع إليه، واضبطه على 8.4 قبل أن تفعلها الاستضافة على الموقع الحقيقي.
أداة Refit: فحص يحترم الحد الأدنى
كاتب المصدر الأصلي بنى أداة سطر أوامر مفتوحة المصدر اسمها Refit تجمع الخطوات 3 و5 السابقة. تشغّل PHPCompatibility وقواعد مختارة من Rector وphp -l، وتقرأ الحد الأدنى من الترويسة، ولا تطبق أي إصلاح لا يعمل عليه. الحزمة منشورة على Packagist باسم qiyuanlabs/refit، برخصة MIT، وأحدث إصدار وقت الكتابة هو 0.1.1 ويحتاج PHP 8.1 أو أحدث لتشغيله.
composer require --dev qiyuanlabs/refit "phpcompatibility/php-compatibility:^10.0@alpha"
vendor/bin/refit scan --php=8.4 --html=report.html
وفي التكامل المستمر (CI)، يُرجع هذا الأمر رمز خروج 1 إذا وجد في كودك ما ينكسر على الإصدار المستهدف:
refit scan --php=8.4 --fail-on=breaks
بصراحة: الأداة في إصدارها الأول وعمرها أيام، ومطورها يقدّم خدمات ترقية مدفوعة. استخدمها لتوليد تقرير أولي، لا كحكم نهائي، وراجع كل تغيير بنفسك قبل الدمج.
قواعد Rector التي غيّرت سلوك كود حقيقي
يذكر الكاتب خمس قواعد من Rector استبعدها بعد أن غيّرت سلوك إضافات فعلية. الفكرة ليست أن Rector خاطئ، بل أن كود إضافات عمرها عشر سنوات ليس الكود الذي صُممت له هذه القواعد:
SensitiveConstantNameRector: يحوّل الثوابت إلى حروف كبيرة إذا لم يجدdefine()لها، فيكسر ثابتاً معرّفاً في ملف آخر.AssignArrayToStringRector: يحوّل$x = ''إلى مصفوفة حتى لو استُخدم المتغير نصاً بعدها.RemoveExtraParametersRector: يحذف معاملات قد تقرؤها الدالة عبرfunc_get_args().WrapVariableVariableNameInCurlyBracesRector: يعيد دلالة PHP 5 إلى كود يعمل على PHP 7 منذ سنوات.StringifyStrNeedlesRector: يضيف تحويلات(string)لا داعي لها في PHP 8.
إذا كنت تشغّل Rector بنفسك على إضافة، فاستبعد هذه القواعد أو راجع نتائجها سطراً سطراً.
اقرأ أيضًا
الأسئلة الشائعة
س: هل يكفي أن تعمل إضافتي على PHP 8.4 دون أخطاء قاتلة؟
ج: لا. تحذيرات الإهمال تملأ سجل الأخطاء وتتحول إلى أخطاء في إصدارات لاحقة، والاستثناءات الجديدة مثل DateMalformedStringException لا تظهر إلا مع بيانات معينة. اختبر بالبيانات الحقيقية لا بالتثبيت النظيف فقط.
س: ما الفرق بين Rector وPHPCompatibility؟
ج: PHPCompatibility يكتشف الميزات المحذوفة والمهملة في كل إصدار ويبلّغ عنها فقط. Rector يعيد كتابة الكود فعلاً، ولذلك يحتاج مراقبة أكبر حتى لا يرفع الحد الأدنى للإضافة دون قصد.
س: هل أرفع Requires PHP إلى 7.4 أو 8.0 وأرتاح؟
ج: قرار ممكن ويبسّط الصيانة، لكن راجع أولاً إحصاءات إصدارات PHP لدى مستخدميك أو عملائك. أعلن التغيير في سجل التغييرات، وسيمنع ووردبريس التثبيت على السيرفرات الأقدم بدل أن يتعطل الموقع.
س: هل أصلح المكتبات داخل مجلد vendor بنفسي؟
ج: لا. أي تعديل يدوي سيضيع مع أول تحديث. حدّث المكتبة إلى إصدار يدعم PHP 8.4، وإن لم يوجد فابحث عن بديل.
الخلاصة
ترقية إضافة إلى PHP 8.4 ثلاثة أسئلة لا سؤال واحد: ما الذي ينكسر، وما الذي يُصلح آلياً بأمان، وكيف تصلحه دون أن تكسر الإصدار الأدنى. أخطر المشكلات هي التي لا تُعد “محذوفة” أصلاً، مثل DateTime::modify()، ولذلك يبقى البحث اليدوي والتجربة على نسخة تجريبية جزءاً من الخطة.
قدامك لحد 31 ديسمبر قبل ما دعم 8.2 يخلص، فابدأ النهارده بأكتر إضافة عندك عملاء عليها، وقولنا في التعليقات طلع عندك إيه.
مراجع إضافية: PHP: DateTime::modify – Manual، PHP RFC: Deprecate implicitly nullable parameter types، PHP: Supported Versions
مرجع المقال (بالإنجليزية): Two real WordPress plugins on PHP 8.4: what broke, and what I refused to auto-fix
المقال ده مش ترجمة حرفية: متكيّف للقارئ العربي ومضاف عليه سياق ووردبريس وElementor وJetEngine.



