Completed
Push — 2.1 ( f59067...a01579 )
by
unknown
21:13 queued 09:54
created

BaseMailer::createMessage()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 6
cts 6
cp 1
rs 9.6666
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 0
crap 2
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\mail;
9
10
use Yii;
11
use yii\base\Component;
12
13
/**
14
 * BaseMailer serves as a base class that implements the basic functions required by [[MailerInterface]].
15
 *
16
 * Concrete child classes should may focus on implementing the [[sendMessage()]] method.
17
 *
18
 * For more details and usage information on BaseMailer, see the [guide article on mailing](guide:tutorial-mailing).
19
 *
20
 * @see BaseMessage
21
 *
22
 * @property Composer $composer Message composer instance. Note that the type of this property differs in getter and setter. See
23
 * [[getComposer()]] and [[setComposer()]] for details.
24
 *
25
 * @author Paul Klimov <[email protected]>
26
 * @since 2.0
27
 */
28
abstract class BaseMailer extends Component implements MailerInterface
29
{
30
    /**
31
     * @event MailEvent an event raised right before send.
32
     * You may set [[MailEvent::isValid]] to be false to cancel the send.
33
     */
34
    const EVENT_BEFORE_SEND = 'beforeSend';
35
    /**
36
     * @event MailEvent an event raised right after send.
37
     */
38
    const EVENT_AFTER_SEND = 'afterSend';
39
40
    /**
41
     * @var array the configuration that should be applied to any newly created
42
     * email message instance by [[createMessage()]] or [[compose()]]. Any valid property defined
43
     * by [[MessageInterface]] can be configured, such as `from`, `to`, `subject`, `textBody`, `htmlBody`, etc.
44
     *
45
     * For example:
46
     *
47
     * ```php
48
     * [
49
     *     'charset' => 'UTF-8',
50
     *     'from' => '[email protected]',
51
     *     'bcc' => '[email protected]',
52
     * ]
53
     * ```
54
     */
55
    public $messageConfig = [];
56
    /**
57
     * @var string the default class name of the new message instances created by [[createMessage()]]
58
     */
59
    public $messageClass = BaseMessage::class;
60
    /**
61
     * @var bool whether to save email messages as files under [[fileTransportPath]] instead of sending them
62
     * to the actual recipients. This is usually used during development for debugging purpose.
63
     * @see fileTransportPath
64
     */
65
    public $useFileTransport = false;
66
    /**
67
     * @var string the directory where the email messages are saved when [[useFileTransport]] is true.
68
     */
69
    public $fileTransportPath = '@runtime/mail';
70
    /**
71
     * @var callable a PHP callback that will be called by [[send()]] when [[useFileTransport]] is true.
72
     * The callback should return a file name which will be used to save the email message.
73
     * If not set, the file name will be generated based on the current timestamp.
74
     *
75
     * The signature of the callback is:
76
     *
77
     * ```php
78
     * function ($mailer, $message)
79
     * ```
80
     */
81
    public $fileTransportCallback;
82
83
    /**
84
     * @var Composer|array|string|callable message composer.
85
     * @since 2.1
86
     */
87
    private $_composer;
88
89
90
    /**
91
     * @return Composer message composer instance.
92
     * @since 2.1
93
     */
94 1
    public function getComposer()
95
    {
96 1
        if (!is_object($this->_composer) || $this->_composer instanceof \Closure) {
97 1
            if (is_array($this->_composer) && !isset($this->_composer['class'])) {
98 1
                $this->_composer['class'] = Composer::class;
99
            }
100 1
            $this->_composer = Yii::createObject($this->_composer);
101
        }
102 1
        return $this->_composer;
103
    }
104
105
    /**
106
     * @param Composer|array|string|callable $composer message composer instance or DI compatible configuration.
107
     * @since 2.1
108
     */
109 5
    public function setComposer($composer)
110
    {
111 5
        $this->_composer = $composer;
112 5
    }
113
114
    /**
115
     * Creates a new message instance and optionally composes its body content via view rendering.
116
     *
117
     * @param string|array|null $view the view to be used for rendering the message body. This can be:
118
     *
119
     * - a string, which represents the view name or path alias for rendering the HTML body of the email.
120
     *   In this case, the text body will be generated by applying `strip_tags()` to the HTML body.
121
     * - an array with 'html' and/or 'text' elements. The 'html' element refers to the view name or path alias
122
     *   for rendering the HTML body, while 'text' element is for rendering the text body. For example,
123
     *   `['html' => 'contact-html', 'text' => 'contact-text']`.
124
     * - null, meaning the message instance will be returned without body content.
125
     *
126
     * The view to be rendered can be specified in one of the following formats:
127
     *
128
     * - path alias (e.g. "@app/mail/contact");
129
     * - a relative view name (e.g. "contact") located under [[viewPath]].
130
     *
131
     * @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file.
132
     * @return MessageInterface message instance.
133
     */
134 6
    public function compose($view = null, array $params = [])
135
    {
136 6
        $message = $this->createMessage();
137 6
        if ($view === null) {
138 5
            return $message;
139
        }
140
141 1
        $this->getComposer()->compose($message, $view, $params);
142
143 1
        return $message;
144
    }
145
146
    /**
147
     * Creates a new message instance.
148
     * The newly created instance will be initialized with the configuration specified by [[messageConfig]].
149
     * If the configuration does not specify a 'class', the [[messageClass]] will be used as the class
150
     * of the new message instance.
151
     * @return MessageInterface message instance.
152
     */
153 6
    protected function createMessage()
154
    {
155 6
        $config = $this->messageConfig;
156 6
        if (!array_key_exists('class', $config)) {
157 6
            $config['class'] = $this->messageClass;
158
        }
159 6
        $config['mailer'] = $this;
160 6
        return Yii::createObject($config);
161
    }
162
163
    /**
164
     * Sends the given email message.
165
     * This method will log a message about the email being sent.
166
     * If [[useFileTransport]] is true, it will save the email as a file under [[fileTransportPath]].
167
     * Otherwise, it will call [[sendMessage()]] to send the email to its recipient(s).
168
     * Child classes should implement [[sendMessage()]] with the actual email sending logic.
169
     * @param MessageInterface $message email message instance to be sent
170
     * @return bool whether the message has been sent successfully
171
     */
172 3
    public function send($message)
173
    {
174 3
        if (!$this->beforeSend($message)) {
175
            return false;
176
        }
177
178 3
        $address = $message->getTo();
179 3
        if (is_array($address)) {
180
            $address = implode(', ', array_keys($address));
181
        }
182 3
        Yii::info('Sending email "' . $message->getSubject() . '" to "' . $address . '"', __METHOD__);
183
184 3
        if ($this->useFileTransport) {
185 1
            $isSuccessful = $this->saveMessage($message);
186
        } else {
187 2
            $isSuccessful = $this->sendMessage($message);
188
        }
189 3
        $this->afterSend($message, $isSuccessful);
190
191 3
        return $isSuccessful;
192
    }
193
194
    /**
195
     * Sends multiple messages at once.
196
     *
197
     * The default implementation simply calls [[send()]] multiple times.
198
     * Child classes may override this method to implement more efficient way of
199
     * sending multiple messages.
200
     *
201
     * @param array $messages list of email messages, which should be sent.
202
     * @return int number of messages that are successfully sent.
203
     */
204
    public function sendMultiple(array $messages)
205
    {
206
        $successCount = 0;
207
        foreach ($messages as $message) {
208
            if ($this->send($message)) {
209
                $successCount++;
210
            }
211
        }
212
213
        return $successCount;
214
    }
215
216
    /**
217
     * Sends the specified message.
218
     * This method should be implemented by child classes with the actual email sending logic.
219
     * @param MessageInterface $message the message to be sent
220
     * @return bool whether the message is sent successfully
221
     */
222
    abstract protected function sendMessage($message);
223
224
    /**
225
     * Saves the message as a file under [[fileTransportPath]].
226
     * @param MessageInterface $message
227
     * @return bool whether the message is saved successfully
228
     */
229 1
    protected function saveMessage($message)
230
    {
231 1
        $path = Yii::getAlias($this->fileTransportPath);
232 1
        if (!is_dir($path)) {
233 1
            mkdir($path, 0777, true);
234
        }
235 1
        if ($this->fileTransportCallback !== null) {
236 1
            $file = $path . '/' . call_user_func($this->fileTransportCallback, $this, $message);
237
        } else {
238
            $file = $path . '/' . $this->generateMessageFileName();
239
        }
240 1
        file_put_contents($file, $message->toString());
241
242 1
        return true;
243
    }
244
245
    /**
246
     * @return string the file name for saving the message when [[useFileTransport]] is true.
247
     */
248
    public function generateMessageFileName()
249
    {
250
        $time = microtime(true);
251
252
        return date('Ymd-His-', $time) . sprintf('%04d', (int) (($time - (int) $time) * 10000)) . '-' . sprintf('%04d', mt_rand(0, 10000)) . '.eml';
253
    }
254
255
    /**
256
     * This method is invoked right before mail send.
257
     * You may override this method to do last-minute preparation for the message.
258
     * If you override this method, please make sure you call the parent implementation first.
259
     * @param MessageInterface $message
260
     * @return bool whether to continue sending an email.
261
     */
262 2
    public function beforeSend($message)
263
    {
264 2
        $event = new MailEvent(['message' => $message]);
265 2
        $this->trigger(self::EVENT_BEFORE_SEND, $event);
266
267 2
        return $event->isValid;
268
    }
269
270
    /**
271
     * This method is invoked right after mail was send.
272
     * You may override this method to do some postprocessing or logging based on mail send status.
273
     * If you override this method, please make sure you call the parent implementation first.
274
     * @param MessageInterface $message
275
     * @param bool $isSuccessful
276
     */
277 2
    public function afterSend($message, $isSuccessful)
278
    {
279 2
        $event = new MailEvent(['message' => $message, 'isSuccessful' => $isSuccessful]);
280 2
        $this->trigger(self::EVENT_AFTER_SEND, $event);
281 2
    }
282
}
283