Completed
Push — 2.1 ( 83fa82...a136f4 )
by
unknown
12:35
created

BaseMailer::compose()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 6
cts 6
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 2
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
     * Renders the specified view with optional parameters and layout.
218
     * The view will be rendered using the [[view]] component.
219
     * @param string $view the view name or the [path alias](guide:concept-aliases) of the view file.
220
     * @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file.
221
     * @param string|bool $layout layout view name or [path alias](guide:concept-aliases). If false, no layout will be applied.
222
     * @return string the rendering result.
223
     */
224
    public function render($view, $params = [], $layout = false)
225
    {
226
        $output = $this->getView()->render($view, $params, $this);
0 ignored issues
show
Documentation Bug introduced by
The method getView does not exist on object<yii\mail\BaseMailer>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
227
        if ($layout !== false) {
228
            return $this->getView()->render($layout, ['content' => $output, 'message' => $this->_message], $this);
0 ignored issues
show
Documentation introduced by
The property _message does not exist on object<yii\mail\BaseMailer>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Documentation Bug introduced by
The method getView does not exist on object<yii\mail\BaseMailer>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
229
        }
230
231
        return $output;
232
    }
233
234
    /**
235
     * Sends the specified message.
236
     * This method should be implemented by child classes with the actual email sending logic.
237
     * @param MessageInterface $message the message to be sent
238
     * @return bool whether the message is sent successfully
239
     */
240
    abstract protected function sendMessage($message);
241
242
    /**
243
     * Saves the message as a file under [[fileTransportPath]].
244
     * @param MessageInterface $message
245
     * @return bool whether the message is saved successfully
246
     */
247 1
    protected function saveMessage($message)
248
    {
249 1
        $path = Yii::getAlias($this->fileTransportPath);
250 1
        if (!is_dir($path)) {
251 1
            mkdir($path, 0777, true);
252
        }
253 1
        if ($this->fileTransportCallback !== null) {
254 1
            $file = $path . '/' . call_user_func($this->fileTransportCallback, $this, $message);
255
        } else {
256
            $file = $path . '/' . $this->generateMessageFileName();
257
        }
258 1
        file_put_contents($file, $message->toString());
259
260 1
        return true;
261
    }
262
263
    /**
264
     * @return string the file name for saving the message when [[useFileTransport]] is true.
265
     */
266
    public function generateMessageFileName()
267
    {
268
        $time = microtime(true);
269
270
        return date('Ymd-His-', $time) . sprintf('%04d', (int) (($time - (int) $time) * 10000)) . '-' . sprintf('%04d', mt_rand(0, 10000)) . '.eml';
271
    }
272
273
    /**
274
     * This method is invoked right before mail send.
275
     * You may override this method to do last-minute preparation for the message.
276
     * If you override this method, please make sure you call the parent implementation first.
277
     * @param MessageInterface $message
278
     * @return bool whether to continue sending an email.
279
     */
280 2
    public function beforeSend($message)
281
    {
282 2
        $event = new MailEvent(['message' => $message]);
283 2
        $this->trigger(self::EVENT_BEFORE_SEND, $event);
284
285 2
        return $event->isValid;
286
    }
287
288
    /**
289
     * This method is invoked right after mail was send.
290
     * You may override this method to do some postprocessing or logging based on mail send status.
291
     * If you override this method, please make sure you call the parent implementation first.
292
     * @param MessageInterface $message
293
     * @param bool $isSuccessful
294
     */
295 2
    public function afterSend($message, $isSuccessful)
296
    {
297 2
        $event = new MailEvent(['message' => $message, 'isSuccessful' => $isSuccessful]);
298 2
        $this->trigger(self::EVENT_AFTER_SEND, $event);
299 2
    }
300
}
301