PHP實現定時任務(非linux-shell方式,與操做系統無關)

頁面交互效果

下面是寫好的示例前端交互頁面,主要是列表頁面,編輯頁面。php

主要交互有:前端

1 開啓定時任務進程
2 關閉定時任務進程
3 新增一項定時任務
4 編輯已有的定時任務
5 刪除定時任務
6 開始一項定時任務
7 中止一項定時任務linux

定時任務分兩種:git

1 執行一次
2 循環執行github

定時任務執行的內容:web

定時任務執行的內容其實仍是一個http請求。 經過定時的方式,指定時間執行或者循執行。ajax

1 列表頁面

圖片名稱

2 新建\編輯頁面

圖片名稱

代碼地址,演示地址

示例代碼是基於Laravel,Alpaca-spa框架編寫,而且做爲 ‘Alpaca-Spa-Laravel後臺管理平臺’的一個模塊兒集成於系統中。shell

內容 說明 地址
Alpaca-Spa 主頁 http://www.tkc8.com
Alpaca-Spa-Laravel 後臺管理端 http://full.tkc8.com
Alpaca-Spa-Sui 手機端sui http://full.tkc8.com/app
oschina 代碼 http://git.oschina.net/cc-sponge/Alpaca-Spa-Laravel
github 代碼 https://github.com/big-sponge/Alpaca-Spa-Laravel

適用範圍

定時精確時間不低於1秒。web服務重啓、或者php重啓。該定時任務不會自動重啓。json

實現原理

用PHP實現定時任務(非linux-shell方式,與操做系統平臺無關),主要兩個技術點:瀏覽器

1)PHP後臺進程
2)PHP異步處理

1 PHP後臺進程

通常狀況,用命令行能夠開啓一個php後臺進程。而在瀏覽器中經過HTTP請求一個php處理,會由於瀏覽器關閉,或者請求超時,使得後臺的php處理中斷。可是php提供了兩個方法能夠會忽略瀏覽器關閉、請求超時:

ignore_user_abort(true);     // 忽略客戶端斷開
        set_time_limit(0);           // 設置執行不超時

所以,咱們可使用這兩個函數從瀏覽器以HTTP請求的方式開啓一個php後臺進程。

2 PHP異步處理

php語言自己沒有能夠跨平臺好用異步處理方法,可是能夠經過curl或者fsockopen建立一個請求來實現異步處理。這裏咱們用fsockopen方法實現:

$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 獲取定時任務列表

/* 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");

完整的類代碼以下:

<?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
     * @return array
     */
    protected function noLogin()
    {
        return [];
    }

    /**
     * 設置不須要權限驗證的Action,不加Action前綴
     * @author Chengcheng
     * @date   2016年10月23日 20:39:25
     * @return 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後臺進程。爲了不出現多個後臺進程,須要藉助一個配置來標識該後臺進程是否已經啓動。

{"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,則繼續執行

完整類的代碼以下:

<?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();
    }
}

實現異步處理的類

完整類的代碼以下:

<?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格式存放在下面的配置文件中

[{"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() 執行定時任務指定的任務

完整類的代碼以下:

<?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
     * @return array
     */
    public function setConfig($crontab)
    {
        $this->task_json = $crontab;
        return $this;
    }

    /**
     * 查看定時任務
     * @author Chengcheng
     * @date 2016-10-23 20:34:00
     * @return 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
     * @return 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
     * @return 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
     * @return 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
     * @return 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
     * @return 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
     * @return 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
     * @return 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;
    }
}

定時任務進程入口控制器

主要是爲了實現異步開啓後臺進程

<?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
     * @return 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實現定時任務的核心類與方法, 完整的代碼請參看代碼服務器中提供的源碼。

歡迎討論

圖片名稱

做者: Sponge 郵箱: 1796512918@qq.com

相關文章
相關標籤/搜索