Close a Connection, but continue to do Processing - php

I have a PHP site that performs Cron's that are triggered during client executions instead of a Cron manager. One of the Cron's that are performed takes a few seconds to execute, and it keeps the connection between the Client and the Server open until it is complete. Although I know I can set up a Cron to be fired from the Server instead of during Client runs, I would like to know if it is possible without following that format.
So, can the PHP script send a command to Apache (or whatever server it is hosted on) to close the connection between the Client and the Server, but continue to functions (so, without exiting)?

This works on Apache (and apparently not on IIS with FastCGI)
<?php
ignore_user_abort(true); // make sure PHP doesn't stop when the connection closes
// fire and forget - do lots of stuff so the connection actually closes
header("Content-Length: 0");
header("Connection: Close");
flush();
session_write_close(); // if you have a session
do_processing();
// don't forget to `set_time_limit` if your process takes a while

You can use shell to call the PHP binary... example:
shell("php -f /path/to/cronfile.php > /dev/null 2>/dev/null &"); which will run and not wait for a return.
See Asynchronous shell exec in PHP

Typically There is two forms of execution.
Client sided:
The client will remain on the page and the page will continue to process the commands given until completion.
Server Sided:
The client will navigate to a page & You make a switch in the database:
UPDATE Table SET PendinCron=1 WHERE IdentiferCol=$IdentiferData
Then you will have a Cronjob being run at X interval and will only process when the PendinCron is equal to one, if it is not. The Cron will not execute the required task.

Related

shell_exec in php stil working after disconect?

How does the shell_exec work in php? when I disconect the browser connection to the server, will the shell_exec continue working?
The php process will be terminated when the client closes the connection unless you use ignore_user_abort(1);
Also, the process needs to send something to the client to check if he is still connected. A process that has no output may continue to run after the client disconnects.
To execute a command in the background and keep it running after the php process ends, use something like this:
exec($cmd.' > /dev/null &');

Run script on other Server

I have 2 websites, hosted on 2 different servers. They are kind of interlinked. Sometimes I just do stuff on Website-1 and run a script on Website-2. Like I edited something on Website-1 and now I want to run a script on Website-2 to update accordingly on it's server.
Till now I am using following code on website 1.
$file = file_get_contents('Website-2/update.php');
But the problem with this is that my Website-1 server script stops running and wait for the file to return some data. And I don't wanna do anything with that data. I just wanted to run the script.
Is there a way where I can do this in a better way or tell PHP to move to next line of code.
If you want to call the second site without making your user wait for a response,
I would recommend using a message queue.
Site 1 request would put a message to the queue.
Cron job to check queue and run update on site 2 when message exists.
Common queues apps to look at:
[https://aws.amazon.com/sqs/?nc2=h_m1][1]
[https://beanstalkd.github.io/][2]
[https://www.iron.io/mq][3]
[1]: https://aws.amazon.com/sqs/?nc2=h_m1
[2]: https://beanstalkd.github.io/
[3]: https://www.iron.io/mq
What you're trying to achieve is called a web hook and should be implemented with proper authentication, so that not anybody can execute your scripts at any time and overload your server.
On server 2 you need to execute your script asynchronously via workers, threads, message queues or similar.
You can also run the asynchronous command on your server 1. There are many ways to achieve this. Here are some links with more on this.
(Async curl request in PHP)
(https://segment.com/blog/how-to-make-async-requests-in-php/)
Call your remote server as normal. But, In the PHP script you normally call, Take all the functionality and put it in a third script. Then from the old script call the new one with (on Linux)
exec('php -f "{path to new script}.php" $args > /dev/null &');
The & at the end makes this a background or non-blocking call. Because you call it from the remote sever you don't have to change anything on the calling server. The php -f runs a php file. The > /dev/null sends the output from that file to the garbage.
On windows you can use COM and WScript.Shell to do the same thing
$WshShell = new \COM('WScript.Shell');
$oExec = $WshShell->Run('cmd /C php {path to new script}.php', 0, false);
You may want to use escapeshellarg on the filename and any arguments supplied.
So it will look like this
Server1 calls Server2
Script that was called (on Server2) runs exec and kicks off a background job (Server2) then exits
Server1 continues as normal
Server2 continues the background process
So using your example instead of calling:
file_get_contents('Website-2/update.php');
You will call
file_get_contents('Website-2/update_kickstart.php');
In update_kickstart.php put this code
<?php
exec('php -f "{path}update.php" > /dev/null &');
Which will run update.php as a separate background (non-blocking) call. Because it's non-blocking update_kickstart.php will finish and return to searver1 which can go about it's business and update.php will run on server2 independantly
Simple...
The last note is that file_get_contents is a poor choice. I would use SSH and probably PHPSecLib2.0 to connect to server2 and run the exec command directly with a user that has access only to that file(Chroot it or something similar). As it is anyone can call that file and run it. With it behind a SSH login it's protected, with it Chrooted that "special" user can only run that one file.

Execute PHP and let Apache do the rest in the background

This might be a very stupid question so please bear with me.
I have a a php script that makes API calls to Shopify.
The entire point of this php script is to print out statements for each customer.
Now it has to run through about 200 customers.
This entire process takes about 15 minutes.
Ordinarily this runs on a monthly basis with a cron job.
But I need to be able to run it manually as well. I just want the page to execute and do everything in the background with my browser or internet connection playing NO role as to whether the complete execution completes.
The cron job runs header_php.php?run=monthly
Is there anyway I can run it manually, make sure it gets a 200 response from the page, and then close my browser tab and ensure that apache does the rest?
I would be executing it via an AJAX call as well.
Another thing, once each statement is done being processed, the script outputs it to pdf and emails it to the customer. So there's no feedback required from the page when it runs.
Easily doable with simple HTTP headers.
Start output buffering
Output the response, if any
Send Content-length and Connection: close headers
Flush and end output buffers
The browser receives HTTP response
Continue time consuming processing
This SO anwer nails it (the comments are helpful as well).
You can call your script with other script that runs in background the job.
shell_exec('nohup /usr/bin/php /dir/to/your/script.php > /dev/null 2>/dev/null &');
Then you don't need to wait to finish the job
on linux
<?php
exec(''.$command.' > /dev/null 2>&1 &');
?>
on windows
<?php
$shell = new COM("WScript.Shell");
$shell->run($command, 0, false);
?>
where command would be something like
php -f $path/to/filename
if you put that in a page, you can then call it whenever you want and it will spawn a thread that will call apache, but not require the browser to wait for any response.

PHP shell_exec - how to terminate the process if the connection is closed?

I have a working php script using shell_exec to execute an external program that take about 30 seconds to complete.
Problem is that if an user close the browser or the connection is closed for some reason the program executed with shell_exec continue to run for nothing, since it's output can't be sent to the user anymore.
Is there a way to kill this process as soon as the connection is closed?
Thank you for your help.
Finally I've solved it!
I've used the PHP function connection_aborted() to check if the client is disconnected.
http://us2.php.net/manual/en/function.connection-aborted.php
The algorithm is the following (pseudocode)
shell_exec("script");//script is a shell script that launch the program in another thread thus this return immediately
$task_completed=false;
while(!$task_completed){
if(connection_aborted()==1){
kill_the_process();//this will kill the running process launched by script
die();
}
$task_completed=...;//true if the task completed
sleep(1);
}
You could make a manager that runs/executes/stops the running php scripts. This script would run constantly -maybe as daemon- and check the status of the script/process, the user status and close the script if the user is gone. I think you could do this well with Sockets.
How to Use Sockets in JavaScript\HTML?
http://php.net/manual/en/book.sockets.php
you can use to get the list of all running process
$res = shell_exec('ps -x');
$res = explode("\n",$res);
print_r($res);
and use this to kill process with given ProcessID
shell_exec('kill -KILL ProcessID');

Will a script continue to run even after closing a page?

if i call a php file via jquery ajax, that contains a script to do some stuff that takes a while — for instance uploading a big video — and then I close the page: does the php script keep loading the video or not?
See here:
http://php.net/manual/en/function.ignore-user-abort.php
int ignore_user_abort ([ bool $value ] )
Sets whether a client disconnect should cause a script to be aborted.
When running PHP as a command line script, and the script's tty goes away without the script being terminated then the script will die the next time it tries to write anything, unless value is set to TRUE
There also is a PHP configuration option of the same name:
http://php.net/manual/en/misc.configuration.php
By default, if you do nothing, according to the PHP manual the default is to abort the script.
http://php.net/manual/en/features.connection-handling.php
NECESSARY UPDATE
It seems I (unknowingly) tricked my way to "reputation points", because I did NOT supply the (correct) answer, but here it is now thanks to testing and continued nudging from "mellamokb":
Quote:
"Ok, I took a look at the PHP source code and, if I didn't miss anything, I now have the answer. The "ignore_user_abort" flag is only checked when PHP receive an error trying to output something to the user. So, in my understanding, there is no way to interrupt code which doesn't produce any output."
Okay, I wasn't totally off, but it is important to know that it all depends on whether or not your script produced any output!
If you read THIS, also DO check out the comments below.
A PHP Script running through a web server will not stop until:
someone kill the server
the server kill the php scrip
When the user abort the script, PHP will continue until it try to send something back to the browser.
For example still script will continue fore ever even if the user abort:
while(true){
echo 'go'.PHP_EOL;
}
It will go on forever because the "echo", will write into the buffer, and the buffer will not be sent to the browser until the script finish, which will never happen.
The following script will stop as soon as the user abort:
while(true){
echo 'go'.PHP_EOL;
flush();
ob_flush();
}
This script will stop, because flush() and ob_flush() will force PHP to send its buffer to the browser, which will stop the PHP script if the user has aborted.
The function ignore-user-abort() will force PHP to ignore the abort in this case.
Moreover if you are using PHP session, they are another tricky situation.
For example, if you are doing AJAX, and you actually send two AJAX request to a PHP script and that PHP script has need of session with session_start().
The first AJAX query will work normally, however the second one will have to wait until the first call is finish, because the first script has a locked on the session.
The first script could eventually prematurely release the session with session_write_close();
By default no. See Connection Handling documentation, especially:
You can decide whether or not you want
a client disconnect to cause your
script to be aborted. Sometimes it is
handy to always have your scripts run
to completion even if there is no
remote browser receiving the output.
The default behaviour is however for
your script to be aborted when the
remote client disconnects.
The script will run the time set by max_execution_time (default is 30s)
Warning This function has no effect when PHP is running in safe mode.
There is no workaround other than turning off safe mode or changing
the time limit in the php.ini.
Note: The set_time_limit() function and
the configuration directive max_execution_time only affect the
execution time of the script itself. Any time spent on activity that
happens outside the execution of the script such as system calls using
system(), stream operations, database queries, etc. is not included
when determining the maximum time that the script has been running.
This is not true on Windows where the measured time is real.
quote from http://php.net/manual/en/function.set-time-limit.php
you can test this by running
<?php
unlink('cocorico.txt');
while(true){
file_put_contents('cocorico.txt', microtime(true).PHP_EOL, FILE_APPEND);
}
and it will stop after 30s (despite you close your browser or not)
you can get you default exec time by echo ini_get('max_execution_time'); and can be set like set_time_limit(3);
The answer marked as accepted is only correct about the ignore_user_abort but don't panic that your "fail" scripts will run forever if you don't set max exec time to 0 - unlimited;
From my little understanding of how these stuff works. By the point of view of the HTTP protocol I would say yes, the script would keep running, because the browser just sends a request to the server asking for the page, then the server starts executing the script and does not sends or receives information from the browser untill the script is done loading and producing the html output, and just then the server sends the resulting output to the browser and has done the job.
See, there is no way for a browser to "tell" the server that the user is not viewing the page anymore through the HTTP protocol. However, the HTTP protocol runs on top of the TCP connection through stream sockets, the TCP connection is kept alive till one of the ends choses to abort the connection (or a certain timeout is reached), now I really don't know how the browser handles this. The browser could just open a connection, send a request and close the connection, then the server waits for the script and sends the response on another connection. Or the browser could open a connection, KEEP this connection alive till the server responds on the same connection. If the thing works that way then the server would really have a way to know if the user is not viewing the page anymore simply by checking if the connection is still alive or has been shutdown by the client. So that would be a no.
Dunno much about that tho.

Categories