Completed
Push — master ( b9d025...603d38 )
by Arman
28s queued 13s
created

Mailer::getBody()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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

388
            $this->setFrom(/** @scrutinizer ignore-type */ ...$from);
Loading history...
389
        }
390
391
        if ($address) {
392
            $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

392
            $this->setAddress(/** @scrutinizer ignore-type */ ...$address);
Loading history...
393
        }
394
395
        if ($message) {
396
            $this->setBody($message);
397
        }
398
399
        $this->setOptions($options);
400
401
        $this->prepare();
402
        if ($this->message) {
403
            if ($this->templatePath) {
404
                $body = $this->createFromTemplate();
405
            } else {
406
                $body = is_array($this->message) ? implode($this->message) : $this->message;
407
            }
408
409
            $this->htmlContent = $body;
410
        }
411
        if (config()->get('mailer.mail_trap')) {
412
            $sent = $this->mailer->preSend();
413
            $this->saveMessage($this->mailer->getLastMessageID(), $this->mailer->getSentMIMEMessage());
414
            return $sent;
415
        }
416
        if (config()->get('mailer.current') == 'smtp') {
417
            return $this->mailer->send();
418
        } else if (config()->get('mailer.current') == 'sendinblue') {
419
            $this->data = '{  
420
                "sender":' . json_encode($this->from) . ',
421
                "to":' . json_encode($this->addresses) . ',
422
                "subject":"' . $this->subject . '",
423
                "htmlContent":"' . trim(str_replace("\n", "", $this->htmlContent)) . '"
424
                }';
425
        } else if (config()->get('mailer.current') == 'mailgun') {
426
            $to = [];
427
            foreach ($this->addresses as $key => $value) {
428
                array_push($to, $value['email']);
429
            }
430
431
            $this->data = [
432
                "from" => $this->from['name'] . " " . $this->from['email'],
433
                "to" => implode(',', $to),
434
                "subject" => $this->subject,
435
                "html" => trim(str_replace("\n", "", $this->htmlContent))
436
            ];
437
        } else if (config()->get('mailer.current') == 'mandrill') {
438
            $this->data = [
439
                'message' => [
440
                    'subject' => $this->subject,
441
                    'html' => trim(str_replace("\n", "", $this->htmlContent)),
442
                    'from_email' => $this->from['email'],
443
                    'from_name' => $this->from['name'],
444
                    'to' => $this->addresses,
445
                ]
446
            ];
447
        } else if (config()->get('mailer.current') == 'sendgrid') {
448
            $this->data = [
449
                'personalizations' => [
450
                    ['to' => $this->addresses]
451
                ],
452
                'from' => [
453
                    'email' => $this->from['email']
454
                ],
455
                'subject' => $this->subject,
456
                'content' => [
457
                    [
458
                        'type' => 'text/html',
459
                        'value' => trim(str_replace("\n", "", $this->htmlContent))
460
                    ]
461
                ]
462
            ];
463
        }
464
        return $this->mailerAdapter->sendMail($this->data);
465
    }
466
467
    /**
468
     * Save the message on local file
469
     * @param string $id
470
     * @param string $content
471
     * @throws \Quantum\Exceptions\DiException
472
     * @throws \ReflectionException
473
     */
474
    private function saveMessage(string $id, string $content)
475
    {
476
        $fs = Di::get(FileSystem::class);
477
478
        $emailsDirectory = base_dir() . DS . 'shared' . DS . 'emails';
479
480
        if ($fs->isDirectory($emailsDirectory)) {
481
            $fs->put($emailsDirectory . DS . $this->getFilename($id), $content);
482
        }
483
    }
484
485
    /**
486
     * Sets the options
487
     * @param array $options
488
     */
489
    private function setOptions(array $options)
490
    {
491
        foreach ($options as $name => $params) {
492
            if (method_exists(__CLASS__, $method = 'set' . ucfirst($name))) {
493
                if (is_array($params)) {
494
                    $this->$method(...$params);
495
                } else {
496
                    $this->$method($params);
497
                }
498
            }
499
        }
500
    }
501
502
    /**
503
     * Fetches message ID
504
     * @param string $lastMessageId
505
     * @return string
506
     */
507
    private function getFilename(string $lastMessageId): string
508
    {
509
        preg_match('/<(.*?)@/', $lastMessageId, $matches);
510
        return $matches[1] . '.eml';
511
    }
512
513
    /**
514
     * Prepares the data
515
     * @throws \PHPMailer\PHPMailer\Exception
516
     */
517
    private function prepare()
518
    {
519
        $this->mailer->setFrom($this->from['email'], $this->from['name']);
520
521
        if ($this->subject) {
522
            $this->mailer->Subject = $this->subject;
523
        }
524
525
        if ($this->message) {
526
            if ($this->templatePath) {
527
                $body = $this->createFromTemplate();
528
            } else {
529
                $body = is_array($this->message) ? implode($this->message) : $this->message;
530
            }
531
532
            $this->mailer->Body = $body;
533
        }
534
535
        $this->fillProperties('addAddress', $this->addresses);
536
537
        $this->fillProperties('addReplyTo', $this->replyToAddresses);
538
539
        $this->fillProperties('addCC', $this->ccAddresses);
540
541
        $this->fillProperties('addBCC', $this->bccAddresses);
542
543
        $this->fillProperties('addAttachment', $this->attachments);
544
545
        $this->fillProperties('addStringAttachment', $this->stringAttachments);
546
    }
547
548
    /**
549
     * Files the php mailer properties
550
     * @param string $method
551
     * @param array $fields
552
     */
553
    private function fillProperties(string $method, array $fields = [])
554
    {
555
        if (!empty($fields)) {
556
            foreach ($fields as $field) {
557
                if (is_string($field)) {
558
                    $this->mailer->$method($field);
559
                } else {
560
                    $valOne = current($field);
561
                    next($field);
562
                    $valTwo = current($field);
563
                    $this->mailer->$method($valOne, $valTwo);
564
                    reset($field);
565
                }
566
            }
567
        }
568
    }
569
570
    /**
571
     * Setups SMTP
572
     */
573
    private function setupSmtp()
574
    {
575
        $this->mailer->isSMTP();
576
        $this->mailer->SMTPAuth = true;
577
        $this->mailer->Host = config()->get('mailer.default.mail_host');
578
        $this->mailer->SMTPSecure = config()->get('mailer.default.mail_secure');
579
        $this->mailer->Port = config()->get('mailer.default.mail_port');
580
        $this->mailer->Username = config()->get('mailer.default.mail_username');
581
        $this->mailer->Password = config()->get('mailer.default.mail_password');
582
    }
583
584
    /**
585
     * Setups the debugging
586
     */
587
    private function setupDebugging()
588
    {
589
        if (config()->get('debug')) {
590
            $this->mailer->SMTPDebug = SMTP::DEBUG_SERVER;
591
592
            $this->mailer->Debugoutput = function ($message) {
593
                Debugger::addToStore(Debugger::MAILS, LogLevel::WARNING, $message);
594
595
                $logFile = logs_dir() . DS . date('Y-m-d') . '.log';
596
                $logMessage = '[' . date('Y-m-d H:i:s') . '] ' . LogLevel::WARNING . ': ' . $message . PHP_EOL;
597
598
                warning($logMessage, new FileLogger($logFile));
599
            };
600
        } else {
601
            $this->mailer->SMTPDebug = SMTP::DEBUG_OFF;
602
        }
603
    }
604
605
    /**
606
     * Create message body from email template
607
     * @return string
608
     */
609
    private function createFromTemplate(): string
610
    {
611
        ob_start();
612
        ob_implicit_flush(0);
613
614
        if (!empty($this->message) && is_array($this->message)) {
615
            extract($this->message, EXTR_OVERWRITE);
616
        }
617
618
        require $this->templatePath . '.php';
619
620
        return ob_get_clean();
621
    }
622
}
623