This article describes how to use PHP to implement pure timer tasks and meet the business needs of tasks. if you need it, you can refer to the timer tasks, which are common in WEB applications, there are two ways to implement a timer task using PHP: 1) use the Crontab command to write a shell script, call the PHP file in the script, and then regularly execute the script; 2) use ignore_user_abort () and set_time_limit () together to run the script out of the browser. The former uses the features of Linux and does not have much to do with PHP itself. The latter has limited use cases and can only trigger the script once in an HTTP request. after the script is executed, it exits. So how can we use pure PHP to implement pure timer tasks and meet the business needs of task recognition?
Basic knowledge
This program is developed in Linux and runs in cli mode. here is a brief introduction to basic knowledge.
CLI:PHP command line mode. common WEB applications use fpm;
Process:Processes are the basic unit for running programs. processes run independently and do not interfere with each other. they have independent runtime spaces and each process has a process control block;
Inter-process communication:Since processes run independently, we need a mechanism to ensure information exchange between different processes. Inter-process communication mainly includes pipelines, IPC (shared memory, signals, message queues), and sockets;
PCNTL extension:A php process extension mainly uses the pcntl_alarm () function. for details, refer to the official website.
Implementation principle
Use a three-dimensional array to save all the tasks to be executed. The first-level index is the timestamp, the value is the method for executing the task, and the callback parameters. the specific array format is as follows:
Array ('20140901' => array (1, array ('class', 'func'), array (), true),) description: 1438156396 timestamp array (1, array ('class', 'func'), array (), true) parameters are represented in sequence: execution interval, callback function, whether the parameter passed to the callback function is persistent (true is always stored in the data; otherwise, it is deleted after execution)
These tasks can be methods of any class. Since it is a scheduled task, we need something similar to timing. this solution uses semaphores to send SIGALRM signals to the current process every second, capture the signals, and trigger the signal processing function, traverse data cyclically to determine whether there are tasks to be executed at the current time. If yes, the callback method is used and the parameter is passed to the method.
<? Php/*** Timer */class Timer {// save all scheduled tasks public static $ task = array (); // the scheduled interval public static $ time = 1; /*** enable the service ** @ param $ time int */public static function run ($ time = null) {if ($ time) {self :: $ time = $ time;} self: installHandler (); pcntl_alarm (1);}/*** register the signal processing function */public static function installHandler () {pcntl_signal (SIGALRM, array ('timer', 'signalhandler');}/*** signal processing function */public static function signalHandler () {self :: task (); // after a signal event is executed, the next pcntl_alarm (self: $ time) is triggered );} /*** execution callback */public static function task () {if (empty (self: $ task) {// no task, return;} foreach (self:: $ task as $ time => $ arr) {$ current = time (); foreach ($ arr as $ k => $ job) {// traverse each task $ func = $ job ['function'];/* callback function */$ argv = $ job ['argv']; /* callback function parameter */$ interval = $ job ['interval'];/* interval */$ persist = $ job ['persist ']; /* persistence */if ($ current = $ time) {// The current time has the execution task // call the callback function and pass the call_user_func_array parameter ($ func, $ argv ); // delete this task unset (self: $ task [$ time] [$ k]);} if ($ persist) {// if persistence is performed, the data is written to the array, wait for the next awakening of self: $ task [$ current + $ interval] [] = $ job;} if (empty (self: $ task [$ time]) {unset (self ::$ task [$ time]) ;}}/ *** add task */public static function add ($ interval, $ func, $ argv = array (), $ persist = false) {if (is_null ($ interval) {return ;}$ time = time () + $ interval; // write scheduled task self: $ task [$ time] [] = array ('func' => $ func, 'argv' => $ argv, 'interval' => $ interval, 'persist' => $ persist);}/*** delete all timer tasks */public function dellAll () {self :: $ task = array ();}}
This is the core part of the timer class. there is a static variable that stores all the tasks to be executed. why is it static here? Think for yourself. when the process receives the SIGALRM signal, it triggers the signalHandler function, and then traverses the array sequentially to check whether there are tasks to be executed at the current time. if yes, it calls back and passes parameters to delete the current job, then, check whether the persistence task is to be performed. If yes, continue to write the current job into the event array and wait for the next trigger. Finally, set an alarm signal for the current process. it can be seen that this timer will be triggered again from the internal as long as it is triggered once to get the self-loop purpose.
<?php class DoJob { public function job( $param = array() ) { $time = time(); echo "Time: {$time}, Func: ".get_class()."::".__FUNCTION__."(".json_encode($param).")\n"; } }
This is a callback class and function. for convenience, we have added a lot of debugging information. Timer classes and callbacks are available. let's take a look at the usage scenarios.
<?php require_once(__DIR__."/Timer.php"); require_once(__DIR__."/DoJob.php"); Timer::dellAll(); Timer::add( 1, array('DoJob','job'), array(),true); Timer::add( 3, array('DoJob','job'),array('a'=>1), false); echo "Time start: ".time()."\n"; Timer::run(); while(1) { sleep(1); pcntl_signal_dispatch(); }
The code is very short. here two jobs are registered, and then the timer is run to capture the signal trigger action in an infinite loop. if not captured, the previously registered processing function cannot be triggered. this self-loop timer is developed completely. the running result is as follows:
Like the tasks added in our scenario class, two tasks were executed at 90, one for a persistent job without parameters and the other for a non-persistent job with parameters, then the non-persistent job is not executed.
Summary
1. the current process cannot exit before receiving the signal. here I use a true loop with the condition always. in our actual production environment, we need to create such a prerequisite. for example, we have a group of services that are always running, regardless of IO access, waiting for socket links, etc, the current service will not be terminated, even if the process is blocked, there will be no problem. In this scenario, it is used in a continuously running service.
2. Currently, PHP only supports trigger in seconds and does not support smaller time units. it is basically enough for scheduled tasks.
The above is all the content of this article, hoping to help you learn.