I would like to automate the process of sending this email. I mean when a condition is met then the email should automatically be sent to the given email addresses which is already given as hard-coded values. So for an example in my case a Technician has to complete 40jobs a day and if he finished 35 at the end of the day an email should be sent to the supervisor(provided his email id is already given) with Subject name and the body should be like ” The Technician 1234 has completed only 35 jobs for the day instead of 40. I was wondering how can implement this as Im very new to the field of PHP. Please anyone help me out. If possible please provide me with an example.
Thanks
Dilip
You could run a cron that uses the php mail function to send a report about each programmer at a set time each day. The cron script would look something like:
<?php
$to = 'admin#mydomain.com';//the set up email to mail too
$result = mysql_query('select programmer_id, job_count, job_requirement from table'); //query the database
while($job = mysql_fetch_object($result)){
mail($to,'Job counts for ' . $job->programmer_id,"Completed $job->job_count out of $job->job_requirement jobs","FROM: jobcounter#mydomain.com");
}
Have a script check the conditions and send emails, and run it periodically: http://www.google.com/search?q=cronjob
I'm assuming here that your problem is the triggering of the event, rather than the sending of the email itself.
In the example you described, you would need to have a script run at a certain time of day that would check for any condition that would require an email sending, and then send the email. Under any unix-like system, cron is the ideal solution to this. If you are on some kind of basic shared hosting you may not be able to set this up. In that case you would need to set up a task to run on a machine that you do have control over that would call a URL that would run the PHP script. This could be a cronjob, or a Scheduled Task under Windows.
If your example was switched around so that, say, an email was to be sent as soon as a technician completed 40 jobs then you would be able to send the email as part of the script that handled the form submission from the technician whenever he completed a task.
Setup a Cron Job that runs at the "end of the day":
23 55 * * * /path/to/php /path/to/script.php
Have it run a PHP script that can query your Job Store for whatever condition you want to check. For example with Job Store being a database.
$db = new PDO(/* config */);
$result = $pdo->query(
'SELECT count(id) as "tasks_done"
FROM tasks WHERE engineer = "Tom"
AND finished_at = now()');
$result = $result->fetchAll();
if($result[0]['tasks_done'] < 40) {
mail( ... )
}
If the condition is met, send the mail. The above needs refining of course. Dont expect being able to copy/paste it.
Also see:
What is the best method for scheduled tasks in PHP
http://greenservr.com/projects/crontab/crontab.phps
you should use mail() function:
$technicianId = 1234;
$jobsNeeded = 40;
$jobsDone = getJobsDone($technicianId);
if ($jobsDone <= $jobsNeeded) {
mail('supervisor#yourcompany.com', 'Technician '.$technicianId.' slacking', 'The Technician '.$technicianId.' has completed only '.$jobsDone.' jobs for the day instead of '.$jobsNeeded);
}
Could have a cronjob that runs at the end of each day which checks what each technician has done and emails it to the manager.
Related
I have a scenario that has got me very confused and I need some other brains on this to help point me in the right direction.
I have a PHP script that I have had running for about 3 years with no issues starting to do something weird. The script's job is to pull records from a MySQL DB that contains rows of emails to send out. By rows of emails, I mean a record with a subject, body, To and From names and emails, and so on. I also have a column labeled [sent] which has a default value of 0 meaning that the email has not been sent, After a successful send, it changes the value to 1 so the main SQL call only looks for records where sent = 0. And of course, I have an ID column.
To send the email out I am using AWS SES (Simple Email Service) SDK. I am using a try/catch when sending the email to ensure to catch any errors if they happen, but for the most part, this script runs great or used to at least lol.
PHP Script
// the emails are one-to-one, meaning that for every record only one email is sent out.
// There is never a reason to send out any duplicates
$sql = "SELECT * FROM table_with_emails WHERE sent = 0";
$result = $conn->query($sql);
while ($row = $result->fetch_array()) {
// This is the record ID of each row so I can log it later in the [emails_with_or_without_errors] table.
$email_id = $row['ID'];'
try {
$result = $SesClient->sendEmail([
'Destination' => [
'ToAddresses' => $to_address_recipient_emails,
'BccAddresses' => $bcc_address_recipient_emails,
'CcAddresses' => $cc_address_recipient_emails
],
'ReplyToAddresses' => ["$from_name <$reply_to_email>"],
'Source' => $sender_email,
'Message' => [
'Body' => [
'Html' => [
'Charset' => $char_set,
'Data' => $html_body,
],
'Text' => [
'Charset' => $char_set,
'Data' => $plaintext_body,
],
],
'Subject' => [
'Charset' => $char_set,
'Data' => $subject,
],
],
]);
$messageId = $result['MessageId'];
$timestamp = time();
$ok = "Email sent successfully";
// Log the email as successful along with the row's ID, this column should never have any duplicate entries.
$sql_error = "INSERT INTO emails_with_or_without_errors (status,ok,timestamp,email_id) VALUES ('ID: $messageId','$ok','$timestamp','$email_id')";
$result_error = $conn->query($sql_error);
// After we log the transaction I then mark the row in table_with_emails `sent = 1` so that it will not choose that record again.
$sql_update = "UPDATE table_with_emails SET sent = 1 WHERE ID = '$email_id'";
$result_update = $conn->query($sql_update);
} catch (AwsException $e) {
// I catch the error and log it, but this almost never happens
}
}
What's going on?
This is where the confusion starts. This script has always run as a cronjob every minute. For some reason about 1.5 weeks ago, duplicate emails have been being sent out. I know this because A) Customers called in support telling us they are getting duplicates, and B) the emails_with_or_without_errors table column email_id also contains duplicate IDs. This should never happen since that row should immediately be updated to sent = 1.
Also how many duplicates that are sent out are random. Sometimes 2, 3, 4, and 5, but usually no more than 5. What's kind of making my head hurt is if you look at the code in the try/catch you can see that after a successful send of that email it will immediately log it but most important mark that record as sent = 1. This should be preventing duplicate emails from going out, but for some reason, after the email is sent successfully it is still able to send it out again regardless of sent = 1
Here is where it gets worse. If I instead stop the cronjob from running on the server and go to the script's URL directly and run it manually from my browser every minute it runs absolutely fine. No duplicates ever!
This only happens when I run it as a cronjob
So the first thing I did was
Checked to see if there is more than one instance of cron running.
Nope, just one.
I restarted the server to see if that fixes it, Nope not that.
I thought to myself "Maybe there is a delay in writing to the table_with_emails table sent = 1. That would make sense to the random amount of duplicates going out. I can see this happening if the loop tries to send the next email, but if there is a delay to writing sent = 1 it would keep sending out the same email again until the row is updated, but this does not make sense because if that was the case then it would do it whether I run it manually or as a cronjob so that can't be it.
I also confirmed that AWS SES is not sending out the same email several times because when I log the response ID from AWS they are all unique. That tells me it is sending out separate emails and not duplicates.
Final Thoughts
Why does the script run fine when it is run manually from a browser,
but not as a cronjob?
How in the world can that record be sent out with duplicates when directly after the email is sent out successfully it
should be updating the record as sent = 1 preventing the main SQL
statement from retrieving it again?
That's what I got, I really don't think my code is the issue and there is something else outside the box I am not seeing and I haven't touched that script in a few years, something else changed somewhere.
Can anyone give me ideas on where to look? and thanks in advance.
I have had this issue and this happens when cron job triggers again while the previous one is in progress. I pulled my hairs figuring out the issue as situation was same running cron job from browser just worked fine.
Solution:
I created another script which have following code and it triggers the cron job code making sure that now instance of php-cli is running
$pid = shell_exec("ps -A | grep php-cli | awk '{print $1}'");
if(empty($pid)) {
shell_exec("php-cli ~path-to-my-script/cron.php >> path-to-cronfolder/cron/err.txt 2>&1 & echo $!");
}
save it to file and add this file to cron job
Additionally since then I have design change to my packages so that I don't need to run the cron job every 5 minutes or so by including the above code where ever email cron needs to be run. so there is no pileup of any emails in db they are sent with user actions as following
1- user completes actions on ui and post data to server
2- server process the data, do the need full create entry in alert table ready to send
3- finally include the file with above code which triggers the cron script immediately and send out the email instantly
as backup I do run cron job every 30 minutes so if an email was missed for any reason its sent out (this has not happened yet).
$i = 1;
foreach ($recipients as $email => $name) {
$mail->ClearAddresses();
$mail->AddBCC($email, $name);
if (!$mail->send()) {
$send = 0;
} else {
$send = 1;
}
$query = "INSERT INTO `newsletter_send`(`email`, `id_newsletter`, `date`, `send`) VALUES ('$email',$id_newsletter, NOW(),$send) ";
$stmt = $link->prepare($query) or die('error');
$stmt->execute();
$mail->clearAllRecipients();
if (($i % 100) == 0) {
sleep(60);
}
$i++;
}
What is the best way to send a large emails without sleep() and without to wait the page to finish loading? In addition to the cron job you have other ideas ?
EDIT: I have 680 users who will receive the email, but after a while I get 500 Internal Server Error.. why? It maybe time_limit?
Message queues.
beanstalkd is a good solution.
You can then use a SDK like pheanstalk to handle the queue and its jobs.
EDIT: If you have restricted access to your server (for example, if you are using a shared hosting) message queues as a service are also an option.
IronMQ
CloudAMQP
AWS (Amazon Web Services) SQS
A good way to send a large amount of emails at a fast pace is to have a lot of worker scripts doing the job instead of 1 php page (GiamPy gave a good example for one of the ways that can be done and I won't mention it since I don't want to be redundant).
One simple (though somewhat hacky) option is: for you to make 20 php scripts in a file. You could name them mailer1.php, mailer1.php, ..., mailer20.php. Then, you could create a folder called mail and put two files inside:
mail/config.txt
and
mail/email.txt
Inside mail/config.txt, you would include the following lines of text:
T
15
where the first line has a T for TRUE meaning you want the mailers to send the mail out as fast as they can in intervals of 15 seconds each. You can obviously change the interval time as well to whatever you like.
And in mail/email.txt you would have the complete email you want to send
After having done all that you make the mailer files. You can make 1 first, write the code, and then copy paste it 19 times to have 20 scripts in total. The code inside could look something like this:
<?php
$pathconfig = "mail/config.txt";
$pathemail = "mail/email.txt";
$email = file_get_contents($pathemail);//now you have the email saved
$filehandleconfig = fopen($pathconfig, "r");
$bool = trim(fgets($pathconfig));
$sleeptime = (integer) trim(fgets($pathconfig));
fclose($filehandleconfig);
while ($bool === 'T')
{
//... code that sends the email
//recheck if 'T' is still 'T':
$filehandleconfig = fopen($pathconfig, "r");
$bool = trim(fgets($pathconfig));
fclose($filehandleconfig);
sleep($sleeptime);
}
?>
So what the previous code would basically do is extract the email that needs to be sent at the beginning, and also extract the time it will sleep after sending an email, and if it should continue to send emails.
What that means is that the mail/config.txt file is your controlpanel, and if you change 'T' to be anything else that 'T' (like 'F' for instance), then all the scripts will terminate.
The downside to this option is that it's a bit hacky, though the upside is that it can be developed in a matter of minutes.
This is more of a logic question than language question, though the approach might vary depending on the language. In this instance I'm using Actionscript and PHP.
I have a flash graphic that is getting data stored in a mysql database served from a PHP script. This part is working fine. It cycles through database entries every time it is fired.
The graphic is not on a website, but is being used at 5 locations, set to load and run at regular intervals (all 5 locations fire at the same time, or at least within <500ms of each-other). This is real-time info, so time is of the essence, currently the script loads and parses at all 5 locations between 30ms-300ms (depending on the distance from the server)
I was originally having a pagination problem, where each of the 5 locations would pull a different database entry since i was moving to the next entry every time the script runs. I solved this by setting the script to only move to the next entry after a certain amount of time passed, solving the problem.
However, I also need the script to send an email every time it displays a new entry, I only want it to send one email. I've attempted to solve this by adding a "has been emailed" boolean to the database. But, since all the scripts run at the same time, this rarely works (it does sometimes). Most of the time I get 5 emails sent. The timeliness of sending this email doesn't have to be as fast as the graphic gets info from the script, 5-10 second delay is fine.
I've been trying to come up with a solution for this. Currently I'm thinking of spawning a python script through PHP, that has a random delay (between 2 and 5 seconds) hopefully alleviating the problem. However, I'm not quite sure how to run exec() command from php without the script waiting for the command to finish. Or, is there a better way to accomplish this?
UPDATE: here is my current logic (relevant code only):
//get the top "unread" information from the database
$query="SELECT * FROM database WHERE Read = '0' ORDER BY Entry ASC LIMIT 1";
//DATA
$emailed = $row["emailed"];
$Entry = $row["databaseEntryID"];
if($emailed == 0)
{
**CODE TO SEND EMAIL**
$EmailSent="UPDATE database SET emailed = '1' WHERE databaseEntryID = '$Entry'";
$mysqli->query($EmailSent);
}
Thanks!
You need to use some kind of locking. E.g. database locking
function send_email_sync($message)
{
sql_query("UPDATE email_table SET email_sent=1 WHERE email_sent=0");
$result = FALSE;
if(number_of_affacted_rows() == 1) {
send_email_now($message);
$result = TRUE;
}
return $result;
}
The functions sql_query and number_of_affected_rows need to be adapted to your particular database.
Old answer:
Use file-based locking: (only works if the script only runs on a single server)
function send_email_sync($message)
{
$fd = fopen(__FILE__, "r");
if(!$fd) {
die("something bad happened in ".__FILE__.":".__LINE__);
}
$result = FALSE;
if(flock($fd, LOCK_EX | LOCK_NB)) {
if(!email_has_already_been_sent()) {
actually_send_email($message);
mark_email_as_sent();
$result = TRUE; //email has been sent
}
flock($fd, LOCK_UN);
}
fclose($fd);
return $result;
}
You will need to lock the row in your database by using a transaction.
psuedo code:
Start transaction
select row .. for update
update row
commit
if (mysqli_affected_rows ( $connection )) >1
send_email();
I am developing a desktop software where it charge user per execution the main action. For example say it will charge user 0.1$ for per PDF print.
and my software provide multithreading. .
so, if it run single thread it works fine :)
but the problem is if user run multiple thread at one (say 10/20 threads)
it (php) also continues user to allow the server/execution even balance get below zero..
though my php script check whether balance is positive ..
but after user run multiple threads balance become like -5.95$ or -25.75$ etc
and that is a big security/financial issue..
here is the code I am using:
<?php
$strSQL = "Select * from users where Email = '$strUser'";
$return = mysql_query($strSQL, $strDBConn);
$strDBData = mysql_fetch_array($return, MYSQL_ASSOC);
//checking balance
$strBalance = $strDBData['Balance'];
if($strBalance < 0)
{
// if balance 0 then exit so, my software/thread will not process further
mysql_close($strDBConn);
exit('Balance Exceed');
}
//rest of the codes that realted to service executaion
// code that substract the balnce
$dblCost = 0.25;
$strSQL = "Update users set Balance = Balance - '$dblCost' where Email = '$strUser'";
$return = mysql_query($strSQL, $strDBConn);
//rest finising codes
?>
any help/suggestion would be highly appreciated..
thanks in advance.
best regards
I think, this is a quite similar question:
What is equivalent of the C# lock statement in PHP?
First, try to switch away from the old "mysql" to somethin new, maybe some PDO like DB access ;).
Then, for getting around with multi-thread in php, it can be a good idea, to write a file for every userid (!) and lock this file, when there's a request. When file is locked in another thread, wait for x seconds for the file to be unlocked by the locker-thread. If it is not unlocked within time, something went wrong. When in locked-thread all went good, unlock the file after every operation needed.
Theoraticaly you will be good with then till there's a multi-thread soloution in PHP ;)
I'm extremely new to PHP and my first project was developing a comment system. So now it is great but I would like to give the commenters the choice to get emails when there is a new comment. First I decide to create another row in my table called:
'email_notifications'. In the submit.php for the comment form.
To email everyone.... I wan't to use:
$emails = mysql_query("SELECT * FROM email_notifications");
while($row=mysql_fetch_assoc($emails)) {
mail($row['email'],'New Comment on...','There was a new comment o....',"From: no-reply#jdrag.x10.mx");
}
I'm not even sure this would work. But my first concern was that it was going to email people more than once.
Because, if people comment more than once with the same email, wont it just email that email more than once when it goes though the database for all the emails. Thats my big concern. Anyway I could email people when there is a new comment without email people more than once each time there is a new comment.
Summary(what I want to accomplish):
1. Someone submits a comment.
2. everyone above gets an email
I hope I made myself clear. If not, I apologize. Thanks in advance. Remember, im still a beginner :)
Assuming your table contains everyones email address this should work:
$emails = mysql_query("SELECT * FROM email_notifications");
while($row=mysql_fetch_assoc($emails)) {
$subject = "There was a new comment";
$message = "no-reply#jdrag.x10.mx";
mail($row['email'],$subject,$message);
}
However I would try to use Prepared Statments or Stored Proceedures for your database calls.
Heres the same example using Prepared Statements:
$mysqli=new mysqli("server", "username", "password", "database");
$stmt = $mysqli->prepare("SELECT email FROM email_notifications");
$stmt->execute();
$stmt->bind_result($email);
while($stmt->fetch()){
$subject = "There was a new comment";
$message = "no-reply#jdrag.x10.mx";
mail($email,$subject,$message);
}
I would not recomment putting the mail() function inside any loop, there's a possibility to create too many mails in a short timestan, which will probably result in your domain considered a spammer.
In your code where the new comment is created you can track all distinct identifiers for all comments before the created one:
$query = mysql_query("SELECT DISTINCT `userId` FROM `comments` WHERE `object` = {$yourObject} AND `userId` != {$currentUser}");
This will select all user identifiers except for the user who posted the latest comment. Then you have to create the notifications in your appropriate table, watching out for duplicates so as to prevent multiple identical notifications.
Last part is creating a mail queue script on your server that will slowly send the notifications. There are many ways to to that, i prefer a CLI script which is launched via crontab (if not launched already). There, there's just an infinite-looping cycle with a sleep of like 5 seconds to a minute (if no notifications are found) and a simple mail() sending. Here's an improvised example based on an existing CLI script:
#!/usr/bin/php -q
<?php
// acquire a process id file or prevent launching a second instance.
$filename = "/path/to/mailer.pid";
$file = fopen($filename, 'w+');
if (!flock($file, LOCK_EX + LOCK_NB)) exit(0);
// force an uninterruptable script
ignore_user_abort(true);
set_time_limit(0);
// mysql connection routines somewhere here
while (true) {
$query = mysql_query("SELECT * FROM `mail_notifications` ORDER BY `timestamp` ASC LIMIT 0, 1");
if (!mysql_num_rows($query)) {
// Oh, no notifications pending. Take a little break
sleep(60);
continue;
}
$data = mysql_fetch_assoc($query);
// your mailing magic here
sleep(5);
}
?>
Serious people write POSIX-compatible scripts which can be launched as daemons, I didn't have the urge to try that, though. The infinite loop is a dangerous thing, but that works for me at least.