إذا سبق لك أن رأيت موقعًا "يعمل" ولكنه لم يعد يرسل رسائل البريد الإلكتروني، أو لم يعد ينشر المحتوى المجدول، أو ترك سلة تسوق WooCommerce منتهية الصلاحية لعدة أيام دون أي خطأ واضح، فمن المحتمل أن يكون لديك WP-Cron يفشل بصمت.
المشكلة
أكثر ما يُثير الإحباط في WP-Cron هو أنه غالبًا ما يفشل دون أي رسالة في لوحة التحكم. لا تكتشف ذلك إلا من خلال الآثار الجانبية (تأخير المنشورات المُجدولة، عدم إرسال رسائل البريد الإلكتروني، عدم تنفيذ عملية التنظيف)، أو في سجلات النظام إن حالفك الحظ.
الأخطاء الشائعة الموجودة في wp-content/debug.log (أو في سجلات PHP-FPM/Apache):
PHP Fatal error: Uncaught Error: Call to undefined function myplugin_do_cleanup() in /wp-content/plugins/myplugin/myplugin.php:123
Stack trace:
#0 /wp-includes/class-wp-hook.php(324): myplugin_cleanup_callback()
#1 /wp-includes/class-wp-hook.php(348): WP_Hook->apply_filters()
#2 /wp-includes/plugin.php(517): WP_Hook->do_action()
#3 /wp-cron.php(188): do_action_ref_array()
#4 {main}
thrown in /wp-content/plugins/myplugin/myplugin.php on line 123
cURL error 28: Operation timed out after 5000 milliseconds with 0 bytes received
WordPress database error Deadlock found when trying to get lock; try restarting transaction for query UPDATE wp_options SET option_value = ... WHERE option_name = 'cron'
أين يظهر:
- الواجهة الأمامية في بعض الأحيان لا يحدث ذلك في أي مكان، لأن WP-Cron يتم تشغيله في الخلفية بعد طلب الزائر.
- إداري : المنشورات "المخطط لها" التي لا تزال عالقة، والإجراءات المؤجلة (الفهرسة، والمزامنة) التي لا تتقدم.
- الفسفور الابيض بين cron.php : إذا قمت بتشغيله يدويًا، فقد ترى أخطاء 200/500، أو مهلات من جانب الخادم.
- واجهة برمجة التطبيقات / AJAX : تقوم بعض الإضافات بجدولة المهام أثناء استدعاءات REST، ثم لا يتم تنفيذ المهمة أبدًا.
الظروف النموذجية:
- بعد تفعيل إضافة التخزين المؤقت/شبكة توصيل المحتوى/جدار حماية تطبيقات الويب (Cloudflare، أو التخزين المؤقت للخادم، أو إضافة الأداء) التي تحظر أو تخزن استدعاء
wp-cron.php. - بعد الترحيل: تم تغيير النطاق، أو تم فرض استخدام HTTPS، أو تم تطبيق قواعد جدار حماية أكثر صرامة، أو تم تكوين نظام cron بشكل خاطئ.
- بعد تحديث PHP: التبديل إلى PHP 8.1+ ووظيفة cron تقوم بتشغيل كود قديم أو مهمل أو كارثي.
- في المواقع ذات حركة المرور المنخفضة: لا يتم تشغيل WP-Cron "في الوقت المحدد".
لمن هذا الدليل؟ أنت تدير مواقع الويب WordPress 6.9.4 (أبريل 2026) وتريد تحديد الهوية بدقة أي المهام تفشل؟ لماذاوتطبيق إصلاحات فعّالة (برمجية وخادمية). في النهاية، ستتعلم كيفية ضبط WP-Cron، وتحديد الأسباب (قفل cron، ذاكرة التخزين المؤقت، انتهاء المهلة، تعريفات الخطافات غير الصحيحة)، والتحقق من صحة إعادة التشغيل.
ملخص سريع
- WP-Cron ليس "مجدولًا زمنيًا حقيقيًا" يعتمد ذلك على مُشغّل HTTP. بدون حركة مرور أو إذا
wp-cron.phpإذا تم حظرك، فلن يتم تشغيل مهامك. - ابدأ بالملاحظة : قم بتسجيل تنفيذ خطافات cron (بدء/إنهاء/خطأ) وقم بقياس المدة.
- تحقق من القفل (
doing_cron) وسلامة تخزين الأحداث (خيار)cron)، خاصة في بيئة متعددة الخوادم. - أكثر حالات الفشل شيوعاً لم يتم تسجيل الخطاف في الوقت الصحيح، عدد الوسائط غير صحيح، خطأ فادح في PHP، مهلة HTTP، حظر ذاكرة التخزين المؤقت/جدار حماية تطبيقات الويب
wp-cron.php. - الحل الأكثر موثوقية في بيئة الإنتاج: قم بتعطيل مشغل "الزوار" واستخدم مهمة cron للنظام (أو جدولة خارجية) تستدعي WP-CLI.
الأعراض
ما تراه على الموقع الإلكتروني (من الأكثر شيوعاً إلى الأكثر تضليلاً):
- المقالات المخطط لها والتي تبقى في حالة "مخطط لها" أو تنتقل إلى حالة "منشورة" مع تأخير لعدة ساعات/أيام.
- رسائل البريد الإلكتروني (النماذج، الإشعارات، الملخصات) يتم إرسالها متأخرة أو لا يتم إرسالها مطلقاً.
- Nettoyages (البيانات المؤقتة، والسجلات، والمراجعات، والجلسات) لم يتم تنفيذها مطلقًا: قاعدة البيانات تنمو، والأداء يتدهور.
- مهام الملحق تم حظر: مزامنة نظام إدارة علاقات العملاء، الاستيراد/التصدير، الفهرسة، مسح ذاكرة التخزين المؤقت، إعادة إنشاء الصور المصغرة.
- ارتفاع مفاجئ وعشوائي في وحدة المعالجة المركزية/الإدخال/الإخراج عندما يتم تشغيل WP-Cron أخيرًا، فإنه "يلحق" بالتراكم.
- 500 خطأ متقطع في
/wp-cron.phpفي سجلات الخادم، دون أي تأثير مرئي فوري. - REST/AJAX : لا تحدث الإجراءات المؤجلة (مثل روابط الويب المعلقة)، بدون ظهور خطأ في وحدة تحكم المتصفح.
الإشارات الثانوية التي صادفتها غالباً في المواقع التي تستخدم Divi 5 / Elementor / Avada:
- يقوم مُنشئ المواقع بتشغيل عمليات في الخلفية (إنشاء ملفات CSS، وتحسينها، وتحميلها مسبقًا) مُجدولة عبر cron. إذا كان WP-Cron معطلاً، فستلاحظ وجود تناقضات (ملفات CSS مفقودة، وعدم مسح ذاكرة التخزين المؤقت) وستبحث في المكان الخطأ.
- تُضيف بعض إضافات "الأداء" المقترنة بهذه القوالب طبقة تخزين مؤقت قوية تؤدي في النهاية إلى إخفاء ou كتلة الدعوة إلى
wp-cron.php.
لماذا وصلت؟
نسخة مبسطة (لترتيب أفكارك)
يقوم WP-Cron بتنفيذ المهام المجدولة عندما يزور شخص ما موقعك (أو عندما يستدعي الخادم ذلك). wp-cron.phpإذا لم تكن هناك زيارة، فلن يكون هناك مُحفِّز. إذا تم حظر المكالمة (بسبب ذاكرة التخزين المؤقت، أو جدار الحماية، أو نظام أسماء النطاقات، أو بروتوكول SSL)، فلن يتم تنفيذ أي مهام. وإذا تعطلت إحدى المهام (خطأ فادح في PHP)، فقد لا يتم تشغيل المهام اللاحقة أبدًا، دون أي إشعار واضح.
النسخة التقنية (ما يحدث خلف الكواليس)
WordPress يخزن أحداث cron في الخيار cron (في قاعدة البيانات). لكل استعلام، spawn_cron() قد يحاول تشغيل استدعاء HTTP غير محظور إلى wp-cron.phpعن طريق تثبيت قفل عبر الخيار doing_cron لتجنب التنفيذ المتزامن. ثم wp-cron.php تهمة WordPress وينفذ الخطافات المجدولة عبر do_action_ref_array().
المتغيرات التي تؤدي إلى نفس الأعراض:
- التفعيل مستحيل :
wp-cron.phpيُرجع 403 (WAF)، أو 301 loopback (HTTPS)، أو مهلة (DNS)، أو يتم تخزينه مؤقتًا. - زناد نادر : موقع ذو حركة مرور منخفضة، أو صفحات يتم عرضها في ذاكرة التخزين المؤقت الثابتة دون تنفيذ PHP (وبالتالي دون فرصة للإنشاء).
- تنفيذ فاشل : خطأ فادح في PHP، الذاكرة، تجاوز الحد الأقصى لمهلة التنفيذ، تعطل قاعدة البيانات، توقيع رد الاتصال غير صحيح.
- مغلق :
doing_cronيبقى "عالقا" بعد حدوث عطل، أو إيقاف PHP-FPM، أو في بيئة متعددة الخوادم سيئة التنسيق.
الأسباب المحتملة (من الأكثر شيوعاً إلى الأقل شيوعاً):
- ذاكرة التخزين المؤقت/جدار حماية تطبيقات الويب/شبكة توصيل المحتوى الكتل أو الذاكرات المؤقتة
/wp-cron.php. - حركة مرور منخفضة + زوار WP-Cron: لا يتم تشغيل أي شيء.
- خطأ فادح في PHP أثناء تنفيذ مهمة (غالباً بعد تحديث PHP 8.1+ أو تحديث الملحق).
- تم تسجيل المقطع الصوتي بشكل غير صحيح (تم التسجيل متأخرًا جدًا، أو كمسؤول فقط)، أو عدد قليل من الحجج.
- مهلة (واجهة برمجة تطبيقات HTTP، الطلبات الخارجية، معالجة الصور) والتي تتجاوز وقت الخادم.
- قفل تنفيذ مهمة كرون الحظر / التزامن (حركة مرور عالية، خوادم متعددة، ذاكرة تخزين مؤقتة للكائنات).
- تخزين كرون تالف (اختيار
cron(حجم كبير جدًا، تحميل تلقائي إشكالي، قاعدة بيانات غير مستقرة).
مخطط تشخيصي (سريع ولكنه مفيد)
| عرض | السبب المحتمل | التحقق | الحلول |
|---|---|---|---|
| المنشورات المخطط لها متأخراً | موقع ذو حركة مرور منخفضة / تم حظر wp-cron.php | اختبر ملف wp-cron.php، وتحقق من سجلات 403/timeout | نظام Cron + تعطيل WP-Cron للزوار |
| لا يوجد شيء قيد التشغيل، ولا تحدث أي أخطاء. | ذاكرة التخزين المؤقت/ذاكرة التخزين المؤقت لشبكة توصيل المحتوى wp-cron.php | يتم تخزين العناوين مؤقتًا على /wp-cron.php، وقواعد جدار حماية تطبيقات الويب (WAF). | استبعاد ملف wp-cron.php من ذاكرة التخزين المؤقت/جدار حماية تطبيقات الويب |
| بعض المهام فقط | خطأ فادح في PHP داخل دالة رد الاتصال | ملف debug.log، وسجلات PHP-FPM، ومراقب الاستعلامات (إن أمكن تشغيله) | أصلح دالة الاستدعاء، وعالج الأخطاء، وقلل الحمل |
| عمليات إعدام غير نظامية | تعطل عملية doing_cron بسبب القفل / التزامن | افحص خيار `doing_cron` ولاحظ الطوابع الزمنية. | نظام قفل نظيف + نظام كرون + تجنب المنافسة |
| تم ضبط wp-cron.php على 500 | مهلة زمنية/ذاكرة/خطأ فادح | سجل أخطاء الخادم، قم بزيادة حجم السجلات، ثم أعد إنتاج الخطأ عبر واجهة سطر الأوامر. | تشغيل حدث cron في WP-CLI + تصحيح الكود/الأداء |
المتطلبات الأساسية قبل البدء
- حماية قاعدة البيانات والملفات، أو لقطة إذا كنت تستخدم خادمًا افتراضيًا خاصًا/سحابيًا. لا تختبر "بشكل عشوائي" في بيئة الإنتاج.
- بيئة الاختبار : استنساخ بيئة الاختبار بنفس إصدارات PHP/الامتدادات ونفس ذاكرة التخزين المؤقت (وإلا ستفوتك المشكلة).
- إصدارات : WordPress 6.9.4، PHP 8.1+ (8.2/8.3 جيد)، والمكونات الإضافية المحدثة.
- تفعيل التسجيل في مرحلة الاختبار (أو مؤقتًا في مرحلة الإنتاج):
/**
* wp-config.php (staging idéalement)
* Objectif : capturer les fatals et warnings pendant les exécutions cron.
*/
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false ); // Évite d'afficher des erreurs aux visiteurs
- أدوات :
- مراقبة الاستعلام (مفيد لأخطاء PHP/HTTP، ولكن ليس دائمًا لملف wp-cron.php إذا كان خارج الدورة العادية).
- فحص الصحة واستكشاف الأخطاء وإصلاحها (عزل تعارضات القوالب/الإضافات).
- WP-CLI (عند الإمكان): ضروري لإطلاق الأحداث بدون بروتوكول HTTP.
الوثائق الرسمية المفيدة التي يجب الاحتفاظ بها مفتوحة:
- واجهة برمجة تطبيقات Cron (دليل مطور الإضافات)
- wp_schedule_event()
- wp_schedule_single_event()
- wp_next_scheduled()
- wp_unschedule_event()
الحل الأول: راقب وسجل WP-Cron (بدون تخمين)
الفخ الكلاسيكي: أنت "تظن" أن WP-Cron لا يعمل، بينما المشكلة الحقيقية هي مهمة تتعطل، أو خطاف مُعرّف بشكل خاطئ. لذلك نقوم بمراقبة الأداء.
1) تحقق مما هو مخطط له (يوصى باستخدام WP-CLI)
على الخادم:
# Liste des événements cron planifiés
wp cron event list --fields=hook,next_run,recurrence,args --format=table
# Lancer tous les événements "dus" maintenant (attention en prod)
wp cron event run --due-now
Si wp cron event list تكون فارغة عندما يفترض أن تقوم الإضافات بجدولة المهام؛ غالبًا ما يكون لديك:
- إضافة تقوم فقط بجدولة التفعيل (وقد فشل التفعيل)،
- موقع مستنسخ دون تنفيذ روتين التنشيط،
- مهمة تم حذفها أثناء عملية الترحيل.
2) أضف مسجلاً بسيطاً لخطافات كرون المستهدفة
أفضل استخدام إضافة صغيرة (يجب استخدامها) لمنع تعطيل السمة الفرعية أو إضافة المقاطع البرمجية من تعطيل أدواتك.
Créez wp-content/mu-plugins/cron-observer.php :
<?php
/**
* Plugin Name: MU - Cron Observer
* Description: Journalise l'exécution de certains hooks WP-Cron (WordPress 6.9.4+).
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
final class BPCAB_Cron_Observer {
/**
* Liste des hooks à observer.
* Ajoutez ici les hooks critiques (newsletter, purge cache, sync, etc.).
*/
private array $hooks = [
'wp_version_check',
'wp_update_plugins',
'wp_update_themes',
// Exemples : remplacez par vos hooks réels
'myplugin_cleanup_daily',
'myplugin_sync_hourly',
];
public function register(): void {
foreach ( $this->hooks as $hook ) {
// Priorité très basse pour être exécuté tôt, et accepter jusqu'à 10 arguments.
add_action( $hook, [ $this, 'log_start' ], 0, 10 );
add_action( $hook, [ $this, 'log_end' ], PHP_INT_MAX, 10 );
}
}
public function log_start( ...$args ): void {
$hook = current_filter();
$this->log( sprintf( '[CRON][START] hook=%s args=%s', $hook, wp_json_encode( $args ) ) );
}
public function log_end( ...$args ): void {
$hook = current_filter();
$this->log( sprintf( '[CRON][END] hook=%s', $hook ) );
}
private function log( string $message ): void {
// Utilise error_log : récupérable via debug.log si WP_DEBUG_LOG=true, sinon logs PHP-FPM.
error_log( $message );
}
}
add_action( 'plugins_loaded', static function () {
$observer = new BPCAB_Cron_Observer();
$observer->register();
} );
لماذا هذا مفيد: إذا رأيت [START] بلا [END] بالنسبة للخطاف، من المحتمل أن يكون لديك خطأ فادح في PHP، أو مهلة زمنية، أو exit جنون في رد نداء الملحق.
3) تسجيل الوفيات (معالج الإيقاف)
لا تُسجّل العمليات الخطيرة دائمًا بشكلٍ سليم في سجلات التطبيق. أضف معالج إنهاء التنفيذ (ضمن المكون الإضافي mu-plugin):
<?php
// À ajouter dans le même fichier MU, ou un autre MU-plugin.
add_action( 'muplugins_loaded', static function () {
register_shutdown_function( static function () {
$error = error_get_last();
if ( empty( $error ) ) {
return;
}
$fatal_types = [ E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR ];
if ( ! in_array( $error['type'], $fatal_types, true ) ) {
return;
}
// Si l'exécution provient probablement de wp-cron.php, on le note explicitement.
$is_cron = ( defined( 'DOING_CRON' ) && DOING_CRON );
error_log(
sprintf(
'[CRON][FATAL] doing_cron=%s message=%s file=%s line=%d',
$is_cron ? 'yes' : 'no',
$error['message'],
$error['file'],
(int) $error['line']
)
);
} );
} );
حالة نادرة في الواقع العملي: لدى بعض مزودي خدمات الاستضافة، يتم اقتطاع سجلات PHP. البادئة [CRON] يتيح لك ذلك التصفية بسرعة.
4) اكتشاف قفل "doing_cron" العالق
عند بدء تشغيل WP-Cron، يقوم ووردبريس بتعيين قفل عبر الخيار doing_cronإذا توقفت عملية ما، فقد يبقى القفل (أو يتم تجديده في حلقة بسبب تكوين سيئ).
مقتطف تشخيصي (تقييم WP-CLI):
wp eval 'var_dump( get_option("doing_cron") );'
إذا رأيت قيمة "حديثة" لا تتغير أبدًا على الرغم من عدم تشغيل أي شيء، فغالبًا ما يكون لديك:
- نداء إلى
wp-cron.phpوالتي تتكرر/تنتهي مهلتها - ذاكرة تخزين مؤقتة للكائنات الدائمة ذات تكوين سيئ في بيئة متعددة الخوادم،
- جدار حماية تطبيقات الويب (WAF) الذي يحظر الطلب بعد تطبيق القفل.
الحل الثاني: تحسين موثوقية تشغيل مهام cron (حركة المرور، ذاكرة التخزين المؤقت، الخادم)
إذا لم يتم تشغيل WP-Cron، يمكنك إصلاح 50 دالة رد نداء: لن يتغير شيء. نبدأ بجعل التنفيذ موثوقًا.
الحالة الأولى (شائعة جدًا): تم حظر /wp-cron.php بواسطة ذاكرة التخزين المؤقت/جدار حماية تطبيقات الويب/شبكة توصيل المحتوى
فحص سريع:
- فتح
https://votre-domaine.tld/wp-cron.php?doing_wp_cron=1في متصفح (أو عبر curl). - انظر إلى كود HTTP والعناوين المخزنة مؤقتًا.
curl -I "https://votre-domaine.tld/wp-cron.php?doing_wp_cron=1"
ما تريد:
- HTTP 200 (أو 204 حسب الخادم)،
- لا إعادة توجيه متكررة (301/302 مكررة)،
- لا يوجد تخزين مؤقت عنيف،
- لا توجد حماية ضد التحديات/البرامج الآلية على هذا الرابط.
الحلول النموذجية:
- استبعاد
/wp-cron.phpذاكرة التخزين المؤقت للملحقات (Rocket/Perfmatters/إلخ) وذاكرة التخزين المؤقت للخادم. - في Cloudflare/WAF: أنشئ قاعدة "تجاوز" (بدون تخزين مؤقت، بدون تحدٍ) لـ
/wp-cron.php. - إذا قمت بفرض استخدام HTTPS عبر المكون الإضافي وقام الخادم بفرضه أيضًا: قم بتصحيح المصدر (إعادة التوجيه من جانب الخادم أفضل).
الحالة الثانية: موقع ذو حركة مرور منخفضة (عدد زوار WP-Cron غير كافٍ)
في المواقع الإلكترونية/المدونات ذات الزيارات المنخفضة، يكون WP-Cron بطيئًا بطبيعته. الحل الأمثل في عام 2026: نظام cron + WP-CLI.
الخطوة أ: تعطيل "الزوار" في WP-Cron
في wp-config.php :
/**
* Désactive le déclenchement WP-Cron lors des visites.
* Vous DEVEZ configurer un cron système derrière, sinon plus rien ne tourne.
*/
define( 'DISABLE_WP_CRON', true );
الخطوة ب: تكوين مهمة cron للنظام (لينكس) عبر WP-CLI
مثال: تشغيل كل 5 دقائق.
*/5 * * * * cd /var/www/votre-site && /usr/bin/wp cron event run --due-now --quiet
لماذا نستخدم WP-CLI بدلاً من curl؟ wp-cron.php :
- لا توجد تبعية لبروتوكول HTTP/DNS/SSL/WAF.
- تحسين إمكانية المراقبة (stdout/stderr، رموز الخروج).
- مفاجآت أقل مع المخبأ.
حالة استثنائية: في بيئة متعددة الخوادم، قم بتشغيل مهمة cron هذه على وحيد استخدم عقدة (أو قفلًا موزعًا). وإلا، فستحدث عمليات تنفيذ متزامنة وآثار جانبية (إرسال بريد إلكتروني مزدوج، حذف مزدوج، إلخ).
الحالة الثالثة: مهلة HTTP أثناء عملية التوليد (طلبات الاسترجاع)
يعتمد WP-Cron غالبًا على طلبات الاتصال الذاتي (حيث يستدعي الموقع نفسه). قد يفشل هذا مع بعض مزودي خدمات الاستضافة (بسبب نظام أسماء النطاقات الداخلي، أو بروتوكول IPv6، أو جدار الحماية). تحقق من "طلبات الاتصال الذاتي" في قسم "حالة الموقع"، أو من خلال سجلات النظام.
المصدر الرسمي: صحة الموقع
الإصلاحات المتكررة:
- السماح للخادم بالانضمام إلى نطاقه الخاص (جدار الحماية الصادر).
- قم بتصحيح DNS/hosts إذا لزم الأمر (نادر الحدوث، لكنني رأيته في مجموعات Docker سيئة الإعداد).
- قم بالتحويل إلى نظام cron (مرة أخرى).
الحل الثالث: إصلاح المهام الفاشلة (خطاف خاطئ، وسائط، مهلات زمنية)
نحن هنا نتعامل مع حالة يتم فيها تشغيل WP-Cron بشكل صحيح، ولكن مهمة واحدة أو أكثر الفشل. وهنا تكمن التكلفة الباهظة للأخطاء "الصامتة".
مثال 1: تم تسجيل الخطاف في المكان الخطأ (أو فقط كمسؤول)
قبل الكود (المعطل). لقد رأيت هذا النمط كثيراً في قالب فرعي، أو في جزء من الكود تم لصقه في أداة إنشاء:
<?php
// functions.php (AVANT) - problème : le hook cron n'est enregistré qu'en admin
if ( is_admin() ) {
add_action( 'myplugin_cleanup_daily', 'myplugin_cleanup_callback' );
}
function myplugin_cleanup_callback() {
// Nettoyage...
}
سبب العطل: wp-cron.php ليس بالضرورة أن يكون "مسؤولاً"، ولا يتم ربط رد الاتصال الخاص بك بالخطاف وقت التنفيذ. النتيجة: يتم تشغيل الحدث، لكنه لا يفعل شيئاً (لا يحدث أي شيء).
الكود بعد (مصحح):
<?php
// functions.php ou (mieux) un plugin dédié (APRÈS)
add_action( 'myplugin_cleanup_daily', 'myplugin_cleanup_callback' );
/**
* Callback cron : doit être enregistré à chaque chargement (front/admin/cron).
*/
function myplugin_cleanup_callback(): void {
// Nettoyage...
}
ملاحظة: إذا كان الكود الخاص بك يعتمد على إضافات خارجية، فاحتفظ بـ plugins_loaded (أو لاحقًا) لضمان تحميل التبعيات، ولكن لا تقم إلى خطاف خاص بالمسؤولين فقط.
مثال 2: عدد خاطئ من الوسائط (خطأ فادح في PHP 8.x)
الكود السابق (معطل). الحدث مرر يلزم وجود وسيط واحد، لكن دالة الاستدعاء تتوقع وسيطين:
<?php
// AVANT : le callback attend 2 arguments, mais add_action n'en accepte qu'1 par défaut
add_action( 'myplugin_sync_hourly', 'myplugin_sync_callback' );
function myplugin_sync_callback( int $site_id, string $mode ): void {
// ...
}
في PHP 8.1 والإصدارات الأحدث، قد يتسبب هذا النوع من عدم التوافق في ظهور تحذيرات، أو حتى أخطاء فادحة، وذلك بحسب الكود (مثل أنواع البيانات الصارمة، أو عمليات القيم الفارغة، إلخ). وفي cron، نادرًا ما يظهر هذا الخطأ على الشاشة.
الكود بعد (مصحح):
<?php
// APRÈS : on déclare le nombre d'arguments acceptés
add_action( 'myplugin_sync_hourly', 'myplugin_sync_callback', 10, 2 );
/**
* @param int $site_id Identifiant du site.
* @param string $mode Mode de synchronisation.
*/
function myplugin_sync_callback( int $site_id, string $mode ): void {
// ...
}
وفيما يتعلق بالتخطيط، تأكد من تضمين الحجج التالية:
<?php
// Planification correcte (exemple)
if ( ! wp_next_scheduled( 'myplugin_sync_hourly', [ 42, 'delta' ] ) ) {
wp_schedule_event( time() + 60, 'hourly', 'myplugin_sync_hourly', [ 42, 'delta' ] );
}
مثال 3: التخطيط المكرر (الأحداث المكررة، تراكم المهام)
قبل تطبيق الكود (المعطل): خطة لكل حمولة بدون ضمانات.
<?php
// AVANT : chaque page vue ajoute un nouvel événement
add_action( 'init', function () {
wp_schedule_event( time(), 'hourly', 'myplugin_cleanup_daily' );
} );
الأعراض: ترى العشرات/المئات من الأحداث المتطابقة في قائمة cron، وارتفاعات في استخدام وحدة المعالجة المركزية عند تشغيلها.
الكود بعد (مصحح):
<?php
add_action( 'init', function () {
// Garde-fou : ne planifie que si rien n'est déjà planifié
if ( ! wp_next_scheduled( 'myplugin_cleanup_daily' ) ) {
wp_schedule_event( time() + 300, 'daily', 'myplugin_cleanup_daily' );
}
} );
ملاحظة: إذا كنت بحاجة إلى حجج، wp_next_scheduled() يجب أن تتلقى نفس مجموعة الوسائط بالضبط، وإلا فلن "ترى" الحدث الموجود.
المثال 4: تستغرق المهمة وقتًا طويلاً جدًا (انتهت المهلة) وعدم وجود تقسيم للأجزاء
قبل تطبيق الكود (المعطل): معالجة جماعية لجميع البيانات دفعة واحدة (مثلاً، 50,000 منشور). في Cron الخاص بـ HTTP، ستكتب الأمر التالي: max_execution_time أو الذاكرة.
<?php
add_action( 'myplugin_rebuild_index', function () {
$posts = get_posts( [
'post_type' => 'post',
'posts_per_page' => -1,
'fields' => 'ids',
] );
foreach ( $posts as $post_id ) {
// Rebuild index (long)
myplugin_index_post( $post_id );
}
} );
الكود بعد التعديل: نقسم العملية إلى دفعات ونعيد جدولة حدث واحد طالما بقي عمل. هذا هو النمط الأكثر موثوقية الذي أستخدمه في بيئة الإنتاج.
<?php
add_action( 'myplugin_rebuild_index_batch', 'myplugin_rebuild_index_batch_callback', 10, 2 );
/**
* Traite l'indexation par lots pour éviter les timeouts.
*
* @param int $offset Décalage.
* @param int $limit Taille du lot.
*/
function myplugin_rebuild_index_batch_callback( int $offset, int $limit ): void {
$ids = get_posts( [
'post_type' => 'post',
'posts_per_page' => $limit,
'offset' => $offset,
'fields' => 'ids',
'orderby' => 'ID',
'order' => 'ASC',
] );
foreach ( $ids as $post_id ) {
myplugin_index_post( (int) $post_id );
}
// S'il reste des posts, on replanifie un lot.
if ( count( $ids ) === $limit ) {
wp_schedule_single_event( time() + 30, 'myplugin_rebuild_index_batch', [ $offset + $limit, $limit ] );
}
}
/**
* Démarre la reconstruction (à appeler depuis un écran admin ou WP-CLI).
*/
function myplugin_start_rebuild_index(): void {
wp_schedule_single_event( time() + 5, 'myplugin_rebuild_index_batch', [ 0, 200 ] );
}
لماذا يحل هذا المشكلة؟ لأنه يقلل وقت التنفيذ إلى نطاق زمني محدد، ويتجنب حالات انتهاء المهلة، ويستأنف العملية بعد انقطاعها. كما أنه أكثر عدلاً بالنسبة لقاعدة البيانات.
المثال 5: أخطاء صامتة على جانب واجهة برمجة تطبيقات HTTP (wp_remote_get) وعدم معالجتها
قبل الكود (المعطل): يتم تجاهل الأخطاء، لذلك لا يمكنك معرفة ما إذا كان الأمر يتعلق بانتهاء المهلة، أو خطأ 401، أو خطأ في نظام أسماء النطاقات (DNS).
<?php
add_action( 'myplugin_sync_hourly', function () {
$response = wp_remote_get( 'https://api.exemple.tld/sync' );
$data = json_decode( wp_remote_retrieve_body( $response ), true );
// ... utilise $data sans vérifier
} );
الكود بعد (المصحح): معالجة الأخطاء + التسجيل + التراجع البسيط.
<?php
add_action( 'myplugin_sync_hourly', 'myplugin_sync_hourly_callback' );
function myplugin_sync_hourly_callback(): void {
$url = 'https://api.exemple.tld/sync';
$response = wp_remote_get( $url, [
'timeout' => 15, // Évitez 5s si l'API est lente
] );
if ( is_wp_error( $response ) ) {
error_log( '[CRON][SYNC][ERROR] wp_remote_get failed: ' . $response->get_error_message() );
// Backoff : retenter dans 10 minutes
wp_schedule_single_event( time() + 600, 'myplugin_sync_hourly' );
return;
}
$status = (int) wp_remote_retrieve_response_code( $response );
if ( $status < 200 || $status >= 300 ) {
error_log( sprintf( '[CRON][SYNC][ERROR] HTTP %d for %s', $status, $url ) );
return;
}
$body = wp_remote_retrieve_body( $response );
$data = json_decode( $body, true );
if ( JSON_ERROR_NONE !== json_last_error() ) {
error_log( '[CRON][SYNC][ERROR] JSON invalide: ' . json_last_error_msg() );
return;
}
// Traitement...
}
ما يتجنبه هذا: المهام التي "تنجح" ولكنها لا تفعل شيئًا، وتضيع ساعات في الشك في WP-Cron بينما المشكلة تكمن في واجهة برمجة التطبيقات البعيدة.
عمليات التحقق بعد التصحيح
- WP-CLI :
wp cron event list: خطافاتك موجودة، مع تنفيذ لاحق متسق.wp cron event run --due-now: يتم التنفيذ بدون أخطاء فادحة وبدون مهلة زمنية.
- سجلات ترى أزواجًا
[CRON][START]/[CRON][END]بالنسبة للخطافات المرصودة. - الفسفور الابيض بين cron.php : A
curl -Iيُعيد رمز الاستجابة 200 بدون إعادة توجيه مشبوهة. - وظيفي : يتم نشر منشور مجدول في الوقت المحدد، ويتم إجراء عملية تنظيف، ويتم إرسال بريد إلكتروني تجريبي.
اختبار بسيط أستخدمه: جدولة حدث واحد "في غضون دقيقتين" والتحقق من تنفيذه.
# Planifie un événement test (hook "bpcab_cron_test") dans 120 secondes
wp eval 'wp_schedule_single_event(time()+120, "bpcab_cron_test"); echo "OKn";'
# Ajoutez ensuite un callback (MU-plugin) pour logguer ce hook.
<?php
// Dans un MU-plugin de test
add_action( 'bpcab_cron_test', static function () {
error_log( '[CRON][TEST] événement test exécuté' );
} );
إذا لم ينجح ذلك أيضاً
الإجراء الذي أتبعه عندما تستمر المشكلة (ترتيب مهم):
-
تأكيد وضع التشغيل :
- Si
DISABLE_WP_CRONفيtrue، تحقق من وجود نظام cron وتشغيله (سجلات cron، مؤقتات systemd، لوحة الاستضافة). - وإلا، فحاول
/wp-cron.phpإلى curl والبحث 403/301/timeout.
- Si
-
تقليص النطاق :
- قم بتعطيل ذاكرة التخزين المؤقت/جدار حماية تطبيقات الويب (WAF) مؤقتًا على جانب المكون الإضافي إن أمكن.
- باستخدام خاصية فحص الحالة الصحية، انتقل إلى "وضع استكشاف الأخطاء وإصلاحها" واختبر فقط المكون الإضافي الذي يقوم بجدولة المهمة.
-
تعقب القاتل :
- مكن
WP_DEBUG_LOGوأضف معالج الإغلاق (الحل 1). - تحقق من سجلات PHP-FPM/Apache/Nginx (غالباً ما تكون أكثر اكتمالاً من debug.log).
- مكن
-
تحقق من المنافسة :
- خادم متعدد: منفذ cron واحد، أو قفل موزع.
- ذاكرة التخزين المؤقت للكائنات الدائمة: تأكد من أنها مستقرة (Redis/Memcached) ومُهيأة بشكل صحيح.
-
تحقق من قاعدة البيانات :
- حالات التعطل/القفل: انظر إلى سجلات MySQL/MariaDB.
- خيار
cronضخم: غالباً ما يكون علامة على تكرار الأحداث.
-
الاختبار عبر WP-CLI :
wp cron event run <hook>لعزل مهمة ما.- إذا كان WP-CLI يعمل ولكن HTTP يفشل: فالمشكلة في الشبكة/جدار حماية تطبيقات الويب/ذاكرة التخزين المؤقت، وليست في الكود.
في هذه المرحلة، تحقق أيضًا من وحدة تحكم المتصفح فقط إذا كان الملحق الخاص بك يُفعّل إجراءات مجدولة عبر AJAX/REST (على سبيل المثال، إجراء مجدول بعد طلب من المسؤول). قد يمنع خطأ REST 401/403 عملية الجدولة الأولية.
الأخطاء الشائعة والمزالق
| عرض | السبب المحتمل | يوصى بالحل |
|---|---|---|
| لا ترى أي سجلات | WP_DEBUG_LOG معطلة، أو سجلات PHP غير قابلة للوصول |
فعّل ملف debug.log على بيئة الاختبار، واستخدم ملف error_log وسجلات الخادم. |
| المهام موجودة لكنها لا تفعل شيئاً | لا يمكن إرفاق رد الاتصال إلا بصلاحيات المسؤول، أو لم يتم تحميل الملف. | يتحرك add_action() في إضافة يتم تحميلها في كل مكان |
| خطأ فادح "دالة غير معرفة" | الدالة التي تم تعريفها بعد الاستخدام، أو التبعية التي تم إلغاء تحميلها | أرفق الكود بـ plugins_loaded وتحقق من ترتيب التحميل |
| مهلات عشوائية | المهام التي تستغرق وقتاً طويلاً جداً (حلقات ضخمة، واجهة برمجة تطبيقات بطيئة) | التجزئة + الأحداث الفردية + مهلات HTTP المعدلة |
| إرسال رسائل البريد الإلكتروني مرتين | نظام Cron يعمل على خادمين / التزامن | مُنفِّذ cron واحد، أو قفل التطبيق |
| لقد "أصلحتم" المشكلة في مرحلة الإنتاج، لكنها تزداد سوءًا. | الاختبار بدون حفظ، بدون مرحلة تجريبية | التراجع + إعادة الإنتاج على المسرح |
| خطأ بعد النسخ/اللصق | قوس مفقود/فاصلة منقوطة في مقتطف | استخدم إضافة MU ذات إصدار محدد، وCI lint PHP |
| لم يتم تفعيل الخطاف أبدًا | لقد استخدمتَ خطافًا غير مناسب (init (مقابل التنشيط) |
التخطيط للتفعيل والحماية wp_next_scheduled() |
| لم يعد الكود من درس تعليمي قديم يعمل | عدم التوافق مع PHP 8.1+، والممارسات القديمة | الترقية (الأنواع، معالجة الأخطاء، WP-CLI) |
خطأ أراه باستمرار: "نسخ الكود إلى المكان الخطأ". واحد add_action() قد لا يتم تحميلها في وحدة Divi/Elementor (أو مدير القصاصات) أثناء wp-cron.phpإذا كنت مضطرًا لاستخدام مدير مقتطفات التعليمات البرمجية، فتأكد من أنه يعمل على الواجهة الأمامية + الإدارة + كرونوإلا، فقم بالتبديل إلى MU-plugin.
بديل / متغير
طريقة بدون كتابة أكواد: إضافة إدارة المهام المجدولة (cron)
لفحص أحداث cron بسرعة عبر لوحة التحكم، استخدم إضافة مثل الفسفور الابيض كرونترول يبقى الأمر عمليًا. يمكنك:
- عرض الخطافات المجدولة،
- تنفيذ حدث يدويًا،
- إزالة الأحداث المكررة.
القيد: لا يقوم بتصحيح /wp-cron.php محظور، ولا يحل محل WP-CLI في بيئة الإنتاج.
طريقة متقدمة: استخدام مُنفِّذ cron عبر خدمة حاوية (نمط نظيف)
إذا كنت تقوم بتطوير إضافة تقوم بجدولة المهام، فقم بتنظيم وظائف الاستدعاء الخاصة بك على غرار الخدمات القابلة للاختبار. هذا يقلل من "الأخطاء الصامتة" ويجعل عملية التتبع أسهل.
مثال بسيط (إضافة): حاوية بسيطة للغاية + مُشغّل. إنه ليس إطار عمل، بل مجرد منهجية.
<?php
/**
* Exemple pédagogique : container minimaliste pour tâches cron.
* Objectif : éviter les fonctions globales et faciliter les tests/logs.
*/
final class BPCAB_Container {
private array $factories = [];
private array $instances = [];
public function set( string $id, callable $factory ): void {
$this->factories[ $id ] = $factory;
}
public function get( string $id ) {
if ( isset( $this->instances[ $id ] ) ) {
return $this->instances[ $id ];
}
if ( ! isset( $this->factories[ $id ] ) ) {
throw new RuntimeException( 'Service introuvable: ' . $id );
}
$this->instances[ $id ] = ( $this->factories[ $id ] )( $this );
return $this->instances[ $id ];
}
}
final class BPCAB_Cron_Task_Cleanup {
public function run(): void {
// Exemple : opération idempotente
global $wpdb;
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_timeout_%' AND option_value < UNIX_TIMESTAMP()" );
}
}
// Bootstrap
add_action( 'plugins_loaded', static function () {
$container = new BPCAB_Container();
$container->set( 'task.cleanup', static fn() => new BPCAB_Cron_Task_Cleanup() );
add_action( 'bpcab_cleanup_daily', static function () use ( $container ) {
try {
$container->get( 'task.cleanup' )->run();
} catch ( Throwable $e ) {
// Journalisation explicite : sinon ce sera "silencieux"
error_log( '[CRON][CLEANUP][FATAL] ' . $e->getMessage() );
throw $e; // Optionnel : selon votre stratégie
}
} );
} );
باستخدام هذا النهج، يمكنك الاستبدال error_log عبر Monolog باستخدام جسر، أو عن طريق التوجيه إلى خدمة خارجية. والأهم من ذلك: أنك تعرف بالضبط أين تضع كود try/catch.
تجنب هذه المشكلة في المستقبل
- يفضل استخدام نظام cron في مرحلة الإنتاج، وخاصة إذا كان الموقع يتلقى حركة مرور قليلة أو مخفيًا بشكل كبير، فإن "الزوار" في WP-Cron هم بمثابة حل احتياطي وليس استراتيجية.
- اجعل مهامك متكررة لا تتغير : إذا تم تشغيلها مرتين، فيجب ألا تتعطل (على سبيل المثال، استخدم أقفال التطبيق، وتحقق من الحالة قبل الإرسال).
- تقطيع بالنسبة لأي عملية قد تستغرق وقتاً طويلاً، إذا كان من الممكن أن تستغرق المهمة أكثر من 10-20 ثانية، فقم بحذفها.
- مجلة : كحد أدنى، بداية/نهاية + أخطاء WP_Error + المدد الزمنية. الأخطاء "الصامتة" تكلف أكثر من الأخطاء الصاخبة.
- تجنب التخطيط لعملية التهيئة دون وجود ضماناتحدد موعدًا لتفعيل الإضافة، واستخدمها.
wp_next_scheduled(). - مراقب : فحص خارجي كل X دقيقة (مراقب وقت التشغيل) والذي يستدعي نقطة نهاية داخلية آمنة أو يتحقق من أن نبضات القلب المجدولة قد تحركت.
الأمان: لا تُعرِض رابط "تشغيل cron الآن" العام دون مصادقة. لقد رأيت مواقع تسمح فيها نقطة نهاية غير محمية لأي شخص بتشغيل عمليات استيراد كثيفة (هجمات حجب الخدمة على التطبيقات).
الموارد
- دليل مطوري ووردبريس - واجهة برمجة تطبيقات Cron
- مرجع — wp_schedule_event()
- مرجع — wp_schedule_single_event()
- مرجع — wp_next_scheduled()
- WP Crontrol (wordpress.org)
- فحص الصحة وحل المشكلات (wordpress.org)
- نظام تتبع الأخطاء الأساسي لـ WordPress (ابحث عن التذاكر المتعلقة بـ WP-Cron)
- نسخة GitHub المتطابقة — wordpress-develop (شفرة المصدر wp-cron.php)
- PHP.net — register_shutdown_function()
أسئلة fréquentes
هل من المفترض أن يكون WP-Cron موثوقًا "افتراضيًا"؟
بالنسبة للمدونات ذات الزيارات الثابتة والتي لا تعتمد على التخزين المؤقت المكثف، غالبًا ما يكون هذا الأسلوب فعالًا. مع ذلك، بمجرد تفعيل التخزين المؤقت للصفحات الكاملة، أو وجود جدار حماية تطبيقات ويب صارم، أو انخفاض عدد الزيارات، ينخفض مستوى الموثوقية بشكل ملحوظ. في بيئة الإنتاج، أتعامل مع WP-Cron كآلية تشغيل للتطبيق وأقوم بتشغيلها عبر cron النظامي.
هل سيؤدي تعطيل WP-Cron إلى تعطيل Divi 5 / Elementor / Avada؟
لا، ليس كذلك طالما استبدلتَ المُشغِّل بمهمة مجدولة للنظام. يمكن أن تعتمد أدوات البناء والإضافات المرتبطة بها على المهام المجدولة (عمليات الحذف، وإعادة التوليد). بدون مُنفِّذ، ستظهر آثار جانبية.
لماذا يتم نشر مقالاتي المجدولة عند تسجيل دخولي إلى لوحة التحكم؟
لأن زيارتك تُطلق طلب PHP يؤدي بدوره إلى تشغيل WP-Cron. في المواقع ذات الزيارات المنخفضة، تُعد هذه علامة نموذجية: حيث تنتظر المهام الطلب التالي.
كيف يمكننا معرفة المهمة المحددة التي يقوم بها النبات؟
الأداة: تسجيل بداية/نهاية الخطافات الحرجة + معالج الإغلاق لرصد حالات الفشل. ثم تشغيل الخطافات واحدة تلو الأخرى عبر WP-CLI (wp cron event run <hook>) للعزل.
أرى مئات الأحداث المتطابقة، هل هذا أمر خطير؟
نعم: هذا يدل على التخطيط المزدوج (غالباً على init بلا wp_next_scheduled()ستواجه تراكمًا في الطلبات، وارتفاعًا مفاجئًا في استهلاك وحدة المعالجة المركزية، وأحيانًا إجراءات مكررة (رسائل بريد إلكتروني). صحح الكود، ثم أزل التكرارات (باستخدام WP-CLI أو WP Crontrol).
هل يمكن لملحق التخزين المؤقت أن يمنع تشغيل WP-Cron؟
نعم، خاصة إذا /wp-cron.php تم تخزين هذا الرابط مؤقتًا، أو تم التحقق منه، أو تم حظره. استثنِ هذا الرابط من حماية التخزين المؤقت ومكافحة البرامج الآلية. اختبره على curl -I.
يعمل WP-CLI، لكن ملف wp-cron.php ينتهي وقته عبر HTTP: ماذا أفعل؟
توجد مشكلة في الشبكة/جدار حماية تطبيقات الويب/ذاكرة التخزين المؤقت/شهادة SSL على مسار HTTP. احتفظ بـ WP-CLI كأداة تنفيذ (مهمة مجدولة للنظام)، وقم بإصلاح سبب مشكلة HTTP إذا لزم الأمر بالنسبة لروابط الاسترجاع الأخرى.
هل يمكنني "إجبار" WP-Cron على التشغيل بشكل متكرر؟
يمكنك تشغيل المهام بشكل متكرر عبر نظام cron (كل 1-5 دقائق)، ولكن تجنب جدولة المهام التي تستهلك موارد النظام بكثرة. اضبط التكرار، وأضف تقسيمًا للمهام، وقِس النتائج.
هل ينبغي عليّ تخزين سجلات cron الخاصة بي في قاعدة البيانات؟
تجنب هذا كرد فعل أولي: فأنت تخاطر بإضافة عمليات إدخال/إخراج لقاعدة البيانات إلى مهام هشة أصلاً. ابدأ بـ error_log + سجلات الخادم. إذا كنت بحاجة إلى بيانات تاريخية، فاستخدم جدولًا مخصصًا ومستوى تسجيل قابل للتكوين.
كيفية تجنب التنفيذ المزدوج في بيئات متعددة الخوادم؟
يُفضّل استخدام مُنفِّذ cron واحد. وإلا، يُنصح بتطبيق قفل موزّع (Redis) حول المهام الحرجة. بدون ذلك، ستواجه حالات تضارب، خاصةً مع عمليات التحميل والمزامنة.