skip to Main Content

Edit: See my answer to this post for the solution to this particular problem; I also removed parts of the question which were no longer relevant to the solution.

I’m new to PHP and development in general and I’m asking, because I haven’t found a working solution for my problem yet.

As part of a project I am trying to run a batch script. The batch script creates some analysis files and crucially compiles and starts 2 executables I need to keep running for later.

To start the script, I compose a call $cmd where I set 2 environment variables and the working directory and add some necessary parameters that the script needs in order to start.

I then run

exec($cmd);

This call produces all files in the working directory, and starts the processes, however the batch script now waits for the executable processes to finish, as it cleans up some temporary files after the programs are finished. At this point the PHP program stops executing and waits for the batch script to finish, which it doesn’t do.

Over the course of the last 2 days I have tried multiple solutions proposed here

or

exec($cmd.' > NUL 2>&1 &')

and

pclose(popen("start /B " . $cmd . " 1> ".$scrptDir." 2>&1 &", "r"));

where $scrptDir is the working directory I described earlier.

I have not found a working solution, at best the script doesn’t start at all and at worst it produces a syntax error when making an AJAX call, which admittedly I have not yet understood why exactly it does that, as AJAX was implemented by a predecessor and left pretty undocumented.

Edit: The syntax errors were caused by myself, because I forgot the semicolon.

This is the PHP code:

    public function runScript() {
        //-- compose the command line to start script 
        $cmd = $this->getCall();

        //-- execute the script
        exec($cmd);        
      
    }
} 

Updates:
Starting the script via

pclose(popen("start /B " . $cmd . " 1> NUL 2>&1 &", "r"));

or

popen("start /B " . $cmd . " 1> NUL 2>&1 &", "r");

or

exec('nohup ' . $cmd . ' 2>&1 &');

has the same outcome: the script runs, but the executables close and the script performs the cleanup.

Using

exec($cmd.' > NUL 2>&1 &');

does not have different behaviour from

exec($cmd);

2

Answers


  1. Chosen as BEST ANSWER

    Ok, I found a workaround.

    Instead of

    exec($cmd)
    

    I used

    $filename = "help.bat";
    $file = fopen($filename,'a+');
    fwrite($file, $cmd);
    fclose($file);
    pclose(popen("start /B ". $filename, "r"));
    

    I wrote the composed command $cmd into a batch file and then executed that batch file instead. I think the reason why it is working now is that my composed call was a bit too complex for me, so I tried to make it simpler.


  2. This is one way to do it. Please be advised that this is not the best way to go about it, especially if you need to do something with the results of an AJAX call.

    jQuery

    Let’s say that you initiate the AJAX request like this:

    $(document).ready(function() {
        $.ajax({
            type: 'POST',
            url: 'myscript.php',
            data: {'val1':val1,'val2':val2,....},
            timeout: 100,
            success: function(data) {
                // whatever it is you need to do on success, although you could leave it empty
            },
            error: function (xmlhttprequest, textstatus, message, desc, err) {
                if(textstatus === "timeout") {
                    // do something else
                } else {
                    // cover the case of an error which is not a timeout
                }
            }
        });
    });
    

    The key part here is the timeout. This will abort the pending AJAX, meaning that the JS will not wait for the response, but the server-side script called with it will still execute.


    Vanilla Javascript

    If you’re not using jQuery, you could do the same thing in vanilla Javascript, like this.

    var xmlHttp = new XMLHttpRequest();
    xmlHttp.open("POST", "myscript.php", true);
    
    // Post the request with your data
    xmlHttp.send(
        JSON.stringify({
            val1 : val1,
            val2 : val2,
            ... etc
        })
    );
    
    xmlHttp.onload = function(e) {
        // Check if the request was a success
        if (this.readyState === XMLHttpRequest.DONE && (this.status === 201 || this.staus === 200)) {
            clearTimeout(xmlHttpTimeout); 
            // do whatever it is you do on successful response
        }
    }
    
    // Timeout to abort in 100 miliseconds
    var xmlHttpTimeout = setTimeout(ajaxTimeout,100);
    function ajaxTimeout(){
       xmlHttp.abort();
       // do what you would normally do once initiating the AJAX
    }
    

    If you’re unsure whether the AJAX request is reaching the backend, and there are no console errors or network errors, you can do the following.

    First, create a script called test.php (or give it another name, if you already have a script called like that). Then, inside that script, place the following.

    <?php
    
    sleep(15);
    $filename = "zztest.txt";
    $file = fopen($filename,'a+');
    
    fwrite($file, date("Y-m-d H:i:s"));
    sleep(5);
    fwrite($file, PHP_EOL.date("Y-m-d H:i:s").PHP_EOL."********".PHP_EOL);
    fclose($file);
    
    ?>
    

    What this will do is:

    1. Upon initialization, wait for 15 seconds (that’s what sleep(15) does)
    2. Create (or open, if it already exists) a *.txt file, and write the current date and time to it
    3. Keep the file open – notice that there is no fclose(...) after fwrite(…). This is generally not good practice, because something might break the script execution, and the file would remain locked, but it’s alright in this case, because it’s for testing purposes
    4. Once the date and time have been written to the text file, wait for another 5 seconds (sleep(5)), and then write the current date and time to it. PHP_EOL is the newline character
    5. Finally, close the file

    The next step is to replace myscript.php call inside your AJAX, so that it calls test.php. The rest of your AJAX setup can remain the same.

    Then, execute AJAX as if you would normally. Wait for about 20 seconds, and check if a file called zztest.txt has been created, and if it has, what its contents are.

    If it hasn’t, then the problem is in the front-end (the AJAX call).

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search