hook.'_lock', 10, true)){ return; } $jobs = $this->get_jobs(); if($this->weight){ $queue = $jobs; $i = 0; while($queue){ $i++; $queue = array_filter($queue, fn($job)=> is_array($job) && wpjam_get($job, 'weight') >= $i); $jobs = array_merge($jobs, $queue); } } $count = $this->get_count(true); $index = $count % count($jobs); $callback = $jobs[$index]['callback']; is_callable($callback) && $callback(); } public function get_jobs(){ return array_values(maybe_callback($this->jobs)); } public function get_count($increment=false){ $name = $this->hook.'_counter:'.wpjam_date('Y-m-d'); return $increment ? wpjam_increment($name) : (get_transient($name) ?: 0); } public static function cleanup(){ $invalid = 0; foreach(self::get_all() as $ts => $cron){ foreach($cron as $hook => $dings){ if(!has_filter($hook)){ // 系统不存在的定时作业,清理掉 array_walk($dings, fn($data)=> wp_unschedule_event($ts, $hook, $data['args'])); $invalid++; } } } return $invalid; } public static function get($id){ $data = wpjam_get(self::get_all(), $id); $args = wpjam_lines($id, '[', fn($v)=> trim($v, ']')); return $data ? [ 'hook' => $args[1], 'timestamp' => $args[0], 'time' => wpjam_date('Y-m-d H:i:s', $args[0]), 'cron_id' => $id, 'schedule' => $data['schedule'] ?? '', 'args' => $data['args'] ?? [] ] : []; } public static function insert($data){ !has_filter($data['hook']) && wp_die('无效的 Hook'); return wpjam_schedule($data['hook'], $data); } public static function do($id){ return ($data = self::get($id)) ? (do_action_ref_array($data['hook'], $data['args']) || true) : true; } public static function delete($id){ return ($data = self::get($id)) ? wp_unschedule_event($data['timestamp'], $data['hook'], $data['args']) : true; } public static function get_all(){ return _get_cron_array() ?: []; } public static function query_items($args){ if($GLOBALS['current_tab'] == 'crons'){ foreach(self::get_all() as $ts => $cron){ foreach($cron as $hook => $dings){ foreach($dings as $key => $data){ $items[] = [ 'cron_id' => $ts.'['.$hook.']['.$key.']', 'time' => wpjam_date('Y-m-d H:i:s', $ts), 'hook' => $hook, 'schedule' => $data['schedule'] ?? '' ]; } } } return $items; } return wpjam_map(wpjam_get_cron('wpjam_scheduled')->get_jobs(), fn($item)=> $item+wpjam_map([ 'job_id' => 'uniqid', 'function' => 'render' ], fn($v)=> wpjam_callback($v, $item['callback']))); } public static function get_actions(){ return $GLOBALS['current_tab'] == 'crons' ? [ 'add' => ['title'=>'新建', 'response'=>'list'], 'do' => ['title'=>'立即执行', 'direct'=>true, 'confirm'=>true, 'bulk'=>2], 'delete' => ['title'=>'删除', 'direct'=>true, 'confirm'=>true, 'bulk'=>true, 'response'=>'list'] ] : []; } public static function get_fields($action_key='', $id=0){ return $GLOBALS['current_tab'] == 'crons' ? [ 'hook' => ['title'=>'Hook', 'type'=>'text', 'show_admin_column'=>true], 'time' => ['title'=>'运行时间', 'type'=>'timestamp','show_admin_column'=>true, 'value'=>time()], 'schedule' => ['title'=>'频率', 'type'=>'select', 'show_admin_column'=>true, 'options'=>[''=>'只执行一次']+wp_list_pluck(wp_get_schedules(), 'display')], ] : [ 'function' => ['title'=>'回调函数', 'type'=>'view', 'show_admin_column'=>true], 'weight' => ['title'=>'作业权重', 'type'=>'view', 'show_admin_column'=>true], ]; } public static function get_tabs(){ return ['crons' => [ 'title' => '定时作业', 'order' => 20, 'function' => 'list', 'list_table' => [ 'plural' => 'crons', 'singular' => 'cron', 'model' => self::class, 'primary_key' => 'cron_id', ] ]]+(wpjam_get_cron('wpjam_scheduled') ? ['jobs' => [ 'title' => '作业列表', 'function' => 'list', 'list_table' => [ 'model' => self::class, 'plural' => 'jobs', 'singular' => 'job', 'primary_key' => 'job_id', 'per_page' => 1000, 'summary' => fn()=> '今天已经运行 '.wpjam_get_cron('wpjam_scheduled')->get_count().' 次', ] ]] : []); } public static function add_hooks(){ wpjam_hook('cron_schedules', '+', [ 'five_minutes' => ['interval'=>300, 'display'=>'每5分钟一次'], 'fifteen_minutes' => ['interval'=>900, 'display'=>'每15分钟一次'] ]); } } function wpjam_register_cron($hook, $args=[]){ if(is_callable($hook)){ return wpjam_register_job($hook, $args); } $cb = ($args['callback'] ?? '') ?: wpjam_register('WPJAM_Cron', $hook, $args+['hook'=>$hook]); add_action($hook, $cb); wpjam_is_scheduled_event($hook) || wpjam_schedule_event($hook, $args); return $cb; } function wpjam_register_job($job, $args=[]){ $args = is_array($args) ? $args : (is_numeric($args) ? ['weight'=>$args] : []); $args = array_merge($args, is_callable($job) ? ['callback'=>$job] : []); if(!empty($args['callback']) && is_callable($args['callback'])){ $cron = 'wpjam_scheduled'; $cron = wpjam_get_cron($cron) ?: wpjam_register_cron($cron, ['recurrence'=>'five_minutes', 'weight'=>true]); return $cron->update_arg('jobs[]', wp_parse_args($args, ['weight'=>1, 'day'=>-1])); } } function wpjam_get_cron($hook){ return wpjam_get_registered('WPJAM_Cron', $hook); } function wpjam_schedule_event($hook, $data){ $args = array_filter([$data['recurrence'] ?? '']); $cb = 'wp_schedule_'.($args ? '' : 'single_').'event'; return $cb($data['timestamp'] ?? time(), ...[...$args, $hook, $data['args'] ?? []]); } function wpjam_is_scheduled_event($hook){ // 不用判断参数 return array_any(WPJAM_Cron::get_all(), fn($cron)=> isset($cron[$hook])); } wpjam_add_menu_page('wpjam-crons', [ 'parent' => 'wpjam-basic', 'menu_title' => '定时作业', 'order' => 9, 'summary' => __FILE__, 'network' => false, 'function' => 'tab', 'model' => 'WPJAM_Cron' ]);