Passed
Pull Request — master (#128)
by
unknown
04:21
created

Mailer::send()   B

Complexity

Conditions 11
Paths 120

Size

Total Lines 53
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 11
eloc 36
c 1
b 0
f 0
nc 120
nop 4
dl 0
loc 53
rs 7.15

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Quantum PHP Framework
5
 *
6
 * An open source software development framework for PHP
7
 *
8
 * @package Quantum
9
 * @author Arman Ag. <[email protected]>
10
 * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org)
11
 * @link http://quantum.softberg.org/
12
 * @since 2.9.0
13
 */
14
15
namespace Quantum\Libraries\Mailer;
16
17
use SendinBlue\Client\Api\TransactionalEmailsApi;
18
use SendinBlue\Client\Model\SendSmtpEmail;
19
use Quantum\Libraries\Storage\FileSystem;
20
use SendinBlue\Client\Configuration;
21
use PHPMailer\PHPMailer\PHPMailer;
22
use Quantum\Debugger\Debugger;
23
use Quantum\Logger\FileLogger;
24
use PHPMailer\PHPMailer\SMTP;
25
use Quantum\Loader\Setup;
26
use GuzzleHttp\Client;
27
use Psr\Log\LogLevel;
28
use Quantum\Di\Di;
29
30
/**
31
 * Mailer class
32
 * @package Quantum\Libraries\Mailer
33
 * @uses \PHPMailer\PHPMailer\PHPMailer
34
 */
35
class Mailer
36
{
37
38
    /**
39
     * PHP Mailer instance
40
     * @var \PHPMailer\PHPMailer\PHPMailer
41
     */
42
    private $mailer;
43
44
    /**
45
     * From address and name
46
     * @var array
47
     */
48
    private $from = [];
49
50
    /**
51
     * To addresses
52
     * @var array
53
     */
54
    private $addresses = [];
55
56
    /**
57
     * Reply To addresses
58
     * @var array
59
     */
60
    private $replyToAddresses = [];
61
62
    /**
63
     * CC addresses
64
     * @var array
65
     */
66
    private $ccAddresses = [];
67
68
    /**
69
     * BCC addresses
70
     * @var array
71
     */
72
    private $bccAddresses = [];
73
74
    /**
75
     * Email subject
76
     * @var string
77
     */
78
    private $subject = null;
79
80
    /**
81
     * Email body
82
     * @var string|array
83
     */
84
    private $message = null;
85
86
    /**
87
     * Email attachments
88
     * @var array
89
     */
90
    private $attachments = [];
91
92
    /**
93
     * Email attachments created from string
94
     * @var array
95
     */
96
    private $stringAttachments = [];
97
98
    /**
99
     * Template path
100
     * @var string
101
     */
102
    private $templatePath;
103
104
    /**
105
     * Sendinblue api key
106
     * @var string | null
107
     */
108
    private $api_key = null;
109
110
    /**
111
     * Html message content
112
     * @var string | null
113
     */
114
    private $htmlContent = null;
115
116
    /**
117
     * Mailer constructor.
118
     */
119
    public function __construct()
120
    {
121
        if (!config()->has('mailer') || !config()->has('mailer.current')) {
122
            config()->import(new Setup('config', 'mailer'));
123
        }
124
125
        if (config()->get('mailer')['current'] === 'default') {
126
            $this->mailer = new PHPMailer(true);
127
            $this->mailer->CharSet = 'UTF-8';
128
            if (config()->has('mailer.default.mail_host')) {
129
                $this->setupSmtp();
130
                $this->setupDebugging();
131
            } else {
132
                $this->mailer->isMail();
133
            }
134
            $this->mailer->AllowEmpty = true;
135
            $this->mailer->isHTML(true);
136
        } elseif (config()->get('mailer')['current'] == 'sendinblue') {
137
            $this->api_key = config()->get('mailer')['sendinblue']['api_key'];
138
        }
139
    }
140
141
    /**
142
     * Sets the from email and the name
143
     * @param string $email
144
     * @param string|null $name
145
     * @return $this
146
     */
147
    public function setFrom(string $email, ?string $name = null): Mailer
148
    {
149
        $this->from['email'] = $email;
150
        $this->from['name'] = $name;
151
        return $this;
152
    }
153
154
    /**
155
     * Gets from email and the name
156
     * @return array
157
     */
158
    public function getFrom(): array
159
    {
160
        return $this->from;
161
    }
162
163
    /**
164
     * Sets "To" addresses
165
     * @param string $email
166
     * @param string|null $name
167
     * @return $this
168
     */
169
    public function setAddress(string $email, ?string $name = null): Mailer
170
    {
171
        array_push($this->addresses, [
172
            'email' => $email,
173
            'name' => $name
174
        ]);
175
176
        return $this;
177
    }
178
179
    /**
180
     * Gets "To" addresses
181
     * @return array
182
     */
183
    public function getAddresses(): array
184
    {
185
        return $this->addresses;
186
    }
187
188
    /**
189
     * Sets "Reply-To" addresses
190
     * @param string $email
191
     * @param string|null $name
192
     * @return $this
193
     */
194
    public function setReplay(string $email, ?string $name = null): Mailer
195
    {
196
        array_push($this->replyToAddresses, [
197
            'email' => $email,
198
            'name' => $name
199
        ]);
200
201
        return $this;
202
    }
203
204
    /**
205
     * Gets "Reply-To" addresses
206
     * @return array
207
     */
208
    public function getReplays(): array
209
    {
210
        return $this->replyToAddresses;
211
    }
212
213
    /**
214
     * Sets "CC" addresses
215
     * @param string $email
216
     * @param string|null $name
217
     * @return $this
218
     */
219
    public function setCC(string $email, ?string $name = null): Mailer
220
    {
221
        array_push($this->ccAddresses, [
222
            'email' => $email,
223
            'name' => $name
224
        ]);
225
226
        return $this;
227
    }
228
229
    /**
230
     * Gets "CC" addresses
231
     * @return array
232
     */
233
    public function getCCs(): array
234
    {
235
        return $this->ccAddresses;
236
    }
237
238
    /**
239
     * Sets "BCC" addresses
240
     * @param string $email
241
     * @param string|null $name
242
     * @return $this
243
     */
244
    public function setBCC(string $email, ?string $name = null): Mailer
245
    {
246
        array_push($this->bccAddresses, [
247
            'email' => $email,
248
            'name' => $name
249
        ]);
250
251
        return $this;
252
    }
253
254
    /**
255
     * Get "BCC" addresses
256
     * @return array
257
     */
258
    public function getBCCs(): array
259
    {
260
        return $this->bccAddresses;
261
    }
262
263
    /**
264
     * Sets the subject
265
     * @param string|null $subject
266
     * @return $this
267
     */
268
    public function setSubject(?string $subject): Mailer
269
    {
270
        $this->subject = $subject;
271
        return $this;
272
    }
273
274
    /**
275
     * Gets the subject
276
     * @return string
277
     */
278
    public function getSubject(): ?string
279
    {
280
        return $this->subject;
281
    }
282
283
    /**
284
     * Sets the template
285
     * @param string $templatePath
286
     * @return $this
287
     */
288
    public function setTemplate(string $templatePath): Mailer
289
    {
290
        $this->templatePath = $templatePath;
291
        return $this;
292
    }
293
294
    /**
295
     * Gets the template
296
     * @return string
297
     */
298
    public function getTemplate(): string
299
    {
300
        return $this->templatePath;
301
    }
302
303
    /**
304
     * Sets the body
305
     * @param string|array $message
306
     * @return $this
307
     */
308
    public function setBody($message): Mailer
309
    {
310
        $this->message = $message;
311
        return $this;
312
    }
313
314
    /**
315
     * Gets the body
316
     * @return string|array
317
     */
318
    public function getBody()
319
    {
320
        return $this->message;
321
    }
322
323
    /**
324
     * Sets attachments from the path on the filesystem
325
     * @param string $attachments
326
     * @return $this
327
     */
328
    public function setAttachment(string $attachments): Mailer
329
    {
330
        array_push($this->attachments, $attachments);
331
        return $this;
332
    }
333
334
    /**
335
     * Gets the attachments
336
     * @return array
337
     */
338
    public function getAttachments(): array
339
    {
340
        return $this->attachments;
341
    }
342
343
    /**
344
     * Sets attachment from the string
345
     * @param string $content
346
     * @param string $filename
347
     * @return $this
348
     */
349
    public function setStringAttachment(string $content, string $filename): Mailer
350
    {
351
        array_push($this->stringAttachments, [
352
            'content' => $content,
353
            'filename' => $filename
354
        ]);
355
356
        return $this;
357
    }
358
359
    /**
360
     * Gets the string attachments
361
     * @return array
362
     */
363
    public function getStringAttachments(): array
364
    {
365
        return $this->stringAttachments;
366
    }
367
368
    /**
369
     * Sends the email
370
     * @param array|null $from
371
     * @param array|null $address
372
     * @param string|null $message
373
     * @param array|null $options
374
     * @return bool
375
     * @throws \PHPMailer\PHPMailer\Exception
376
     * @throws \Quantum\Exceptions\DiException
377
     * @throws \ReflectionException
378
     */
379
    public function send(?array $from = null, ?array $address = null, ?string $message = null, ?array $options = []): bool
380
    {
381
        if ($from) {
382
            $this->setFrom(...$from);
0 ignored issues
show
Bug introduced by
$from is expanded, but the parameter $email of Quantum\Libraries\Mailer\Mailer::setFrom() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

382
            $this->setFrom(/** @scrutinizer ignore-type */ ...$from);
Loading history...
383
        }
384
385
        if ($address) {
386
            $this->setAddress(...$address);
0 ignored issues
show
Bug introduced by
$address is expanded, but the parameter $email of Quantum\Libraries\Mailer\Mailer::setAddress() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

386
            $this->setAddress(/** @scrutinizer ignore-type */ ...$address);
Loading history...
387
        }
388
389
        if ($message) {
390
            $this->setBody($message);
391
        }
392
393
        $this->setOptions($options);
394
        if (config()->get('mailer')['current'] == 'default') {
395
            $this->prepare();
396
397
            if (config()->get('mailer')['default']['mail_trap']) {
398
                $sent = $this->mailer->preSend();
399
                $this->saveMessage($this->mailer->getLastMessageID(), $this->mailer->getSentMIMEMessage());
400
                return $sent;
401
            } else {
402
                return $this->mailer->send();
403
            }
404
        } else if (config()->get('mailer')['current'] == 'sendinblue') {
405
            $config = Configuration::getDefaultConfiguration()->setApiKey('api-key', $this->api_key);
406
            $client = new TransactionalEmailsApi(
407
                new Client(),
408
                $config
409
            );
410
411
            if ($this->message) {
412
                if ($this->templatePath) {
413
                    $body = $this->createFromTemplate();
414
                } else {
415
                    $body = is_array($this->message) ? implode($this->message) : $this->message;
416
                }
417
418
                $this->htmlContent = $body;
419
            }
420
421
            $sendSmtpEmail = new SendSmtpEmail();
422
            $sendSmtpEmail['subject'] = $this->subject;
423
            $sendSmtpEmail['htmlContent'] = $this->htmlContent;
424
            $sendSmtpEmail['sender'] = $this->from;
425
            $sendSmtpEmail['to'] = $this->addresses;
426
427
            try {
428
                $response = $client->sendTransacEmail($sendSmtpEmail);
429
                return 'Email sent successfully with ID: ' . $response->getMessageId();
0 ignored issues
show
Bug Best Practice introduced by
The expression return 'Email sent succe...esponse->getMessageId() returns the type string which is incompatible with the type-hinted return boolean.
Loading history...
430
            } catch (\Exception $e) {
431
                return 'Error sending email: ' . $e->getMessage();
0 ignored issues
show
Bug Best Practice introduced by
The expression return 'Error sending email: ' . $e->getMessage() returns the type string which is incompatible with the type-hinted return boolean.
Loading history...
432
            }
433
        }
0 ignored issues
show
Bug Best Practice introduced by
The function implicitly returns null when the if condition on line 404 is false. This is incompatible with the type-hinted return boolean. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
434
    }
435
436
    /**
437
     * Save the message on local file
438
     * @param string $id
439
     * @param string $content
440
     * @throws \Quantum\Exceptions\DiException
441
     * @throws \ReflectionException
442
     */
443
    private function saveMessage(string $id, string $content)
444
    {
445
        $fs = Di::get(FileSystem::class);
446
447
        $emailsDirectory = base_dir() . DS . 'shared' . DS . 'emails';
448
449
        if ($fs->isDirectory($emailsDirectory)) {
450
            $fs->put($emailsDirectory . DS . $this->getFilename($id), $content);
451
        }
452
    }
453
454
    /**
455
     * Sets the options
456
     * @param array $options
457
     */
458
    private function setOptions(array $options)
459
    {
460
        foreach ($options as $name => $params) {
461
            if (method_exists(__CLASS__, $method = 'set' . ucfirst($name))) {
462
                if (is_array($params)) {
463
                    $this->$method(...$params);
464
                } else {
465
                    $this->$method($params);
466
                }
467
            }
468
        }
469
    }
470
471
    /**
472
     * Fetches message ID
473
     * @param string $lastMessageId
474
     * @return string
475
     */
476
    private function getFilename(string $lastMessageId): string
477
    {
478
        preg_match('/<(.*?)@/', $lastMessageId, $matches);
479
        return $matches[1] . '.eml';
480
    }
481
482
    /**
483
     * Prepares the data
484
     * @throws \PHPMailer\PHPMailer\Exception
485
     */
486
    private function prepare()
487
    {
488
        $this->mailer->setFrom($this->from['email'], $this->from['name']);
489
490
        if ($this->subject) {
491
            $this->mailer->Subject = $this->subject;
492
        }
493
494
        if ($this->message) {
495
            if ($this->templatePath) {
496
                $body = $this->createFromTemplate();
497
            } else {
498
                $body = is_array($this->message) ? implode($this->message) : $this->message;
499
            }
500
501
            $this->mailer->Body = $body;
502
        }
503
504
        $this->fillProperties('addAddress', $this->addresses);
505
506
        $this->fillProperties('addReplyTo', $this->replyToAddresses);
507
508
        $this->fillProperties('addCC', $this->ccAddresses);
509
510
        $this->fillProperties('addBCC', $this->bccAddresses);
511
512
        $this->fillProperties('addAttachment', $this->attachments);
513
514
        $this->fillProperties('addStringAttachment', $this->stringAttachments);
515
    }
516
517
    /**
518
     * Files the php mailer properties
519
     * @param string $method
520
     * @param array $fields
521
     */
522
    private function fillProperties(string $method, array $fields = [])
523
    {
524
        if (!empty($fields)) {
525
            foreach ($fields as $field) {
526
                if (is_string($field)) {
527
                    $this->mailer->$method($field);
528
                } else {
529
                    $valOne = current($field);
530
                    next($field);
531
                    $valTwo = current($field);
532
                    $this->mailer->$method($valOne, $valTwo);
533
                    reset($field);
534
                }
535
            }
536
        }
537
    }
538
539
    /**
540
     * Setups SMTP
541
     */
542
    private function setupSmtp()
543
    {
544
        $this->mailer->isSMTP();
545
        $this->mailer->SMTPAuth = true;
546
        $this->mailer->Host = config()->get('mailer')['default']['mail_host'];
547
        $this->mailer->SMTPSecure = config()->get('mailer')['default']['mail_secure'];
548
        $this->mailer->Port = config()->get('mailer')['default']['mail_port'];
549
        $this->mailer->Username = config()->get('mailer')['default']['mail_username'];
550
        $this->mailer->Password = config()->get('mailer')['default']['mail_password'];
551
    }
552
553
    /**
554
     * Setups the debugging
555
     */
556
    private function setupDebugging()
557
    {
558
        if (config()->get('debug')) {
559
            $this->mailer->SMTPDebug = SMTP::DEBUG_SERVER;
560
561
            $this->mailer->Debugoutput = function ($message) {
562
                Debugger::addToStore(Debugger::MAILS, LogLevel::WARNING, $message);
563
564
                $logFile = logs_dir() . DS . date('Y-m-d') . '.log';
565
                $logMessage = '[' . date('Y-m-d H:i:s') . '] ' . LogLevel::WARNING . ': ' . $message . PHP_EOL;
566
567
                warning($logMessage, new FileLogger($logFile));
568
            };
569
        } else {
570
            $this->mailer->SMTPDebug = SMTP::DEBUG_OFF;
571
        }
572
    }
573
574
    /**
575
     * Create message body from email template
576
     * @return string
577
     */
578
    private function createFromTemplate(): string
579
    {
580
        ob_start();
581
        ob_implicit_flush(0);
582
583
        if (!empty($this->message) && is_array($this->message)) {
584
            extract($this->message, EXTR_OVERWRITE);
585
        }
586
587
        require $this->templatePath . '.php';
588
589
        return ob_get_clean();
590
    }
591
}
592