Browse Source

Merge pull request #1049 from klimov-paul/email-swift-2

Proposal for #31: email component based on SwiftMailer
tags/2.0.0-beta
Qiang Xue 11 years ago
parent
commit
d0a2358217
  1. 2
      .travis.yml
  2. 32
      extensions/swiftmailer/LICENSE.md
  3. 58
      extensions/swiftmailer/README.md
  4. 28
      extensions/swiftmailer/composer.json
  5. 142
      extensions/swiftmailer/yii/swiftmailer/Mailer.php
  6. 237
      extensions/swiftmailer/yii/swiftmailer/Message.php
  7. 212
      framework/yii/mail/BaseMailer.php
  8. 61
      framework/yii/mail/BaseMessage.php
  9. 58
      framework/yii/mail/MailerInterface.php
  10. 152
      framework/yii/mail/MessageInterface.php
  11. 30
      tests/unit/VendorTestCase.php
  12. 69
      tests/unit/extensions/swiftmailer/MailerTest.php
  13. 311
      tests/unit/extensions/swiftmailer/MessageTest.php
  14. 303
      tests/unit/framework/mail/BaseMailerTest.php
  15. 120
      tests/unit/framework/mail/BaseMessageTest.php

2
.travis.yml

@ -18,7 +18,7 @@ before_script:
- tests/unit/data/travis/cubrid-setup.sh
script:
- phpunit --coverage-clover tests/unit/runtime/coveralls/clover.xml --verbose --exclude-group mssql,oci,wincache,xcache,zenddata
- phpunit --coverage-clover tests/unit/runtime/coveralls/clover.xml --verbose --exclude-group mssql,oci,wincache,xcache,zenddata,vendor
after_script:
- php vendor/bin/coveralls

32
extensions/swiftmailer/LICENSE.md

@ -0,0 +1,32 @@
The Yii framework is free software. It is released under the terms of
the following BSD License.
Copyright © 2008-2013 by Yii Software LLC (http://www.yiisoft.com)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Yii Software LLC nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

58
extensions/swiftmailer/README.md

@ -0,0 +1,58 @@
Yii 2.0 Public Preview - SwiftMailer Mail Solution
==================================================
Thank you for choosing Yii - a high-performance component-based PHP framework.
If you are looking for a production-ready PHP framework, please use
[Yii v1.1](https://github.com/yiisoft/yii).
Yii 2.0 is still under heavy development. We may make significant changes
without prior notices. **Yii 2.0 is not ready for production use yet.**
[![Build Status](https://secure.travis-ci.org/yiisoft/yii2.png)](http://travis-ci.org/yiisoft/yii2)
This is the yii2-swiftmailer extension.
Installation
------------
The preferred way to install this extension is through [composer](http://getcomposer.org/download/).
Either run
```
php composer.phar require yiisoft/yii2-swiftmailer "*"
```
or add
```json
"yiisoft/yii2-swiftmailer": "*"
```
to the require section of your composer.json.
*Note: You might have to run `php composer.phar selfupdate`*
Usage & Documentation
---------------------
This extension has to be registered prior to usage.
To enable this view renderer add it to the $rendereres property of your view object.
Example:
```php
<?php
// config.php
return [
//....
'components' => [
'mail' => [
'class' => 'yii\swiftmailer\Mailer',
],
],
];
```
For further instructions refer to the related section in the yii guide.

28
extensions/swiftmailer/composer.json

@ -0,0 +1,28 @@
{
"name": "yiisoft/yii2-swiftmailer",
"description": "The SwiftMailer integration for the Yii framework",
"keywords": ["yii", "swift", "swiftmailer", "mail", "email", "mailer"],
"type": "yii2-extension",
"license": "BSD-3-Clause",
"support": {
"issues": "https://github.com/yiisoft/yii2/issues?state=open",
"forum": "http://www.yiiframework.com/forum/",
"wiki": "http://www.yiiframework.com/wiki/",
"irc": "irc://irc.freenode.net/yii",
"source": "https://github.com/yiisoft/yii2"
},
"authors": [
{
"name": "Paul Klimov",
"email": "klimov.paul@gmail.com"
}
],
"minimum-stability": "dev",
"require": {
"yiisoft/yii2": "*",
"swiftmailer/swiftmailer": "@stable"
},
"autoload": {
"psr-0": { "yii\\swiftmailer\\": "" }
}
}

142
extensions/swiftmailer/yii/swiftmailer/Mailer.php

@ -0,0 +1,142 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\swiftmailer;
use yii\base\InvalidConfigException;
use yii\mail\BaseMailer;
use Yii;
/**
* Mailer based on SwiftMailer library.
*
* By default PHP 'mail' function will be used as default email transport.
* You can setup different email transport via [[vendorMailer]] property:
* ~~~
* 'components' => array(
* ...
* 'email' => array(
* 'class' => 'yii\swiftmailer\Mailer',
* 'transport' => [
* 'class' => 'Swift_SmtpTransport',
* 'host' => 'localhost',
* 'username' => 'username',
* 'password' => 'password',
* 'port' => '587',
* 'encryption' => 'tls',
* ],
* ),
* ...
* ),
* ~~~
*
* @see http://swiftmailer.org
*
* @method Message compose($view = null, array $params = []) creates new message optionally filling up its body via view rendering.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class Mailer extends BaseMailer
{
/**
* @var string message default class name.
*/
public $messageClass = 'yii\swiftmailer\Message';
/**
* @var \Swift_Mailer Swift mailer instance.
*/
private $_swiftMailer;
/**
* @var \Swift_Transport|array Swift transport instance or its array configuration.
*/
private $_transport = [];
/**
* @return array|\Swift_Mailer Swift mailer instance or array configuration.
*/
public function getSwiftMailer()
{
if (!is_object($this->_swiftMailer)) {
$this->_swiftMailer = $this->createSwiftMailer();
}
return $this->_swiftMailer;
}
/**
* @param array|\Swift_Transport $transport
* @throws \yii\base\InvalidConfigException on invalid argument.
*/
public function setTransport($transport)
{
if (!is_array($transport) && !is_object($transport)) {
throw new InvalidConfigException('"' . get_class($this) . '::transport" should be either object or array, "' . gettype($transport) . '" given.');
}
$this->_transport = $transport;
}
/**
* @return array|\Swift_Transport
*/
public function getTransport()
{
if (!is_object($this->_transport)) {
$this->_transport = $this->createTransport($this->_transport);
}
return $this->_transport;
}
/**
* @inheritdoc
*/
public function send($message)
{
Yii::trace('Sending email message', __METHOD__);
return ($this->getSwiftMailer()->send($message->getSwiftMessage()) > 0);
}
/**
* Creates Swift mailer instance.
* @return \Swift_Mailer mailer instance.
*/
protected function createSwiftMailer()
{
return \Swift_Mailer::newInstance($this->getTransport());
}
/**
* Creates email transport instance by its array configuration.
* @param array $config transport configuration.
* @throws \yii\base\InvalidConfigException on invalid transport configuration.
* @return \Swift_Transport transport instance.
*/
protected function createTransport(array $config)
{
if (array_key_exists('class', $config)) {
$className = $config['class'];
unset($config['class']);
} else {
$className = 'Swift_MailTransport';
}
$transport = call_user_func([$className, 'newInstance']);
if (!empty($config)) {
foreach ($config as $name => $value) {
if (property_exists($transport, $name)) {
$transport->$name = $value;
} else {
$setter = 'set' . $name;
if (method_exists($transport, $setter)) {
$transport->$setter($value);
} else {
throw new InvalidConfigException('Setting unknown property: ' . get_class($transport) . '::' . $name);
}
}
}
}
return $transport;
}
}

237
extensions/swiftmailer/yii/swiftmailer/Message.php

@ -0,0 +1,237 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\swiftmailer;
use yii\mail\BaseMessage;
/**
* Email message based on SwiftMailer library.
*
* @see http://swiftmailer.org/docs/messages.html
* @see \yii\swiftmailer\Mailer
*
* @method Mailer getMailer() returns mailer instance.
* @property \Swift_Message $swiftMessage vendor message instance.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class Message extends BaseMessage
{
/**
* @var \Swift_Message Swift message instance.
*/
private $_swiftMessage;
/**
* @return \Swift_Message Swift message instance.
*/
public function getSwiftMessage()
{
if (!is_object($this->_swiftMessage)) {
$this->_swiftMessage = $this->createSwiftMessage();
}
return $this->_swiftMessage;
}
/**
* @inheritdoc
*/
public function charset($charset)
{
$this->getSwiftMessage()->setCharset($charset);
return $this;
}
/**
* @inheritdoc
*/
public function from($from)
{
$this->getSwiftMessage()->setFrom($from);
$this->getSwiftMessage()->setReplyTo($from);
return $this;
}
/**
* @inheritdoc
*/
public function to($to)
{
$this->getSwiftMessage()->setTo($to);
return $this;
}
/**
* @inheritdoc
*/
public function cc($cc)
{
$this->getSwiftMessage()->setCc($cc);
return $this;
}
/**
* @inheritdoc
*/
public function bcc($bcc)
{
$this->getSwiftMessage()->setBcc($bcc);
return $this;
}
/**
* @inheritdoc
*/
public function subject($subject)
{
$this->getSwiftMessage()->setSubject($subject);
return $this;
}
/**
* @inheritdoc
*/
public function textBody($text)
{
$this->setBody($text, 'text/plain');
return $this;
}
/**
* @inheritdoc
*/
public function htmlBody($html)
{
$this->setBody($html, 'text/html');
return $this;
}
/**
* Sets the message body.
* If body is already set and its content type matches given one, it will
* be overridden, if content type miss match the multipart message will be composed.
* @param string $body body content.
* @param string $contentType body content type.
*/
protected function setBody($body, $contentType)
{
$message = $this->getSwiftMessage();
$oldBody = $message->getBody();
if (empty($oldBody)) {
$parts = $message->getChildren();
$partFound = false;
foreach ($parts as $key => $part) {
if (!($part instanceof \Swift_Mime_Attachment)) {
/* @var $part \Swift_Mime_MimePart */
if ($part->getContentType() == $contentType) {
unset($parts[$key]);
$partFound = true;
break;
}
}
}
if ($partFound) {
reset($parts);
$message->setChildren($parts);
$message->addPart($body, $contentType);
} else {
$message->setBody($body, $contentType);
}
} else {
$oldContentType = $message->getContentType();
if ($oldContentType == $contentType) {
$message->setBody($body, $contentType);
} else {
$message->setBody(null);
$message->setContentType(null);
$message->addPart($oldBody, $oldContentType);
$message->addPart($body, $contentType);
}
}
}
/**
* @inheritdoc
*/
public function attach($fileName, array $options = [])
{
$attachment = \Swift_Attachment::fromPath($fileName);
if (!empty($options['fileName'])) {
$attachment->setFilename($options['fileName']);
}
if (!empty($options['contentType'])) {
$attachment->setContentType($options['contentType']);
}
$this->getSwiftMessage()->attach($attachment);
return $this;
}
/**
* @inheritdoc
*/
public function attachContent($content, array $options = [])
{
$attachment = \Swift_Attachment::newInstance($content);
if (!empty($options['fileName'])) {
$attachment->setFilename($options['fileName']);
}
if (!empty($options['contentType'])) {
$attachment->setContentType($options['contentType']);
}
$this->getSwiftMessage()->attach($attachment);
return $this;
}
/**
* @inheritdoc
*/
public function embed($fileName, array $options = [])
{
$embedFile = \Swift_EmbeddedFile::fromPath($fileName);
if (!empty($options['fileName'])) {
$embedFile->setFilename($options['fileName']);
}
if (!empty($options['contentType'])) {
$embedFile->setContentType($options['contentType']);
}
return $this->getSwiftMessage()->embed($embedFile);
}
/**
* @inheritdoc
*/
public function embedContent($content, array $options = [])
{
$embedFile = \Swift_EmbeddedFile::newInstance($content);
if (!empty($options['fileName'])) {
$embedFile->setFilename($options['fileName']);
}
if (!empty($options['contentType'])) {
$embedFile->setContentType($options['contentType']);
}
return $this->getSwiftMessage()->embed($embedFile);
}
/**
* @inheritdoc
*/
public function toString()
{
return $this->getSwiftMessage()->toString();
}
/**
* Creates the Swift email message instance.
* @return \Swift_Message email message instance.
*/
protected function createSwiftMessage()
{
return new \Swift_Message();
}
}

212
framework/yii/mail/BaseMailer.php

@ -0,0 +1,212 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\mail;
use yii\base\Component;
use yii\base\InvalidConfigException;
use Yii;
use yii\base\ViewContextInterface;
/**
* BaseMailer provides the basic interface for the email mailer application component.
* It provides the default configuration for the email messages.
* Particular implementation of mailer should provide implementation for the [[send()]] method.
*
* @see BaseMessage
*
* @property \yii\base\View|array $view view instance or its array configuration.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
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.
*/
public $viewPath = '@app/mails';
/**
* @var string HTML layout view name.
*/
public $htmlLayout = 'layouts/html';
/**
* @var string text layout view name.
*/
public $textLayout = 'layouts/text';
/**
* @var array configuration, which should be applied by default to any new created
* email message instance.
* In addition to normal [[Yii::createObject()]] behavior extra config keys are available:
* - 'charset' argument for [[MessageInterface::charset()]]
* - 'from' argument for [[MessageInterface::from()]]
* - 'to' argument for [[MessageInterface::to()]]
* - 'cc' argument for [[MessageInterface::cc()]]
* - 'bcc' argument for [[MessageInterface::bcc()]]
* - 'subject' argument for [[MessageInterface::subject()]]
* - 'textBody' argument for [[MessageInterface::textBody()]]
* - 'htmlBody' argument for [[MessageInterface::htmlBody()]]
* For example:
* ~~~
* array(
* 'charset' => 'UTF-8',
* 'from' => 'noreply@mydomain.com',
* 'bcc' => 'developer@mydomain.com',
* )
* ~~~
*/
public $messageConfig = [];
/**
* @var string message default class name.
*/
public $messageClass = 'yii\mail\BaseMessage';
/**
* @param array|\yii\base\View $view view instance or its array configuration.
* @throws \yii\base\InvalidConfigException on invalid argument.
*/
public function setView($view)
{
if (!is_array($view) && !is_object($view)) {
throw new InvalidConfigException('"' . get_class($this) . '::view" should be either object or array, "' . gettype($view) . '" given.');
}
$this->_view = $view;
}
/**
* @return \yii\base\View view instance.
*/
public function getView()
{
if (!is_object($this->_view)) {
$this->_view = $this->createView($this->_view);
}
return $this->_view;
}
/**
* Creates view instance from given configuration.
* @param array $config view configuration.
* @return \yii\base\View view instance.
*/
protected function createView(array $config)
{
if (!array_key_exists('class', $config)) {
$config['class'] = 'yii\web\View';
}
return Yii::createObject($config);
}
/**
* @inheritdoc
*/
public function compose($view = null, array $params = [])
{
$message = $this->createMessage();
if ($view !== null) {
$params['message'] = $message;
if (is_array($view)) {
if (array_key_exists('html', $view)) {
$message->htmlBody($this->render($view['html'], $params, $this->htmlLayout));
}
if (array_key_exists('text', $view)) {
$message->textBody($this->render($view['text'], $params, $this->textLayout));
}
} else {
$html = $this->render($view, $params, $this->htmlLayout);
$message->htmlBody($html);
$message->textBody(strip_tags($html));
}
}
return $message;
}
/**
* Creates mew message instance using configuration from [[messageConfig]].
* If 'class' parameter is omitted, [[messageClass]] will be used.
* @return MessageInterface message instance.
*/
protected function createMessage()
{
$config = $this->messageConfig;
if (!array_key_exists('class', $config)) {
$config['class'] = $this->messageClass;
}
$directSetterNames = [
'charset',
'from',
'to',
'cc',
'bcc',
'subject',
'textBody',
'htmlBody',
];
$directSetterConfig = [];
foreach ($config as $name => $value) {
if (in_array($name, $directSetterNames, true)) {
$directSetterConfig[$name] = $value;
unset($config[$name]);
}
}
$message = Yii::createObject($config);
foreach ($directSetterConfig as $name => $value) {
$message->$name($value);
}
return $message;
}
/**
* Sends a couple of messages at once.
* Note: some particular mailers may benefit from sending messages as batch,
* saving resources, for example on open/close connection operations,
* they may override this method to create their specific implementation.
* @param array $messages list of email messages, which should be sent.
* @return integer number of successful sends.
*/
public function sendMultiple(array $messages)
{
$successCount = 0;
foreach ($messages as $message) {
if ($this->send($message)) {
$successCount++;
}
}
return $successCount;
}
/**
* Renders a view.
* @param string $view the view name or the path alias of the view file.
* @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file.
* @param string|boolean $layout layout view name, if false given no layout will be applied.
* @return string the rendering result.
*/
public function render($view, $params = [], $layout = false)
{
$output = $this->getView()->render($view, $params, $this);
if ($layout !== false) {
return $this->getView()->render($layout, ['content' => $output], $this);
} else {
return $output;
}
}
/**
* Finds the view file corresponding to the specified relative view name.
* @param string $view a relative view name. The name does NOT start with a slash.
* @return string the view file path. Note that the file may not exist.
*/
public function findViewFile($view)
{
return Yii::getAlias($this->viewPath) . DIRECTORY_SEPARATOR . $view;
}
}

61
framework/yii/mail/BaseMessage.php

@ -0,0 +1,61 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\mail;
use yii\base\InvalidParamException;
use yii\base\Object;
use yii\helpers\FileHelper;
use Yii;
/**
* BaseMessage represent the single email message.
* It functionality depends on application component 'email',
* which should provide the actual email sending functionality as well as
* default message configuration.
*
* @see BaseMailer
*
* @property \yii\mail\BaseMailer $mailer mailer component instance. This property is read-only.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
abstract class BaseMessage extends Object implements MessageInterface
{
/**
* @return \yii\mail\BaseMailer mailer component instance.
*/
public function getMailer()
{
return Yii::$app->getComponent('mail');
}
/**
* @inheritdoc
*/
public function send()
{
return $this->getMailer()->send($this);
}
/**
* PHP magic method that returns the string representation of this object.
* @return string the string representation of this object.
*/
public function __toString()
{
// __toString cannot throw exception
// use trigger_error to bypass this limitation
try {
return $this->toString();
} catch (\Exception $e) {
trigger_error($e->getMessage());
return '';
}
}
}

58
framework/yii/mail/MailerInterface.php

@ -0,0 +1,58 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\mail;
/**
* MailerInterface is an interface, which any mailer should apply.
* Mailer creates and sends messages. Also it allows composition of the message
* body via view rendering:
* ~~~php
* Yii::$app->mail->compose('contact/html', ['contactForm' => $form])
* ->from('from@domain.com')
* ->to($form->email)
* ->subject($form->subject)
* ->send();
* ~~~
*
* @see MessageInterface
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
interface MailerInterface
{
/**
* Creates new message optionally filling up its body via view rendering.
* The view to be rendered can be specified in one of the following formats:
* - path alias (e.g. "@app/mails/contact/body");
* - relative path (e.g. "contact"): the actual view file will be resolved by [[\yii\base\ViewContextInterface]].
* @param string|array $view view, which should be used to render message body
* - if string - the view name or the path alias of the HTML body view file, in this case
* text body will be composed automatically from html one.
* - if array - list of views for each body type in format: ['html' => 'htmlView', 'text' => 'textView']
* @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file.
* @return MessageInterface message instance.
*/
public function compose($view = null, array $params = []);
/**
* Sends the given email message.
* @param object $message email message instance
* @return boolean whether the message has been sent.
*/
public function send($message);
/**
* Sends a couple of messages at once.
* Note: some particular mailers may benefit from sending messages as batch,
* saving resources, for example on open/close connection operations.
* @param array $messages list of email messages, which should be sent.
* @return integer number of successful sends.
*/
public function sendMultiple(array $messages);
}

152
framework/yii/mail/MessageInterface.php

@ -0,0 +1,152 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\mail;
/**
* MessageInterface is an interface, which email message should apply.
* Together with application component, which matches the [[MailerInterface]],
* it introduces following mail sending syntax:
* ~~~php
* Yii::$app->mail->compose()
* ->from('from@domain.com')
* ->to('to@domain.com')
* ->subject('Message Subject')
* ->textBody('Plain text content')
* ->htmlBody('<b>HTML content</b>')
* ->send();
* ~~~
*
* @see MailerInterface
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
interface MessageInterface
{
/**
* Set the character set of this message.
* @param string $charset character set name.
* @return static self reference.
*/
public function charset($charset);
/**
* Sets message sender.
* @param string|array $from sender email address.
* You may pass an array of addresses if this message is from multiple people.
* You may also specify sender name in addition to email address using format:
* [email => name].
* @return static self reference.
*/
public function from($from);
/**
* Sets message receiver.
* @param string|array $to receiver email address.
* You may pass an array of addresses if multiple recipients should receive this message.
* You may also specify receiver name in addition to email address using format:
* [email => name].
* @return static self reference.
*/
public function to($to);
/**
* Set the Cc (additional copy receiver) addresses of this message.
* @param string|array $cc copy receiver email address.
* You may pass an array of addresses if multiple recipients should receive this message.
* You may also specify receiver name in addition to email address using format:
* [email => name].
* @return static self reference.
*/
public function cc($cc);
/**
* Set the Bcc (hidden copy receiver) addresses of this message.
* @param string|array $bcc hidden copy receiver email address.
* You may pass an array of addresses if multiple recipients should receive this message.
* You may also specify receiver name in addition to email address using format:
* [email => name].
* @return static self reference.
*/
public function bcc($bcc);
/**
* Sets message subject.
* @param string $subject message subject
* @return static self reference.
*/
public function subject($subject);
/**
* Sets message plain text content.
* @param string $text message plain text content.
* @return static self reference.
*/
public function textBody($text);
/**
* Sets message HTML content.
* @param string $html message HTML content.
* @return static self reference.
*/
public function htmlBody($html);
/**
* Attaches existing file to the email message.
* @param string $fileName full file name
* @param array $options options for embed file. Valid options are:
* - fileName: name, which should be used to attach file.
* - contentType: attached file MIME type.
* @return static self reference.
*/
public function attach($fileName, array $options = []);
/**
* Attach specified content as file for the email message.
* @param string $content attachment file content.
* @param array $options options for embed file. Valid options are:
* - fileName: name, which should be used to attach file.
* - contentType: attached file MIME type.
* @return static self reference.
*/
public function attachContent($content, array $options = []);
/**
* Attach a file and return it's CID source.
* This method should be used when embedding images or other data in a message.
* @param string $fileName file name.
* @param array $options options for embed file. Valid options are:
* - fileName: name, which should be used to attach file.
* - contentType: attached file MIME type.
* @return string attachment CID.
*/
public function embed($fileName, array $options = []);
/**
* Attach a content as file and return it's CID source.
* This method should be used when embedding images or other data in a message.
* @param string $content attachment file content.
* @param array $options options for embed file. Valid options are:
* - fileName: name, which should be used to attach file.
* - contentType: attached file MIME type.
* @return string attachment CID.
*/
public function embedContent($content, array $options = []);
/**
* Sends this email message.
* @return boolean success.
*/
public function send();
/**
* Returns string representation of this message.
* @return string the string representation of this message.
*/
public function toString();
}

30
tests/unit/VendorTestCase.php

@ -0,0 +1,30 @@
<?php
namespace yiiunit;
use yii\base\NotSupportedException;
use Yii;
/**
* This is the base class for all yii framework unit tests, which requires
* external vendor libraries to function.
*/
class VendorTestCase extends TestCase
{
/**
* This method is called before the first test of this test class is run.
* Attempts to load vendor autoloader.
* @throws \yii\base\NotSupportedException
*/
public static function setUpBeforeClass()
{
$vendorDir = __DIR__ . '/vendor';
Yii::setAlias('@vendor', $vendorDir);
$vendorAutoload = $vendorDir . '/autoload.php';
if (file_exists($vendorAutoload)) {
require_once($vendorAutoload);
} else {
throw new NotSupportedException("Vendor autoload file '{$vendorAutoload}' is missing.");
}
}
}

69
tests/unit/extensions/swiftmailer/MailerTest.php

@ -0,0 +1,69 @@
<?php
namespace yiiunit\extensions\swiftmailer;
use Yii;
use yii\swiftmailer\Mailer;
use yii\swiftmailer\Message;
use yiiunit\VendorTestCase;
/**
* @group vendor
* @group mail
* @group swiftmailer
*/
class MailerTest extends VendorTestCase
{
public function setUp()
{
$this->mockApplication([
'components' => [
'email' => $this->createTestEmailComponent()
]
]);
}
/**
* @return Mailer test email component instance.
*/
protected function createTestEmailComponent()
{
$component = new Mailer();
return $component;
}
// Tests :
public function testSetupTransport()
{
$mailer = new Mailer();
$transport = \Swift_MailTransport::newInstance();
$mailer->setTransport($transport);
$this->assertEquals($transport, $mailer->getTransport(), 'Unable to setup transport!');
}
/**
* @depends testSetupTransport
*/
public function testConfigureTransport()
{
$mailer = new Mailer();
$transportConfig = [
'class' => 'Swift_SmtpTransport',
'host' => 'localhost',
];
$mailer->setTransport($transportConfig);
$transport = $mailer->getTransport();
$this->assertTrue(is_object($transport), 'Unable to setup transport via config!');
$this->assertEquals($transportConfig['class'], get_class($transport), 'Invalid transport class!');
$this->assertEquals($transportConfig['host'], $transport->getHost(), 'Invalid transport host!');
}
public function testGetSwiftMailer()
{
$mailer = new Mailer();
$this->assertTrue(is_object($mailer->getSwiftMailer()), 'Unable to get Swift mailer instance!');
}
}

311
tests/unit/extensions/swiftmailer/MessageTest.php

@ -0,0 +1,311 @@
<?php
namespace yiiunit\extensions\swiftmailer;
use Yii;
use yii\helpers\FileHelper;
use yii\swiftmailer\Mailer;
use yii\swiftmailer\Message;
use yiiunit\VendorTestCase;
/**
* @group vendor
* @group mail
* @group swiftmailer
*/
class MessageTest extends VendorTestCase
{
/**
* @var string test email address, which will be used as receiver for the messages.
*/
protected $testEmailReceiver = 'someuser@somedomain.com';
public function setUp()
{
$this->mockApplication([
'components' => [
'mail' => $this->createTestEmailComponent()
]
]);
$filePath = $this->getTestFilePath();
if (!file_exists($filePath)) {
FileHelper::createDirectory($filePath);
}
}
public function tearDown()
{
$filePath = $this->getTestFilePath();
if (file_exists($filePath)) {
FileHelper::removeDirectory($filePath);
}
}
/**
* @return string test file path.
*/
protected function getTestFilePath()
{
return Yii::getAlias('@yiiunit/runtime') . DIRECTORY_SEPARATOR . basename(get_class($this)) . '_' . getmypid();
}
/**
* @return Mailer test email component instance.
*/
protected function createTestEmailComponent()
{
$component = new Mailer();
return $component;
}
/**
* @return Message test message instance.
*/
protected function createTestMessage()
{
return Yii::$app->getComponent('mail')->compose();
}
/**
* Creates image file with given text.
* @param string $fileName file name.
* @param string $text text to be applied on image.
* @return string image file full name.
*/
protected function createImageFile($fileName = 'test.jpg', $text = 'Test Image')
{
if (!function_exists('imagecreatetruecolor')) {
$this->markTestSkipped('GD lib required.');
}
$fileFullName = $this->getTestFilePath() . DIRECTORY_SEPARATOR . $fileName;
$image = imagecreatetruecolor(120, 20);
$textColor = imagecolorallocate($image, 233, 14, 91);
imagestring($image, 1, 5, 5, $text, $textColor);
imagejpeg($image, $fileFullName);
imagedestroy($image);
return $fileFullName;
}
/**
* Finds the attachment object in the message.
* @param Message $message message instance
* @return null|\Swift_Mime_Attachment attachment instance.
*/
protected function getAttachment(Message $message)
{
$messageParts = $message->getSwiftMessage()->getChildren();
$attachment = null;
foreach ($messageParts as $part) {
if ($part instanceof \Swift_Mime_Attachment) {
$attachment = $part;
break;
}
}
return $attachment;
}
// Tests :
public function testGetSwiftMessage()
{
$message = new Message();
$this->assertTrue(is_object($message->getSwiftMessage()), 'Unable to get Swift message!');
}
/**
* @depends testGetSwiftMessage
*/
public function testSetupHeaders()
{
$charset = 'utf-16';
$subject = 'Test Subject';
$to = 'someuser@somedomain.com';
$cc = 'ccuser@somedomain.com';
$bcc = 'bccuser@somedomain.com';
$messageString = $this->createTestMessage()
->charset($charset)
->subject($subject)
->to($to)
->cc($cc)
->bcc($bcc)
->toString();
$this->assertContains('charset=' . $charset, $messageString, 'Incorrect charset!');
$this->assertContains('Subject: ' . $subject, $messageString, 'Incorrect "Subject" header!');
$this->assertContains('To: ' . $to, $messageString, 'Incorrect "To" header!');
$this->assertContains('Cc: ' . $cc, $messageString, 'Incorrect "Cc" header!');
$this->assertContains('Bcc: ' . $bcc, $messageString, 'Incorrect "Bcc" header!');
}
/**
* @depends testGetSwiftMessage
*/
public function testSetupFrom()
{
$from = 'someuser@somedomain.com';
$messageString = $this->createTestMessage()
->from($from)
->toString();
$this->assertContains('From: ' . $from, $messageString, 'Incorrect "From" header!');
$this->assertContains('Reply-To: ' . $from, $messageString, 'Incorrect "Reply-To" header!');
}
/**
* @depends testGetSwiftMessage
*/
public function testSend()
{
$message = $this->createTestMessage();
$message->to($this->testEmailReceiver);
$message->from('someuser@somedomain.com');
$message->subject('Yii Swift Test');
$message->textBody('Yii Swift Test body');
$this->assertTrue($message->send());
}
/**
* @depends testSend
*/
public function testAttachFile()
{
$message = $this->createTestMessage();
$message->to($this->testEmailReceiver);
$message->from('someuser@somedomain.com');
$message->subject('Yii Swift Attach File Test');
$message->textBody('Yii Swift Attach File Test body');
$fileName = __FILE__;
$message->attach($fileName);
$this->assertTrue($message->send());
$attachment = $this->getAttachment($message);
$this->assertTrue(is_object($attachment), 'No attachment found!');
$this->assertContains($attachment->getFilename(), $fileName, 'Invalid file name!');
}
/**
* @depends testSend
*/
public function testAttachContent()
{
$message = $this->createTestMessage();
$message->to($this->testEmailReceiver);
$message->from('someuser@somedomain.com');
$message->subject('Yii Swift Create Attachment Test');
$message->textBody('Yii Swift Create Attachment Test body');
$fileName = 'test.txt';
$fileContent = 'Test attachment content';
$message->attachContent($fileContent, ['fileName' => $fileName]);
$this->assertTrue($message->send());
$attachment = $this->getAttachment($message);
$this->assertTrue(is_object($attachment), 'No attachment found!');
$this->assertEquals($fileName, $attachment->getFilename(), 'Invalid file name!');
}
/**
* @depends testSend
*/
public function testEmbedFile()
{
$fileName = $this->createImageFile('embed_file.jpg', 'Embed Image File');
$message = $this->createTestMessage();
$cid = $message->embed($fileName);
$message->to($this->testEmailReceiver);
$message->from('someuser@somedomain.com');
$message->subject('Yii Swift Embed File Test');
$message->htmlBody('Embed image: <img src="' . $cid. '" alt="pic">');
$this->assertTrue($message->send());
$attachment = $this->getAttachment($message);
$this->assertTrue(is_object($attachment), 'No attachment found!');
$this->assertContains($attachment->getFilename(), $fileName, 'Invalid file name!');
}
/**
* @depends testSend
*/
public function testEmbedContent()
{
$fileFullName = $this->createImageFile('embed_file.jpg', 'Embed Image File');
$message = $this->createTestMessage();
$fileName = basename($fileFullName);
$contentType = 'image/jpeg';
$fileContent = file_get_contents($fileFullName);
$cid = $message->embedContent($fileContent, ['fileName' => $fileName, 'contentType' => $contentType]);
$message->to($this->testEmailReceiver);
$message->from('someuser@somedomain.com');
$message->subject('Yii Swift Embed File Test');
$message->htmlBody('Embed image: <img src="' . $cid. '" alt="pic">');
$this->assertTrue($message->send());
$attachment = $this->getAttachment($message);
$this->assertTrue(is_object($attachment), 'No attachment found!');
$this->assertEquals($fileName, $attachment->getFilename(), 'Invalid file name!');
$this->assertEquals($contentType, $attachment->getContentType(), 'Invalid content type!');
}
/**
* @depends testSend
*/
public function testSendAlternativeBody()
{
$message = $this->createTestMessage();
$message->to($this->testEmailReceiver);
$message->from('someuser@somedomain.com');
$message->subject('Yii Swift Alternative Body Test');
$message->htmlBody('<b>Yii Swift</b> test HTML body');
$message->textBody('Yii Swift test plain text body');
$this->assertTrue($message->send());
$messageParts = $message->getSwiftMessage()->getChildren();
$textPresent = false;
$htmlPresent = false;
foreach ($messageParts as $part) {
if (!($part instanceof \Swift_Mime_Attachment)) {
/* @var $part \Swift_Mime_MimePart */
if ($part->getContentType() == 'text/plain') {
$textPresent = true;
}
if ($part->getContentType() == 'text/html') {
$htmlPresent = true;
}
}
}
$this->assertTrue($textPresent, 'No text!');
$this->assertTrue($htmlPresent, 'No HTML!');
}
/**
* @depends testGetSwiftMessage
*/
public function testSerialize()
{
$message = $this->createTestMessage();
$message->to($this->testEmailReceiver);
$message->from('someuser@somedomain.com');
$message->subject('Yii Swift Alternative Body Test');
$message->textBody('Yii Swift test plain text body');
$serializedMessage = serialize($message);
$this->assertNotEmpty($serializedMessage, 'Unable to serialize message!');
$unserializedMessaage = unserialize($serializedMessage);
$this->assertEquals($message, $unserializedMessaage, 'Unable to unserialize message!');
}
}

303
tests/unit/framework/mail/BaseMailerTest.php

@ -0,0 +1,303 @@
<?php
namespace yiiunit\framework\mail;
use Yii;
use yii\base\View;
use yii\mail\BaseMailer;
use yii\mail\BaseMessage;
use yii\helpers\FileHelper;
use yiiunit\TestCase;
/**
* @group mail
*/
class BaseMailerTest extends TestCase
{
public function setUp()
{
$this->mockApplication([
'components' => [
'mail' => $this->createTestMailComponent(),
]
]);
$filePath = $this->getTestFilePath();
if (!file_exists($filePath)) {
FileHelper::createDirectory($filePath);
}
}
public function tearDown()
{
$filePath = $this->getTestFilePath();
if (file_exists($filePath)) {
FileHelper::removeDirectory($filePath);
}
}
/**
* @return string test file path.
*/
protected function getTestFilePath()
{
return Yii::getAlias('@yiiunit/runtime') . DIRECTORY_SEPARATOR . basename(get_class($this)) . '_' . getmypid();
}
/**
* @return Mailer test email component instance.
*/
protected function createTestMailComponent()
{
$component = new Mailer();
$component->viewPath = $this->getTestFilePath();
return $component;
}
/**
* @return Mailer mailer instance
*/
protected function getTestMailComponent()
{
return Yii::$app->getComponent('mail');
}
// Tests :
public function testSetupView()
{
$mailer = new Mailer();
$view = new View();
$mailer->setView($view);
$this->assertEquals($view, $mailer->getView(), 'Unable to setup view!');
$viewConfig = [
'params' => [
'param1' => 'value1',
'param2' => 'value2',
]
];
$mailer->setView($viewConfig);
$view = $mailer->getView();
$this->assertTrue(is_object($view), 'Unable to setup view via config!');
$this->assertEquals($viewConfig['params'], $view->params, 'Unable to configure view via config array!');
}
/**
* @depends testSetupView
*/
public function testGetDefaultView()
{
$mailer = new Mailer();
$view = $mailer->getView();
$this->assertTrue(is_object($view), 'Unable to get default view!');
}
public function testCreateMessage()
{
$mailer = new Mailer();
$message = $mailer->compose();
$this->assertTrue(is_object($message), 'Unable to create message instance!');
$this->assertEquals($mailer->messageClass, get_class($message), 'Invalid message class!');
}
/**
* @depends testCreateMessage
*/
public function testDefaultMessageConfig()
{
$mailer = new Mailer();
$notPropertyConfig = [
'charset' => 'utf-16',
'from' => 'from@domain.com',
'to' => 'to@domain.com',
'cc' => 'cc@domain.com',
'bcc' => 'bcc@domain.com',
'subject' => 'Test subject',
'textBody' => 'Test text body',
'htmlBody' => 'Test HTML body',
];
$propertyConfig = [
'id' => 'test-id',
'encoding' => 'test-encoding',
];
$messageConfig = array_merge($notPropertyConfig, $propertyConfig);
$mailer->messageConfig = $messageConfig;
$message = $mailer->compose();
foreach ($notPropertyConfig as $name => $value) {
$this->assertEquals($value, $message->{'_' . $name});
}
foreach ($propertyConfig as $name => $value) {
$this->assertEquals($value, $message->$name);
}
}
/**
* @depends testGetDefaultView
*/
public function testRender()
{
$mailer = $this->getTestMailComponent();
$viewName = 'test_view';
$viewFileName = $this->getTestFilePath() . DIRECTORY_SEPARATOR . $viewName . '.php';
$viewFileContent = '<?php echo $testParam; ?>';
file_put_contents($viewFileName, $viewFileContent);
$params = [
'testParam' => 'test output'
];
$renderResult = $mailer->render($viewName, $params);
$this->assertEquals($params['testParam'], $renderResult);
}
/**
* @depends testRender
*/
public function testRenderLayout()
{
$mailer = $this->getTestMailComponent();
$filePath = $this->getTestFilePath();
$viewName = 'test_view';
$viewFileName = $filePath . DIRECTORY_SEPARATOR . $viewName . '.php';
$viewFileContent = 'view file content';
file_put_contents($viewFileName, $viewFileContent);
$layoutName = 'test_layout';
$layoutFileName = $filePath . DIRECTORY_SEPARATOR . $layoutName . '.php';
$layoutFileContent = 'Begin Layout <?php echo $content; ?> End Layout';
file_put_contents($layoutFileName, $layoutFileContent);
$renderResult = $mailer->render($viewName, [], $layoutName);
$this->assertEquals('Begin Layout ' . $viewFileContent . ' End Layout', $renderResult);
}
/**
* @depends testCreateMessage
* @depends testRender
*/
public function testCompose()
{
$mailer = $this->getTestMailComponent();
$mailer->htmlLayout = false;
$mailer->textLayout = false;
$htmlViewName = 'test_html_view';
$htmlViewFileName = $this->getTestFilePath() . DIRECTORY_SEPARATOR . $htmlViewName . '.php';
$htmlViewFileContent = 'HTML <b>view file</b> content';
file_put_contents($htmlViewFileName, $htmlViewFileContent);
$textViewName = 'test_text_view';
$textViewFileName = $this->getTestFilePath() . DIRECTORY_SEPARATOR . $textViewName . '.php';
$textViewFileContent = 'Plain text view file content';
file_put_contents($textViewFileName, $textViewFileContent);
$message = $mailer->compose([
'html' => $htmlViewName,
'text' => $textViewName,
]);
$this->assertEquals($htmlViewFileContent, $message->_htmlBody, 'Unable to render html!');
$this->assertEquals($textViewFileContent, $message->_textBody, 'Unable to render text!');
$message = $mailer->compose($htmlViewName);
$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!');
}
}
/**
* Test Mailer class
*/
class Mailer extends BaseMailer
{
public $messageClass = 'yiiunit\framework\mail\Message';
public $sentMessages = [];
public function send($message)
{
$this->sentMessages[] = $message;
}
}
/**
* Test Message class
*/
class Message extends BaseMessage
{
public $id;
public $encoding;
public $_charset;
public $_from;
public $_to;
public $_cc;
public $_bcc;
public $_subject;
public $_textBody;
public $_htmlBody;
public function charset($charset)
{
$this->_charset = $charset;
return $this;
}
public function from($from)
{
$this->_from = $from;
return $this;
}
public function to($to)
{
$this->_to = $to;
return $this;
}
public function cc($cc)
{
$this->_cc = $cc;
return $this;
}
public function bcc($bcc)
{
$this->_bcc = $bcc;
return $this;
}
public function subject($subject)
{
$this->_subject = $subject;
return $this;
}
public function textBody($text)
{
$this->_textBody = $text;
return $this;
}
public function htmlBody($html)
{
$this->_htmlBody = $html;
return $this;
}
public function attachContent($content, array $options = []) {}
public function attach($fileName, array $options = []) {}
public function embed($fileName, array $options = []) {}
public function embedContent($content, array $options = []) {}
public function toString()
{
return get_class($this);
}
}

120
tests/unit/framework/mail/BaseMessageTest.php

@ -0,0 +1,120 @@
<?php
namespace yiiunit\framework\mail;
use Yii;
use yii\mail\BaseMailer;
use yii\mail\BaseMessage;
use yiiunit\TestCase;
/**
* @group mail
*/
class BaseMessageTest extends TestCase
{
public function setUp()
{
$this->mockApplication([
'components' => [
'mail' => $this->createTestEmailComponent()
]
]);
}
/**
* @return Mailer test email component instance.
*/
protected function createTestEmailComponent()
{
$component = new TestMailer();
return $component;
}
/**
* @return TestMailer mailer instance.
*/
protected function getMailer()
{
return Yii::$app->getComponent('mail');
}
// Tests :
public function testGetMailer()
{
$mailer = $this->getMailer();
$message = $mailer->compose();
$this->assertEquals($mailer, $message->getMailer());
}
public function testSend()
{
$mailer = $this->getMailer();
$message = $mailer->compose();
$message->send();
$this->assertEquals($message, $mailer->sentMessages[0], 'Unable to send message!');
}
public function testToString()
{
$mailer = $this->getMailer();
$message = $mailer->compose();
$this->assertEquals($message->toString(), '' . $message);
}
}
/**
* Test Mailer class
*/
class TestMailer extends BaseMailer
{
public $messageClass = 'yiiunit\framework\mail\TestMessage';
public $sentMessages = array();
public function send($message)
{
$this->sentMessages[] = $message;
}
}
/**
* Test Message class
*/
class TestMessage extends BaseMessage
{
public $text;
public $html;
public function charset($charset) {}
public function from($from) {}
public function to($to) {}
public function cc($cc) {}
public function bcc($bcc) {}
public function subject($subject) {}
public function textBody($text) {
$this->text = $text;
}
public function htmlBody($html) {
$this->html = $html;
}
public function attachContent($content, array $options = []) {}
public function attach($fileName, array $options = []) {}
public function embed($fileName, array $options = []) {}
public function embedContent($content, array $options = []) {}
public function toString()
{
return get_class($this);
}
}
Loading…
Cancel
Save