2

I am wanting to execute a large, database intensive script, but do not need to wait for the process to finish. I would simply like to call the script, let it run in the background and then redirect to another page.

EDIT: i am working on a local Zend community server, on Windows 7. I have access to remote linux servers where the project also resides, so i can do this on linux or windows.

i have this

public function createInstanceAction()
{
    //calls a seperate php process which creates the instance
    exec('php -f /path/to/file/createInstance.php');


    Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('adminhtml')->__('Instance creation process started. This may take up to a few minutes.'));
    $this->_redirect('instances/adminhtml_instances/');
    return;
}

this works perfectly, but the magento application hangs around for the process to finish. it does everything i expect, logging to file from time to time, and am happy with how its running. Now all i would like to do is have this script start, the controller action does not hang around, but instead redirects and thats that. from what I have learnt about exec(), you can do so by changing the way i call exec() above, to :

exec('php -f /path/to/file/createInstance.php > /dev/null 2>&1 &');

which i took from here

if i add "> /dev/null 2>&1 &" to the exec call, it doesnt wait around as expected, but it does not execute the script anymore. Could someone tell me why, and if so, tell me how i can get this to work please?

Could this be a permission related issue?

thanks

EDIT : Im assuming it would be an issue to have any output logged to file if i call the exec function with (/dev/null 2>&1 &) as that would cancel that. is that correct?

Community
  • 1
  • 1
activeDev
  • 785
  • 4
  • 11
  • 24
  • i am on windows (see edit), have attempted nohup on a development server where the project also resides. nohup runs from the command line, but again when called from php, it seems to run, but waits for completion, no matter what i have tried so far. thanks for the suggestion. – activeDev Jul 16 '12 at 06:37
  • If you are on Windows, why are you using the Unix shell escapes? `2>/dev/null` and `&`. That won't work either. – mario Jul 16 '12 at 14:37
  • thanks mario, i didnt realise they were unix shell escapes. embarassing i know, but so we learn. i have an answer for the question, now that i have worked it out. do i post my solution (on two os's) or edit my question with the answer? thanks for the help. – activeDev Jul 16 '12 at 19:31
  • Post your newest snippet as answer. Wait a day, then accept that as answer. – mario Jul 16 '12 at 19:55

5 Answers5

2

After taking time to fully understand my own question and the way it could be answered, i have prepared my solution.

Thanks to all for your suggestions, and for excusing my casual, unpreparedness when asking the question.

The answer to the above question depends on a number of things, such as the operating system you are referring to, which php modules you are running and even as far as what webserver you are running. So if i had to start the question again, the first thing i would do is state what my setup is.

I wanted to achieve this on two environments :

1.) Windows 7 running Zend server community edition. 2.) Linux (my OS is Linux odysseus 2.6.32-5-xen-amd64 #1 SMP Fri Sep 9 22:23:19 UTC 2011 x86_64)

to get this right, i wanted it to work either way when deploying to windows or linux, so i used php to determine what the operating system was.

public function createInstanceAction()
{

    //determines what operating system is Being used 
    if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN')
    {
        //This is a windows server

        //call a seperate php process to run independently from the broswer action

        pclose(popen("start php /path/to/script/script.php","r"));

    }
    else
    {
        //assuming its linux, but in fact it simply means its not windows
        // to check for linux specifically use (strtoupper(substr(PHP_OS, 0, 3)) === 'LIN')
        exec('php -f /path/to/file/script.php >/dev/null 2>&1 &');
    }
    //the browser will not hang around for this process to complete, and you can contimue with whatever actions you want.

    //myscript log any out put so i can capture info as it runs
}

In short, ask questions once you understand them. there are many ways to to achieve the above, and this is just one solution that works for my development and production environments.

thanks for the help all.

activeDev
  • 785
  • 4
  • 11
  • 24
1

PHP popen

From the docs (this should help you do other stuff, while that process is working; not sure if closing the current PHP process will kill the opened process):

/* Add redirection so we can get stderr. */
$handle = popen('/path/to/executable 2>&1', 'r');
echo "'$handle'; " . gettype($handle) . "\n";
$read = fread($handle, 2096);
echo $read;
pclose($handle);

Solution 2: Trick the browser to close the connection (assuming there is a browser involved):

ob_start();
?><html><!--example html body--></html><?php
$strContents=ob_get_clean(); 

header("Connection: Close");
header("Content-encoding: none");//doesn't work without this, I don't know why:(

ignore_user_abort(true);

header("Content-type: text/html");
header("Content-Length: ".strlen($strContents));
echo $strContents;

flush();
//at this point a real browser would close the connection and finish rendering; 
//crappy http clients like some curl implementations (and not only) would wait for the server to close the connection, then finish rendering/serving results...:(

//TODO: add long running operations here, exec, or whatever you have.
oxygen
  • 5,891
  • 6
  • 37
  • 69
  • im having no luck with this :/ – activeDev Jul 15 '12 at 17:10
  • strangely, both work for me. which one would have been a fit for your scenario, and did you get any errors? – oxygen Jul 15 '12 at 19:27
  • also, please edit your question and include the webserver and how is PHP run (mod_php, fastcgi, etc.), and your operating system (there are differences, such as on Windows `flush()` does not nothing). – oxygen Jul 15 '12 at 19:33
  • thanks for getting back to me. i have edited the question as you asked. i have tried the PHP popen solution you suggested. it runs the executable, but the browser still waits for its termination. – activeDev Jul 16 '12 at 06:44
  • Ok, so then it is rather a browser issue of waiting for a response. Please try the code "Solution 2". Although the PHP process will be waiting for the script to finish (this could be a good thing as it allows logging and locking to prevent duplicate requests), the browser will immediately receive a response, close the connection to the server and finish rendering. – oxygen Jul 16 '12 at 07:53
  • i have tried it with no success, but will work at it again now, thanks alot – activeDev Jul 16 '12 at 08:00
  • Place ob_start before any output. You need to send those headers, and headers will not be sent after any output is sent. – oxygen Jul 16 '12 at 08:05
  • Ok, now I see you edited your question. As I said above, `flush()` doesn't work on Windows (it is ignored) so `Solution 2` is not applicable (you could switch to Linux to use it). The first solution might start working on Linux, if it is not already working on Windows. – oxygen Jul 17 '12 at 14:37
  • yip, my fault for asking a bad question. i tried your suggestions with positive results and thus upvoted it, although i guess i wanted to answer my own question as constructively as possible for other users to see my mistakes and custom soluton. thanks again! – activeDev Jul 17 '12 at 15:02
  • Is the the popen solution working on Linux as well as on Windows? If yes, then this is the accepted answer. `php -f /path/to/file/script.php >/dev/null 2>&1 &` was already listed in the question, so it is not an addition to my answer, so my answer is fully copied into the answer you just posted. – oxygen Jul 17 '12 at 15:09
  • i did not manage to get it working on both. seeing that you have helped so much i will try this, and if i get it working, i will mark this as the correct answer. thanks again mate! – activeDev Jul 17 '12 at 15:15
1

You could write a wrapper-script, say createInstance.sh like

#! /bin/bash

trap "" SIGHUP
php -f "$1" > logfile.txt 2>&1 &

Then you call the script from within PHP:

exec('bash "/path/to/file/createInstance.sh"');

which should detach the new php process most instantly from the script. If that doesen't help, you might try to use SIGABRT, SIGTERM or SIGINT instead of SIGHUP, I don't know exactly which signal is sent.

PhilMasteG
  • 3,095
  • 1
  • 20
  • 27
  • does : php -f "$1" > logfile.txt 2>&1 & create a file in the same directory as the .sh script, or the root of the application? i cant seem to find it. thanks for your help. – activeDev Jul 15 '12 at 15:39
  • also, $1? should i expect the contents of logfile.txt to contain "$1". just checking thats it not some kind of paramter. inexperienced with bash to be honest! – activeDev Jul 15 '12 at 15:42
  • In `createInstance.sh` the `$1` is the first parameter. So now, that you mention it, my fault, sorry.It has to be `exec('bash "/path/to/file/createInstance.sh" "/path/to/file/createInstance.php"');` in PHP of course. `logfile.txt` is usually written to the directory the calling php script lies. But you can instead also use `/path/to/log/log.txt` – PhilMasteG Jul 15 '12 at 16:11
1

I've been able to use:

shell_exec("nohup $command  > /dev/null & echo $!")

Where $command is for example:

php script.php --parameter 1

I've noticed some strange behavior with this. For example running mysql command line doesn't work, only php scripts seem to work.

Also, running cd /path/to/dir && php nohup $command ... doesn't work either, I had to chdir() within the PHP script and then run the command for it to work.

kalenjordan
  • 2,446
  • 1
  • 24
  • 38
0

The PHP executable included with Zend Server seems to be what's causing attempts to run a script in the background (using the ampersand & operator in the exec) to fail.

We tested this using our standard PHP executable and it worked fine. It's something to do with the version shipped with Zend Server though our limited attempts to figure out what that was going on have not turned anything up.

CVEEP
  • 441
  • 4
  • 12