Completed
Push — master ( 488bfe...03348d )
by Michał
06:17
created

Mailer::createMessage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php namespace nyx\notify\transports\mail;
2
3
/**
4
 * Mailer
5
 *
6
 * @package     Nyx\Notify
7
 * @version     0.1.0
8
 * @author      Michal Chojnacki <[email protected]>
9
 * @copyright   2012-2017 Nyx Dev Team
10
 * @link        https://github.com/unyx/nyx
11
 */
12
class Mailer implements interfaces\Mailer
13
{
14
    /**
15
     * @var interfaces\Driver   The Driver used to actually send Messages.
16
     */
17
    protected $driver;
18
19
    /**
20
     * @var \Illuminate\Contracts\View\Factory  The View Factory responsible for creating the requested views.
21
     */
22
    protected $viewFactory;
23
24
    /**
25
     * @var array   An array of (optional) 'always to' and 'always from' addresses to simplify the creation and testing
26
     *              of Messages.
27
     */
28
    protected $always = [];
29
30
    /**
31
     * @var int     The maximum number of retries allowed to make upon driver connection failures.
32
     */
33
    protected $allowedRetries = 5;
34
35
    /**
36
     * @var int     The current number of connection retries made.
37
     */
38
    protected $currentRetries = 0;
39
40
    /**
41
     * Constructs a new Mailer instance.
42
     *
43
     * @param   interfaces\Driver                   $driver         The Driver to use to actually send Messages.
44
     * @param   \Illuminate\Contracts\View\Factory  $viewFactory    The View Factory responsible for creating the requested views.
45
     */
46
    public function __construct(interfaces\Driver $driver, \Illuminate\Contracts\View\Factory $viewFactory)
47
    {
48
        $this->driver      = $driver;
49
        $this->viewFactory = $viewFactory;
50
    }
51
52
    /**
53
     * Sets the "always from" message header, which is used to populate the "from" field on Messages that do not yet
54
     * have a sender set.
55
     *
56
     * @param   string|array    $address
57
     * @return  $this
58
     */
59
    public function setAlwaysFrom($address) : Mailer
60
    {
61
        $this->always['from'] = $address;
62
63
        return $this;
64
    }
65
66
    /**
67
     * Sets the "always to" message header, which is used to populate the "to" field on *all* outgoing Messages,
68
     * regardless if they already have any recipients set.
69
     *
70
     * This functionality is primarily intended for testing purposes.
71
     *
72
     * @param   string|array    $address
73
     * @return  $this
74
     */
75
    public function setAlwaysTo($address)
76
    {
77
        $this->always['to'] = $address;
78
79
        return $this;
80
    }
81
82
    /**
83
     * {@inheritDoc}
84
     */
85
    public function send($view, array $data = null, callable $builder = null, array &$failures = null) : int
86
    {
87
        if ($view instanceof Message) {
88
            $message = $view;
89
        } else {
90
            $message = $this->createMessage($view);
91
        }
92
93
        // Pass the view data to the Message, if it was given.
94
        if (isset($data)) {
95
            $message->with($data);
96
        }
97
98
        // Render the views and associate the resulting output as the body and parts of the outgoing Message.
99
        $this->buildEntities($message);
100
101
        // Set the "always from" header field but only if no sender is already set. Opposed to "always to",
102
        // the "always from" field does not override existing headers.
103
        if (isset($this->always['from']) && empty($message->getFrom())) {
104
            $message->setFrom($this->always['from']);
105
        }
106
107
        if (isset($builder)) {
108
            return call_user_func($builder, $message);
109
        }
110
111
        // The "always to" header field overrides any recipients set on outgoing Messages, if it's configured.
112
        if (isset($this->always['to'])) {
113
            $message->setTo($this->always['to']);
114
        }
115
116
        return $this->doSend($message, $failures);
117
    }
118
119
    /**
120
     * Performs the actual sending of a MIME Message using the configured Driver.
121
     *
122
     * @param   \Swift_Mime_Message $message    The Message to send.
123
     * @param   array               &$failures  A reference to an array which will hold data about all requested
124
     *                                          recipients sending to whom failed.
125
     * @return  int                             The number of recipients the Message has been sent to.
126
     */
127
    protected function doSend(\Swift_Mime_Message $message, array &$failures = null) : int
128
    {
129
        try {
130
131
            if ($this->driver instanceof interfaces\drivers\Process && !$this->driver->isStarted()) {
132
                $this->driver->start();
133
            }
134
135
            $count = $this->driver->send($message, $failures);
136
137
            // Reset the retry counter on successful requests.
138
            $this->currentRetries = 0;
139
140
            return $count;
141
142
        } catch (\Swift_RfcComplianceException $exception) {
143
144
            if (isset($failures)) {
145
                foreach ($message->getTo() as $address => $name) {
146
                    $failures[] = $address;
147
                }
148
            }
149
150
        } catch (\Swift_TransportException $exception) {
151
            return $this->handleTransportException($message, $exception, $failures);
152
        }
153
    }
154
155
    /**
156
     * Attempts to recover from a Driver Exception by retrying delivery, restarting the Driver's Process if need be,
157
     * unless the maximum number of allowed retries has been exceeded.
158
     *
159
     * @param   \Swift_Mime_Message         $message    The Message we are trying to send.
160
     * @param   \Swift_TransportException   $exception  The Exception we are attempting to recover from.
161
     * @param   array                       &$failures  A reference to an array which will hold data about all requested
162
     *                                                  recipients sending to whom failed.
163
     * @return  int                                     The number of recipients the Message has been sent to.
164
     * @throws  \RuntimeException                       When recovery was impossible due to exceeding the maximum number
165
     *                                                  of allowed retries.
166
     */
167
    protected function handleTransportException(\Swift_Mime_Message $message, \Swift_TransportException $exception, array &$failures = null) : int
0 ignored issues
show
Unused Code introduced by
The parameter $exception is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
168
    {
169
        // Prevent further re-tries if we're at or above our threshold.
170
        if ($this->currentRetries >= $this->allowedRetries) {
171
            throw new \RuntimeException('The mail transport connection failed. Retried connecting '.$this->currentRetries.' time(s).');
172
        }
173
174
        // Transport exceptions in case of Process drivers may just be temporary, so we'll try to re-establish
175
        // the process (connection) from scratch in those cases.
176
        if ($this->driver instanceof interfaces\drivers\Process) {
177
            $this->driver->stop();
178
        }
179
180
        $this->currentRetries++;
181
182
        // Try again.
183
        return $this->doSend($message, $failures);
184
    }
185
186
    /**
187
     * Renders a Message's views and associates the resulting output as the body
188
     * and respective MIME parts of the Message.
189
     *
190
     * @param   Message $message    The Message whose MIME entities should be set.
191
     */
192
    protected function buildEntities(Message $message)
193
    {
194
        list($view, $plain, $raw) = $this->determineViews($message->getViews());
195
196
        $data = $message->getViewData();
197
198
        if (isset($view)) {
199
            $message->setBody($this->viewFactory->make($view, $data)->render(), 'text/html');
200
        }
201
202
        if (isset($plain)) {
203
            $method = isset($view) ? 'addPart' : 'setBody';
204
205
            $message->$method($this->viewFactory->make($plain, $data)->render(), 'text/plain');
206
        }
207
208
        if (isset($raw)) {
209
            $method = (isset($view) || isset($plain)) ? 'addPart' : 'setBody';
210
211
            $message->$method($raw, 'text/plain');
212
        }
213
    }
214
215
    /**
216
     * Determines what type of views (html, text or raw) are specified in the given $view value.
217
     *
218
     * @param   string|array    $view       The value to base on.
219
     * @return  array                       A numerically indexed array with the respective view names corresponding to:
220
     *                                      0 => html, 1 => text, 2 => raw view names.
221
     * @throws  \InvalidArgumentException   If the given value is neither a string nor an array.
222
     */
223
    protected function determineViews($view) : array
224
    {
225
        if (is_string($view)) {
226
            return [$view];
227
        }
228
229
        if (is_array($view)) {
230
231
            if (isset($view[0])) {
232
                return [$view[0], $view[1]];
233
            }
234
235
            return [
236
                $view['html'] ?? null,
237
                $view['text'] ?? null,
238
                $view['raw']  ?? null,
239
            ];
240
        }
241
242
        throw new \InvalidArgumentException('Unrecognized view format given - unable to determine which views to render.');
243
    }
244
245
    /**
246
     * Creates a new Mail Message.
247
     *
248
     * @param   string|array    $view   The view(s) to associate the Message with.
249
     * @return  Message
250
     */
251
    protected function createMessage($view) : Message
252
    {
253
        return new Message((array) $view);
254
    }
255
}
256