views:

282

answers:

6

How can a PHP script start another PHP script, and then exit, leaving the other script running?

Also, is there any way for the 2nd script to inform the PHP script when it reaches a particular line?

A: 

Would:

include 'otherfile.php';
die();

do the trick?

Ray Hidayat
-1 PHP's `include` and `require` statements are synchronous
Justin Johnson
What if the 2nd PHP is set to ignore_user_abort = TRUE ??
Jenko
I don't believe `die();` counts as a "client disconnect" as specified here: http://www.php.net/manual/en/function.ignore-user-abort.php
Justin Johnson
I know they're synchronous and don't perform a client disconnect, but he didn't mention he wanted those, otherwise I wouldn't have posted this. If you call ignore_user_abort(true), this code would do exactly what you wanted it to except it wouldn't disconnect the client.
Ray Hidayat
+1  A: 

Here's a shot in the dark: you could try using php's OS execution functions with &.

exec("./somescript.php &");

Additionally, if that doesn't work, you can try

exec("nohup ./somescript.php &");

Edit: nohup is a POSIX command to ignore the HUP (hangup) signal, enabling the command to keep running after the user who issues the command has logged out. The HUP (hangup) signal is by convention the way a terminal warns depending processes of logout.

Justin Johnson
`nohup`?? What does that do?
Jenko
+1  A: 

Would pcntl_fork() do something similar to what you're ultimately trying to accomplish? http://www.php.net/manual/en/function.pcntl-fork.php

jlleblanc
+2  A: 

You can effectively achieve this by forking and then calling include or require.

parent.php:

<?php

    $pid = pcntl_fork();
    if ($pid == -1) {
        die("couldn't fork");
    } else if ($pid) { // parent script
        echo "Parent waiting at " . date("H:i:s") . "\n";
        pcntl_wait($status);
        echo "Parent done at " . date("H:i:s") . "\n";
    } else {
        // child script
        echo "Sleeper started at " . date("H:i:s") . "\n";
        include('sleeper.php');
        echo "Sleeper done at " . date("H:i:s") . "\n";
    }

?>

sleeper.php:

<?php
sleep(3);
?>

Output:

$ php parent.php
Sleeper started at 01:22:02
Parent waiting at 01:22:02
Sleeper done at 01:22:05
Parent done at 01:22:05

However, forking does not inherently allow any inter-process communication, so you'd have to find some other way to inform the parent that the child has reached the specific line, like you asked in the question.

Mark Rushakoff
+1 I don't think that's exactly what he wanted, but still very useful
Justin Johnson
+4  A: 

Here's how to do it. You tell the browser to read in the first N characters of output and then close the connection, while your script keeps running until it's done.

<?php
ob_end_clean();
header("Connection: close");
ignore_user_abort(); // optional
ob_start();
echo ('Text the user will see');
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush();     // Will not work
flush();            // Unless both are called !

// At this point, the browser has closed connection to the web server

// Do processing here
include('other_script.php');

echo('Text user will never see');
?>
Milan Babuškov
Can you explain your code a little bit? For example, the flushing tricks the browser into thinking the HTTP request is over?
Jenko
Yea, it works! Please also post this answer to this question -- http://stackoverflow.com/questions/1436575/can-a-php-script-trick-the-browser-into-thinking-the-http-request-is-over
Jenko
I edited the answer to explain it a little bit.
Milan Babuškov
This code is straight from the php manual. http://www.php.net/manual/en/features.connection-handling.php#71172
GZipp
Yes, GZipp, I know. Actually, this code was written somewhere around 2004 on some mailing list, and I just kept it in a .txt file until now. Obviously someone took it and copy/pasted to comments section of PHP manual as well. In any case, I don't claim I wrote it - just that this is the way to solve this problem.
Milan Babuškov
A: 

If you don't want to build the pcntl extension, then a good alternative is to use proc_open().

http://www.php.net/manual/en/function.proc-open.php

Use that together with stream_select() so your PHP process can sleep until something happens with the child process you created.

That will effectively create a process in the background, without blocking the parent PHP process. You PHP can read and write to STDIN, STDOUT, STDERR.

To make the browser complete loading (stop the load progress indicator) then you can use what Milan Babuškov mentioned.

The key to making the browser think the HTTP request is complete, is to send it the content length. To do this you can start buffering the request, then flush it after you send the Content-Length header.

eg:

<?php

ob_start();

// render the HTML page and/or process stuff

header('Content-Length: '.ob_get_length());
ob_flush();
flush();

// can do more processing

?>
bucabay