Completed
Push — master ( 46f3eb...7c230f )
by Mathieu
02:46 queued 40s
created

Tracker::addOpenTrackingImage()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 9.8333
c 0
b 0
f 0
cc 2
nc 2
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Charcoal\Email\Services;
6
7
// From 'locomotivemtl/charcoal-factory'
8
use Charcoal\Factory\FactoryInterface;
9
10
use Charcoal\Email\Email;
11
use Charcoal\Email\Objects\Link;
12
use Charcoal\Email\Objects\LinkLog;
13
use Charcoal\Email\Objects\OpenLog;
14
15
/**
16
 * Tracker Service.
17
 *
18
 * Provide methods to
19
 * - add tracking pixel to email's HTML content;
20
 * - replace all links in HTML content with tracking links;
21
 * - track the opening of an email (request to the pixel);
22
 * - track the clicking of an email link (request to tracking link);
23
 */
24
class Tracker
25
{
26
27
    /**
28
     * @var string
29
     */
30
    private $baseUrl;
31
32
    /**
33
     * @var FactoryInterface
34
     */
35
    private $modelFactory;
36
37
    /**
38
     * @param string           $baseUrl      Base URL.
39
     * @param FactoryInterface $modelFactory Model factory to create link and log objects.
40
     */
41
    public function __construct(string $baseUrl, FactoryInterface $modelFactory)
42
    {
43
        $this->baseUrl = $baseUrl;
44
        $this->modelFactory = $modelFactory;
45
    }
46
47
    /**
48
     * @param Email  $email      Email object to update.
49
     * @param string $emailLogId Email log ID, to generate image link for.
50
     * @return void
51
     */
52
    public function addOpenTrackingImage(Email &$email, string $emailLogId): void
53
    {
54
        $html = $email->msgHtml();
55
        $regexp = '/(<body.*?>)/i';
56
        $pixel = sprintf('<img src="%s" alt="" />', $this->baseUrl.'email/v1/open/'.$emailLogId.'.png');
57
        if (preg_match($regexp, $html) != false) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing preg_match($regexp, $html) of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
58
            $parts = preg_split($regexp, $html, -1, (PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE));
59
            $html = $parts[0].$parts[1].$pixel.$parts[2];
60
        } else {
61
            $html .= $pixel;
62
        }
63
        $email->setMsgHtml($html);
64
    }
65
66
    /**
67
     * @param Email  $email      Email object to update.
68
     * @param string $emailLogId Email log ID, to generate links for.
69
     * @return void
70
     */
71
    public function replaceLinksWithTracker(Email &$email, string $emailLogId): void
72
    {
73
        $html = $email->msgHtml();
74
75
        $callback = function(array $matches) use ($emailLogId): string {
76
            $linkId  = $this->createLink($emailLogId, $matches[1]);
77
            $linkUrl = $this->baseUrl.'email/v1/link/'.$linkId;
78
            return str_replace($matches[1], $linkUrl, $matches[0]);
79
        };
80
        $regexp = '/<a\s+(?:[^>]*?\s+)?href="([^"]*)"/';
81
        $html = preg_replace_callback($regexp, $callback, $html);
82
        $email->setMsgHtml($html);
83
    }
84
85
    /**
86
     * @param string      $emailLogId Email log ID, to track.
87
     * @param string|null $ip         Client IP address.
88
     * @return void
89
     */
90
    public function trackOpen(string $emailLogId, ?string $ip): void
91
    {
92
        $log = $this->modelFactory->create(OpenLog::class);
93
        $log['email'] = $emailLogId;
94
        $log['ts'] = 'now';
95
        $log['ip'] = $ip;
96
        $log->save();
97
    }
98
99
    /**
100
     * @param string      $linkId Link ID, to track.
101
     * @param string|null $ip     Client IP address.
102
     * @return void
103
     */
104
    public function trackLink(string $linkId, ?string $ip): void
105
    {
106
        $log = $this->modelFactory->create(LinkLog::class);
107
        $log['link'] = $linkId;
108
        $log['ts'] = 'now';
109
        $log['ip'] = $ip;
110
        $log->save();
111
    }
112
113
    /**
114
     * @param string $emailLogId Email log ID, to create link for.
115
     * @param string $url        URL to redirect to.
116
     * @return string
117
     */
118
    private function createLink(string $emailLogId, string $url): string
119
    {
120
        $link = $this->modelFactory->create(Link::class);
121
        $link['email'] = $emailLogId;
122
        $link['url'] = $url;
123
        $link->save();
124
        return $link->id();
125
    }
126
}
127