i wanted to break down a process (loop) into some parts, for example if have 128 emails to send :
function subs_emails(){
$subscribers = //find subscribers
if(!empty($subscribers )){
foreach($subscribers as $i => $subscriber){
sendEmail($subscriber->id);
if($i % 15 == 0){ //<-- send email per 15
sleep(60); //to pause the process for 60 seconds
}
}
return true;
}else{
return false;
}
}
will this works ?? or is there any other "better approach" solution ?? need advice please
thanks
The usual approach would be to send only a few emails at once and mark the sent ones on the background(via database flag sent=1 for example)
Then call the script every few minutes via a cronjob
This way you dont run into problems with php timeouts when sending emails to a lot of subscribers
sleep() will cause the script to stall for the defined number of seconds (60 in your example). It won't really be breaking the loop but instead merely delaying it.
A possible solution is to make a note of which subscriber has already been sent an email. Then you can have your script execute at regular intervals via cron and only load a small amount of those who have not yet been sent an email. For example:
Script executes every 10mins
Load 15 subscribers who have not been flagged as already notified
Loop through all 15 loaded subscribers and send each an email
Set flag on all 15 to say they have been sent the email
Script will then run 10mins later to process the next 15 subscribers
Related
I am trying to send out a bigger amount of emails using PHP in a symfony 3.4 system. Now the script stops due to php time execution limit, which of course I need to work around.
My idea is to put all emails to sent in a table email_queue and then send them after they are saved in the queue.
So I am saving all these emails, displaying a page with a progress bar and making an ajax call which should send emails out up to under the time limit, report back how many it sent out (to update the progress bar) and if something is left in the queue, the scipt is called again till everything is sent.
So far so good. Now I try to measure the execution time of the ajax script part to stop sending before I reach the 30 seconds time limit.
Now for some reason the measurement is always much smaller then the 30 seconds, but before the measured execution time even reaches something like 2 seconds, the script stops due to reaching the time limit. Why is that so?
This is how I do my measurement:
$executionTimes = [];
$executionPoints = [];
$timeStart = $_SERVER['REQUEST_TIME_FLOAT'];
foreach ($mailQueue as $mail) {
// do some mail configuration
// send the email
$now = microtime(true);
$executionTime = $now - $timeStart;
$executionTimes[] = $executionTime;
$executionPoints[] = $now;
if ($executionTime >= 10) {
break;
}
}
// return data
Using $_SERVER['REQUEST_TIME_FLOAT'] I thought I'd get the real execution time including all the symfony overhead. Is that not correct?
Here is the result of one run:
{
"timeStart": 1525702394.248,
"executionPoints": [
1525702394.863065,
1525702394.866609,
1525702394.870812,
1525702394.874702,
1525702394.878718,
1525702394.882434,
1525702394.886418,
1525702394.890428,
1525702394.894365,
1525702394.899119
],
"executionTimes": [
0.6150650978088379,
0.6186091899871826,
0.622812032699585,
0.626702070236206,
0.6307179927825928,
0.6344339847564697,
0.6384181976318359,
0.6424281597137451,
0.6463651657104492,
0.6511189937591553
]
}
So the last execution measurement was under 0.7 seconds, but there the script execution stoped.
I did a lot of research already but I just can't figure out why my script is doing things it shouldn't. Any ideas would be highly appreciated.
I searched around but i couldn't find a solution other than set_time_limit(0) which won't work on most of the shared hosting around.
Basically i have a script that send messages to my user's friends when they want. Some of my users have +4000 friends and the script gets into trouble.
Currently im calling this script in the background with AJAX. As i don't need/want the user to wait until this finish i would love to have some kind of background proccesing.
My current code:
global $client, $emails, $subject, $message;
_info("got on_auth_success cb, jid ".$client->full_jid->to_string());
$client->set_status("available!", "dnd", 10);
set_time_limit(60*10);
if( count($emails) < 40 ){
foreach( $emails as $email )
{
$msg = new XMPPMsg(array('to'=>'-'.$email.'#chat.facebook.com'), $message);
$client->send($msg);
sleep(1);
}
}
else
{
$counter = 0;
//Lets create batches
foreach( $emails as $email )
{
$counter++;
$msg = new XMPPMsg(array('to'=>'-'.$email.'#chat.facebook.com'), $message);
$client->send($msg);
sleep(1);
if( $counter == 50 )
{
sleep(10);
$counter = 0;
}
}
}
$client->send_end_stream();
Would be a good solution to use exec ? like for example
exec("doTask.php $arg1 $arg2 $arg3 >/dev/null 2>&1 &");
I need a solution that works on most of the hosting as this is a wordpress plugin that can be installed on any host. Thanks!
It would be ideal if you put this into some sort of cron. Build a list of emails to send and store them in a queue and have a cron script of some sort process that queue. Wordpress does have it's own cron mechanism (see wp_cron), but it might be difficult on low-traffic sites to run frequently enough to send that number of emails. Granted, you could use cron proper to make sure wp_cron is run. You should see How to execute a large PHP Script? as it's very related.
If you really want it to be synchronous, since you are using background ajax you could also just make a number of smaller ajax calls. For example, you could make your ajax script perform 20 emails at a time, and then return the number of emails remaining. Then your client code on the browser knows there's still some left and calls the background php via ajax again, perhaps with a counter indicating the current position. [Edit: But this is reliant on your users being patient enough to keep their browsers open to complete the send, and they may well need to have relatively stable connection to the internet.]
In PHP, I want to put a number of second delay on each iteration of the loop.
for ($i=0; $i <= 10; $i++) {
$file_exists=file_exists($location.$filename);
if($file_exists) {
break;
}
//sleep for 3 seconds
}
How can I do this?
Use PHP sleep() function. http://php.net/manual/en/function.sleep.php
This stops execution of next loop for the given number of seconds. So something like this
for ($i=0; $i <= 10; $i++) {
$file_exists=file_exists($location.$filename);
if($file_exists) {
break;
}
sleep(3); // this should halt for 3 seconds for every loop
}
I see what you are doing... your delaying a script to constantly check for a file on the filesystem (one that is being uploaded or being written by another script I assume). This is a BAD way to do it.
Your script will run slowly. Choking the server if several users are running that script.
Your server may timeout for some users.
HDD access is a costly resource.
There are better ways to do this.
You could use Ajax. And use a timeout to call your PHP script every few seconds. This will avoid the slow script loading. And also you can keep doing it constantly (the current for loop will only run for 33 seconds and then stop).
You can use a database. In some cases database access is faster than HDD access. Especially with views and caching. The script creating the file/uploading the file can set a flag in a table (i.e. file_exists) and then you can have a script that checks that field in your database.
You can use sleep(3) which sleeps the thread for 3 seconds.
Correction sleep method in php are in seconds.
Hare are two ways to sleep php script for some period of time. When you have your code and want to pause script working for some time use these functions.
In these examples the first part of code will be done on script run and the second part of code will be done but with time delay.
Using sleep() function you can define sleep time in seconds.
Example:
echo "Message 1";
// The first part of code.
$timeInSeconds = 3;
sleep($timeInSeconds);
// The second part of code.
echo "Message 2";
This way it is possible to sleep php script for 3 seconds. Using this function you can sleep script for whole number (integer) of seconds.
Using usleep() function you can define sleep time in microseconds. This sleep time is convenient for intervals that require more precise time than one second.
Example:
echo "Message 1";
// The first part of code.
$timeInMicroSeconds = 2487147;
usleep($timeInMicroSeconds);
// The second part of code.
echo "Message 2";
You can use this function if you want to sleep php for smaller time values than second (float). In this example I have put script to sleep for 2.487147 seconds.
Have you considered using a PHP Daemon script using supervisorD. I use it in multiple tasks that are required to be running all the time.
The catch is making sure that each time you are running your script you check for memory resources. If its too high, stop the process and then let it restart itself up again.
I have successfully used this process to be always checking database records for tasks to process.
It might be overkill but worth considering.
I wanted to execute a bunch of code for 5 seconds and if it has not finished executing within the specificed time frame I need to execute another piece of code..
Whether it's possible?
Ex..
There are two functions A and B
If A takes more than 30 seconds to execute the control should pass on to B
During function A you could periodically check how long the script has been executing, and if it goes over x seconds, run B:
function checkTime($start) {
$current = time();
$secondsToExecute = 5;
if (($start+$secondsToExecute) <= $current) {
func_b();
}
}
function func_a($start) {
// do some code
checkTime($start);
// do some code
checkTime($start);
// do some code
}
function func_b() {
// do something else
exit();
}
func_a(time());
http://php.net/manual/en/features.connection-handling.php
Set a time limit and a shutdown function, which checks if the status is 2 (timeout) and does your stuff if so.
One thing to note is that the time limit set this way only counts actual php processing time. Time spent with php waiting for another process or a database or http connection, etc, will not count and your time limit will not be considered reached.
If you need to count actual time that passed, even if it was not php processing time, you're going to have to go with the above suggested answer. Manually inserting that time check in places where it makes sense is the best, i.e. inside loops that you know may run too long, maybe even not on every iteration but on every N iterations, etc. Alternatively a more general approach is to use register_tick_function(), but that might lead to a noticeable performance hit with a low tick count, and you must take care to unregister it or use appropriate flags so you don't end up infinitely starting more and more calls to your timeout handling code once the timeout has happened.
Other approaches are also possible, you can register a handler for some signal using pcntl_signal() and have it sent to your process when the time limit is reached by an outside program ('man timeout' if you are on a linux box) or by a fork()-ed instance of your own php script, etc.
Forgive me for this noob question, but is there such a setting that sets a certain amount of time (e.g. milli/seconds) that has to pass in between sending emails through a script? How is that setting called and where do I change that setting?
I'll give an example:
I used to have a PHP script that sends emails like so:
for ($i=0; $i<count($emails); $i++) {
mail($email[$i],'test','test');
}
It turned out that not all emails were sent successfully because the script ran so fast that there was not enough time in between sending emails that was required by the server.
Did I make sense?
You can use one of these functions to do nothing for a while :
sleep() : Delays the program execution for the given number of seconds.
usleep() : Delays program execution for the given number of micro seconds.
Putting one of those in your loop should help.
For example :
for ($i=0; $i<count($emails); $i++) {
mail($email[$i],'test','test');
usleep(100*1000); // 100 milli-seconds
}
This script is untested but the theory is sound. Upon each send mail, check with a delay sequence to see if the mail has sent. I've set a limit to ensure the script does not fail - with the print, the execution time shouldn't be a problem.
for ($i=0; $i<count($emails); $i++) {
$sent = mail($email[$i],'test','test');
$count = 0;
while($sent == false) {
usleep(500); // half a second - test this number until the minimum is found
$count++;
if($count == 1000) {
echo "Email to " . $email[$i] . " failed due to timeout</br>";
break;
}
}
}
does that help?