From b965d4eec75948cc663df7205b2db8669edd15bc Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 9 Nov 2013 22:24:27 -0500 Subject: [PATCH] Implemented file transport for mails. --- extensions/swiftmailer/Mailer.php | 2 +- framework/yii/mail/BaseMailer.php | 80 +++++++++++++++++++++++++-- tests/unit/framework/mail/BaseMailerTest.php | 26 ++++++++- tests/unit/framework/mail/BaseMessageTest.php | 2 +- 4 files changed, 101 insertions(+), 9 deletions(-) diff --git a/extensions/swiftmailer/Mailer.php b/extensions/swiftmailer/Mailer.php index 7c37a3f..35b3b7c 100644 --- a/extensions/swiftmailer/Mailer.php +++ b/extensions/swiftmailer/Mailer.php @@ -104,7 +104,7 @@ class Mailer extends BaseMailer /** * @inheritdoc */ - public function send($message) + protected function sendMessage($message) { $address = $message->getTo(); if (is_array($address)) { diff --git a/framework/yii/mail/BaseMailer.php b/framework/yii/mail/BaseMailer.php index 52f31ce..4cc5279 100644 --- a/framework/yii/mail/BaseMailer.php +++ b/framework/yii/mail/BaseMailer.php @@ -16,7 +16,7 @@ use yii\web\View; /** * BaseMailer serves as a base class that implements the basic functions required by [[MailerInterface]]. * - * Concrete child classes should may focus on implementing the [[send()]] method. + * Concrete child classes should may focus on implementing the [[sendMessage()]] method. * * @see BaseMessage * @@ -29,10 +29,6 @@ use yii\web\View; abstract class BaseMailer extends Component implements MailerInterface, ViewContextInterface { /** - * @var \yii\base\View|array view instance or its array configuration. - */ - private $_view = []; - /** * @var string directory containing view files for this email messages. * This can be specified as an absolute path or path alias. */ @@ -71,6 +67,27 @@ abstract class BaseMailer extends Component implements MailerInterface, ViewCont * @var string the default class name of the new message instances created by [[createMessage()]] */ public $messageClass = 'yii\mail\BaseMessage'; + /** + * @var boolean whether to save email messages as files under [[fileTransportPath]] instead of sending them + * to the actual recipients. This is usually used during development for debugging purpose. + * @see fileTransportPath + */ + public $useFileTransport = false; + /** + * @var string the directory where the email messages are saved when [[useFileTransport]] is true. + */ + public $fileTransportPath = '@runtime/mail'; + /** + * @var callback a PHP callback that will be called by [[send()]] when [[useFileTransport]] is true. + * The callback should return a file name which will be used to save the email message. + * If not set, the file name will be generated based on the current timestamp. + */ + public $fileTransportCallback; + + /** + * @var \yii\base\View|array view instance or its array configuration. + */ + private $_view = []; /** * @param array|View $view view instance or its array configuration that will be used to @@ -173,6 +190,30 @@ abstract class BaseMailer extends Component implements MailerInterface, ViewCont } /** + * Sends the given email message. + * This method will log a message about the email being sent. + * If [[useFileTransport]] is true, it will save the email as a file under [[fileTransportPath]]. + * Otherwise, it will call [[sendMessage()]] to send the email to its recipient(s). + * Child classes should implement [[sendMessage()]] with the actual email sending logic. + * @param MessageInterface $message email message instance to be sent + * @return boolean whether the message has been sent successfully + */ + public function send($message) + { + $address = $message->getTo(); + if (is_array($address)) { + $address = implode(', ', array_keys($address)); + } + Yii::trace('Sending email "' . $message->getSubject() . '" to "' . $address . '"', __METHOD__); + + if ($this->useFileTransport) { + return $this->saveMessage($message); + } else { + return $this->sendMessage($message); + } + } + + /** * Sends multiple messages at once. * * The default implementation simply calls [[send()]] multiple times. @@ -212,6 +253,35 @@ abstract class BaseMailer extends Component implements MailerInterface, ViewCont } /** + * Sends the specified message. + * This method should be implemented by child classes with the actual email sending logic. + * @param MessageInterface $message the message to be sent + * @return boolean whether the message is sent successfully + */ + abstract protected function sendMessage($message); + + /** + * Saves the message as a file under [[fileTransportPath]]. + * @param MessageInterface $message + * @return boolean whether the message is saved successfully + */ + protected function saveMessage($message) + { + $path = Yii::getAlias($this->fileTransportPath); + if (!is_dir(($path))) { + mkdir($path, 0777, true); + } + if ($this->fileTransportCallback !== null) { + $file = $path . '/' . call_user_func($this->fileTransportCallback); + } else { + $time = microtime(true); + $file = $path . '/' . date('Ymd-His-', $time) . sprintf('%04d', (int)(($time - (int)$time) * 10000)) . '-' . sprintf('%04d', mt_rand(0, 10000)) . '.txt'; + } + file_put_contents($file, $message->toString()); + return true; + } + + /** * Finds the view file corresponding to the specified relative view name. * This method will return the view file by prefixing the view name with [[viewPath]]. * @param string $view a relative view name. The name does NOT start with a slash. diff --git a/tests/unit/framework/mail/BaseMailerTest.php b/tests/unit/framework/mail/BaseMailerTest.php index 58e4c02..1c3ee22 100644 --- a/tests/unit/framework/mail/BaseMailerTest.php +++ b/tests/unit/framework/mail/BaseMailerTest.php @@ -208,6 +208,28 @@ class BaseMailerTest extends TestCase $this->assertEquals($htmlViewFileContent, $message->_htmlBody, 'Unable to render html by direct view!'); $this->assertEquals(strip_tags($htmlViewFileContent), $message->_textBody, 'Unable to render text by direct view!'); } + + public function testUseFileTransport() + { + $mailer = new Mailer(); + $this->assertFalse($mailer->useFileTransport); + $this->assertEquals('@runtime/mail', $mailer->fileTransportPath); + + $mailer->fileTransportPath = '@yiiunit/runtime/mail'; + $mailer->useFileTransport = true; + $mailer->fileTransportCallback = function () { + return 'message.txt'; + }; + $message = $mailer->compose() + ->setTo('to@example.com') + ->setFrom('from@example.com') + ->setSubject('test subject') + ->setTextBody('text body' . microtime(true)); + $this->assertTrue($mailer->send($message)); + $file = Yii::getAlias($mailer->fileTransportPath) . '/message.txt'; + $this->assertTrue(is_file($file)); + $this->assertEquals($message->toString(), file_get_contents($file)); + } } /** @@ -218,7 +240,7 @@ class Mailer extends BaseMailer public $messageClass = 'yiiunit\framework\mail\Message'; public $sentMessages = []; - public function send($message) + protected function sendMessage($message) { $this->sentMessages[] = $message; } @@ -340,6 +362,6 @@ class Message extends BaseMessage public function toString() { - return get_class($this); + return var_export($this, true); } } diff --git a/tests/unit/framework/mail/BaseMessageTest.php b/tests/unit/framework/mail/BaseMessageTest.php index 0ffedf3..d80d4f7 100644 --- a/tests/unit/framework/mail/BaseMessageTest.php +++ b/tests/unit/framework/mail/BaseMessageTest.php @@ -71,7 +71,7 @@ class TestMailer extends BaseMailer public $messageClass = 'yiiunit\framework\mail\TestMessage'; public $sentMessages = array(); - public function send($message) + protected function sendMessage($message) { $this->sentMessages[] = $message; }