I have the following cod which basically sends a message with some user data from MySQL. Now the thing is the email in question gets sent sometimes but not if I need to resend it. Here is the code:
<?php
//including the database connection file
include("config.php");
//getting id of the data from url
$id = $_GET['id'];
//fetching data in descending order (lastest entry first)
$result = mysql_query("SELECT * FROM my_table_name Where id='$id'");
if($res = mysql_fetch_array($result)) {
$name = $res['name'];
$email = $res['email'];
$date = $res['date'];
if(empty($id)) {
echo "<font color='red'>Error: Did not send.</font><br/>";
} else {
// The message
$message = "Hi $name,\r\n\r\nThis email is to inform you that your
\r\n\r\nRegards, \r\n Me.";
$headers = 'From: no-reply#mydomain.org' . "\r\n" .
'Reply-To: no-reply#mydomain.org' . "\r\n" .
'X-Mailer: PHP/' . phpversion();
$Subject = 'Hello';
// Send
mail($email, $Subject, $message, $headers);
//display success message
echo "<font color='green'>Email sent successfully to $name.</font><br/>";
echo "<br/><a href='index.php' class='button button1 link'>Go Back to Overview</a>";
}
}
?>
Any help is greatly appreciated.
If I understood your question correctly and it was about mail function call failing sometimes:
You can add retry logic to your mail call:
$triesLeft = 5;
while ($triesLeft > 0) {
$rc = mail(...);
if ($rc === true) {
break;
}
sleep(1); // Space to improve here - see exponential backoff, for instance
$triesLeft -= 1;
}
if ($triesLeft === 0) {
// Log critical error
}
But this might and will introduce delays in your response time, and in some cases client will even get a timeout.
So usually the solution to this problem is not to send the email right away, but to quickly put it in any kind of queue (even simple db table will suffice), and then make another worker script that will send all that is unsent.
You can move this retry logic to this worker script and sleep all you want, it won't block client.
The only "downsides" to this solution is that:
You will not be able to show result of mail call to your client (which is useless anyway since it means only that is was "accepted for delivery", not actually delivered), and
You'll have a bit more moving parts and you'll have to write a bit more code. But it's either that or accepting that some of your mail calls will fail.
An outline of a solution might be:
form.php (gets called when customer submits form):
get request parameters
form email subject, body from request parameters
save email to db with status = unsent
worker.php (should be always running in an infinite loop or something like that in background on server):
get unsent email from db
try to send it, retrying if needed
if sent successfully - mark email as sent
if failed to send - mark email an failed, log critical error, mitigate manually
Related
It is 1 AM and I am struggling for 3-4 hours to see what's wrong with my script...
My database has ~400 emails. I set $ChunkSize as counter for the loop and also to count which is the next chunk to be processed.
I've set some echo() to debug
echo "This is the " . $GLOBALS["ChunkSize"] . " chunk. <br>";
It should output what chunk is processed at that time.
If I disable mail() then I don't get 503 Service Unavailable but every echo() displays at the same time, not in the order of processing.
I also found out that some emails arrive, but not to everyone. Also, if some emails are sent, that means foreach() should have processed at least one chunk, that means it should display at least one echo().
I've set break 1; so every time it breaks out of foreach() it should display the echo() with the chunk number processed by foreach() but it doesn't.
What I am doing wrong?
$connection = mysql_connect($hostname, $username, $password);
mysql_select_db($dbname, $connection);
$result = mysql_query("SHOW COLUMNS FROM `Emails`");
while($row = mysql_fetch_array($result)){
$Addresses[] = $row['Field'];}
$Subject = "Test";
$Message = "
Test
";
$Headers = array( EMPTY FOR SECURITY REASONS );
$Headers = implode( "\r\n" , $Headers );
$ChunkAddresses = 50;
$EmailChunkArray = array_chunk($Addresses, $ChunkAddresses);
$ArraySize = count ($EmailChunkArray);
$ChunkSize = 0;
ChunkLoop: {
$EmailChunkArrayLoop = $GLOBALS["EmailChunkArray"];
foreach ($EmailChunkArrayLoop[$GLOBALS["ChunkSize"]] as $ToChunkLoop) {
if ($GLOBALS["ChunkSize"] <= $GLOBALS["ArraySize"]) {
mail($ToChunkLoop,$GLOBALS["Subject"],$GLOBALS["Message"],$GLOBALS["Headers"]);
echo "This is the " . $GLOBALS["ChunkSize"] . " chunk. <br>";
} else if ($GLOBALS["ChunkSize"] == $GLOBALS["ArraySize"]){
exit();}
$GLOBALS["ChunkSize"]++;
break 1;}
}
if ($GLOBALS["ChunkSize"] != $GLOBALS["ArraySize"]){
echo "Test. <br>";
goto ChunkLoop;
} else {
echo "Finished! <br>";}
Create script that will only do one thing - send mail.
sendMail.php
<?php
// Get recipient from the argv array
$recipient = $_SERVER['argv'][1];
// Mail args
$subject = 'HELLOOOOOOO';
$message = 'BLablabla';
$headers = [...]; // optional or not
// Send it
mail($recipient, $subject, $message, $headers);
And inside of Your code where You do:
mail($ToChunkLoop,$GLOBALS["Subject"],$GLOBALS["Message"],$GLOBALS["Headers"]);
Replace with:
$recipient = escapeshellarg($ToChunkLoop);
exec("php /path/to/sendMail.php ".$recipient." > /dev/null &"); // that will call mail script and will not wait when execution will end
Feel free to adapt my code examples as You wish
P.S. this solution is for cases when You don't want to pay for normal batch mail sending, mail subscription or dedicated, vps services and have just small web hosting. (:
P.S.. it's not a brilliant solution, but done for requirements provided by question author
I have made a contact form based on this tutorial:
http://blog.teamtreehouse.com/create-ajax-contact-form
I'm using PHP Version 5.3.10-1ubuntu3.4 on my server and I've been having trouble with http_response_code(); which is what the example tutorial at the above link uses. I've read http_response_code(); only works with PHP 5.4. So instead I have reverted to using header();.
I have my form working just fine and it's displaying a success message when I submit, rather than errors when I was using http_response_code(); but my PHP isn't that great and I am wanting to know if what I have done is acceptable or if I should be doing it a different way? Please correct my code if so.
Here's the contents of my mailer.php file, where you can see I've commented out http_response_code(); and am using header();.
if ($_SERVER["REQUEST_METHOD"] == "POST") {
// Get the form fields and remove whitespace.
$name = strip_tags(trim($_POST["name"]));
$name = str_replace(array("\r","\n"),array(" "," "),$name);
$email = filter_var(trim($_POST["email"]), FILTER_SANITIZE_EMAIL);
$phone = trim($_POST["phone"]);
$company = trim($_POST["company"]);
$minbudget = trim($_POST["minbudget"]);
$maxbudget = trim($_POST["maxbudget"]);
$message = trim($_POST["message"]);
$deadline = trim($_POST["deadline"]);
$referred = trim($_POST["referred"]);
// Check that data was sent to the mailer.
if ( empty($name) OR empty($phone) OR empty($message) OR !filter_var($email, FILTER_VALIDATE_EMAIL)) {
// Set a 400 (bad request) response code and exit.
//http_response_code(400);
header("HTTP/1.1 400 Bad Request");
echo "Error (400). That's not good, refresh and try again otherwise please email me and let me know you are having trouble submitting this form.";
exit;
}
// Set the recipient email address.
// FIXME: Update this to your desired email address.
$recipient = "myemail#domain.com";
// Set the email subject.
$subject = "Website enquiry from $name";
// Build the email content.
$email_content = "Name: $name\n";
$email_content .= "Email: $email\n\n";
$email_content .= "Phone: $phone\n";
$email_content .= "Company: $company\n\n";
$email_content .= "Budget: $minbudget $maxbudget\n";
$email_content .= "Deadline: $deadline\n";
//$email_content .= "Max Budget: $maxbudget\n";
$email_content .= "\n$message\n\n";
$email_content .= "Referred: $referred\n";
// Build the email headers.
$email_headers = "From: $name <$email>";
// Send the email.
if (mail($recipient, $subject, $email_content, $email_headers)) {
// Set a 200 (okay) response code.
//http_response_code(200);
header("HTTP/1.1 200 OK");
echo "Thank You! I'll be in touch soon.";
} else {
// Set a 500 (internal server error) response code.
//http_response_code(500);
header("HTTP/1.0 500 Internal Server Error");
echo "Error (500). That's not good, refresh and try again otherwise please email me and let me know you are having trouble submitting this form.";
}
} else {
// Not a POST request, set a 403 (forbidden) response code.
//http_response_code(403);
header("HTTP/1.1 403 Forbidden");
echo "Error (403). That's not good, refresh and try again otherwise please email me and let me know you are having trouble submitting this form.";
}
I've managed to answer this on my own similar question by going through the PHP source code to work out exactly what happens.
The two methods are essentially functionally equivalent. http_response_code is basically a shorthand way of writing a http status header, with the added bonus that PHP will work out a suitable Reason Phrase to provide by matching your response code to one of the values in an enumeration it maintains within php-src/main/http_status_codes.h.
Note that this means your response code must match a response code that PHP knows about. You can't create your own response codes using this method, however you can using the header method. Note also that http_response_code is only available in PHP 5.4.0 and higher.
In summary - The differences between http_response_code and header for setting response codes:
Using http_response_code will cause PHP to match and apply a Reason Phrase from a list of Reason Phrases that are hard-coded into the PHP source code.
Because of point 1 above, if you use http_response_code you must set a code that PHP knows about. You can't set your own custom code, however you can set a custom code (and Reason Phrase) if you use the header function.
http_response_code is only available in PHP 5.4.0 and higher
Easy solution:
/**
* Sets the response code and reason
*
* #param int $code
* #param string $reason
*/
function setResponseCode($code, $reason = null) {
$code = intval($code);
if (version_compare(phpversion(), '5.4', '>') && is_null($reason))
http_response_code($code);
else
header(trim("HTTP/1.0 $code $reason"));
}
you can use it as:
setResponseCode(404);
or
setResponseCode(401,'Get back to the shadow');
To answer your main question, the biggest response I could see to using headers vs http_response_code(), is that http_response_code() is only supported on PHP 5.4 and greater, older versions would fail using that function.
Using headers as you are in your example will insure you're code will work on older versions.
I am working on a massmail script which sends an e-mail to every e-mail id present in a particular database.
But there is some issue.
Like I have following database:
id email link
1 a#bc.com bc.com
2 b#cd.com cd.com
And suppose the mail content is : 'Testing this script'
The scripts sends email to a#bc.com perfectly but second time it sends the email, i.e to b#cd.com the content gets doubled.
I mean the second recipient receives an e-mail like this :
Testing this script
Testing this script
The third recipient receives an e-mail with the content repeat three times and the fourth one receives it with four times and so on.
The script grabs e-mail addresses from the email field in the database and sends e-mail to them.
My Code:
<?
include "header.php";
include "config2.php";
$subject="Massmail";
$headers .= "Content-type:text/html;charset=iso-8859-1" . "\r\n";
mysql_connect($server, $db_user, $db_pass)
or die ("Database CONNECT Error");
$resultquery = mysql_db_query($database, "select * from $table");
while ($query = mysql_fetch_array($resultquery))
{
$mailto=$query[$table_email];
$domain=$query[$table_link];
$domain2 = str_replace(array('http://','HTTP://','Http://'), '',$domain);
$handle = fopen("http://$domain2","r") or die("Unable to open link ( $domain ). <a href='javascript:history.go(-1);'>Go back</a> and please try again ");
$contents = '';
while (!feof($handle)) {
$contents .= fread($handle, 8192);
$contents = str_replace('window.location = "/abc.html"','window.location = ""',$contents);
$contents = mb_convert_encoding($contents, "HTML-ENTITIES", "auto");
}
$i = md5(uniqid(rand(), true)) . '.' . html;
$fh = fopen("/home/host/public_html/content/$i", "w");
fwrite($fh, $contents);
fclose($fh);
$filename = '/files/$i';
$message1 .= "Testing Mail Script Version 2";
mail($mailto, $subject, $message1, $headers, "-f" . 'noreply#domain.com');
echo 'Mail sent to '.$mailto.'<br>';
sleep($seconds);
}
include "footer.php";
?>
I have tried to echo the mail that has to be sent and I get this:
To: a#bc.com
Subject: Massmail
Message:
Testing mail script
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
to: b#cd.com
Subject: Massmail
Message:
Testing mail scriptTesting mail script
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Your help will be greatly appreciated. Thanks in advance
In the code line: $message1 .= "Testing Mail Script Version 2"; the period is a concatenate, so each time you loop it, you duplicate the message another time.
Agreed. Sounds like your loop is not functioning correctly? If you can't post the full code, try to post an abstraction for us to diagnose your issue.
If you cant, I would look into looping based on the number of email address you have, not some arbitrary counter. That might help you out.
Suppose this is your table. With id, email, link, message structured as:
id email message
1 a#test.com hello, how are you doing..
2 b#test.com hey, dude this is me..
3 c#test.com we are sending you this...
Now, let's assume you have 100 records. What I would do is (assuming $result is a mysql array returning all the results from mysql ex SELECT * FROM mail...)
I would try:
for($i=0; $i <= count($result); $i++){
$send = mail($result['to'], $result['subject'],
$result['message'], $result['headers']);
if(!$send){
echo 'e-mail, sending has stopped';
break;
}
else{
echo 'all e-mails are sent successfully'; }
}
Never mind, I fixed it. #Pirion's post helped me. I changed
$message1 .= "Testing Mail Script Version 2";
to
$message = "Testing Mail Script Version 2";
And everything worked perfectly. Thank You So Much Guys For Helping Me Out.
Ok so long story short, I have a simple mailto function I want to apply/run for every name on a db list. Since it's not working, I removed all the mail stuff from it and to test to make sure the while loop was working with the db, did this
<?php
$connect2db = mysqli_connect('127.0.0.1','root','pass','dbnamehere');
if(!$connect2db){
die("Sorry but theres a connection to database error" . mysqli_error);
}
$sn_query = "SELECT * FROM email_list";
$sn_queryResult = mysqli_query($connect2db, $sn_query) or die("Sorry but theres a connection to database error" . mysqli_error);
$sn_rowSelect = mysqli_fetch_array($sn_queryResult);
$to = $sn_rowSelect;
?>
<br/><br/>
////lower part on page //////<br/><br/>
<?php
while($sn_rowSelect = mysqli_fetch_array($sn_queryResult) ) {
echo "hello there" . " " . $sn_rowSelect['firstname'] . " <br/>";
}
?>
Now this works, it goess through my db and prints out all my first names from the database list. In my noob brain, id think that if i remove the echo lines, and enter the appropriate mailto information, that it would loop just like before, but send mail to each name. so i did this:
<?php
$sn_query = "SELECT email FROM email_list";
$sn_queryResult = mysqli_query($connect2db, $sn_query) or die("Sorry but theres a connection to database error" . mysqli_error);
$sn_rowSelect = mysqli_fetch_array($sn_queryResult);
$to = implode(",",$sn_rowSelect);
$from = $_POST['sender'];
$subject = $_POST['subj'];
$mssg = $_POST['message'];
$headers = "MIME-Version: 1.0rn";
$headers .= "From: $from\r\n";
$mailstatus = mail($to, $subject, $mssg, $headers);
?>
<br/><br/>
//////////<br/><br/>
<?php
while($sn_rowSelect = mysqli_fetch_array($sn_queryResult) ) {
$mailstatus;
if($mailstatus) {
echo "Success";
}else{
echo "There was a problem sending the mail. Check your code and make sure that the e-mail address $to is valid\n";
}
}
?>
now this emails the first name on my list, but not the rest.
I don't get any errors so not sure what the problem is. I was going to try an if statement with num_rows but somewhere else, on StackOverflow, someone said that didn't help since the while loop took care of it by itself. (I tried it either way and it still emailed only the first name) I'm trying here but to no avail.
You have not called the mail() function inside your loop. You call it once outside. Instead try something like the following.
Assuming you have retrieved the $to address from your database query (like you did with the firstname in testing), pull it from the rowset, and use it in mail():
while($sn_rowSelect = mysqli_fetch_array($sn_queryResult) ) {
// Get the $to address:
$to = $sn_rowSelect['email'];
// Call mail() inside the loop.
$mailstatus = mail($to, $subject, $mssg, $headers);
if($mailstatus) {
echo "Success";
}else{
echo "There was a problem sending the mail. Check your code and make sure that the e-mail address $to is valid\n";
}
}
Note also, that since you call mysql_fetch_array() at the top of your script, your while loop will start with the second row. You should remove the first call to mysql_fetch_array() that occurs before the loop.
$sn_queryResult = mysqli_query($connect2db, $sn_query) or die("Sorry but theres a connection to database error" . mysqli_error);
// Don't want this...
//$sn_rowSelect = mysqli_fetch_array($sn_queryResult);
I've recently posted here accessing $_SESSION when using file_get_contents in PHP about a problem I was having and the general consensus is that I'm not doing it right... while I generally think "as long as it works..." I thought I'd get some feedback on how I could do it better...
I was to send the exact same email in the exact same format from multiple different areas.
When a job is entered (automatically as a part of the POST)
Manually when reviewing jobs to re-assign to another installer
The original script is a php page which is called using AJAX to send the work order request - this worked by simply calling a standard php page, returning the success or error message and then displaying within the calling page.
Now I have tried to use the same page within the automated job entry so it accepts the job via a form, logs it and mails it.
My problem is (as you can see from the original post) the function file_get_contents() is not good for this cause in the automated script...
My problem is that from an AJAX call I need to do things like include the database connection initialiser, start the session and do whatever else needs to be done in a standalone page... Some or all of these are not required if it is an include so it makes the file only good for one purpose...
How do I make the file good for both purposes? I guess I'm looking for recommendations for the best file layout and structure to cater for both scenarios...
The current file looks like:
<?php
session_start();
$order_id = $_GET['order_id'];
include('include/database.php');
function getLineItems($order_id) {
$query = mysql_query("SELECT ...lineItems...");
//Print rows with data
while($row = mysql_fetch_object($query)) {
$lineItems .= '...Build Line Item String...';
}
return $lineItems;
}
function send_email($order_id) {
//Get data for current job to display
$query = mysql_query("SELECT ...Job Details...");
$row = mysql_fetch_object($query);
$subject = 'Work Order Request';
$email_message = '...Build Email...
...Include Job Details...
'.getLineItems($order_id).'
...Finish Email...';
$headers = '...Create Email Headers...';
if (mail($row->primary_email, $subject, $email_message, $headers)) {
$query = mysql_query("...log successful send...");
if (mysql_error()!="") {
$message .= '...display mysqlerror()..';
}
$message .= '...create success message...';
} else {
$query = mysql_query("...log failed send...");
if (mysql_error()!="") {
$message .= '...display mysqlerror()..';
}
$message .= '...create failed message...';
}
return $message;
} // END send_email() function
//Check supplier info
$query = mysql_query("...get suppliers info attached to order_id...");
if (mysql_num_rows($query) > 0) {
while($row = mysql_fetch_object($query)) {
if ($row->primary_email=="") {
$message .= '...no email message...';
} else if ($row->notification_email=="") {
$message .= '...no notifications message...';
} else {
$message .= send_email($order_id);
}
}
} else {
$message .= '...no supplier matched message...';
}
print $message;
?>
make a function and include it
Do separate functions. Authentication (which requires sessions) from mail sending (which don't)
Then include mail sending function into both tasks.