Passed
Push — master ( b39628...7861b1 )
by Mathieu
02:37 queued 11s
created

Email::logSend()   B

Complexity

Conditions 5
Paths 7

Size

Total Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 39
rs 8.9848
c 0
b 0
f 0
cc 5
nc 7
nop 2
1
<?php
2
3
namespace Charcoal\Email;
4
5
use Exception;
6
use InvalidArgumentException;
7
8
// PSR-3 (logger) dependencies
9
use Psr\Log\LoggerAwareInterface;
10
use Psr\Log\LoggerAwareTrait;
11
12
// From 'phpmailer/phpmailer'
13
use PHPMailer\PHPMailer\PHPMailer;
14
15
// From 'charcoal-config'
16
use Charcoal\Config\ConfigurableInterface;
17
use Charcoal\Config\ConfigurableTrait;
18
19
// Module 'charcoal-factory'
20
use Charcoal\Factory\FactoryInterface;
21
22
// Module 'charcoal-view'
23
use Charcoal\View\GenericView;
24
use Charcoal\View\ViewableInterface;
25
use Charcoal\View\ViewableTrait;
26
27
// Module 'charcoal-queue'
28
use Charcoal\Queue\QueueableInterface;
29
use Charcoal\Queue\QueueableTrait;
30
31
32
use Charcoal\Email\EmailInterface;
33
use Charcoal\Email\EmailConfig;
34
use Charcoal\Email\EmailLog;
35
use Charcoal\Email\EmailQueueItem;
36
37
/**
38
 * Default implementation of the `EmailInterface`.
39
 */
40
class Email implements
41
    ConfigurableInterface,
42
    EmailInterface,
43
    LoggerAwareInterface,
44
    QueueableInterface,
45
    ViewableInterface
46
{
47
    use ConfigurableTrait;
48
    use LoggerAwareTrait;
49
    use QueueableTrait;
50
    use ViewableTrait;
51
    use EmailAwareTrait;
52
53
    /**
54
     * The campaign ID.
55
     *
56
     * @var string $campaign
57
     */
58
    private $campaign;
59
60
    /**
61
     * The recipient email address(es).
62
     *
63
     * @var array $to
64
     */
65
    private $to = [];
66
67
    /**
68
     * The CC recipient email address(es).
69
     *
70
     * @var array $cc
71
     */
72
    private $cc = [];
73
74
    /**
75
     * The BCC recipient email address(es).
76
     *
77
     * @var array $bcc
78
     */
79
    private $bcc = [];
80
81
    /**
82
     * The sender's email address.
83
     *
84
     * @var string $from
85
     */
86
    private $from;
87
88
    /**
89
     * The email address to reply to the message.
90
     *
91
     * @var string $replyTo
92
     */
93
    private $replyTo;
94
95
    /**
96
     * The email subject.
97
     *
98
     * @var string $subject
99
     */
100
    private $subject;
101
102
    /**
103
     * The HTML message body.
104
     *
105
     * @var string $msgHtml
106
     */
107
    private $msgHtml;
108
109
    /**
110
     * The plain-text message body.
111
     *
112
     * @var string $msgTxt
113
     */
114
    private $msgTxt;
115
116
    /**
117
     * @var array $attachments
118
     */
119
    private $attachments = [];
120
121
    /**
122
     * Whether the email should be logged.
123
     *
124
     * @var boolean $log
125
     */
126
    private $log;
127
128
    /**
129
     * Whether the email should be tracked.
130
     *
131
     * @var boolean $track
132
     */
133
    private $track;
134
135
    /**
136
     * The data to pass onto the view controller.
137
     *
138
     * @var array $templateData
139
     */
140
    private $templateData = [];
141
142
    /**
143
     * @var PHPMailer $phpMailer PHP Mailer instance.
144
     */
145
    private $phpMailer;
146
147
    /**
148
     * @var FactoryInterface $templateFactory
149
     */
150
    private $templateFactory;
151
152
    /**
153
     * @var FactoryInterface $queueItemFactory
154
     */
155
    private $queueItemFactory;
156
157
    /**
158
     * @var FactoryInterface $logFactory
159
     */
160
    private $logFactory;
161
162
    /**
163
     * Construct a new Email object.
164
     *
165
     * @param array $data Dependencies and settings.
166
     */
167
    public function __construct(array $data)
168
    {
169
        $this->phpMailer = new PHPMailer(true);
170
        $this->setLogger($data['logger']);
171
        $this->setView($data['view']);
172
        $this->setConfig($data['config']);
173
        $this->setTemplateFactory($data['template_factory']);
174
        $this->setQueueItemFactory($data['queue_item_factory']);
175
        $this->setLogFactory($data['log_factory']);
176
    }
177
178
    /**
179
     * Set the email's data.
180
     *
181
     * @param array $data The data to set.
182
     * @return Email Chainable
183
     */
184
    public function setData(array $data)
185
    {
186
        foreach ($data as $prop => $val) {
187
            $func = [$this, $this->setter($prop)];
188
            if (is_callable($func)) {
189
                call_user_func($func, $val);
190
            } else {
191
                $this->{$prop} = $val;
192
            }
193
        }
194
195
        return $this;
196
    }
197
198
    /**
199
     * Set the campaign ID.
200
     *
201
     * @param  string $campaign The campaign identifier.
202
     * @throws InvalidArgumentException If the campaign is invalid.
203
     * @return self
204
     */
205
    public function setCampaign($campaign)
206
    {
207
        if (!is_string($campaign)) {
208
            throw new InvalidArgumentException(
209
                'Campaign must be a string'
210
            );
211
        }
212
213
        $this->campaign = $campaign;
214
215
        return $this;
216
    }
217
218
    /**
219
     * Get the campaign identifier.
220
     *
221
     * If it has not been explicitely set, it will be auto-generated (with uniqid).
222
     *
223
     * @return string
224
     */
225
    public function campaign()
226
    {
227
        if ($this->campaign === null) {
228
            $this->campaign = $this->generateCampaign();
229
        }
230
231
        return $this->campaign;
232
    }
233
234
    /**
235
     * Set the recipient email address(es).
236
     *
237
     * @param string|array $email The recipient email address(es).
238
     * @throws InvalidArgumentException If the email address is invalid.
239
     * @return self
240
     */
241 View Code Duplication
    public function setTo($email)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
242
    {
243
        if (is_string($email)) {
244
            $email = [ $email ];
245
        }
246
247
        if (!is_array($email)) {
248
            throw new InvalidArgumentException(
249
                'Must be an array of recipients.'
250
            );
251
        }
252
253
        $this->to = [];
254
255
        // At this point, `$email` can be an _email array_ or an _array of emails_...
256
        if (isset($email['email'])) {
257
            // Means we're not dealing with multiple emails
258
            $this->addTo($email);
259
        } else {
260
            foreach ($email as $recipient) {
261
                $this->addTo($recipient);
262
            }
263
        }
264
265
        return $this;
266
    }
267
268
    /**
269
     * Add a recipient email address.
270
     *
271
     * @param  mixed $email The recipient email address to add.
272
     * @throws InvalidArgumentException If the email address is invalid.
273
     * @return self
274
     */
275
    public function addTo($email)
276
    {
277
        $this->to[] = $this->parseEmail($email);
278
        return $this;
279
    }
280
281
    /**
282
     * Get the recipient's email addresses.
283
     *
284
     * @return string[]
285
     */
286
    public function to()
287
    {
288
        return $this->to;
289
    }
290
291
    /**
292
     * Set the carbon copy (CC) recipient email address(es).
293
     *
294
     * @param string|array $email The CC recipient email address(es).
295
     * @throws InvalidArgumentException If the email address is invalid.
296
     * @return self
297
     */
298 View Code Duplication
    public function setCc($email)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
299
    {
300
        if (is_string($email)) {
301
            $email = [ $email ];
302
        }
303
304
        if (!is_array($email)) {
305
            throw new InvalidArgumentException(
306
                'Must be an array of CC recipients.'
307
            );
308
        }
309
310
        $this->cc = [];
311
312
        // At this point, `$email` can be an _email array_ or an _array of emails_...
313
        if (isset($email['email'])) {
314
            // Means we're not dealing with multiple emails
315
            $this->addCc($email);
316
        } else {
317
            foreach ($email as $recipient) {
318
                $this->addCc($recipient);
319
            }
320
        }
321
322
        return $this;
323
    }
324
325
    /**
326
     * Add a CC recipient email address.
327
     *
328
     * @param mixed $email The CC recipient email address to add.
329
     * @throws InvalidArgumentException If the email address is invalid.
330
     * @return self
331
     */
332
    public function addCc($email)
333
    {
334
        $this->cc[] = $this->parseEmail($email);
335
        return $this;
336
    }
337
338
    /**
339
     * Get the CC recipient's email address.
340
     *
341
     * @return string[]
342
     */
343
    public function cc()
344
    {
345
        return $this->cc;
346
    }
347
348
    /**
349
     * Set the blind carbon copy (BCC) recipient email address(es).
350
     *
351
     * @param string|array $email The BCC recipient email address(es).
352
     * @throws InvalidArgumentException If the email address is invalid.
353
     * @return self
354
     */
355 View Code Duplication
    public function setBcc($email)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
356
    {
357
        if (is_string($email)) {
358
            // Means we have a straight email
359
            $email = [ $email ];
360
        }
361
362
        if (!is_array($email)) {
363
            throw new InvalidArgumentException(
364
                'Must be an array of BCC recipients.'
365
            );
366
        }
367
368
        $this->bcc = [];
369
370
        // At this point, `$email` can be an _email array_ or an _array of emails_...
371
        if (isset($email['email'])) {
372
            // Means we're not dealing with multiple emails
373
            $this->addBcc($email);
374
        } else {
375
            foreach ($email as $recipient) {
376
                $this->addBcc($recipient);
377
            }
378
        }
379
380
        return $this;
381
    }
382
383
    /**
384
     * Add a BCC recipient email address.
385
     *
386
     * @param mixed $email The BCC recipient email address to add.
387
     * @throws InvalidArgumentException If the email address is invalid.
388
     * @return self
389
     */
390
    public function addBcc($email)
391
    {
392
        $this->bcc[] = $this->parseEmail($email);
393
        return $this;
394
    }
395
396
    /**
397
     * Get the BCC recipient's email address.
398
     *
399
     * @return string[]
400
     */
401
    public function bcc()
402
    {
403
        return $this->bcc;
404
    }
405
406
    /**
407
     * Set the sender's email address.
408
     *
409
     * @param  string|array $email An email address.
410
     * @throws InvalidArgumentException If the email is not a string or an array.
411
     * @return self
412
     * @todo   Implement optional "Sender" field.
413
     */
414
    public function setFrom($email)
415
    {
416
        $this->from = $this->parseEmail($email);
417
        return $this;
418
    }
419
420
    /**
421
     * Get the sender's email address.
422
     *
423
     * @return string
424
     */
425
    public function from()
426
    {
427
        if ($this->from === null) {
428
            $this->setFrom($this->config()->defaultFrom());
429
        }
430
431
        return $this->from;
432
    }
433
434
    /**
435
     * Set email address to reply to the message.
436
     *
437
     * @param  mixed $email The sender's "Reply-To" email address.
438
     * @throws InvalidArgumentException If the email is not a string or an array.
439
     * @return self
440
     */
441
    public function setReplyTo($email)
442
    {
443
        $this->replyTo = $this->parseEmail($email);
444
        return $this;
445
    }
446
447
    /**
448
     * Get email address to reply to the message.
449
     *
450
     * @return string
451
     */
452
    public function replyTo()
453
    {
454
        if ($this->replyTo === null) {
455
            $this->replyTo = $this->config()->defaultReplyTo();
456
        }
457
458
        return $this->replyTo;
459
    }
460
461
    /**
462
     * Set the email subject.
463
     *
464
     * @param  string $subject The email subject.
465
     * @throws InvalidArgumentException If the subject is not a string.
466
     * @return self
467
     */
468
    public function setSubject($subject)
469
    {
470
        if (!is_string($subject)) {
471
            throw new InvalidArgumentException(
472
                'Subject needs to be a string'
473
            );
474
        }
475
476
        $this->subject = $subject;
477
478
        return $this;
479
    }
480
481
    /**
482
     * Get the email subject.
483
     *
484
     * @return string The emails' subject.
485
     */
486
    public function subject()
487
    {
488
        return $this->subject;
489
    }
490
491
    /**
492
     * Set the email's HTML message body.
493
     *
494
     * @param  string $body The HTML message body.
495
     * @throws InvalidArgumentException If the message is not a string.
496
     * @return self
497
     */
498
    public function setMsgHtml($body)
499
    {
500
        if (!is_string($body)) {
501
            throw new InvalidArgumentException(
502
                'HTML message needs to be a string'
503
            );
504
        }
505
506
        $this->msgHtml = $body;
507
508
        return $this;
509
    }
510
511
    /**
512
     * Get the email's HTML message body.
513
     *
514
     * If the message is not explitely set, it will be
515
     * auto-generated from a template view.
516
     *
517
     * @return string
518
     */
519
    public function msgHtml()
520
    {
521
        if ($this->msgHtml === null) {
522
            $this->msgHtml = $this->generateMsgHtml();
523
        }
524
        return $this->msgHtml;
525
    }
526
527
    /**
528
     * Set the email's plain-text message body.
529
     *
530
     * @param string $body The message's text body.
531
     * @throws InvalidArgumentException If the parameter is invalid.
532
     * @return self
533
     */
534
    public function setMsgTxt($body)
535
    {
536
        if (!is_string($body)) {
537
            throw new InvalidArgumentException(
538
                'Plan-text message needs to be a string'
539
            );
540
        }
541
542
        $this->msgTxt = $body;
543
544
        return $this;
545
    }
546
547
    /**
548
     * Get the email's plain-text message body.
549
     *
550
     * If the plain-text message is not explitely set,
551
     * it will be auto-generated from the HTML message.
552
     *
553
     * @return string
554
     */
555
    public function msgTxt()
556
    {
557
        if ($this->msgTxt === null) {
558
            $this->msgTxt = $this->stripHtml($this->msgHtml());
559
        }
560
561
        return $this->msgTxt;
562
    }
563
564
    /**
565
     * Set the email's attachments.
566
     *
567
     * @param  array $attachments The file attachments.
568
     * @return self
569
     */
570
    public function setAttachments(array $attachments)
571
    {
572
        foreach ($attachments as $att) {
573
            $this->addAttachment($att);
574
        }
575
576
        return $this;
577
    }
578
579
    /**
580
     * Add an attachment to the email.
581
     *
582
     * @param  mixed $attachment A single file attachment.
583
     * @return self
584
     */
585
    public function addAttachment($attachment)
586
    {
587
        $this->attachments[] = $attachment;
588
        return $this;
589
    }
590
591
    /**
592
     * Get the email's attachments.
593
     *
594
     * @return array
595
     */
596
    public function attachments()
597
    {
598
        return $this->attachments;
599
    }
600
601
    /**
602
     * Enable or disable logging for this particular email.
603
     *
604
     * @param  boolean $log The log flag.
605
     * @return self
606
     */
607
    public function setLog($log)
608
    {
609
        $this->log = !!$log;
610
        return $this;
611
    }
612
613
    /**
614
     * Determine if logging is enabled for this particular email.
615
     *
616
     * @return boolean
617
     */
618
    public function log()
619
    {
620
        if ($this->log === null) {
621
            $this->log = $this->config()->defaultLog();
622
        }
623
        return $this->log;
624
    }
625
626
    /**
627
     * Enable or disable tracking for this particular email.
628
     *
629
     * @param boolean $track The track flag.
630
     * @return self
631
     */
632
    public function setTrack($track)
633
    {
634
        $this->track = !!$track;
635
        return $this;
636
    }
637
638
    /**
639
     * Determine if tracking is enabled for this particular email.
640
     *
641
     * @return boolean
642
     */
643
    public function track()
644
    {
645
        if ($this->track === null) {
646
            $this->track = $this->config()->defaultTrack();
647
        }
648
        return $this->track;
649
    }
650
651
    /**
652
     * Send the email to all recipients
653
     *
654
     * @return boolean Success / Failure.
655
     * @todo Implement methods and property for toggling rich-text vs. plain-text
656
     *       emails (`$mail->isHTML(true)`).
657
     */
658
    public function send()
659
    {
660
        $this->logger->debug(
661
            'Attempting to send an email',
662
            $this->to()
663
        );
664
665
        $mail = $this->phpMailer;
666
667
        try {
668
            $this->setSmtpOptions($mail);
669
670
            $mail->CharSet = 'UTF-8';
671
672
            // Setting reply-to field, if required.
673
            $replyTo = $this->replyTo();
674
            if ($replyTo) {
675
                $replyArr = $this->emailToArray($replyTo);
676
                $mail->addReplyTo($replyArr['email'], $replyArr['name']);
677
            }
678
679
            // Setting from (sender) field.
680
            $from = $this->from();
681
            $fromArr = $this->emailToArray($from);
682
            $mail->setFrom($fromArr['email'], $fromArr['name']);
683
684
            // Setting to (recipients) field(s).
685
            $to = $this->to();
686
            foreach ($to as $recipient) {
687
                $toArr = $this->emailToArray($recipient);
688
                $mail->addAddress($toArr['email'], $toArr['name']);
689
            }
690
691
            // Setting cc (carbon-copy) field(s).
692
            $cc = $this->cc();
693
            foreach ($cc as $ccRecipient) {
694
                $ccArr = $this->emailToArray($ccRecipient);
695
                $mail->addCC($ccArr['email'], $ccArr['name']);
696
            }
697
698
            // Setting bcc (black-carbon-copy) field(s).
699
            $bcc = $this->bcc();
700
            foreach ($bcc as $bccRecipient) {
701
                $bccArr = $this->emailToArray($bccRecipient);
702
                $mail->addBCC($bccArr['email'], $bccArr['name']);
703
            }
704
705
            // Setting attachment(s), if required.
706
            $attachments = $this->attachments();
707
            foreach ($attachments as $att) {
708
                $mail->addAttachment($att);
709
            }
710
711
            $mail->isHTML(true);
712
713
            $mail->Subject = $this->subject();
714
            $mail->Body    = $this->msgHtml();
715
            $mail->AltBody = $this->msgTxt();
716
717
            $ret = $mail->send();
718
719
            $this->logSend($ret, $mail);
720
721
            return $ret;
722
        } catch (Exception $e) {
723
            $this->logger->error(
724
                sprintf('Error sending email: %s', $e->getMessage())
725
            );
726
        }
727
    }
728
729
    /**
730
     * Set the SMTP's options for PHPMailer.
731
     *
732
     * @param PHPMailer $mail The PHPMailer to setup.
733
     * @return void
734
     */
735
    protected function setSmtpOptions(PHPMailer $mail)
736
    {
737
        $config = $this->config();
738
        if (!$config['smtp']) {
739
            return;
740
        }
741
742
        $this->logger->debug(
743
            sprintf('Using SMTP "%s" server to send email', $config['smtp_hostname'])
744
        );
745
746
        $mail->isSMTP();
747
        $mail->Host       = $config['smtp_hostname'];
748
        $mail->Port       = $config['smtp_port'];
749
        $mail->SMTPAuth   = $config['smtp_auth'];
750
        $mail->Username   = $config['smtp_username'];
751
        $mail->Password   = $config['smtp_password'];
752
        $mail->SMTPSecure = $config['smtp_security'];
753
    }
754
755
    /**
756
     * Enqueue the email for each recipient.
757
     *
758
     * @param mixed $ts A date/time to initiate the queue processing.
759
     * @return boolean Success / Failure.
760
     */
761
    public function queue($ts = null)
762
    {
763
        $recipients = $this->to();
764
        $author     = $this->from();
765
        $subject    = $this->subject();
766
        $msgHtml    = $this->msgHtml();
767
        $msgTxt     = $this->msgTxt();
768
        $campaign   = $this->campaign();
769
        $queueId    = $this->queueId();
770
771
        foreach ($recipients as $to) {
772
            if (is_string($to) && !empty($to)) {
773
                $queueItem = $this->queueItemFactory()->create(EmailQueueItem::class);
774
775
                $queueItem->setTo($to);
776
                $queueItem->setFrom($author);
777
                $queueItem->setSubject($subject);
778
                $queueItem->setMsgHtml($msgHtml);
779
                $queueItem->setMsgTxt($msgTxt);
780
                $queueItem->setCampaign($campaign);
781
                $queueItem->setProcessingDate($ts);
782
                $queueItem->setQueueId($queueId);
783
784
                $res = $queueItem->save();
0 ignored issues
show
Unused Code introduced by
$res is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
785
            } else {
786
                $this->logger->warning('Could not queue email, null or empty value');
787
            }
788
        }
789
790
        return true;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return true; (boolean) is incompatible with the return type declared by the interface Charcoal\Queue\QueueableInterface::queue of type Charcoal\Queue\QueueableInterface.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
791
    }
792
793
    /**
794
     * Log the queue event.
795
     *
796
     * @return void
797
     * @todo Implement log qeueing.
798
     */
799
    protected function logQueue()
800
    {
801
    }
802
803
    /**
804
     * Set the template data for the view.
805
     *
806
     * @param array $data The template data.
807
     * @return Email Chainable
808
     */
809
    public function setTemplateData(array $data)
810
    {
811
        $this->templateData = $data;
812
        return $this;
813
    }
814
815
    /**
816
     * Get the template data for the view.
817
     *
818
     * @return array
819
     */
820
    public function templateData()
821
    {
822
        return $this->templateData;
823
    }
824
825
    /**
826
     * Get the custom view controller for rendering
827
     * the email's HTML message.
828
     *
829
     * Unlike typical `ViewableInterface` objects, the view controller is not
830
     * the email itself but an external "email" template.
831
     *
832
     * @see    ViewableInterface::viewController()
833
     * @return \Charcoal\App\Template\TemplateInterface|array
834
     */
835
    public function viewController()
836
    {
837
        $templateIdent = $this->templateIdent();
838
839
        if (!$templateIdent) {
840
            return [];
841
        }
842
843
        $templateFactory = clone($this->templateFactory());
844
        $templateFactory->setDefaultClass(GenericEmailTemplate::class);
0 ignored issues
show
Bug introduced by
The method setDefaultClass() does not exist on Charcoal\Factory\FactoryInterface. Did you maybe mean defaultClass()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
845
        $template = $templateFactory->create($templateIdent);
846
847
        $template->setData($this->templateData());
848
849
        return $template;
850
    }
851
852
    /**
853
     * @param FactoryInterface $factory The factory to use to create email template objects.
854
     * @return Email Chainable
855
     */
856
    protected function setTemplateFactory(FactoryInterface $factory)
857
    {
858
        $this->templateFactory = $factory;
859
        return $this;
860
    }
861
862
    /**
863
     * @return FactoryInterface
864
     */
865
    protected function templateFactory()
866
    {
867
        return $this->templateFactory;
868
    }
869
870
    /**
871
     * @param FactoryInterface $factory The factory to use to create email queue item objects.
872
     * @return Email Chainable
873
     */
874
    protected function setQueueItemFactory(FactoryInterface $factory)
875
    {
876
        $this->queueItemFactory = $factory;
877
        return $this;
878
    }
879
880
    /**
881
     * @return FactoryInterface
882
     */
883
    protected function queueItemFactory()
884
    {
885
        return $this->queueItemFactory;
886
    }
887
888
    /**
889
     * @param FactoryInterface $factory The factory to use to create log objects.
890
     * @return Email Chainable
891
     */
892
    protected function setLogFactory(FactoryInterface $factory)
893
    {
894
        $this->logFactory = $factory;
895
        return $this;
896
    }
897
898
    /**
899
     * @return FactoryInterface
900
     */
901
    protected function logFactory()
902
    {
903
        return $this->logFactory;
904
    }
905
906
    /**
907
     * Get the email's HTML message from the template, if applicable.
908
     *
909
     * @see    ViewableInterface::renderTemplate()
910
     * @return string
911
     */
912
    protected function generateMsgHtml()
913
    {
914
        $templateIdent = $this->templateIdent();
915
916
        if (!$templateIdent) {
917
            $message = '';
918
        } else {
919
            $message = $this->renderTemplate($templateIdent);
920
        }
921
922
        return $message;
923
    }
924
925
    /**
926
     * Generates a unique identifier ideal for a campaign ID.
927
     *
928
     * @return string
929
     */
930
    protected function generateCampaign()
931
    {
932
        return uniqid();
933
    }
934
935
    /**
936
     * Allow an object to define how the key getter are called.
937
     *
938
     * @param string $key The key to get the getter from.
939
     * @return string The getter method name, for a given key.
940
     */
941
    protected function getter($key)
942
    {
943
        $getter = $key;
944
        return $this->camelize($getter);
945
    }
946
947
    /**
948
     * Allow an object to define how the key setter are called.
949
     *
950
     * @param string $key The key to get the setter from.
951
     * @return string The setter method name, for a given key.
952
     */
953
    protected function setter($key)
954
    {
955
        $setter = 'set_'.$key;
956
        return $this->camelize($setter);
957
    }
958
959
    /**
960
     * Convert an HTML string to plain-text.
961
     *
962
     * @param string $html The HTML string to convert.
963
     * @return string The resulting plain-text string.
964
     */
965
    protected function stripHtml($html)
966
    {
967
        $str = html_entity_decode($html);
968
969
        // Strip HTML (Replace br with newline, remove "invisible" tags and strip other tags)
970
        $str = preg_replace('#<br[^>]*?>#siu', "\n", $str);
971
        $str = preg_replace(
972
            [
973
                '#<applet[^>]*?.*?</applet>#siu',
974
                '#<embed[^>]*?.*?</embed>#siu',
975
                '#<head[^>]*?>.*?</head>#siu',
976
                '#<noframes[^>]*?.*?</noframes>#siu',
977
                '#<noscript[^>]*?.*?</noscript>#siu',
978
                '#<noembed[^>]*?.*?</noembed>#siu',
979
                '#<object[^>]*?.*?</object>#siu',
980
                '#<script[^>]*?.*?</script>#siu',
981
                '#<style[^>]*?>.*?</style>#siu'
982
            ],
983
            '',
984
            $str
985
        );
986
        $str = strip_tags($str);
987
988
        // Trim whitespace
989
        $str = str_replace("\t", '', $str);
990
        $str = preg_replace('#\n\r|\r\n#', "\n", $str);
991
        $str = preg_replace('#\n{3,}#', "\n\n", $str);
992
        $str = preg_replace('/ {2,}/', ' ', $str);
993
        $str = implode("\n", array_map('trim', explode("\n", $str)));
994
        $str = trim($str)."\n";
995
        return $str;
996
    }
997
998
    /**
999
     * Log the send event for each recipient.
1000
     *
1001
     * @param  boolean $result Success or failure.
1002
     * @param  mixed   $mailer The raw mailer.
1003
     * @return void
1004
     */
1005
    protected function logSend($result, $mailer)
1006
    {
1007
        if ($this->log() === false) {
1008
            return;
1009
        }
1010
1011
        if (!$result) {
1012
            $this->logger->error('Email could not be sent.');
1013
        } else {
1014
            $this->logger->debug(
1015
                sprintf('Email "%s" sent successfully.', $this->subject()),
1016
                $this->to()
1017
            );
1018
        }
1019
1020
        $recipients = array_merge(
1021
            $this->to(),
1022
            $this->cc(),
1023
            $this->bcc()
1024
        );
1025
1026
        foreach ($recipients as $to) {
1027
            $log = $this->logFactory()->create('charcoal/email/email-log');
1028
1029
            $log->setQueueId($this->queueId());
1030
            $log->setMessageId($mailer->getLastMessageId());
1031
            $log->setCampaign($this->campaign());
1032
            $log->setSendTs('now');
1033
            $log->setFrom($mailer->From);
1034
            $log->setTo($to);
1035
            $log->setSubject($this->subject());
1036
1037
            if (!empty($mailer->getSMTPInstance()->getError()['smtp_code'])) {
1038
                $log->setErrorCode($mailer->getSMTPInstance()->getError()['smtp_code']);
1039
            }
1040
1041
            $log->save();
1042
        }
1043
    }
1044
1045
    /**
1046
     * Transform a snake_case string to camelCase.
1047
     *
1048
     * @param string $str The snake_case string to camelize.
1049
     * @return string The camelCase string.
1050
     */
1051
    private function camelize($str)
1052
    {
1053
        return lcfirst(implode('', array_map('ucfirst', explode('_', $str))));
1054
    }
1055
1056
    /**
1057
     * Temporary hack to fulfills the Configurable Interface.
1058
     *
1059
     * @return EmailConfig
1060
     */
1061
    public function createConfig()
1062
    {
1063
        // This should really be avoided.
1064
        $this->logger->warning('AbstractEmail::createConfig() was called, but should not.');
1065
        return new \Charcoal\Email\EmailConfig();
1066
    }
1067
}
1068