Emulating successful send via phpmailer ($mail->Send()) - php

When I'm building and testing my website on local server, I would like to emulate successful sending via phpmailer, so I avoid actually sending emails.
I normally use if ($mail->Send()) { the mail was sent, now do this }.
For local testing I think the best would be to skip the whole phpmailer inclusion, instead of adding a lot of if statements etc.
But skipping phpmailer would then cause php to complain about $mail->Send(), $mail->addAddress('emailaddress') etc.
How could I fake the function (or object/class) so that calls to $mail->Send() are always true, and the rest $mail->something() etc. are just ignored/true, so that no email is sent?

Extend the PHPMailer class and override the public function send().
class UnitTestMailer extends PHPMailer {
public function send() {
return $this;
}
}
class User {
public function __construct(PHPMailer $mailer) {
$this->mailer = $mailer;
}
public function sendActiviation() {
return $this->mailer->send();
}
}
// ... somewhere in your test
public function test_if_from_is_properly_set() {
// ...
$user = new User(new UnitTestMailer);
// ...
$mailer = $user->sendActivation();
$this->assertEquals($expectedFrom, $mailer->From);
}

Why emulate?
I use INI files to provide configuration variables for PHPMailer depending on the environment. Live obviously has the server's mail settings. Local uses my Gmail account credentials to send mail.
You can have the best of both worlds :)
Keep a config.ini file for example somewhere in your working directory (I tend to use root but that's preference) and make sure it's in your .gitignore or similar. Your local version would look something like:
[PHPMailer Settings]
EMAIL_HOST = "smtp.gmail.com"
EMAIL_PORT = 587
EMAIL_SMTPSECURE = "tls"
EMAIL_SMTPAUTH = "true"
EMAIL_USERNAME = "my_email#gmail.com"
EMAIL_PASSWORD = "my_password"
then in your PHP:
$ini = parse_ini_file($_SERVER['DOCUMENT_ROOT'] . "/config.ini", true, INI_SCANNER_TYPED);
// use settings from INI file:
$foo = $ini['PHPMailer Settings']['EMAIL_HOST'];
$bar = $ini['PHPMailer Settings']['EMAIL_PORT'];
Security Bonus
Change the syntax of your INI file to look like the below and rename it to config.ini.php:
; <?php
; die();
; /*
[PHPMailer Settings]
EMAIL_HOST = "smtp.gmail.com"
EMAIL_PORT = 587
EMAIL_SMTPSECURE = "tls"
EMAIL_SMTPAUTH = "true"
EMAIL_USERNAME = "my_email#gmail.com"
EMAIL_PASSWORD = "my_password"
; */ ?>
(remember to use the new filename in your PHP code)
PHP can still parse the settings, but if anyone tried to access the INI file it would be parsed as PHP comments and just show ";"

Related

Unable to move mails to sent folder of IMAP using Laravel

I am sending emails from my laravel website using SMTP. When i send email to the user then i want to copy that mail to IMAP sent folder too.
Here is my code when i am sending mail to user:
$mail = Mail::to($this->receiver)
->send(new ComplaintMail($this->sender->user_email,$this->subject,$this->complaint,$this->answers,$this->sender));
$path = "{mypath.com:993/imap/ssl}Sent";
$imapStream = imap_open($path,$this->sender->user_email,$this->sender->email_password);
$result = imap_append($imapStream,$path,$mail->getSentMIMEMessage());
imap_close($imapStream);
Also I tried using imap_mail_move() method like so:
$mail = Mail::to($this->receiver)
->send(new ComplaintMail($this->sender->user_email,$this->subject,$this->complaint,$this->answers,$this->sender));
$path = "{mypath.com:993/imap/ssl}Sent";
$imapStream = imap_open($path,$this->sender->user_email,$this->sender->email_password);
imap_mail_move($imapStream,$mail,$path);
imap_close($imapStream);
Both ways it didn't worked out
In ComplaintMail class, build function looks like :
public function build()
{
return $this->from($this->sender)
->subject($this->subject)
->markdown('emails.complaint');
}

PHPMailer executing around 2 seconds

I was following PHPMailer tutorial and some tutorials in Internet but I still can't make execution less than 2 second. On many website it says it shouldn't take more than 0.4s. I tried it from my local machine and from AWS machine. Execution time same.
class BatchMailer {
private static $mail;
private static $initialized = false;
private static function initialize() {
if (self::$initialized)
return;
self::$mail = new PHPMailer;
self::$mail->SMTPDebug = 2;
self::$mail->isSMTP();
self::$mail->Host = 'smtp.gmail.com';
self::$mail->Port = 587;
self::$mail->SMTPSecure = 'tls';
self::$mail->SMTPAuth = true;
self::$mail->Username = '***';
self::$mail->Password = '***';
self::$mail->SMTPKeepAlive = true;
self::$mail->setFrom('***#gmail.com', 'Title');
self::$mail->isHTML(true);
self::$mail->AltBody = 'Please use an HTML-enabled email client to view this message.';
self::$initialized = true;
}
public static function setSubject($subject) {
self::initialize();
self::$mail->Subject = $subject;
}
public static function setBody($body) {
self::initialize();
self::$mail->Body = stripslashes($body);
}
public static function sendTo() {
self::initialize();
self::$mail->clearAddresses();
$recipients = array(
'***#gmail.com' => 'Person One'
);
foreach($recipients as $email => $name) {
self::$mail->AddCC($email, $name);
}
self::$mail->send();
return;
}
static function test() {
self::setSubject('subject');
self::setBody('body');
self::sendTo();
}
}
SMTP is often slow, especially when things like greetdelay/tarpitting are used as anti-spam measures. 2 seconds is not that slow - the SMTP spec allows for timeouts of 10-20 minutes! It's really unsuited to real-time use, i.e. during a web page submission, but that doesn't seem to stop many trying to use it that way. To maximise performance you can install a local mail server to use as a relay, or hand off your message send to a separate process that doesn't mind waiting for a while, for example by submitting using an async ajax request from your page so the user is not blocked from doing other things.
If you're sending larger volumes of email it's important to use a relay and SMTP keepalive while submitting it. I have no trouble sustaining over 200 messages/second with PHPMailer.
Nice class BTW - tidier than most of the things I see on SO! $initialized is not needed - just check whether self::$mail is set instead.

Making PHP's mail() asynchronous

I have PHP's mail() using ssmtp which doesn't have a queue/spool, and is synchronous with AWS SES.
I heard I could use SwiftMail to provide a spool, but I couldn't work out a simple recipe to use it like I do currently with mail().
I want the least amount of code to provide asynchronous mail. I don't care if the email fails to send, but it would be nice to have a log.
Any simple tips or tricks? Short of running a full blown mail server? I was thinking a sendmail wrapper might be the answer but I couldn't work out nohup.
You have a lot of ways to do this, but handling thread is not necessarily the right choice.
register_shutdown_function: the shutdown function is called after the response is sent. It's not really asynchronous, but at least it won't slow down your request. Regarding the implementation, see the example.
Swift pool: using symfony, you can easily use the spool.
Queue: register the mails to be sent in a queue system (could be done with RabbitMQ, MySQL, redis or anything), then run a cron that consume the queue. Could be done with something as simple as a MySQL table with fields like from, to, message, sent (boolean set to true when you have sent the email).
Example with register_shutdown_function
<?php
class MailSpool
{
public static $mails = [];
public static function addMail($subject, $to, $message)
{
self::$mails[] = [ 'subject' => $subject, 'to' => $to, 'message' => $message ];
}
public static function send()
{
foreach(self::$mails as $mail) {
mail($mail['to'], $mail['subject'], $mail['message']);
}
}
}
//In your script you can call anywhere
MailSpool::addMail('Hello', 'contact#example.com', 'Hello from the spool');
register_shutdown_function('MailSpool::send');
exit(); // You need to call this to send the response immediately
php-fpm
You must run php-fpm for fastcgi_finish_request to be available.
echo "I get output instantly";
fastcgi_finish_request(); // Close and flush the connection.
sleep(10); // For illustrative purposes. Delete me.
mail("test#example.org", "lol", "Hi");
It's pretty easy queuing up any arbitrary code to processed after finishing the request to the user:
$post_processing = [];
/* your code */
$email = "test#example.org";
$subject = "lol";
$message = "Hi";
$post_processing[] = function() use ($email, $subject, $message) {
mail($email, $subject, $message);
};
echo "Stuff is going to happen.";
/* end */
fastcgi_finish_request();
foreach($post_processing as $function) {
$function();
}
Hipster background worker
Instantly time-out a curl and let the new request deal with it. I was doing this on shared hosts before it was cool. (it's never cool)
if(!empty($_POST)) {
sleep(10);
mail($_POST['email'], $_POST['subject'], $_POST['message']);
exit(); // Stop so we don't self DDOS.
}
$ch = curl_init("http://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
curl_setopt($ch, CURLOPT_TIMEOUT, 1);
curl_setopt($ch, CURLOPT_NOSIGNAL, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, [
'email' => 'noreply#example.org',
'subject' => 'foo',
'message' => 'bar'
]);
curl_exec($ch);
curl_close($ch);
echo "Expect an email in 10 seconds.";
Use AWS SES with PHPMailer.
This way is very fast (hundreds of messages per second), and there isn't much code required.
$mail = new PHPMailer;
$mail->isSMTP(); // Set mailer to use SMTP
$mail->Host = 'ssl://email-smtp.us-west-2.amazonaws.com'; // Specify main and backup SMTP servers
$mail->SMTPAuth = true; // Enable SMTP authentication
$mail->Username = 'blah'; // SMTP username
$mail->Password = 'blahblah'; // SMTP password
$mail->SMTPSecure = 'tls'; // Enable TLS encryption, `ssl` also accepted
$mail->Port = 443;
Not sure if i interpreted your question correctly but i hope this helps.
Pthreads is your friend :)
This is a sample of how i made in my production application
class AsynchMail extends Thread{
private $_mail_from;
private $_mail_to;
private $_subject;
public function __construct($subject, $mail_to, ...) {
$this->_subject = $subject;
$this->_mail_to = $mail_to;
// ...
}
// ...
// you must redefine run() method, and to execute it we must call start() method
public function run() {
// here put your mail() function
mail($this->_mail_to, ...);
}
}
TEST SCRIPT EXAMPLE
$mail_to_list = array('Shigeru.Miyamoto#nintendo.com', 'Eikichi.Kawasaki#neogeo.com',...);
foreach($mail_to_list as $mail_to) {
$asynchMail = new AsynchMail($mail_to);
$asynchMail->start();
}
Let me know if you need further help for installing and using thread in PHP
For logging system, i strongly advice you to use Log4PHP : powerful and easy to use and to configure
For sending mails, i also strongly advice you to use PHPMailer
I'm using asynchronous php execution by using beanstalkd.
It is a simple message queue, really lightweight and easy to integrate.
Using the following php wrapper for php https://github.com/pda/pheanstalk
you can do something as follows to implement a email worker:
use Beanstalk\Client;
$msg="dest_email##email_subject##from_email##email_body";
$beanstalk = new Client();
$beanstalk->connect();
$beanstalk->useTube('flux'); // Begin to use tube `'flux'`.
$beanstalk->put(
23, // Give the job a priority of 23.
0, // Do not wait to put job into the ready queue.
60, // Give the job 1 minute to run.
$msg // job body
);
$beanstalk->disconnect();
Then the job would be done in a code placed into a separate php file.
Something like:
use Beanstalk\Client;
$do=true;
try {
$beanstalk = new Client();
$beanstalk->connect();
$beanstalk->watch('flux');
} catch (Exception $e ) {
echo $e->getMessage();
echo $e->getTraceAsString();
$do = false;
}
while ($do) {
$job = $beanstalk->reserve(); // Block until job is available.
$emailParts = explode("##", $job['body'] );
// Use your SendMail function here
if ($i_am_ok) {
$beanstalk->delete($job['id']);
} else {
$beanstalk->bury($job['id'], 20);
}
}
$beanstalk->disconnect();
You can run separately this php file, as an independent php process. Let's say you save it as sender.php, it would be run in Unix as:
php /path/to/sender/sender.php & && disown
This command would run the file and alsow allow you to close the console or logout current user without stopping the process.
Make sure also that your web server uses the same php.ini file as your php command line interpreter. (Might be solved using a link to you favorite php.ini)
I hope it helps.
Your best bet is with a stacking or spooling pattern. It's fairly simple and can be described in 2 steps.
Store your emails in a table with a sent flag on your current thread.
Use cron or ajax to repeatedly call a mail processing php file that will get the top 10 or 20 unsent emails from your database, flag them as sent and actually send them via your favourite mailing method.
An easy way to do it is to call the code which handles your mails asynchronously.
For example if you have a file called email.php with the following code:
// Example array with e-mailaddresses
$emailaddresses = ['example1#test.com', 'example2#example.com', 'example1#example.com'];
// Call your mail function
mailer::sendMail($emailaddresses);
You can then call this asynchronously in a normal request like
exec('nice -n 20 php email.php > /dev/null & echo $!');
And the request will finish without waiting for email.php to finish sending the e-mails. Logging could be added as well in the file that does the e-mails.
Variables can be passed into the exec between the called filename and > /dev/null like
exec('nice -n 20 php email.php '.$var1.' '.$var2.' > /dev/null & echo $!');
Make sure these variables are safe with escapeshellarg(). In the called file these variables can be used with $argv
Welcome to async PHP
https://github.com/shuchkin/react-smtp-client
$loop = \React\EventLoop\Factory::create();
$smtp = new \Shuchkin\ReactSMTP\Client( $loop, 'tls://smtp.google.com:465', 'username#gmail.com','password' );
$smtp->send('username#gmail.com', 'sergey.shuchkin#gmail.com', 'Test ReactPHP mailer', 'Hello, Sergey!')->then(
function() {
echo 'Message sent via Google SMTP'.PHP_EOL;
},
function ( \Exception $ex ) {
echo 'SMTP error '.$ex->getCode().' '.$ex->getMessage().PHP_EOL;
}
);
$loop->run();

Swift Mailer - Can't send mail, and can't find error logs

New to PHP and Swiftmailer and have yet to get it working. I've uploaded the /lib/ directory to a directory in the root of my shared webserver from Hostgator. I've uploaded the following inside in the directory above /lib/:
<?php
require_once 'lib/swift_required.php';
$transport = Swift_SmtpTransport::newInstance('mail.****.com', 25)
->setUsername('****#****.com')
->setPassword('****');
$mailer = Swift_Mailer::newInstance($transport);
$message = Swift_Message::newInstance('Subject Here')
->setFrom(array('****#****.com' => '****'))
->setTo(array('****#****.com' => '****'));
$message->setBody('This is the message');
if (!$mailer->send($message, $errors))
{
echo "Error:";
print_r($errors);
}
?>
It does not send a message, but I am also unable to view any error logs. I have error logging enabled in all of sections in my php.ini - but when I try to go to where I uploaded the .php file in a browser I get a 404 error. When I connect through ssh I have jailshell access. When I tried to go to /var/log/php-scripts.log I did not have permission. Wondering where else I could find errors for this in order to fix it?
You should try to use swiftmailer logger plugin.
The Logger plugins helps with debugging during the process of sending. It can help to identify why an SMTP server is rejecting addresses, or any other hard-to-find problems that may arise.
You only need to create new logger instance and add it to mailer object with registerPlugin() method.
<?php
require_once 'lib/swift_required.php';
$transport = Swift_SmtpTransport::newInstance('mail.****.com', 25)
->setUsername('****#****.com')
->setPassword('****');
$mailer = Swift_Mailer::newInstance($transport);
$logger = new \Swift_Plugins_Loggers_ArrayLogger();
$mailer->registerPlugin(new \Swift_Plugins_LoggerPlugin($logger));
$message = Swift_Message::newInstance('Subject Here')
->setFrom(array('****#****.com' => '****'))
->setTo(array('****#****.com' => '****'));
$message->setBody('This is the message');
if (!$mailer->send($message, $errors)) {
// Dump the log contents
// NOTE: The EchoLogger dumps in realtime so dump() does nothing for it. We use ArrayLogger instead.
echo "Error:" . $logger->dump();
}else{
echo "Successfull.";
}
?>
Edit :
Swift_Plugins_Loggers_ArrayLogger: Keeps a collection of log messages inside an array. The array content can be cleared or dumped out to the screen.
Swift_Plugins_Loggers_EchoLogger: Prints output to the screen in realtime. Handy for very rudimentary debug output.
Use transport exception handling like so:
try {
$mailer->send($message);
}
catch (\Swift_TransportException $e) {
echo $e->getMessage();
}
Under your swift mailer extension there is an exception handler.
root/lib/classes/Swift/SwiftException.php
By default it does some convoluted things with errors to make it really hard to find them. Most of the pages out there also recommend equally convoluted ways of logging the errors.
If you just want to see the error, add an echo, like below (or whatever else you might want to do with it).
class Swift_SwiftException extends Exception
{
/**
* Create a new SwiftException with $message.
*
* #param string $message
*/
public function __construct($message)
{
echo $message;
parent::__construct($message);
}
}

How to send mail using cake php and where to start?

I wanna try to send mail using cake php. I have no experience of sending mail. So, I don't know where to start. Is it need to make mail server? If need, how to make mail server and how to send mail? Please explain step by step. I really don't know where to start.
I'm using xampp and now I test my site at localhost.
I tested following link:
http://book.cakephp.org/view/1286/Sending-a-basic-message
but error occurred cannot be accessed directly.
and then I add code from the following link:
http://book.cakephp.org/view/1290/Sending-A-Message-Using-SMTP
So, my code is following:
function _sendMail(){
$this->Email->to = 'user#gmail.com';
$this->Email->bcc = array('secret#example.coom');
$this->Email->subject = 'Welcome to our really cool things';
$this->Email->replyTo = 'support#example.com';
$this->Email->from = 'Online Application <app#example.coom>';
$this->Email->template = 'simple_message';
$this->Email->sendAs = 'both';
$this->Email->smtpOptions = array(
'port' =>'25',
'timeout' => '30',
'host' => 'ssl://smtp.gmail.com',
'username' => 'my_mail#gmail.com',
'password' =>'aaa',
);
$this->Email->delivery = 'smtp';
$this->Email->send();
}
but error still occurred. But, I didn't make any mail server.Is that OK?
I have a feeling this is to do with your XAMPP configuration:
Try opening "php.ini", it should be somewhere in your server files.
Search for the attribute called “SMTP” in the php.ini file.Generally you can find the line “SMTP=localhost“. change the localhost to the smtp server name of your ISP. And, there is another attribute called “smtp_port” which should be set to 25.I’ve set the following values in my php.ini file.
SMTP = smtp.wlink.com.np
smtp_port = 25
Restart the apache server so that PHP modules and attributes will be reloaded.
ow try to send the mail using the mail() function:
mail(“you#yourdomain.com”,”test subject”,”test body”);
If you get the following warning:
Warning: mail() [function.mail]: “sendmail_from” not set in php.ini or custom “From:” header missing in C:\Program Files\xampp\htdocs\testmail.php on line 1
Specify the following headers and try to send the mail again:
$headers = ‘MIME-Version: 1.0′ . “\r\n”;
$headers .= ‘Content-type: text/html; charset=iso-8859-1′ . “\r\n”;
$headers .= ‘From: sender#sender.com’ . “\r\n”;
mail(“you#yourdomain.com”,”test subject”,”test body”,$headers);
source: http://roshanbh.com.np/2007/12/sending-e-mail-from-localhost-in-php-in-windows-environment.html
Naming the controller function with a leading underscore is Cake's backwards compatible way of designating that the function should be protected, i.e. that the function should not be accessible as a normal controller action. That means you can't access FooController::_sendMail() using the URL /foo/_sendMail, or any other URL for that matter. You should be seeing this, which IMO is a pretty good error message:
Private Method in UsersController
Error: FooController::_sendMail() cannot be accessed directly.
Remove the leading underscore, that's all. This problem has nothing to do with sending email.
Try:
//load mail component in controller
var $components = array('Mail');
//then do
$this->Email->sendAs="html";
$this->Email->from="some#domain.com";
$this->Email->to="someone#domain.com";
$this->Email->subject="Your subject;
$this->Email->send("Your message);
//Check cakephp manual for more reference: http://book.cakephp.org/
Here is an example code for sending an HTML email with CakePHP's Email component
Ex controller : EmailController.php
<?php
class EmailController extends AppController{
public $components=array('Email');
function send(){
//create an array of values to be replaced in email html template//
$emailValues=array('name'=>'MyName','phone'=>'MyPhone');
$this->set('emailValues',$emailValues);//pass to template //
$this->Email->to = 'to#address.com'; //receiver email id
$this->Email->subject = 'Subject Line';
$this->Email->replyTo = 'reply#address.com'; //reply to email//
$this->Email->from = 'SenderName<sender#address.com>'; //sender
$this->Email->template = 'sample';//email template //
$this->Email->sendAs = 'html';
if($this->Email->send()){
//mail send //
}
}
?>
Now create the email template in folder /Views/Email/html/
ie the template path should be
/Views/Email/html/sample.ctp
sample.ctp
<?php
Hi <?php echo $emailValues['name'];?> <br>
Thanks for sharing your phone number '<?php echo $emailValues['phone'];?>' .
<br>
?>

Categories