إذا سبق لك أن نشرت مقالاً لتجده يحمل وسم "منذ 57 عاماً" على الموقع العام بينما تؤكد لوحة التحكم بهدوء أنه نُشر قبل عشر دقائق من الساعة، فأنت تدرك تماماً مرارة هذا الخطأ البرمجي. إنه لا يفسد التنسيق، ولا يتسبب في خطأ فادح، بل ببساطة يجعل محتواك يبدو وكأنه يعود لنصف قرن مضى، ولن تجدي محاولة فحص ملفات القالب (theme) نفعاً في تغيير هذا الرقم.
يميل هذا النوع من المشكلات إلى الصمود أمام الفحوصات المعتادة. قد تبحث باستخدام grep في ملفات single.php و archive.php و functions.php عن استدعاء مشوه لـ get_the_date()، ولكن كل شيء سيبدو طبيعياً. تعيد تحميل الصفحة الحية، فتجد أن شبح عام 1968 لا يزال يطارد سطر الكاتب. عند هذه النقطة، تكون المشكلة تقريباً لا علاقة لها بالقوالب، بل تتعلق بكيفية مرور الوقت عبر بنية WordPress.
لماذا يظهر هذا الرقم تحديداً باستمرار
رقم "منذ 57 عاماً" ليس عشوائياً. غالباً ما تعرض قوالب WordPress الوقت النسبي عبر دالة human_time_diff()، التي تقارن الطابع الزمني للمقال باللحظة الحالية وتطبع عبارة ودودة مثل "منذ ساعتين". ولكي تنجح هذه العملية الحسابية، يحتاج PHP إلى طابع زمني Unix صالح — وهو عدد الثواني منذ 1 يناير 1970، وهو ما يعرف بـ Unix epoch.
عندما يقوم شيء ما بإزالة أو إتلاف الطابع الزمني قبل وصوله إلى تلك العملية الحسابية، تنتهي الدالة غالباً بمقارنة الصفر (أو نقطة بداية غير صالحة مماثلة) بالوقت الحالي. والنتيجة هي نطاق زمني يمتد إلى ما يقرب من خمسة عقود ونصف، يتقدم عاماً تقويمياً واحداً في كل مرة. إنها عرض لقيمة زمنية مفقودة أو مقروءة بشكل خاطئ، وليست قاعدة بيانات مليئة بمقالات مدونات من العصور الغابرة.
خداع لوحة التحكم
أحد أكثر الجوانب إحباطاً في هذا الخطأ هو "ازدواج الشخصية" في لوحة الإدارة الخاصة بك. داخل wp-admin، تظهر قائمة المقالات تاريخ النشر الصحيح لأن WordPress غالباً ما يسحب ذلك مباشرة من جدول wp_posts في MySQL ويقوم بتنسيقه في جانب الخادم (server-side) دون تمريره عبر مرشح الوقت النسبي. ومع ذلك، قد يعتمد الواجهة الأمامية (frontend) على قالب أو إضافة تستدعي human_time_diff()، أو قد تمرر القيمة الخام من قاعدة البيانات عبر سلسلة من تحويلات المنطقة الزمنية في PHP التي يتجاوزها الجزء الخلفي (backend) تماماً.
لذا، تكون البيانات عادةً سليمة، لكن المشكلة تكمن في "الأنبوب" الواصل بين قاعدة البيانات وHTML الذي يتم عرضه، حيث يحدث فيه خلل.
إصدار PHP وإعدادات المنطقة الزمنية
ابدأ بـ PHP. تجعل لوحات الاستضافة عمليات تبديل الإصدارات تبدو غير ضارة، لكن PHP يتعامل مع كائنات الوقت (time objects) بشكل مختلف عبر الإصدارات الرئيسية. قد يواجه موقع يعمل بكود PHP 7.4 القديم في بيئة PHP 8.1 تحولات طفيفة في كيفية تهيئة كائنات DateTime للسلاسل النصية الفارغة أو المشوهة. إذا ترك ملف php.ini الخاص بك date.timezone غير محدد، فإن PHP سيعتمد UTC ويفترض أن الخادم يعرف الأفضل، بينما قد لا يتفق WordPress مع ذلك.
تحقق مما إذا كان شخص ما قد قام بكتابة تعريف المنطقة الزمنية يدوياً داخل wp-config.php. سطر مثل date_default_timezone_set( 'Asia/Kolkata' ); قد يبدو مفيداً، لكن WordPress يتوقع إدارة ساعته الخاصة من خلال القيمة المحددة تحت Settings > General. إن فرض إزاحة (offset) على مستوى PHP يمكن أن يؤدي إلى حالة سباق (race condition) حيث يعتقد النظام الأساسي أن الوقت محلي بينما يعتقد PHP أنه عالمي. وعندما تتصادم هذه الإزاحات أثناء تحويل الطابع الزمني، يمكن أن تنهار النتيجة العائدة إلى قالبك لتصبح صفراً.
تكوينات المنطقة الزمنية للخادم
يحمل خادم MySQL الخاص بك فكرته الخاصة عن الوقت المحلي. يكتب WordPress تاريخين لكل مقال: post_date بالوقت المحلي للموقع، و post_date_gmt بالتوقيت العالمي. إذا تغيرت time_zone العالمية في قاعدة البيانات — على سبيل المثال، من SYSTEM إلى +00:00 بعد عملية نقل البيانات (migration) — فقد تفشل دالة strtotime() في PHP في التوفيق بين السلسلة النصية المخزنة وما تتوقعه. لا تزال قاعدة البيانات تخزن 2024-03-15 14:30:00 ، ولكن السياق المحيط بها يتغير، وتصبح المخرجات التي تم تحليلها خاطئة أو فارغة (null) في PHP.
مثال من الواقع: يقوم مستضيف مدار (managed host) بنقل قاعدة بياناتك من عقدة (node) تعمل بنظام MySQL 5.7 مع توقيت SYSTEM إلى عنقود (cluster) يعمل بنظام MariaDB 10.11 مضبوط على UTC صارم. قالبك لا يتغير، وإعدادات WordPress لا تتغير، ومع ذلك، فإن get_the_time( 'U' ) — تنسيق الطابع الزمني Unix — يعيد فجأة قيمة فارغة لبعض المقالات، فتعود دالة الوقت النسبي إلى عملية حساب "نقطة الصفر" تلك. الحل هو مواءمة المنطقة الزمنية للتطبيق، والمنطقة الزمنية لقاعدة البيانات، وساعة نظام التشغيل، بحيث لا يضطر WordPress إلى التخمين بشأن المرجع الذي يجب استخدامه.
تنسيقات الطابع الزمني لقاعدة البيانات
يخزن نواة WordPress تواريخ المقالات في أعمدة DATETIME في MySQL، وليس في حقول TIMESTAMP. هذا التمييز مهم لأن DATETIME يحفظ قيمة تقويمية دون الوعي بالمنطقة الزمنية (without_zone awareness) في إصدارات MySQL القديمة، بينما يقوم TIMESTAMP بتحويل كل شيء إلى UTC في الخلفية. إذا قام أحد الإضافات (plugins) أو أدوات الهجرة بتغيير مخطط (schema) جدول wp_posts الخاص بك، أو إذا استعاد نسخ احتياطي قديم تواريخ صفرية غير صالحة في الجدول، فقد يقرأ WordPress قيمة مثل 0000-00-00 00:00:00 ويمررها بهدوء إلى القالب الخاص بك.
ترفض أوضاع MySQL الحديثة، وخاصة NO_ZERO_DATE و STRICT_TRANS_TABLES تلك القيم الصفرية النائبة. إذا قام نص برمجي للنسخ الاحتياطي بإدراجها أثناء عملية الهجرة، فقد تكون قاعدة البيانات قد قامت بقصها أو جعلها فارغة (nulled) عند الاستيراد. يمكنك التحقق من ذلك بسرعة باستخدام استعلام SQL مباشر:
SELECT ID, post_title, post_date, post_date_gmt
FROM wp_posts
WHERE post_status = 'publish'
ORDER BY post_date DESC
LIMIT 10;
إذا كانت الأعمدة الخام تبدو صحيحة ولكن الموقع لا يزال يعرض تواريخ قديمة جداً، فإن التلف يحدث بعد عملية الاستعلام. أما إذا كانت الأعمدة نفسها تحتوي على أصفار، فقد وجدت مصدر الخلل.
تعارض الإضافات مع دوال التاريخ
تُعد الإضافات التي تقوم بتصفية مخرجات التاريخ من المتهمين المعتادين. تقوم الأدوات متعددة اللغات مثل WPML أو Polylang بالارتباط بـ get_the_date لترجمة أسماء الأشهر. وتقوم إضافات التخزين المؤقت (Caching plugins) باعتراض كود HTML النهائي. يمكن لأي من الطبقتين أن يمرر عن طريق الخطأ سلسلة نصية غير منسقة إلى دالة تتوقع عدداً صحيحاً من نوع Unix.
قد تقوم إضافة الترجمة باستبدال "March 15, 2024" بما يعادلها محلياً، ولكن إذا قام القالب بعد ذلك بتمرير تلك السلسلة المحلية إلى strtotime() داخل دالة مخصصة، فسيفشل المحلل (parser) في التعامل مع الأحرف غير الإنجليزية وسيعيد القيمة false. تصل هذه القيمة الخاطئة إلى human_time_diff()، التي تقارن الصفر بالوقت الحالي، مما ينتج عنه الفجوة الشهيرة التي تمتد لنصف قرن.
تزيد طبقات التخزين المؤقت للكائنات (Object cache layers) الأمر تعقيداً. يمكن لـ Redis أو Memcached تخزين كائن WP_Post متسلسل من طلب فشل لفترة وجيزة في تحليل التاريخ. يتلقى كل زائر لاحق ذلك الكائن التالف مباشرة من ذاكرة الوصول العشوائي (RAM)، متجاوزاً قاعدة البيانات تماماً. وتستمر المشكلة في الموقع المباشر لأن بيئة الإنتاج (production stack) لديك تحتوي على بنية تحتية للتخزين المؤقت تفتقر إليها النسخة المحلية.
قائمة مرجعية عملية لاستكشاف الأخطاء وإصلاحها
نظرًا لأن العرض يختبئ في أعماق النظام، فأنت بحاجة إلى تقشير الطبقات بشكل منهجي حتى تعود التواريخ إلى وضعها الصحيح.
- استعلم من قاعدة البيانات مباشرة. افتح phpMyAdmin أو قم بتشغيل WP-CLI وافحص قيم
post_dateالخام. إذا كانت صحيحة، فإن قاعدة البيانات ليست هي المشكلة. - انتقل إلى قالب افتراضي. قم بتنشيط Twenty Twenty-Four أو Twenty Twenty-Three. إذا اختفت المشكلة، فقم بمراجعة قالبك النشط والقالب الابن (child theme) بحثاً عن دوال مخصصة تغلف مخرجات الوقت.
- عطّل الإضافات. قم بتغيير اسم المجلد
/wp-content/pluginsمؤقتاً، أو قم بتعطيل جميع الإضافات من لوحة التحكم. أعد تفعيلها واحدة تلو الأخرى، مع فحص الواجهة الأمامية بعد كل عملية تنشيط. - اقرأ سجل الأخطاء. ابحث عن تحذيرات المنطقة الزمنية، أو أخطاء
DateTime، أو شكاوىstrtotime(). غالباً ما تشير هذه الأخطاء إلى الـ
