MandrillHelper::init()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace LeKoala\Mandrill;
4
5
use Mandrill;
6
use Exception;
7
use ReflectionObject;
8
use SilverStripe\Control\Director;
9
use SilverStripe\Core\Environment;
10
use Symfony\Component\Mailer\Mailer;
11
use SilverStripe\Control\Email\Email;
12
use SilverStripe\SiteConfig\SiteConfig;
0 ignored issues
show
Bug introduced by
The type SilverStripe\SiteConfig\SiteConfig was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
13
use SilverStripe\Core\Injector\Injector;
14
use SilverStripe\Core\Config\Configurable;
15
use Symfony\Component\Mailer\MailerInterface;
16
use Symfony\Component\Mailer\Transport\AbstractTransport;
17
18
/**
19
 * This configurable class helps decoupling the api client from SilverStripe
20
 */
21
class MandrillHelper
22
{
23
    use Configurable;
24
25
    /**
26
     * Client instance
27
     *
28
     * @var Mandrill
29
     */
30
    protected static $client;
31
32
    /**
33
     * Use this to set the app domain registered in mandrill.
34
     * Ignored if MANDRILL_DOMAIN is set.
35
     *
36
     * @config
37
     * @var string
38
     */
39
    private static $domain = null;
40
41
42
    /**
43
     * Use this to set the app domain via a siteconfig field. Ignored if $domain is set.
44
     *
45
     * @config
46
     * @var string
47
     */
48
    private static $siteconfig_domain = null;
49
50
    /**
51
     * Use this to set the logging folder. E.g. _logs/emails. Will be appended to BASE_PATH
52
     * so must be relative to this.
53
     *
54
     * @config
55
     * @var string
56
     */
57
    private static $log_folder = null;
58
59
    /**
60
     * Set to true to enable logging. Set to true if MANDRILL_ENABLE_LOGGING env is set.
61
     *
62
     * @config
63
     * @var bool
64
     */
65
    private static $enable_logging = false;
66
67
    /**
68
     * Used to set the mandrill API key if MANDRILL_API_KEY isn't set
69
     *
70
     * @config
71
     * @var string
72
     */
73
    private static $api_key = null;
74
75
    /**
76
     * Set to true if sending should be disabled. E.g. for testing.
77
     * MANDRILL_SENDING_DISABLED env will overwrite this if set.
78
     *
79
     * @config
80
     * @var bool
81
     */
82
    private static $disable_sending = false;
83
84
    /**
85
     * Get the log folder and create it if necessary
86
     *
87
     * @return string
88
     */
89
    public static function getLogFolder()
90
    {
91
        $folder = self::config()->log_folder;
92
        if (empty($folder)) {
93
            return null;
94
        }
95
96
        $logFolder = BASE_PATH . '/' . $folder;
97
        if (!is_dir($logFolder)) {
98
            mkdir($logFolder, 0755, true);
99
        }
100
        return $logFolder;
101
    }
102
103
    /**
104
     * Process environment variable to configure this module
105
     *
106
     * @return void
107
     * @throws Exception
108
     */
109
    public static function init()
110
    {
111
        // We have a key, we can register the transport
112
        $apiKey = static::getAPIKey();
113
        if ($apiKey) {
114
            self::registerTransport();
115
        }
116
    }
117
118
    /**
119
     * Get api key if enabled
120
     *
121
     * @return string|null
122
     */
123
    public static function getAPIKey()
124
    {
125
        // Regular api key used for sending emails (including subaccount support)
126
        $apiKey = Environment::getEnv('MANDRILL_API_KEY');
127
        if ($apiKey) {
128
            return $apiKey;
129
        }
130
131
        $apiKey = self::config()->get('api_key');
132
        if ($apiKey) {
133
            return $apiKey;
134
        }
135
136
        return null;
137
    }
138
139
    /**
140
     * Register the transport with the client
141
     *
142
     * @return MandrillApiTransport The updated mailer
143
     * @throws Exception
144
     */
145
    public static function registerTransport()
146
    {
147
        $client = self::getClient();
148
        $mailer = self::getMailer();
0 ignored issues
show
Unused Code introduced by
The assignment to $mailer is dead and can be removed.
Loading history...
149
        $transport = new MandrillApiTransport($client);
150
        $mailer = new Mailer($transport);
151
        Injector::inst()->registerService($mailer, MailerInterface::class);
152
        return $mailer;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $mailer returns the type Symfony\Component\Mailer\Mailer which is incompatible with the documented return type LeKoala\Mandrill\MandrillApiTransport.
Loading history...
153
    }
154
155
    /**
156
     * Get the api client instance
157
     * @return Mandrill
158
     *
159
     * @throws Exception
160
     */
161
    public static function getClient()
162
    {
163
        if (!self::$client) {
164
            $key = static::getAPIKey();
165
            if (empty($key)) {
166
                throw new Exception("api_key is not configured for " . __class__);
167
            }
168
            self::$client = new Mandrill($key);
169
        }
170
        return self::$client;
171
    }
172
173
    /**
174
     * @param MailerInterface $mailer
175
     * @return AbstractTransport|MandrillApiTransport
176
     */
177
    public static function getTransportFromMailer($mailer)
178
    {
179
        $r = new ReflectionObject($mailer);
180
        $p = $r->getProperty('transport');
181
        $p->setAccessible(true);
182
        return $p->getValue($mailer);
183
    }
184
185
    /**
186
     * Get the mailer instance
187
     *
188
     * @return MailerInterface
189
     */
190
    public static function getMailer()
191
    {
192
        return Injector::inst()->get(MailerInterface::class);
193
    }
194
195
    /**
196
     * @return array
197
     */
198
    public static function listValidDomains()
199
    {
200
        $list = self::config()->valid_domains;
201
        if (!$list) {
202
            $list = [];
203
        }
204
        return $list;
205
    }
206
207
    /**
208
     * Get domain configured for this application
209
     *
210
     * @return bool|string
211
     */
212
    public static function getDomain()
213
    {
214
        // Use env var
215
        $domain = Environment::getEnv('MANDRILL_DOMAIN');
216
        if ($domain) {
217
            return $domain;
218
        }
219
220
        // Use configured domain
221
        $domain = self::config()->domain;
222
        if ($domain) {
223
            return $domain;
224
        }
225
226
        // Look in siteconfig for default sender
227
        $config = SiteConfig::current_site_config();
228
        $config_field = self::config()->siteconfig_domain;
229
        if ($config_field && !empty($config->$config_field)) {
230
            return $config->$config_field;
231
        }
232
233
        // Guess from email
234
        $domain = static::getDomainFromEmail();
235
        if ($domain) {
236
            return $domain;
237
        }
238
239
        // Guess from host
240
        return static::getDomainFromHost();
241
    }
242
243
    /**
244
     * Get domain from admin email
245
     *
246
     * @return bool|string
247
     */
248
    public static function getDomainFromEmail()
249
    {
250
        $email = static::resolveDefaultFromEmail(null, false);
251
        if ($email) {
252
            $domain = substr(strrchr($email, "@"), 1);
253
            return $domain;
254
        }
255
        return false;
256
    }
257
258
    /**
259
     * Resolve default send from address
260
     *
261
     * Keep in mind that an email using send() without a from
262
     * will inject the admin_email. Therefore, SiteConfig
263
     * will not be used
264
     *
265
     * @param string $from
266
     * @param bool $createDefault
267
     * @return string
268
     */
269
    public static function resolveDefaultFromEmail($from = null, $createDefault = true)
270
    {
271
        $original_from = $from;
272
        if (!empty($from)) {
273
            // If we have a sender, validate its email
274
            $from = EmailUtils::get_email_from_rfc_email($from);
275
            if (filter_var($from, FILTER_VALIDATE_EMAIL)) {
276
                return $original_from;
277
            }
278
        }
279
        // Look in siteconfig for default sender
280
        $config = SiteConfig::current_site_config();
281
        $config_field = self::config()->siteconfig_from;
282
        if ($config_field && !empty($config->$config_field)) {
283
            return $config->$config_field;
284
        }
285
        // Use admin email
286
        if ($admin = Email::config()->admin_email) {
287
            return $admin;
288
        }
289
        // If we still don't have anything, create something based on the domain
290
        if ($createDefault) {
291
            return self::createDefaultEmail();
292
        }
293
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
294
    }
295
296
    /**
297
     * Create a sensible default address based on domain name
298
     *
299
     * @return string
300
     */
301
    public static function createDefaultEmail()
302
    {
303
        $fulldom = Director::absoluteBaseURL();
304
        $host = parse_url($fulldom, PHP_URL_HOST);
305
        if (!$host) {
306
            $host = 'localhost';
307
        }
308
        $dom = str_replace('www.', '', $host);
309
310
        return 'postmaster@' . $dom;
311
    }
312
313
    /**
314
     * Get domain name from current host
315
     *
316
     * @return bool|string
317
     */
318
    public static function getDomainFromHost()
319
    {
320
        $base = Environment::getEnv('SS_BASE_URL');
321
        if (!$base) {
322
            $base = Director::protocolAndHost();
323
        }
324
        $host = parse_url($base, PHP_URL_HOST);
325
        $hostParts = explode('.', $host);
326
        $parts = count($hostParts);
327
        if ($parts < 2) {
328
            return false;
329
        }
330
        $domain = $hostParts[$parts - 2] . "." . $hostParts[$parts - 1];
331
        return $domain;
332
    }
333
334
    /**
335
     * Resolve default send to address
336
     *
337
     * @param string $to
338
     * @return string
339
     */
340
    public static function resolveDefaultToEmail($to = null)
341
    {
342
        // In case of multiple recipients, do not validate anything
343
        if (is_array($to) || strpos($to, ',') !== false) {
0 ignored issues
show
Bug introduced by
It seems like $to can also be of type null; however, parameter $haystack of strpos() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

343
        if (is_array($to) || strpos(/** @scrutinizer ignore-type */ $to, ',') !== false) {
Loading history...
344
            return $to;
345
        }
346
        $original_to = $to;
347
        if (!empty($to)) {
348
            $to = EmailUtils::get_email_from_rfc_email($to);
349
            if (filter_var($to, FILTER_VALIDATE_EMAIL)) {
350
                return $original_to;
351
            }
352
        }
353
        $config = SiteConfig::current_site_config();
354
        $config_field = self::config()->siteconfig_to;
355
        if ($config_field && !empty($config->$config_field)) {
356
            return $config->$config_field;
357
        }
358
        if ($admin = Email::config()->admin_email) {
359
            return $admin;
360
        }
361
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
362
    }
363
364
    /**
365
     * Is logging enabled?
366
     *
367
     * @return bool
368
     */
369
    public static function getLoggingEnabled()
370
    {
371
        if (Environment::getEnv('MANDRILL_ENABLE_LOGGING')) {
372
            return true;
373
        }
374
375
        if (self::config()->get('enable_logging')) {
376
            return true;
377
        }
378
379
        return false;
380
    }
381
382
    /**
383
     * Is sending enabled?
384
     *
385
     * @return bool
386
     */
387
    public static function getSendingEnabled()
388
    {
389
        $disabled = Environment::getEnv('MANDRILL_SENDING_DISABLED');
390
        if ($disabled) {
391
            return false;
392
        }
393
        if (self::config()->get('disable_sending')) {
394
            return false;
395
        }
396
        return true;
397
    }
398
}
399