Running a php script via ajax, but only if it is not already running

后端 未结 6 1620
轮回少年
轮回少年 2021-02-13 21:28

My intention is this.

My client.html calls a php script check.php via ajax. I want check.php to check if another script task.php is already being run. If it is, I do not

6条回答
  •  暗喜
    暗喜 (楼主)
    2021-02-13 21:41

    I would use an flock() based mechanism to make sure that task.php runs only once.

    Use a code like this:

    Also note that flock() is, as the PHP documentation points out, portable across all supported operating systems.


    !$
    

    gives you the pid of the last executed program in bash. Like this:

    command &
    pid=$!
    echo pid
    

    Note that you will have to make sure your php code runs on a system with bash support. (Not windows)


    Update (after comment of opener).

    flock() will work on all operating systems (As I mentioned). The problem I see in your code when working with windows is the !$ (As I mentioned ;) ..

    To obtain the pid of the task.php you should use proc_open() to start task.php. I've prepared two example scripts:

    task.php

    $fd = fopen('lock.file', 'w+');
    
    // try to get an exclusive lock. LOCK_NB let the operation not blocking
    // if a process instance is already running. In this case, the else 
    // block will being entered.
    if(flock($fd, LOCK_EX | LOCK_NB )) {
        // your task's code comes here
        sleep(10);
        // ...
        flock($fd, LOCK_UN);
        echo 'success';
        $exitcode = 0;
    } else {
        echo 'already running';
        // return 2 to let check.php know about that
        // task.php is already running
        $exitcode = 2; 
    }
    
    fclose($fd);
    
    exit($exitcode);
    

    check.php

    $cmd = 'php task.php';
    $descriptorspec = array(
       0 => array('pipe', 'r'),  // STDIN 
       1 => array('pipe', 'w'),  // STDOUT
       2 => array('pipe', 'w')   // STDERR
    );
    
    $pipes = array(); // will be set by proc_open()
    
    // start task.php
    $process = proc_open($cmd, $descriptorspec, $pipes);
    
    if(!is_resource($process)) {
        die('failed to start task.php');
    }
    
    // get output (stdout and stderr)
    $output = stream_get_contents($pipes[1]);
    $errors = stream_get_contents($pipes[2]);
    
    do {
        // get the pid of the child process and it's exit code
        $status = proc_get_status($process);
    } while($status['running'] !== FALSE);
    
    // close the process
    proc_close($process);
    
    // get pid and exitcode
    $pid = $status['pid'];
    $exitcode = $status['exitcode'];
    
    // handle exit code
    switch($exitcode) {
        case 0:
            echo 'Task.php has been executed with PID: ' . $pid
               . '. The output was: ' . $output;
            break;
        case 1:
            echo 'Task.php has been executed with errors: ' . $output;
            break;
        case 2:
            echo 'Cannot execute task.php. Another instance is running';
            break;
        default:
            echo 'Unknown error: ' . $stdout;
    }
    

    You asked me why my flock() solution is the best. It's just because the other answer will not reliably make sure that task.php runs once. This is because the race condition I've mentioned in the comments below that answer.

提交回复
热议问题