頁面互動效果
下面是寫好的示例前端互動頁面,主要是列表頁面,編輯頁面。
主要互動有:
1 開啟定時任務程式
2 關閉定時任務程式
3 新增一項定時任務
4 編輯已有的定時任務
5 刪除定時任務
6 開始一項定時任務
7 停止一項定時任務
定時任務分兩種:
1 執行一次
2 迴圈執行
定時任務執行的內容:
定時任務執行的內容其實還是一個http請求。 通過定時的方式,指定時間執行或者循執行。
1 列表頁面
2 新建\編輯頁面
程式碼地址,演示地址
示例程式碼是基於Laravel,Alpaca-spa框架編寫,並且作為 ‘Alpaca-Spa-Laravel後臺管理平臺’的一個模組兒整合於系統中。
程式碼 : http://git.oschina.net/cc-sponge/Alpaca-Spa-Laravel
程式碼 : https://github.com/big-sponge/Alpaca-Spa-Laravel
Alpaca-Spa-Laravel : http://full.tkc8.com
Alpaca-Spa : http://www.tkc8.com
適用範圍
定時精確時間不低於1秒。web服務重啟、或者php重啟。該定時任務不會自動重啟。
實現原理
用PHP實現定時任務(非linux-shell方式,與作業系統平臺無關),主要兩個技術點:
1)PHP後臺程式
2)PHP非同步處理
1 PHP後臺程式
一般情況,用命令列可以開啟一個php後臺程式。而在瀏覽器中通過HTTP請求一個php處理,會因為瀏覽器關閉,或者請求超時,使得後臺的php處理中斷。但是php提供了兩個方法可以會忽略瀏覽器關閉、請求超時:
1 2 |
ignore_user_abort(true); // 忽略客戶端斷開 set_time_limit(0); // 設定執行不超時 |
因此,我們可以使用這兩個函式從瀏覽器以HTTP請求的方式開啟一個php後臺程式。
2 PHP非同步處理
php語言本身沒有可以跨平臺好用非同步處理方法,但是可以通過curl或者fsockopen建立一個請求來實現非同步處理。這裡我們用fsockopen方法實現:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$fp = fsockopen("$ip", $port, $errno, $errstr,1); if (!$fp) { return 'worker error:'."$errstr ($errno)<br />\n"; } else { $out = "POST $url HTTP/1.1\r\n"; $out .= "Host: $ip\r\n"; $out .= "Content-Type:application/x-www-form-urlencoded; charset=UTF-8\r\n"; $out .= "Content-Length: " . strlen($postData) . "\r\n"; $out .= "Connection: close\r\n"; $out .="\r\n"; $out .=$postData; fputs($fp, $out); fclose($fp); } |
前臺互動控制器
為了在前端介面方便控制定時任務的開啟關閉,新增、編輯、刪除,檢視執行狀態。 同時也可以新增許可權控制。 主要有8個操作介面(下面是以Laravel 路由示例):
1 檢視定時任務程式狀態
2 開始定時任務程式 3 停止定時任務程式
4 新增,或者編輯定時任務
5 設定定時任務狀態
6 獲取指定定時任務明細
7 刪除定時任務
8 獲取定時任務列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
/* crontab - status 檢視定時任務守護程式狀態 */ Route::any('crontab/status', "CrontabController@status"); /* crontab - start 開始定時任務 */ Route::any('crontab/start', "CrontabController@start"); /* crontab - stop 停止定時任務守護程式*/ Route::any('crontab/stop', "CrontabController@stop"); /* crontab - editTask 新增,或者編輯定時任務*/ Route::any('crontab/editTask', "CrontabController@editTask"); /* crontab - changeTaskStatus 設定定時任務狀態 */ Route::any('crontab/changeTaskStatus', "CrontabController@changeTaskStatus"); /* crontab - getIndexTask 獲取指定定時任務 */ Route::any('crontab/getIndexTask', "CrontabController@getIndexTask"); /* crontab - removeTask 刪除定時任務 */ Route::any('crontab/removeTask', "CrontabController@removeTask"); /* crontab - listTask 獲取定時任務列表 */ Route::any('crontab/listTask', "CrontabController@listTask"); |
完整的類程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 |
<?php namespace App\Modules\Manage\Controllers; use Crontab\Library\Crontab\AlpacaCrontab; use Crontab\Library\Crontab\AlpacaDaemon; use Crontab\Library\Crontab\AlpacaWorker; use App\Modules\Manage\Controllers\Base\BaseController; use App\Common\Code; use App\Common\Msg; /** * 定時任務管理控制器 * @author Chengcheng * @date 2016-10-19 15:50:00 */ class CrontabController extends BaseController { /** * 設定不需要登入的的Action,不加Action字首 * @author Chengcheng * @date 2016年10月23日 20:39:25 * <a href='http://www.jobbole.com/members/wx1409399284'>@return</a> array */ protected function noLogin() { return []; } /** * 設定不需要許可權驗證的Action,不加Action字首 * @author Chengcheng * @date 2016年10月23日 20:39:25 * <a href='http://www.jobbole.com/members/wx1409399284'>@return</a> array */ protected function noAuth() { // 以下Action不需要角色許可權 return []; } /** * 檢視定時任務守護程式狀態 * @author Chengcheng * @date 2016-10-23 20:34:00 */ public function status() { //檢視守護程式狀態 $result['code'] = Code::SYSTEM_OK; $result['msg'] = Msg::SYSTEM_OK; $result['data'] = AlpacaDaemon::daemon()->status(); //返回結果 return $this->ajaxReturn($result); } /** * 開始定時任務 * @author Chengcheng * @date 2016-10-23 20:34:00 */ public function start() { //非同步開啟守護程式 $result['code'] = Code::SYSTEM_OK; $result['msg'] = Msg::SYSTEM_OK; $result['data'] = AlpacaWorker::worker()->action(['REQUEST_URI' => "/crontab/index/start"]); //返回結果 return $this->ajaxReturn($result); } /** * 停止定時任務守護程式 * @author Chengcheng * @date 2016-10-23 20:34:00 */ public function stop() { //停止守護程式 $result['code'] = Code::SYSTEM_OK; $result['msg'] = Msg::SYSTEM_OK; $result['data'] = AlpacaDaemon::daemon()->stop(); //返回結果 return $this->ajaxReturn($result); } /** * 新增,或者編輯定時任務 * @author Chengcheng * @date 2016-10-23 20:34:00 */ public function editTask() { /* * 1 獲取輸入引數 * BEGIN_TIME 開始時間 * END_TIME 結束時間 * INTERVAL 時間間隔 * NAME 名稱 * STATUS 狀態 1-ENABLED, 2-DISABLE * TASK_TYPE 型別 1-ONCE, 2-LOOP * ACTION 要執行的Action * INDEX 索引,null或者0時候,表示新建 * */ $this->requestData['NAME'] = $this->input('NAME', null); $this->requestData['BEGIN_TIME'] = $this->input('BEGIN_TIME', null); $this->requestData['END_TIME'] = $this->input('END_TIME', null); $this->requestData['INTERVAL'] = $this->input('INTERVAL', null); $this->requestData['TASK_TYPE'] = $this->input('TASK_TYPE', '1'); $this->requestData['ACTION'] = $this->input('ACTION', null); $this->requestData['STATUS'] = $this->input('STATUS', '2'); $this->requestData['INDEX'] = $this->input('INDEX', null); $this->requestData['LAST_TIME'] = $this->input('LAST_TIME', null); //2 檢查引數 if (empty($this->requestData['BEGIN_TIME'])) { $result["code"] = Code::SYSTEM_PARAMETER_NULL; $result["msg"] = sprintf(Msg::SYSTEM_PARAMETER_NULL, 'BEGIN_TIME'); return $this->ajaxReturn($result); } if ($this->requestData['TASK_TYPE'] == 2 && empty($this->requestData['END_TIME'])) { $result["code"] = Code::SYSTEM_PARAMETER_NULL; $result["msg"] = sprintf(Msg::SYSTEM_PARAMETER_NULL, 'END_TIME'); return $this->ajaxReturn($result); } if (empty($this->requestData['ACTION'])) { $result["code"] = Code::SYSTEM_PARAMETER_NULL; $result["msg"] = sprintf(Msg::SYSTEM_PARAMETER_NULL, 'ACTION'); return $this->ajaxReturn($result); } if ($this->requestData['TASK_TYPE'] == 2 && empty($this->requestData['INTERVAL'])) { $result["code"] = Code::SYSTEM_PARAMETER_NULL; $result["msg"] = sprintf(Msg::SYSTEM_PARAMETER_NULL, 'INTERVAL'); return $this->ajaxReturn($result); } //3 設定結束時間 $now = date('Y-m-d H:i:s', time()); $nextTime = date('Y-m-d H:i:s', strtotime($this->requestData['INTERVAL'], strtotime($this->requestData['BEGIN_TIME']))); if ($this->requestData['TASK_TYPE'] == "1" || strtotime($now) < strtotime($this->requestData['BEGIN_TIME'])) { $nextTime = $this->requestData['BEGIN_TIME']; } //4 建立任務 $task = array( 'NAME' => $this->requestData['NAME'], //NAME 'STATUS' => $this->requestData['STATUS'], // 1-ENABLED, 2-DISABLE 'TYPE' => $this->requestData['TASK_TYPE'], // 1-ONCE, 2-LOOP 'INTERVAL' => $this->requestData['INTERVAL'], //year(年),month(月),hour(小時)minute(分),second(秒) 'BEGIN_TIME' => $this->requestData['BEGIN_TIME'], //開始時間 'NEXT_TIME' => $nextTime, //下次執行時間 'LAST_TIME' => $this->requestData['LAST_TIME'], //上次執行時間 'ACTION' => $this->requestData['ACTION'], //執行的ACTION 'END_TIME' => $this->requestData['END_TIME'], //截止時間2 ); //5 判斷是新建還是修改 if (empty($this->requestData['INDEX'])) { //新建 $info = AlpacaCrontab::crontab()->addTask($task); } else { $this->requestData['INDEX'] -= 1; $info = AlpacaCrontab::crontab()->editTask($this->requestData['INDEX'], $task); } //5 返回結果 $result['code'] = Code::SYSTEM_OK; $result['msg'] = Msg::SYSTEM_OK; $result['data'] = $info; return $this->ajaxReturn($result); } /** * 設定定時任務狀態 * @author Chengcheng * @date 2016-10-23 20:34:00 */ public function changeTaskStatus() { /* * 1 獲取輸入引數 * STATUS 狀態 1-ENABLED, 2-DISABLE * INDEX 索引 * */ $this->requestData['STATUS'] = $this->input('STATUS', '2'); $this->requestData['INDEX'] = $this->input('INDEX', null); //2 檢查引數 if (empty($this->requestData['STATUS'])) { $result["code"] = Code::SYSTEM_PARAMETER_NULL; $result["msg"] = sprintf(Msg::SYSTEM_PARAMETER_NULL, 'STATUS'); return $this->ajaxReturn($result); } if (empty($this->requestData['INDEX'])) { $result["code"] = Code::SYSTEM_PARAMETER_NULL; $result["msg"] = sprintf(Msg::SYSTEM_PARAMETER_NULL, 'INDEX'); return $this->ajaxReturn($result); } //3 修改狀態 $this->requestData['INDEX'] -= 1; $data = AlpacaCrontab::crontab()->editTaskStatus($this->requestData['INDEX'], $this->requestData['STATUS']); //4 返回結果 $result['code'] = Code::SYSTEM_OK; $result['msg'] = Msg::SYSTEM_OK; $result['data'] = $data; return $this->ajaxReturn($result); } /** * 查詢單條定時任務 * @author Chengcheng * @date 2016-10-23 20:34:00 */ public function getIndexTask() { /* * 1 獲取輸入引數 * INDEX 索引 * */ $this->requestData['INDEX'] = $this->input('INDEX', null); //2 檢查引數 if (empty($this->requestData['INDEX'])) { $result["code"] = Code::SYSTEM_PARAMETER_NULL; $result["msg"] = sprintf(Msg::SYSTEM_PARAMETER_NULL, 'INDEX'); return $this->ajaxReturn($result); } //3 刪除 $this->requestData['INDEX'] -= 1; $data = AlpacaCrontab::crontab()->getIndexTask($this->requestData['INDEX']); //4 返回結果 $result['code'] = Code::SYSTEM_OK; $result['msg'] = Msg::SYSTEM_OK; $result['data'] = $data; return $this->ajaxReturn($result); } /** * 刪除定時任務 * @author Chengcheng * @date 2016-10-23 20:34:00 */ public function removeTask() { /* * 1 獲取輸入引數 * INDEX 索引 * */ $this->requestData['INDEX'] = $this->input('INDEX', null); //2 檢查引數 if (empty($this->requestData['INDEX'])) { $result["code"] = Code::SYSTEM_PARAMETER_NULL; $result["msg"] = sprintf(Msg::SYSTEM_PARAMETER_NULL, 'INDEX'); return $this->ajaxReturn($result); } //3 刪除 $this->requestData['INDEX'] -= 1; $data = AlpacaCrontab::crontab()->removeTask($this->requestData['INDEX']); //4 返回結果 $result['code'] = Code::SYSTEM_OK; $result['msg'] = Msg::SYSTEM_OK; $result['data'] = $data; return $this->ajaxReturn($result); } /** * 檢視定時任務列表 * @author Chengcheng * @date 2016-10-23 20:34:00 */ public function listTask() { //查詢 $data['task'] = AlpacaCrontab::crontab()->listTask(); $data['total'] = count($data['task']); $data['status'] = AlpacaDaemon::daemon()->status(); //返回結果 $result['code'] = Code::SYSTEM_OK; $result['msg'] = Msg::SYSTEM_OK; $result['data'] = $data; return $this->ajaxReturn($result); } } |
實現後臺程式類
使用 ignore_user_abort(true); set_time_limit(0); 可以從瀏覽器開啟一個php後臺程式。為了避免出現多個後臺程式,需要藉助一個配置來標識該後臺程式是否已經啟動
1 |
{"code":"1001","message":"Stop at:2017-02-24 11:29:43"} |
當code是1001時候,表示後臺程式未啟動,這時通過http請求開啟後臺程式時,正常啟動
當code是1000時候,表示後臺程式已經啟動,這時通過http請求開啟後臺程式時,不做任何操作,以為程式已經啟動
當開啟後臺程式的請求到達後臺時候,讀取配置檔案,如果code是1001,則啟動程式,並且設定code為1000,儲存配置檔案。
當關閉後臺程式的請求到達後臺時候,讀取配置檔案,設定code為1001,儲存配置檔案。
後臺程式在執行時候,每隔一秒讀取配置檔案,判斷code狀態,如果是1001,則結束執行; 如果是1000,則繼續執行
完整類的程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
<?php namespace Crontab\Library\Crontab; /** * 守護程式 * @author Chengcheng * @date 2016年10月21日 17:04:44 */ class AlpacaDaemon { private $daemon_json = __DIR__ . '/deamon.json'; private static $instance; private $events = []; public static function daemon() { return self::getInstance(); } private static function getInstance() { if(!self::$instance){ self::$instance = new self(); self::$instance->daemon_json = base_path('storage') . '/crontab/deamon.json'; } return self::$instance; } public function setDaemon($daemon_json) { $this->daemon_json = $daemon_json; return $this; } public function setEvents(array $events) { $this->events = $events; return $this; } public function status() { $data = json_decode(file_get_contents($this->daemon_json),true); if(empty($data)){ $data = array(); } return $data; } public function stop() { $data =new \stdClass(); $data->code="1001"; $data->message="Stop at:".date("Y-m-d H:i:s" ,time()); file_put_contents($this->daemon_json,json_encode($data),LOCK_EX); $result["result_code"] = "1"; $result["result_message"] = "操作成功"; return $result; } public function start() { $data = json_decode(file_get_contents($this->daemon_json) , true); if(empty($data)){ $data['code']="1001"; } if($data['code'] == "1000" ){ //die("Error - exit, Already running !"); return; } $data['code']="1000"; $data['message']="Start"; file_put_contents($this->daemon_json,json_encode($data),LOCK_EX); ignore_user_abort(true); // 忽略客戶端斷開 set_time_limit(0); // 設定執行不超時 while(true){ $data = json_decode(file_get_contents($this->daemon_json) , true); if(empty($data) || empty($data['code']) || $data['code'] == "1001" ){ break; } if(!empty($this->events)){ foreach ($this->events as $e){ $e(); } } $data['message'] = date("Y-m-d H:i:s" ,time())." : Working ..."; file_put_contents($this->daemon_json, json_encode($data), LOCK_EX); sleep(1); } $this->stop(); } } |
實現非同步處理的類
完整類的程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
<?php namespace Crontab\Library\Crontab; class AlpacaWorker { private static $instance; private $accessToken = ''; public static function worker() { return self::getInstance(); } private static function getInstance() { if(!self::$instance){ self::$instance = new self(); self::$instance->accessToken= 'VyKfohBbwlkTOqp2jvIWPW92'; } return self::$instance; } public function action(array $worker = null) { //獲取引數 $ip = empty($worker['SERVER_ADDR']) ? $_SERVER['SERVER_NAME'] : $worker['SERVER_ADDR']; //伺服器IP地址 $port = empty($worker['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : $worker['SERVER_PORT']; //伺服器埠 $url = empty($worker['REQUEST_URI']) ? '/' :$worker['REQUEST_URI']; //伺服器URL $data = empty($worker['REQUEST_DATA']) ? '' :$worker['REQUEST_DATA']; //請求引數 //格式化請求引數 $postData = ""; $needChar = false; if(is_array($data)){ foreach($data as $key => $val) { $postData .= ($needChar ? "&" : "") . urlencode($key) . "=" . urlencode($val); $needChar = true; } }else{ $postData = $data; } $url=$url."?accessToken=".$this->accessToken; //使用fsockopen方式非同步呼叫action $fp = fsockopen("$ip", $port, $errno, $errstr,1); if (!$fp) { return 'worker error:'."$errstr ($errno)<br />\n"; } else { $out = "POST $url HTTP/1.1\r\n"; $out .= "Host: $ip\r\n"; $out .= "Content-Type:application/x-www-form-urlencoded; charset=UTF-8\r\n"; $out .= "Content-Length: " . strlen($postData) . "\r\n"; $out .= "Connection: close\r\n"; $out .="\r\n"; $out .=$postData; fputs($fp, $out); fclose($fp); } return 'worker success!'; } } |
定時任務處理類
定時任務處理類 主要是實現新增、編輯、刪除定時任務,執行定時任務要處理的方法
定時任務的資訊以json格式存放在下面的配置檔案中
1 |
[{"NAME":"\u6d4b\u8bd5\u5b9a\u65f6\u4efb\u52a12","STATUS":"2","TYPE":"1","INTERVAL":"5 second","BEGIN_TIME":"2017-02-21 11:55:00","NEXT_TIME":"2017-02-21 11:55:00","LAST_TIME":null,"ACTION":"\/main\/crontab\/index2","END_TIME":"2017-02-10 15:55:00"},{"NAME":"TEST - log","STATUS":"2","TYPE":"2","INTERVAL":"5 second","BEGIN_TIME":"2017-08-10 09:00:53","NEXT_TIME":"2017-08-10 09:59:00","LAST_TIME":"2017-08-10 09:58:55","ACTION":"\/crontab\/task\/test","END_TIME":"2017-08-11 09:25:53"}] |
主要的欄位為:
INDEX 索引
BEGIN_TIME 開始時間
END_TIME 結束時間
INTERVAL 時間間隔
NAME 名稱
STATUS 狀態 1-ENABLED, 2-DISABLE
TASK_TYPE 型別 1-ONCE, 2-LOOP
ACTION 要執行的Action
類中主要有7個方法:
listTask() 檢視定時任務列表 addTask() 新增定時任務 editTask() 編輯定時任務 editTaskStatus() 編輯定時任務狀態 getIndexTask() 獲取指定定時任務資訊 removeTask() 刪除定時任務 doTask() 執行定時任務指定的任務
完整類的程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
<?php namespace Crontab\Library\Crontab; /** * 定時任務 * @author Chengcheng * @date 2016年10月21日 17:04:44 */ class AlpacaCrontab { //定時任務檔案 private $task_json = __DIR__ .'/crontab.json'; //單例 private static $instance; //單例 public static function crontab() { return self::getInstance(); } //單例 private static function getInstance() { if(!self::$instance){ self::$instance = new self(); self::$instance->task_json = base_path('storage') . '/crontab/crontab.json'; } return self::$instance; } /** * 配置 * @author Chengcheng * @param array $crontab * @date 2016-10-23 20:34:00 * <a href='http://www.jobbole.com/members/wx1409399284'>@return</a> array */ public function setConfig($crontab) { $this->task_json = $crontab; return $this; } /** * 檢視定時任務 * @author Chengcheng * @date 2016-10-23 20:34:00 * <a href='http://www.jobbole.com/members/wx1409399284'>@return</a> array */ public function listTask() { $tasks = json_decode(file_get_contents($this->task_json)); $i = 0; foreach ($tasks as $task) { $tasks[$i]->INTERVAL = $this->timeToStr($tasks[$i]->INTERVAL); $i++; } return $tasks; } /** * 新增定時任務 * @author Chengcheng * @date 2016-10-23 20:34:00 * <a href='http://www.jobbole.com/members/wx1409399284'>@return</a> array */ public function addTask($task) { $result["result_code"] = "1"; $result["result_message"] = "新增成功"; $tasks = json_decode(file_get_contents($this->task_json),true); $tasks[count($tasks)] = $task; file_put_contents($this->task_json, json_encode($tasks), LOCK_EX); return $result; } /** * 編輯定時任務 * @author Chengcheng * @param string $index * @param string $task * @date 2016-10-23 20:34:00 * <a href='http://www.jobbole.com/members/wx1409399284'>@return</a> array */ public function editTask($index,$task) { $result["result_code"] = "1"; $result["result_message"] = "修改成功"; $tasks = json_decode(file_get_contents($this->task_json)); $tasks[$index] = $task; file_put_contents($this->task_json, json_encode($tasks), LOCK_EX); return $result; } /** * 編輯定時任務狀態 * @author Chengcheng * @param string $index * @param string $status * @date 2016-10-23 20:34:00 * <a href='http://www.jobbole.com/members/wx1409399284'>@return</a> array */ public function editTaskStatus($index,$status) { $result_data["result_code"] = "1"; $result_data["result_message"] = "修改狀態成功[".$status."]"; $tasks = json_decode(file_get_contents($this->task_json)); $tasks[$index]->STATUS = $status; file_put_contents($this->task_json, json_encode($tasks), LOCK_EX); return $result_data; } /** * 獲取定時任務 * @author Chengcheng * @param string $index * @date 2016-10-23 20:34:00 * <a href='http://www.jobbole.com/members/wx1409399284'>@return</a> array */ public function getIndexTask($index) { $result_data["result_code"] = "1"; $result_data["result_message"] = "獲取任務成功【".$index."】"; $tasks = json_decode(file_get_contents($this->task_json)); $result_data["result_data"] = $tasks[$index]; return $result_data; } /** * 刪除定時任務 * @author Chengcheng * @param string $index * @date 2016-10-23 20:34:00 * <a href='http://www.jobbole.com/members/wx1409399284'>@return</a> array */ public function removeTask($index) { $result_data["result_code"] = "1"; $result_data["result_message"] = "刪除任務【".$index."】成功"; $tasks = json_decode(file_get_contents($this->task_json)); array_splice($tasks, $index, 1); file_put_contents($this->task_json, json_encode($tasks), LOCK_EX); return $result_data; } /** * 執行定時任務 * @author Chengcheng * @date 2016-10-23 20:34:00 * <a href='http://www.jobbole.com/members/wx1409399284'>@return</a> array */ public function doTask() { $tasks = json_decode(file_get_contents($this->task_json) ,true); if(empty($tasks)){ return ;} $now = date('Y-m-d H:i:s',time()); foreach ($tasks as &$task){ if(empty($task['STATUS']) || empty($task['TYPE']) || empty($task['BEGIN_TIME']) || empty($task['ACTION']) ) { continue; } if($task['STATUS'] != 1) { continue; } if(!empty($task['END_TIME']) && strtotime($now)>=strtotime($task['END_TIME'])){ $task['NEXT_TIME']='END'; continue; } if($task['TYPE'] == 1 && empty($task['NEXT_TIME']) ) { continue; } if($task['TYPE'] == 2 && empty($task['INTERVAL']) ) { continue; } if(!empty($task['NEXT_TIME']) && $task['NEXT_TIME']=='END' ) { continue; } if($task['TYPE'] == 1 && (strtotime($now)>=strtotime($task['NEXT_TIME']))) { $task['LAST_TIME']= $now; $task['NEXT_TIME']='END'; $task['STATUS']=2; AlpacaWorker::worker()->action(['REQUEST_URI'=>"{$task['ACTION']}"]); continue; } if($task['TYPE'] == 2) { if(empty($task['NEXT_TIME'])){ $task['NEXT_TIME'] = $task['BEGIN_TIME']; } if(strtotime($now)>=strtotime($task['NEXT_TIME'])){ $task['LAST_TIME']= $now; $task['NEXT_TIME']= date('Y-m-d H:i:s',strtotime($task['INTERVAL'])); AlpacaWorker::worker()->action(['REQUEST_URI'=>"{$task['ACTION']}"]); } continue; } } file_put_contents($this->task_json, json_encode($tasks), LOCK_EX); return $tasks; } /** * 格式化時間 * @author Chengcheng * @param string $interval * @date 2016-10-23 20:34:00 * <a href='http://www.jobbole.com/members/wx1409399284'>@return</a> array */ private function timeToStr($interval) { $result = ""; if($interval != null && $interval != ""){ $temp = explode(" ", $interval); $iNumTemp = $temp[0]; $iType = $temp[1]; $iNum = str_replace("+", "", $iNumTemp); $str = ""; switch ($iType){ case "year": $str = "(年)"; break; case "month": $str = "(月)"; break; case "day": $str = "(日)"; break; case "hour": $str = "(小時)"; break; case "minute": $str = "(分)"; break; case "second": $str = "(秒)"; break; default: break; } $result = $iNum. $str; } return $result; } } |
定時任務程式入口控制器
主要是為了實現非同步開啟後臺程式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
<?php namespace Crontab\Controllers; use Crontab\Common\Code; use Crontab\Common\Msg; use Crontab\Controllers\Base\BaseController; use Crontab\Library\Crontab\AlpacaCrontab; use Crontab\Library\Crontab\AlpacaDaemon; use Crontab\Library\Crontab\AlpacaWorker; /** * index * @author Chengcheng * @date 2017-02-22 15:50:00 */ class IndexController extends BaseController { /** * 設定不需要登入的的Action,不加Action字首 * @author Chengcheng * @date 2016年10月23日 20:39:25 * <a href='http://www.jobbole.com/members/wx1409399284'>@return</a> array */ protected function withoutLoginActions() { } /** * 開始定時任務的守護程式 * @author Chengcheng * @date 2016-10-23 20:34:00 */ public function start() { //開始守護程式 $result['code'] = Code::SYSTEM_OK; $result['msg'] = Msg::SYSTEM_OK; //在守護程式中注入定時任務 $events = ['0'=>function(){ AlpacaWorker::worker()->action(['REQUEST_URI'=>"/crontab/index/task"]); }]; AlpacaDaemon::daemon()->setEvents($events); AlpacaDaemon::daemon()->start(); //返回結果 return $this->ajaxReturn($result); } /** * 停止定時任務的守護程式 * @author Chengcheng * @date 2016-10-23 20:34:00 */ public function stop() { //停止守護程式 $result['code'] = Code::SYSTEM_OK; $result['msg'] = Msg::SYSTEM_OK; $result['data'] = AlpacaDaemon::daemon()->stop(); //返回結果 return $this->ajaxReturn($result); } /** * 執行定時任務 * @author Chengcheng * @date 2016-10-23 20:34:00 */ public function task() { //執行定時任務 $result['code'] = Code::SYSTEM_OK; $result['msg'] = Msg::SYSTEM_OK; $result['data'] = AlpacaCrontab::crontab()->doTask(); //返回結果 return $this->ajaxReturn($result); } } |
以上是PHP實現定時任務的核心類與方法, 完整的程式碼請參看程式碼伺服器中提供的原始碼。