Issues (39)

src/EmailUtils.php (3 issues)

1
<?php
2
3
namespace LeKoala\SparkPost;
4
5
use Pelago\Emogrifier\CssInliner;
6
use Symfony\Component\Mime\Address;
7
use Pelago\Emogrifier\HtmlProcessor\HtmlPruner;
8
use Pelago\Emogrifier\HtmlProcessor\CssToAttributeConverter;
9
10
class EmailUtils
11
{
12
    /**
13
     * Inline styles using Pelago Emogrifier V7
14
     *
15
     * This is much better than the functionnality provided by SparkPost anyway
16
     *
17
     * @link https://github.com/MyIntervals/emogrifier#more-complex-example
18
     * @param string $html
19
     * @param string $css (optional) css to inline
20
     * @return string
21
     */
22
    public static function inline_styles($html, $css = '')
23
    {
24
        $domDocument = CssInliner::fromHtml($html)->inlineCss($css)->getDomDocument();
25
26
        HtmlPruner::fromDomDocument($domDocument)->removeElementsWithDisplayNone();
27
        $html = CssToAttributeConverter::fromDomDocument($domDocument)
28
            ->convertCssToVisualAttributes()->render();
29
30
        return $html;
31
    }
32
33
    /**
34
     * @param array<string|int,string|null>|string|null|bool|Address $email
35
     * @return string|null
36
     */
37
    public static function stringify($email)
38
    {
39
        if (!$email || is_bool($email)) {
40
            return null;
41
        }
42
        if ($email instanceof Address) {
43
            if ($email->getName()) {
44
                return $email->getName() . ' <' . $email->getAddress() . '>';
45
            }
46
            return $email->getAddress();
47
        }
48
        if (is_array($email)) {
49
            if (count($email) == 2) {
50
                return $email[1] . ' <' . $email[0] . '>';
51
            }
52
            foreach ($email as $k => $v) {
53
                if ($k) {
54
                    return $v . ' <' . $k . '>';
55
                }
56
                return $v;
57
            }
58
        }
59
        return $email;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $email also could return the type array<integer|string,null|string> which is incompatible with the documented return type null|string.
Loading history...
60
    }
61
62
    /**
63
     * @param array<mixed> $emails
64
     * @return string
65
     */
66
    public static function stringifyArray(array $emails)
67
    {
68
        $result = [];
69
        foreach ($emails as $email) {
70
            $result[] = self::stringify($email);
71
        }
72
        return implode(", ", $result);
73
    }
74
75
    /**
76
     * @param array<string|int,string|null>|string|null|bool $email
77
     * @return bool
78
     */
79
    public static function validate($email)
80
    {
81
        return boolval(filter_var(self::stringify($email), FILTER_VALIDATE_EMAIL));
82
    }
83
84
    /**
85
     * Convert an html email to a text email while keeping formatting and links
86
     *
87
     * @param string $content
88
     * @return string
89
     */
90
    public static function convert_html_to_text($content)
91
    {
92
        // Prevent styles to be included
93
        $content = preg_replace('/<style.*>([\s\S]*)<\/style>/i', '', $content);
94
        // Convert html entities to strip them later on
95
        $content = html_entity_decode($content);
96
        // Bold
97
        $content = str_ireplace(['<strong>', '</strong>', '<b>', '</b>'], "*", $content);
98
        // Replace links to keep them accessible
99
        $content = preg_replace('/<a(.*?)href=[\'"](.*?)[\'"](.*?)>(.*?)<\/a>/i', '$4 ($2)', $content);
100
        // Replace new lines
101
        $content = str_replace(['<br>', '<br/>', '<br />'], "\r\n", $content);
102
        // Remove html tags
103
        $content = strip_tags($content);
104
        // Avoid lots of spaces
105
        $content = preg_replace('/^[\s][\s]+(\S)/m', "\n$1", $content);
106
        // Trim content so that it's nice
107
        $content = trim($content);
108
        return $content;
109
    }
110
111
    /**
112
     * Match all words and whitespace, will be terminated by '<'
113
     *
114
     * Note: use /u to support utf8 strings
115
     *
116
     * @param string|array<string|int,string|null> $rfc_email_string
117
     * @return string
118
     */
119
    public static function get_displayname_from_rfc_email($rfc_email_string)
120
    {
121
        $rfc_email_string = self::stringify($rfc_email_string);
122
        if (!$rfc_email_string) {
123
            return null;
124
        }
125
        $result = preg_match('/[\w\s\-\.]+/u', $rfc_email_string, $matches);
0 ignored issues
show
The assignment to $result is dead and can be removed.
Loading history...
126
        $matches[0] = trim($matches[0]);
127
        return $matches[0];
128
    }
129
130
    /**
131
     * Extract parts between brackets
132
     *
133
     * @param string|array<string|int,string|null> $rfc_email_string
134
     * @return ?string
135
     */
136
    public static function get_email_from_rfc_email($rfc_email_string)
137
    {
138
        $rfc_email_string = self::stringify($rfc_email_string);
139
        if (!$rfc_email_string) {
140
            return null;
141
        }
142
        if (strpos($rfc_email_string, '<') === false) {
143
            return $rfc_email_string;
144
        }
145
        $result = preg_match('/(?:<)(.+)(?:>)$/', $rfc_email_string, $matches);
0 ignored issues
show
The assignment to $result is dead and can be removed.
Loading history...
146
        if (empty($matches)) {
147
            return $rfc_email_string;
148
        }
149
        return $matches[1];
150
    }
151
152
    /**
153
     * @deprecated
154
     * @param \SilverStripe\Control\Email\Email $Email
155
     * @return \Symfony\Component\Mime\Header\Headers
156
     */
157
    public static function getHeaders($Email)
158
    {
159
        return method_exists($Email, 'getSwiftMessage') ? $Email->getSwiftMessage()->getHeaders() : $Email->getHeaders();
160
    }
161
}
162