I'm trying to create a PHP daemon that connects to an IMAP server and processes emails as they come in. I have it close to working, but the daemon keeps grabbing the original emails that it finds the first time the daemon is loaded. I believe the reason is because I'm opening the IMAP connection in the parent process. Example below:
if ($imapConnection=imap_open($authhost,$user,$pass) or die())
{
//start daemon
while()
{
//Grab email headers
$imapHeaders = imap_headers($imapConnection);
$count = sizeof($imapHeaders)
//loop the emails
for($i = 1; $i <= $count, $i++)
{
//process the email
//delete the email
}
System_Daemon::iterate(15);
}
}
imap_close($imapConnection);
I'd like to stay away from putting the IMAP connection within the loop. How can I keep the connection to the IMAP server outside of the loop and still get new emails?
In IMAP, mails stay on the server. So each time you come, if you have not explicitly removed them, old emails are still there. To prevent processing these emails, you could have a var that keeps the amount of mails you treated before, so you could go from $i = 0 (supposed the last arrived) to $i < $var where $var stands for the number of mails already treated.
EDIT :
Since you delete the mail by imap_delete, do an imap_expunge at each loop.
EDIT 2 :
Use imap_reopen, I tried you script on my server using imap_reopen($imapConnection, "{domain.tld}INBOX"); after each loop and it sees the new mail now. Does not do a new authentication, just move your stream.
Related
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.]
I would like to be able to get an array of emails and make sure each email is sent. (e.g. Array > send each email > result) I kind of changed the question here because this is more important, plus I have added a 50 rep. point. Codewise how can I do this?
Apart from still using the mail() function, you probably want to setup a cron job for sending out the mails. For spooling mail send jobs use a separate database table. Or if it's about some sort of mailing list functionality, then a simple recipient list will do.
If you just want to send out a bunch of the same email at once, you
could call implode() on your array of emails to turn it into a string:
$to_string = implode(', ', $to_array);
Or, if you want to try something more complicated, you could use a
foreach loop to cycle through each email and to keep track of
successes and failures:
$success = array();
$failure = array();
foreach ($to_array as $to_email)
{
if (mail($to_email, $subject, $message, ...))
$success[] = $to_email;
else
$failure[] = $to_email;
}
I'm guessing your original question had to do with sending out all these
emails every day or something without necessitating you hitting a
button. If you have ssh access, see what happens if you type:
crontab -e
If you get some sort of error, you will have to speak with your system
administrator about cron. If you get a file, then you can use cron. This
is not a part of your current question, though, so I'll leave it.
The same way. You must have code that sends an email to an email address. Whether they are on the site or not, it is the same code. You just need to know their email address.
EDIT: If you are wondering how you would trigger the email to be sent, maybe you want to schedule it using a cron job, for example send an email every day at midnight.
This says it all, really: http://php.net/manual/en/function.mail.php
You just need an outgoing mail server installed (postfix, exim, sendmail)
Easy way to send an email:
$to = "usermail#test.com";
$from = "my_email#mydomain.com";
$subject = "Hello!";
$contents = "This is an test mail!";
mail($to, $subject, $contents, "From: $from");
If you don't have access to cron jobs then you will probably struggle to run without user interaction.
A common method for dealing with this is to run on every nth page load, or every so-often. This only works if you have a site that's visited about as often as you want to send email. You'll also want to use an ACID-compliant database. Pseudo-code follows.
if (1 == rand(1,100)) { // run once every 100 page loads
$emails = get_emails_to_send();
mark_emails_as_sent($emails);
$results = send_emails($emails);
mark_failures_as_needing_to_be_sent($results);
}
Alternately, you can run it on a timer:
if (time() - get_last_time_run() > $run_at_least_once_every_this_many_seconds) {
$emails = get_emails_to_send();
mark_emails_as_sent($emails);
$results = send_emails($emails);
mark_failures_as_needing_to_be_sent($results);
}
Or you can combine both with a &&. This depends on how often your page gets hit.
If you want to send email more often than you get page hits... too bad ;). Or have an external service call the page every so often.
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
i'm using PEAR Mail_Queue, everything works great, except when i'm trying to send emails to
"bad" recipients (bad syntax like "òla#test.com", "uuu#test,com" , "test#test.com.com")
When the queue finds a bad recipient it just stops, leaving all the others mail in the db queue table...
I just want to make it jump to the next mail, deleting (or not) the bad mail in the queue table...maybe all i need is just some errors handling...
the code i'm using (if you need more code just ask :) ) :
/* How many mails could we send each time the script is called */
$max_amount_mails = 10;
$query=mysql_query("SELECT count(*) FROM mail_queue ORDER by id asc");
$num_tosend= mysql_result($query, 0, 0);
$num_mail=ceil($num_tosend/$max_amount_mails);
/* we use the db_options and mail_options from the config again */
$mail_queue =& new Mail_Queue($db_options, $mail_options);
$mail_queue->setBufferSize(10);
$contaEmailSpeditesi=0;
/* really sending the messages */
for($i=1;$i<=$num_mail;$i++){
$mail_queue->sendMailsInQueue($max_amount_mails,MAILQUEUE_START,1);
sleep(2);
}
thanks !!
You should open a bug report in the pear bug tracker.
You really should filter out the invalid email addresses before adding entries to the mail_queue table - probably not the answer you want though!
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?