| 1 |
<?php |
| 2 |
/* |
| 3 |
Name: 定时作业 |
| 4 |
URI: https://mp.weixin.qq.com/s/mSqzZdslhxwkNHGRpa3WmA |
| 5 |
Description: 定时作业让你可以可视化管理 WordPress 的定时作业 |
| 6 |
Version: 2.0 |
| 7 |
*/ |
| 8 |
class WPJAM_Cron extends WPJAM_Args{ |
| 9 |
public function __invoke(){ |
| 10 |
if(wpjam_lock($this->hook.'_lock', 10, true)){ |
| 11 |
return; |
| 12 |
} |
| 13 |
|
| 14 |
$jobs = $this->get_jobs(); |
| 15 |
|
| 16 |
if($this->weight){ |
| 17 |
$queue = $jobs; |
| 18 |
$i = 0; |
| 19 |
|
| 20 |
while($queue){ |
| 21 |
$i++; |
| 22 |
|
| 23 |
$queue = array_filter($queue, fn($job)=> is_array($job) && wpjam_get($job, 'weight') >= $i); |
| 24 |
$jobs = array_merge($jobs, $queue); |
| 25 |
} |
| 26 |
} |
| 27 |
|
| 28 |
$count = $this->get_count(true); |
| 29 |
$index = $count % count($jobs); |
| 30 |
$callback = $jobs[$index]['callback']; |
| 31 |
|
| 32 |
is_callable($callback) && $callback(); |
| 33 |
} |
| 34 |
|
| 35 |
public function get_jobs(){ |
| 36 |
return array_values(maybe_callback($this->jobs)); |
| 37 |
} |
| 38 |
|
| 39 |
public function get_count($increment=false){ |
| 40 |
$name = $this->hook.'_counter:'.wpjam_date('Y-m-d'); |
| 41 |
|
| 42 |
return $increment ? wpjam_increment($name) : (get_transient($name) ?: 0); |
| 43 |
} |
| 44 |
|
| 45 |
public static function cleanup(){ |
| 46 |
$invalid = 0; |
| 47 |
|
| 48 |
foreach(self::get_all() as $ts => $cron){ |
| 49 |
foreach($cron as $hook => $dings){ |
| 50 |
if(!has_filter($hook)){ // 系统不存在的定时作业,� |
| 51 |
理掉 |
| 52 |
array_walk($dings, fn($data)=> wp_unschedule_event($ts, $hook, $data['args'])); |
| 53 |
|
| 54 |
$invalid++; |
| 55 |
} |
| 56 |
} |
| 57 |
} |
| 58 |
|
| 59 |
return $invalid; |
| 60 |
} |
| 61 |
|
| 62 |
public static function get($id){ |
| 63 |
$data = wpjam_get(self::get_all(), $id); |
| 64 |
$args = wpjam_lines($id, '[', fn($v)=> trim($v, ']')); |
| 65 |
|
| 66 |
return $data ? [ |
| 67 |
'hook' => $args[1], |
| 68 |
'timestamp' => $args[0], |
| 69 |
'time' => wpjam_date('Y-m-d H:i:s', $args[0]), |
| 70 |
'cron_id' => $id, |
| 71 |
'schedule' => $data['schedule'] ?? '', |
| 72 |
'args' => $data['args'] ?? [] |
| 73 |
] : []; |
| 74 |
} |
| 75 |
|
| 76 |
public static function insert($data){ |
| 77 |
!has_filter($data['hook']) && wp_die('无效的 Hook'); |
| 78 |
|
| 79 |
return wpjam_schedule($data['hook'], $data); |
| 80 |
} |
| 81 |
|
| 82 |
public static function do($id){ |
| 83 |
return ($data = self::get($id)) ? (do_action_ref_array($data['hook'], $data['args']) || true) : true; |
| 84 |
} |
| 85 |
|
| 86 |
public static function delete($id){ |
| 87 |
return ($data = self::get($id)) ? wp_unschedule_event($data['timestamp'], $data['hook'], $data['args']) : true; |
| 88 |
} |
| 89 |
|
| 90 |
public static function get_all(){ |
| 91 |
return _get_cron_array() ?: []; |
| 92 |
} |
| 93 |
|
| 94 |
public static function query_items($args){ |
| 95 |
if($GLOBALS['current_tab'] == 'crons'){ |
| 96 |
foreach(self::get_all() as $ts => $cron){ |
| 97 |
foreach($cron as $hook => $dings){ |
| 98 |
foreach($dings as $key => $data){ |
| 99 |
$items[] = [ |
| 100 |
'cron_id' => $ts.'['.$hook.']['.$key.']', |
| 101 |
'time' => wpjam_date('Y-m-d H:i:s', $ts), |
| 102 |
'hook' => $hook, |
| 103 |
'schedule' => $data['schedule'] ?? '' |
| 104 |
]; |
| 105 |
} |
| 106 |
} |
| 107 |
} |
| 108 |
|
| 109 |
return $items; |
| 110 |
} |
| 111 |
|
| 112 |
return wpjam_map(wpjam_get_cron('wpjam_scheduled')->get_jobs(), fn($item)=> $item+wpjam_map([ |
| 113 |
'job_id' => 'uniqid', |
| 114 |
'function' => 'render' |
| 115 |
], fn($v)=> wpjam_callback($v, $item['callback']))); |
| 116 |
} |
| 117 |
|
| 118 |
public static function get_actions(){ |
| 119 |
return $GLOBALS['current_tab'] == 'crons' ? [ |
| 120 |
'add' => ['title'=>'新建', 'response'=>'list'], |
| 121 |
'do' => ['title'=>'立即执行', 'direct'=>true, 'confirm'=>true, 'bulk'=>2], |
| 122 |
'delete' => ['title'=>'删除', 'direct'=>true, 'confirm'=>true, 'bulk'=>true, 'response'=>'list'] |
| 123 |
] : []; |
| 124 |
} |
| 125 |
|
| 126 |
public static function get_fields($action_key='', $id=0){ |
| 127 |
return $GLOBALS['current_tab'] == 'crons' ? [ |
| 128 |
'hook' => ['title'=>'Hook', 'type'=>'text', 'show_admin_column'=>true], |
| 129 |
'time' => ['title'=>'运行时间', 'type'=>'timestamp','show_admin_column'=>true, 'value'=>time()], |
| 130 |
'schedule' => ['title'=>'频率', 'type'=>'select', 'show_admin_column'=>true, 'options'=>[''=>'只执行一次']+wp_list_pluck(wp_get_schedules(), 'display')], |
| 131 |
] : [ |
| 132 |
'function' => ['title'=>'回调函数', 'type'=>'view', 'show_admin_column'=>true], |
| 133 |
'weight' => ['title'=>'作业权重', 'type'=>'view', 'show_admin_column'=>true], |
| 134 |
]; |
| 135 |
} |
| 136 |
|
| 137 |
public static function get_tabs(){ |
| 138 |
return ['crons' => [ |
| 139 |
'title' => '定时作业', |
| 140 |
'order' => 20, |
| 141 |
'function' => 'list', |
| 142 |
'list_table' => [ |
| 143 |
'plural' => 'crons', |
| 144 |
'singular' => 'cron', |
| 145 |
'model' => self::class, |
| 146 |
'primary_key' => 'cron_id', |
| 147 |
] |
| 148 |
]]+(wpjam_get_cron('wpjam_scheduled') ? ['jobs' => [ |
| 149 |
'title' => '作业列表', |
| 150 |
'function' => 'list', |
| 151 |
'list_table' => [ |
| 152 |
'model' => self::class, |
| 153 |
'plural' => 'jobs', |
| 154 |
'singular' => 'job', |
| 155 |
'primary_key' => 'job_id', |
| 156 |
'per_page' => 1000, |
| 157 |
'summary' => fn()=> '今天已经运行 <strong>'.wpjam_get_cron('wpjam_scheduled')->get_count().'</strong> 次', |
| 158 |
] |
| 159 |
]] : []); |
| 160 |
} |
| 161 |
|
| 162 |
public static function add_hooks(){ |
| 163 |
wpjam_hook('cron_schedules', '+', [ |
| 164 |
'five_minutes' => ['interval'=>300, 'display'=>'每5分钟一次'], |
| 165 |
'fifteen_minutes' => ['interval'=>900, 'display'=>'每15分钟一次'] |
| 166 |
]); |
| 167 |
} |
| 168 |
} |
| 169 |
|
| 170 |
function wpjam_register_cron($hook, $args=[]){ |
| 171 |
if(is_callable($hook)){ |
| 172 |
return wpjam_register_job($hook, $args); |
| 173 |
} |
| 174 |
|
| 175 |
$cb = ($args['callback'] ?? '') ?: wpjam_register('WPJAM_Cron', $hook, $args+['hook'=>$hook]); |
| 176 |
|
| 177 |
add_action($hook, $cb); |
| 178 |
|
| 179 |
wpjam_is_scheduled_event($hook) || wpjam_schedule_event($hook, $args); |
| 180 |
|
| 181 |
return $cb; |
| 182 |
} |
| 183 |
|
| 184 |
function wpjam_register_job($job, $args=[]){ |
| 185 |
$args = is_array($args) ? $args : (is_numeric($args) ? ['weight'=>$args] : []); |
| 186 |
$args = array_merge($args, is_callable($job) ? ['callback'=>$job] : []); |
| 187 |
|
| 188 |
if(!empty($args['callback']) && is_callable($args['callback'])){ |
| 189 |
$cron = 'wpjam_scheduled'; |
| 190 |
$cron = wpjam_get_cron($cron) ?: wpjam_register_cron($cron, ['recurrence'=>'five_minutes', 'weight'=>true]); |
| 191 |
|
| 192 |
return $cron->update_arg('jobs[]', wp_parse_args($args, ['weight'=>1, 'day'=>-1])); |
| 193 |
} |
| 194 |
} |
| 195 |
|
| 196 |
function wpjam_get_cron($hook){ |
| 197 |
return wpjam_get_registered('WPJAM_Cron', $hook); |
| 198 |
} |
| 199 |
|
| 200 |
function wpjam_schedule_event($hook, $data){ |
| 201 |
$args = array_filter([$data['recurrence'] ?? '']); |
| 202 |
$cb = 'wp_schedule_'.($args ? '' : 'single_').'event'; |
| 203 |
|
| 204 |
return $cb($data['timestamp'] ?? time(), ...[...$args, $hook, $data['args'] ?? []]); |
| 205 |
} |
| 206 |
|
| 207 |
function wpjam_is_scheduled_event($hook){ // 不用判断参数 |
| 208 |
return array_any(WPJAM_Cron::get_all(), fn($cron)=> isset($cron[$hook])); |
| 209 |
} |
| 210 |
|
| 211 |
wpjam_add_menu_page('wpjam-crons', [ |
| 212 |
'parent' => 'wpjam-basic', |
| 213 |
'menu_title' => '定时作业', |
| 214 |
'order' => 9, |
| 215 |
'summary' => __FILE__, |
| 216 |
'network' => false, |
| 217 |
'function' => 'tab', |
| 218 |
'model' => 'WPJAM_Cron' |
| 219 |
]); |
| 220 |
|