Passed
Push — master ( 00f43d...a0d391 )
by Malte
02:56
created

Message::extractPriority()   B

Complexity

Conditions 8
Paths 13

Size

Total Lines 26
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 22
nc 13
nop 1
dl 0
loc 26
rs 8.4444
c 0
b 0
f 0
1
<?php
2
/*
3
* File:     Message.php
4
* Category: -
5
* Author:   M. Goldenbaum
6
* Created:  19.01.17 22:21
7
* Updated:  -
8
*
9
* Description:
10
*  -
11
*/
12
13
namespace Webklex\IMAP;
14
15
use Carbon\Carbon;
16
use Webklex\IMAP\Exceptions\InvalidMessageDateException;
17
use Webklex\IMAP\Exceptions\MaskNotFoundException;
18
use Webklex\IMAP\Exceptions\MethodNotFoundException;
19
use Webklex\IMAP\Support\AttachmentCollection;
20
use Webklex\IMAP\Support\FlagCollection;
21
use Webklex\IMAP\Support\Masks\MessageMask;
22
23
/**
24
 * Class Message
25
 *
26
 * @package Webklex\IMAP
27
 *
28
 * @property integer msglist
29
 * @property integer uid
30
 * @property integer msgn
31
 * @property integer priority
32
 * @property string subject
33
 * @property string message_id
34
 * @property string message_no
35
 * @property string references
36
 * @property carbon date
37
 * @property array from
38
 * @property array to
39
 * @property array cc
40
 * @property array bcc
41
 * @property array reply_to
42
 * @property array in_reply_to
43
 * @property array sender
44
 *
45
 * @method integer getMsglist()
46
 * @method integer setMsglist(integer $msglist)
47
 * @method integer getUid()
48
 * @method integer setUid(integer $uid)
49
 * @method integer getMsgn()
50
 * @method integer setMsgn(integer $msgn)
51
 * @method integer getPriority()
52
 * @method integer setPriority(integer $priority)
53
 * @method string getSubject()
54
 * @method string setSubject(string $subject)
55
 * @method string getMessageId()
56
 * @method string setMessageId(string $message_id)
57
 * @method string getMessageNo()
58
 * @method string setMessageNo(string $message_no)
59
 * @method string getReferences()
60
 * @method string setReferences(string $references)
61
 * @method carbon getDate()
62
 * @method carbon setDate(carbon $date)
63
 * @method array getFrom()
64
 * @method array setFrom(array $from)
65
 * @method array getTo()
66
 * @method array setTo(array $to)
67
 * @method array getCc()
68
 * @method array setCc(array $cc)
69
 * @method array getBcc()
70
 * @method array setBcc(array $bcc)
71
 * @method array getReplyTo()
72
 * @method array setReplyTo(array $reply_to)
73
 * @method array getInReplyTo()
74
 * @method array setInReplyTo(array $in_reply_to)
75
 * @method array getSender()
76
 * @method array setSender(array $sender)
77
 */
78
class Message {
79
80
    /**
81
     * Client instance
82
     *
83
     * @var Client
84
     */
85
    private $client = Client::class;
86
87
    /**
88
     * Default mask
89
     * @var string $mask
90
     */
91
    protected $mask = MessageMask::class;
92
93
    /** @var array $config */
94
    protected $config = [];
95
96
    /** @var array $attributes */
97
    protected $attributes = [
98
        'message_id' => '',
99
        'message_no' => null,
100
        'subject' => '',
101
        'references' => null,
102
        'date' => null,
103
        'from' => [],
104
        'to' => [],
105
        'cc' => [],
106
        'bcc' => [],
107
        'reply_to' => [],
108
        'in_reply_to' => '',
109
        'sender' => [],
110
        'priority' => 0,
111
    ];
112
113
    /**
114
     * Fetch body options
115
     *
116
     * @var integer
117
     */
118
    public $fetch_options = null;
119
120
    /**
121
     * Fetch body options
122
     *
123
     * @var bool
124
     */
125
    public $fetch_body = null;
126
127
    /**
128
     * Fetch attachments options
129
     *
130
     * @var bool
131
     */
132
    public $fetch_attachment = null;
133
134
    /**
135
     * Fetch flags options
136
     *
137
     * @var bool
138
     */
139
    public $fetch_flags = null;
140
141
    /**
142
     * @var string $header
143
     */
144
    public $header = null;
145
146
    /**
147
     * @var null|object $header_info
148
     */
149
    public $header_info = null;
150
151
    /** @var null|string $raw_body */
152
    public $raw_body = null;
153
154
    /** @var null $structure */
155
    protected $structure = null;
156
157
    /**
158
     * Message body components
159
     *
160
     * @var array   $bodies
161
     * @var AttachmentCollection|array $attachments
162
     * @var FlagCollection|array       $flags
163
     */
164
    public $bodies = [];
165
    public $attachments = [];
166
    public $flags = [];
167
168
    /**
169
     * A list of all available and supported flags
170
     *
171
     * @var array $available_flags
172
     */
173
    private $available_flags = ['recent', 'flagged', 'answered', 'deleted', 'seen', 'draft'];
174
175
    /**
176
     * Message constructor.
177
     *
178
     * @param integer       $uid
179
     * @param integer|null  $msglist
180
     * @param Client        $client
181
     * @param integer|null  $fetch_options
182
     * @param boolean       $fetch_body
183
     * @param boolean       $fetch_attachment
184
     * @param boolean       $fetch_flags
185
     *
186
     * @throws Exceptions\ConnectionFailedException
187
     * @throws InvalidMessageDateException
188
     */
189
    public function __construct($uid, $msglist, Client $client, $fetch_options = null, $fetch_body = false, $fetch_attachment = false, $fetch_flags = false) {
190
191
        $default_mask = $client->getDefaultMessageMask();
192
        if($default_mask != null) {
193
            $this->mask = $default_mask;
194
        }
195
196
        $this->config = config('imap.options');
197
198
        $this->setFetchOption($fetch_options);
199
        $this->setFetchBodyOption($fetch_body);
200
        $this->setFetchAttachmentOption($fetch_attachment);
201
        $this->setFetchFlagsOption($fetch_flags);
202
203
        $this->attachments = AttachmentCollection::make([]);
204
        $this->flags = FlagCollection::make([]);
205
206
        $this->msglist = $msglist;
207
        $this->client = $client;
208
209
        $this->uid =  ($this->fetch_options == IMAP::FT_UID) ? $uid : $uid;
210
        $this->msgn = ($this->fetch_options == IMAP::FT_UID) ? imap_msgno($this->client->getConnection(), $uid) : $uid;
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_msgno() does only seem to accept resource, 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

210
        $this->msgn = ($this->fetch_options == IMAP::FT_UID) ? imap_msgno(/** @scrutinizer ignore-type */ $this->client->getConnection(), $uid) : $uid;
Loading history...
211
212
        $this->parseHeader();
213
214
        if ($this->getFetchFlagsOption() === true) {
215
            $this->parseFlags();
216
        }
217
218
        if ($this->getFetchBodyOption() === true) {
219
            $this->parseBody();
220
        }
221
    }
222
223
    /**
224
     * Call dynamic attribute setter and getter methods
225
     * @param string $method
226
     * @param array $arguments
227
     *
228
     * @return mixed
229
     * @throws MethodNotFoundException
230
     */
231
    public function __call($method, $arguments) {
232
        if(strtolower(substr($method, 0, 3)) === 'get') {
233
            $name = snake_case(substr($method, 3));
0 ignored issues
show
Deprecated Code introduced by
The function snake_case() has been deprecated: Str::snake() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

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

233
            $name = /** @scrutinizer ignore-deprecated */ snake_case(substr($method, 3));

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
234
235
            if(isset($this->attributes[$name])) {
236
                return $this->attributes[$name];
237
            }
238
239
        }elseif (strtolower(substr($method, 0, 3)) === 'set') {
240
            $name = snake_case(substr($method, 3));
0 ignored issues
show
Deprecated Code introduced by
The function snake_case() has been deprecated: Str::snake() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

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

240
            $name = /** @scrutinizer ignore-deprecated */ snake_case(substr($method, 3));

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
241
242
            if(isset($this->attributes[$name])) {
243
                $this->attributes[$name] = array_pop($arguments);
244
245
                return $this->attributes[$name];
246
            }
247
248
        }
249
250
        throw new MethodNotFoundException("Method ".self::class.'::'.$method.'() is not supported');
251
    }
252
253
    /**
254
     * @param $name
255
     * @param $value
256
     *
257
     * @return mixed
258
     */
259
    public function __set($name, $value) {
260
        $this->attributes[$name] = $value;
261
262
        return $this->attributes[$name];
263
    }
264
265
    /**
266
     * @param $name
267
     *
268
     * @return mixed|null
269
     */
270
    public function __get($name) {
271
        if(isset($this->attributes[$name])) {
272
            return $this->attributes[$name];
273
        }
274
275
        return null;
276
    }
277
278
    /**
279
     * Copy the current Messages to a mailbox
280
     *
281
     * @param $mailbox
282
     * @param int $options
283
     *
284
     * @return bool
285
     * @throws Exceptions\ConnectionFailedException
286
     */
287
    public function copy($mailbox, $options = 0) {
288
        return imap_mail_copy($this->client->getConnection(), $this->msglist, $mailbox, $options);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_mail_copy() does only seem to accept resource, 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

288
        return imap_mail_copy(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->msglist, $mailbox, $options);
Loading history...
289
    }
290
291
    /**
292
     * Move the current Messages to a mailbox
293
     *
294
     * @param $mailbox
295
     * @param int $options
296
     *
297
     * @return bool
298
     * @throws Exceptions\ConnectionFailedException
299
     */
300
    public function move($mailbox, $options = 0) {
301
        return imap_mail_move($this->client->getConnection(), $this->msglist, $mailbox, $options);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_mail_move() does only seem to accept resource, 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

301
        return imap_mail_move(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->msglist, $mailbox, $options);
Loading history...
302
    }
303
304
    /**
305
     * Check if the Message has a text body
306
     *
307
     * @return bool
308
     */
309
    public function hasTextBody() {
310
        return isset($this->bodies['text']);
311
    }
312
313
    /**
314
     * Get the Message text body
315
     *
316
     * @return mixed
317
     */
318
    public function getTextBody() {
319
        if (!isset($this->bodies['text'])) {
320
            return false;
321
        }
322
323
        return $this->bodies['text']->content;
324
    }
325
326
    /**
327
     * Check if the Message has a html body
328
     *
329
     * @return bool
330
     */
331
    public function hasHTMLBody() {
332
        return isset($this->bodies['html']);
333
    }
334
335
    /**
336
     * Get the Message html body
337
     * If $replaceImages is callable it should expect string $body as first parameter, $oAttachment as second and return
338
     * the resulting $body.
339
     *
340
     * @var bool|callable $replaceImages
341
     *
342
     * @return string|null
343
     *
344
     * @deprecated 1.4.0:2.0.0 No longer needed. Use AttachmentMask::getImageSrc() instead
345
     */
346
    public function getHTMLBody($replaceImages = false) {
347
        if (!isset($this->bodies['html'])) {
348
            return null;
349
        }
350
351
        $body = $this->bodies['html']->content;
352
        if ($replaceImages !== false) {
353
            $this->attachments->each(function($oAttachment) use(&$body, $replaceImages) {
354
                /** @var Attachment $oAttachment */
355
                if(is_callable($replaceImages)) {
356
                    $body = $replaceImages($body, $oAttachment);
357
                }elseif(is_string($replaceImages)) {
358
                    call_user_func($replaceImages, [$body, $oAttachment]);
359
                }else{
360
                    if ($oAttachment->id && $oAttachment->getImgSrc() != null) {
0 ignored issues
show
Deprecated Code introduced by
The function Webklex\IMAP\Attachment::getImgSrc() has been deprecated: 1.4.0:2.0.0 No longer needed. Use AttachmentMask::getImageSrc() instead ( Ignorable by Annotation )

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

360
                    if ($oAttachment->id && /** @scrutinizer ignore-deprecated */ $oAttachment->getImgSrc() != null) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
361
                        $body = str_replace('cid:'.$oAttachment->id, $oAttachment->getImgSrc(), $body);
0 ignored issues
show
Deprecated Code introduced by
The function Webklex\IMAP\Attachment::getImgSrc() has been deprecated: 1.4.0:2.0.0 No longer needed. Use AttachmentMask::getImageSrc() instead ( Ignorable by Annotation )

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

361
                        $body = str_replace('cid:'.$oAttachment->id, /** @scrutinizer ignore-deprecated */ $oAttachment->getImgSrc(), $body);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
362
                    }
363
                }
364
            });
365
        }
366
367
        return $body;
368
    }
369
370
    /**
371
     * Parse all defined headers
372
     *
373
     * @return void
374
     * @throws Exceptions\ConnectionFailedException
375
     * @throws InvalidMessageDateException
376
     */
377
    private function parseHeader() {
378
        $this->header = $header = imap_fetchheader($this->client->getConnection(), $this->uid, IMAP::FT_UID);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_fetchheader() does only seem to accept resource, 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

378
        $this->header = $header = imap_fetchheader(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, IMAP::FT_UID);
Loading history...
379
380
        $this->priority = $this->extractPriority($this->header);
381
382
        if ($this->header) {
383
            $header = imap_rfc822_parse_headers($this->header);
384
        }
385
386
        if (property_exists($header, 'subject')) {
387
            if($this->config['decoder']['message']['subject'] === 'utf-8') {
388
                $this->subject = imap_utf8($header->subject);
389
            }else{
390
                $this->subject = mb_decode_mimeheader($header->subject);
391
            }
392
        }
393
394
        foreach(['from', 'to', 'cc', 'bcc', 'reply_to', 'sender'] as $part){
395
            $this->extractHeaderAddressPart($header, $part);
0 ignored issues
show
Bug introduced by
It seems like $header can also be of type string; however, parameter $header of Webklex\IMAP\Message::extractHeaderAddressPart() does only seem to accept object, 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

395
            $this->extractHeaderAddressPart(/** @scrutinizer ignore-type */ $header, $part);
Loading history...
396
        }
397
398
        if (property_exists($header, 'references')) {
399
            $this->references = $header->references;
400
        }
401
        if (property_exists($header, 'in_reply_to')) {
402
            $this->in_reply_to = str_replace(['<', '>'], '', $header->in_reply_to);
403
        }
404
        if (property_exists($header, 'message_id')) {
405
            $this->message_id = str_replace(['<', '>'], '', $header->message_id);
406
        }
407
        if (property_exists($header, 'Msgno')) {
408
            $messageNo = (int) trim($header->Msgno);
409
            $this->message_no = ($this->fetch_options == IMAP::FT_UID) ? $messageNo : imap_msgno($this->client->getConnection(), $messageNo);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_msgno() does only seem to accept resource, 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

409
            $this->message_no = ($this->fetch_options == IMAP::FT_UID) ? $messageNo : imap_msgno(/** @scrutinizer ignore-type */ $this->client->getConnection(), $messageNo);
Loading history...
410
        } else {
411
            $this->message_no = imap_msgno($this->client->getConnection(), $this->getUid());
412
        }
413
414
        $this->date = $this->parseDate($header);
0 ignored issues
show
Bug introduced by
It seems like $header can also be of type string; however, parameter $header of Webklex\IMAP\Message::parseDate() does only seem to accept object, 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

414
        $this->date = $this->parseDate(/** @scrutinizer ignore-type */ $header);
Loading history...
415
    }
416
417
    /**
418
     * Try to extract the priority from a given raw header string
419
     * @param string $header
420
     *
421
     * @return int|null
422
     */
423
    private function extractPriority($header) {
424
        if(preg_match('/x\-priority\:.*([0-9]{1,2})/i', $header, $priority)){
425
            $priority = isset($priority[1]) ? (int) $priority[1] : 0;
426
            switch($priority){
427
                case IMAP::MESSAGE_PRIORITY_HIGHEST;
428
                    $priority = IMAP::MESSAGE_PRIORITY_HIGHEST;
429
                    break;
430
                case IMAP::MESSAGE_PRIORITY_HIGH;
431
                    $priority = IMAP::MESSAGE_PRIORITY_HIGH;
432
                    break;
433
                case IMAP::MESSAGE_PRIORITY_NORMAL;
434
                    $priority = IMAP::MESSAGE_PRIORITY_NORMAL;
435
                    break;
436
                case IMAP::MESSAGE_PRIORITY_LOW;
437
                    $priority = IMAP::MESSAGE_PRIORITY_LOW;
438
                    break;
439
                case IMAP::MESSAGE_PRIORITY_LOWEST;
440
                    $priority = IMAP::MESSAGE_PRIORITY_LOWEST;
441
                    break;
442
                default:
443
                    $priority = IMAP::MESSAGE_PRIORITY_UNKNOWN;
444
                    break;
445
            }
446
        }
447
448
        return $priority;
449
    }
450
451
    /**
452
     * Exception handling for invalid dates
453
     *
454
     * Currently known invalid formats:
455
     * ^ Datetime                                   ^ Problem                           ^ Cause
456
     * | Mon, 20 Nov 2017 20:31:31 +0800 (GMT+8:00) | Double timezone specification     | A Windows feature
457
     * | Thu, 8 Nov 2018 08:54:58 -0200 (-02)       |
458
     * |                                            | and invalid timezone (max 6 char) |
459
     * | 04 Jan 2018 10:12:47 UT                    | Missing letter "C"                | Unknown
460
     * | Thu, 31 May 2018 18:15:00 +0800 (added by) | Non-standard details added by the | Unknown
461
     * |                                            | mail server                       |
462
     * | Sat, 31 Aug 2013 20:08:23 +0580            | Invalid timezone                  | PHPMailer bug https://sourceforge.net/p/phpmailer/mailman/message/6132703/
463
     *
464
     * Please report any new invalid timestamps to [#45](https://github.com/Webklex/laravel-imap/issues/45)
465
     *
466
     * @param object $header
467
     *
468
     * @return Carbon|null
469
     * @throws InvalidMessageDateException
470
     */
471
    private function parseDate($header) {
472
        $parsed_date = null;
473
474
        if (property_exists($header, 'date')) {
475
            $date = $header->date;
476
477
            if(preg_match('/\+0580/', $date)) {
478
                $date = str_replace('+0580', '+0530', $date);
479
            }
480
481
            $date = trim(rtrim($date));
482
            try {
483
                $parsed_date = Carbon::parse($date);
484
            } catch (\Exception $e) {
485
                switch (true) {
486
                    case preg_match('/([A-Z]{2,3}[\,|\ \,]\ [0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}.*)+$/i', $date) > 0:
487
                    case preg_match('/([A-Z]{2,3}\,\ [0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ [\-|\+][0-9]{4}\ \(.*)\)+$/i', $date) > 0:
488
                    case preg_match('/([A-Z]{2,3}\, \ [0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ [\-|\+][0-9]{4}\ \(.*)\)+$/i', $date) > 0:
489
                    case preg_match('/([0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{2,4}\ [0-9]{2}\:[0-9]{2}\:[0-9]{2}\ [A-Z]{2}\ \-[0-9]{2}\:[0-9]{2}\ \([A-Z]{2,3}\ \-[0-9]{2}:[0-9]{2}\))+$/i', $date) > 0:
490
                        $array = explode('(', $date);
491
                        $array = array_reverse($array);
492
                        $date = trim(array_pop($array));
493
                        break;
494
                    case preg_match('/([0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ UT)+$/i', $date) > 0:
495
                        $date .= 'C';
496
                        break;
497
                }
498
                try{
499
                    $parsed_date = Carbon::parse($date);
500
                } catch (\Exception $_e) {
501
                    throw new InvalidMessageDateException("Invalid message date. ID:".$this->getMessageId(), 1000, $e);
502
                }
503
            }
504
        }
505
506
        return $parsed_date;
507
    }
508
509
    /**
510
     * Parse additional flags
511
     *
512
     * @return void
513
     * @throws Exceptions\ConnectionFailedException
514
     */
515
    private function parseFlags() {
516
        $this->flags = FlagCollection::make([]);
517
518
        $flags = imap_fetch_overview($this->client->getConnection(), $this->uid, IMAP::FT_UID);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_fetch_overview() does only seem to accept resource, 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

518
        $flags = imap_fetch_overview(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, IMAP::FT_UID);
Loading history...
519
        if (is_array($flags) && isset($flags[0])) {
520
            foreach($this->available_flags as $flag) {
521
                $this->parseFlag($flags, $flag);
522
            }
523
        }
524
    }
525
526
    /**
527
     * Extract a possible flag information from a given array
528
     * @param array $flags
529
     * @param string $flag
530
     */
531
    private function parseFlag($flags, $flag) {
532
        $flag = strtolower($flag);
533
534
        if (property_exists($flags[0], strtoupper($flag))) {
535
            $this->flags->put($flag, $flags[0]->{strtoupper($flag)});
536
        } elseif (property_exists($flags[0], ucfirst($flag))) {
537
            $this->flags->put($flag, $flags[0]->{ucfirst($flag)});
538
        } elseif (property_exists($flags[0], $flag)) {
539
            $this->flags->put($flag, $flags[0]->$flag);
540
        }
541
    }
542
543
    /**
544
     * Get the current Message header info
545
     *
546
     * @return object
547
     * @throws Exceptions\ConnectionFailedException
548
     */
549
    public function getHeaderInfo() {
550
        if ($this->header_info == null) {
551
            $this->header_info =
552
            $this->header_info = imap_headerinfo($this->client->getConnection(), $this->getMessageNo());
0 ignored issues
show
Bug introduced by
$this->getMessageNo() of type string is incompatible with the type integer expected by parameter $msg_no of imap_headerinfo(). ( Ignorable by Annotation )

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

552
            $this->header_info = imap_headerinfo($this->client->getConnection(), /** @scrutinizer ignore-type */ $this->getMessageNo());
Loading history...
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $stream_id of imap_headerinfo() does only seem to accept resource, 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

552
            $this->header_info = imap_headerinfo(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->getMessageNo());
Loading history...
553
        }
554
555
        return $this->header_info;
556
    }
557
558
    /**
559
     * Extract a given part as address array from a given header
560
     * @param object $header
561
     * @param string $part
562
     */
563
    private function extractHeaderAddressPart($header, $part) {
564
        if (property_exists($header, $part)) {
565
            $this->$part = $this->parseAddresses($header->$part);
566
        }
567
    }
568
569
    /**
570
     * Parse Addresses
571
     *
572
     * @param $list
573
     *
574
     * @return array
575
     */
576
    private function parseAddresses($list) {
577
        $addresses = [];
578
579
        foreach ($list as $item) {
580
            $address = (object) $item;
581
582
            if (!property_exists($address, 'mailbox')) {
583
                $address->mailbox = false;
584
            }
585
            if (!property_exists($address, 'host')) {
586
                $address->host = false;
587
            }
588
            if (!property_exists($address, 'personal')) {
589
                $address->personal = false;
590
            }
591
592
            $personalParts = imap_mime_header_decode($address->personal);
593
594
            $address->personal = '';
595
            foreach ($personalParts as $p) {
596
                $address->personal .= $p->text;
597
            }
598
599
            $address->mail = ($address->mailbox && $address->host) ? $address->mailbox.'@'.$address->host : false;
600
            $address->full = ($address->personal) ? $address->personal.' <'.$address->mail.'>' : $address->mail;
0 ignored issues
show
Bug introduced by
Are you sure $address->mail of type false|string can be used in concatenation? ( Ignorable by Annotation )

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

600
            $address->full = ($address->personal) ? $address->personal.' <'./** @scrutinizer ignore-type */ $address->mail.'>' : $address->mail;
Loading history...
601
602
            $addresses[] = $address;
603
        }
604
605
        return $addresses;
606
    }
607
608
    /**
609
     * Parse the Message body
610
     *
611
     * @return $this
612
     * @throws Exceptions\ConnectionFailedException
613
     */
614
    public function parseBody() {
615
        $this->structure = imap_fetchstructure($this->client->getConnection(), $this->uid, IMAP::FT_UID);
0 ignored issues
show
Documentation Bug introduced by
It seems like imap_fetchstructure($thi...klex\IMAP\IMAP::FT_UID) of type object is incompatible with the declared type null of property $structure.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_fetchstructure() does only seem to accept resource, 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

615
        $this->structure = imap_fetchstructure(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, IMAP::FT_UID);
Loading history...
616
617
        if(property_exists($this->structure, 'parts')){
618
            $parts = $this->structure->parts;
619
620
            foreach ($parts as $part)  {
621
                foreach ($part->parameters as $parameter)  {
622
                    if($parameter->attribute == "charset")  {
623
                        $encoding = $parameter->value;
624
625
                        $encoding = preg_replace('/Content-Transfer-Encoding/', '', $encoding);
626
                        $encoding = preg_replace('/iso-8859-8-i/', 'iso-8859-8', $encoding);
627
628
                        $parameter->value = $encoding;
629
                    }
630
                }
631
            }
632
        }
633
634
        $this->fetchStructure($this->structure);
635
636
        return $this;
637
    }
638
639
    /**
640
     * Fetch the Message structure
641
     *
642
     * @param $structure
643
     * @param mixed $partNumber
644
     *
645
     * @throws Exceptions\ConnectionFailedException
646
     */
647
    private function fetchStructure($structure, $partNumber = null) {
648
        if ($structure->type == IMAP::MESSAGE_TYPE_TEXT &&
649
            ($structure->ifdisposition == 0 ||
650
                ($structure->ifdisposition == 1 && !isset($structure->parts) && $partNumber == null)
651
            )
652
        ) {
653
            if ($structure->subtype == "PLAIN") {
654
                if (!$partNumber) {
655
                    $partNumber = 1;
656
                }
657
658
                $encoding = $this->getEncoding($structure);
659
660
                $content = imap_fetchbody($this->client->getConnection(), $this->uid, $partNumber, $this->fetch_options | IMAP::FT_UID);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_fetchbody() does only seem to accept resource, 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

660
                $content = imap_fetchbody(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, $partNumber, $this->fetch_options | IMAP::FT_UID);
Loading history...
661
                $content = $this->decodeString($content, $structure->encoding);
662
663
                // We don't need to do convertEncoding() if charset is ASCII (us-ascii):
664
                //     ASCII is a subset of UTF-8, so all ASCII files are already UTF-8 encoded
665
                //     https://stackoverflow.com/a/11303410
666
                //
667
                // us-ascii is the same as ASCII:
668
                //     ASCII is the traditional name for the encoding system; the Internet Assigned Numbers Authority (IANA)
669
                //     prefers the updated name US-ASCII, which clarifies that this system was developed in the US and
670
                //     based on the typographical symbols predominantly in use there.
671
                //     https://en.wikipedia.org/wiki/ASCII
672
                //
673
                // convertEncoding() function basically means convertToUtf8(), so when we convert ASCII string into UTF-8 it gets broken.
674
                if ($encoding != 'us-ascii') {
675
                    $content = $this->convertEncoding($content, $encoding);
676
                }
677
678
                $body = new \stdClass;
679
                $body->type = "text";
680
                $body->content = $content;
681
682
                $this->bodies['text'] = $body;
683
684
                $this->fetchAttachment($structure, $partNumber);
685
686
            } elseif ($structure->subtype == "HTML") {
687
                if (!$partNumber) {
688
                    $partNumber = 1;
689
                }
690
691
                $encoding = $this->getEncoding($structure);
692
693
                $content = imap_fetchbody($this->client->getConnection(), $this->uid, $partNumber, $this->fetch_options | IMAP::FT_UID);
694
                $content = $this->decodeString($content, $structure->encoding);
695
                if ($encoding != 'us-ascii') {
696
                    $content = $this->convertEncoding($content, $encoding);
697
                }
698
699
                $body = new \stdClass;
700
                $body->type = "html";
701
                $body->content = $content;
702
703
                $this->bodies['html'] = $body;
704
            }
705
        } elseif ($structure->type == IMAP::MESSAGE_TYPE_MULTIPART) {
706
            foreach ($structure->parts as $index => $subStruct) {
707
                $prefix = "";
708
                if ($partNumber) {
709
                    $prefix = $partNumber.".";
710
                }
711
                $this->fetchStructure($subStruct, $prefix.($index + 1));
712
            }
713
        } else {
714
            if ($this->getFetchAttachmentOption() === true) {
715
                $this->fetchAttachment($structure, $partNumber);
716
            }
717
        }
718
    }
719
720
    /**
721
     * Fetch the Message attachment
722
     *
723
     * @param object $structure
724
     * @param mixed  $partNumber
725
     *
726
     * @throws Exceptions\ConnectionFailedException
727
     */
728
    protected function fetchAttachment($structure, $partNumber) {
729
730
        $oAttachment = new Attachment($this, $structure, $partNumber);
731
732
        if ($oAttachment->getName() !== null) {
0 ignored issues
show
introduced by
The condition $oAttachment->getName() !== null is always true.
Loading history...
733
            if ($oAttachment->getId() !== null) {
0 ignored issues
show
introduced by
The condition $oAttachment->getId() !== null is always true.
Loading history...
734
                $this->attachments->put($oAttachment->getId(), $oAttachment);
735
            } else {
736
                $this->attachments->push($oAttachment);
737
            }
738
        }
739
    }
740
741
    /**
742
     * Fail proof setter for $fetch_option
743
     *
744
     * @param $option
745
     *
746
     * @return $this
747
     */
748
    public function setFetchOption($option) {
749
        if (is_long($option) === true) {
750
            $this->fetch_options = $option;
751
        } elseif (is_null($option) === true) {
752
            $config = config('imap.options.fetch', IMAP::FT_UID);
753
            $this->fetch_options = is_long($config) ? $config : 1;
754
        }
755
756
        return $this;
757
    }
758
759
    /**
760
     * Fail proof setter for $fetch_body
761
     *
762
     * @param $option
763
     *
764
     * @return $this
765
     */
766
    public function setFetchBodyOption($option) {
767
        if (is_bool($option)) {
768
            $this->fetch_body = $option;
769
        } elseif (is_null($option)) {
770
            $config = config('imap.options.fetch_body', true);
771
            $this->fetch_body = is_bool($config) ? $config : true;
772
        }
773
774
        return $this;
775
    }
776
777
    /**
778
     * Fail proof setter for $fetch_attachment
779
     *
780
     * @param $option
781
     *
782
     * @return $this
783
     */
784
    public function setFetchAttachmentOption($option) {
785
        if (is_bool($option)) {
786
            $this->fetch_attachment = $option;
787
        } elseif (is_null($option)) {
788
            $config = config('imap.options.fetch_attachment', true);
789
            $this->fetch_attachment = is_bool($config) ? $config : true;
790
        }
791
792
        return $this;
793
    }
794
795
    /**
796
     * Fail proof setter for $fetch_flags
797
     *
798
     * @param $option
799
     *
800
     * @return $this
801
     */
802
    public function setFetchFlagsOption($option) {
803
        if (is_bool($option)) {
804
            $this->fetch_flags = $option;
805
        } elseif (is_null($option)) {
806
            $config = config('imap.options.fetch_flags', true);
807
            $this->fetch_flags = is_bool($config) ? $config : true;
808
        }
809
810
        return $this;
811
    }
812
813
    /**
814
     * Decode a given string
815
     *
816
     * @param $string
817
     * @param $encoding
818
     *
819
     * @return string
820
     */
821
    public function decodeString($string, $encoding) {
822
        switch ($encoding) {
823
            case IMAP::MESSAGE_ENC_7BIT:
824
                return $string;
825
            case IMAP::MESSAGE_ENC_8BIT:
826
                return quoted_printable_decode(imap_8bit($string));
827
            case IMAP::MESSAGE_ENC_BINARY:
828
                return imap_binary($string);
829
            case IMAP::MESSAGE_ENC_BASE64:
830
                return imap_base64($string);
831
            case IMAP::MESSAGE_ENC_QUOTED_PRINTABLE:
832
                return quoted_printable_decode($string);
833
            case IMAP::MESSAGE_ENC_OTHER:
834
                return $string;
835
            default:
836
                return $string;
837
        }
838
    }
839
840
    /**
841
     * Convert the encoding
842
     *
843
     * @param $str
844
     * @param string $from
845
     * @param string $to
846
     *
847
     * @return mixed|string
848
     */
849
    public function convertEncoding($str, $from = "ISO-8859-2", $to = "UTF-8") {
850
851
        $from = EncodingAliases::get($from);
852
        $to = EncodingAliases::get($to);
853
854
        // We don't need to do convertEncoding() if charset is ASCII (us-ascii):
855
        //     ASCII is a subset of UTF-8, so all ASCII files are already UTF-8 encoded
856
        //     https://stackoverflow.com/a/11303410
857
        //
858
        // us-ascii is the same as ASCII:
859
        //     ASCII is the traditional name for the encoding system; the Internet Assigned Numbers Authority (IANA)
860
        //     prefers the updated name US-ASCII, which clarifies that this system was developed in the US and
861
        //     based on the typographical symbols predominantly in use there.
862
        //     https://en.wikipedia.org/wiki/ASCII
863
        //
864
        // convertEncoding() function basically means convertToUtf8(), so when we convert ASCII string into UTF-8 it gets broken.
865
        if (strtolower($from) == 'us-ascii' && $to == 'UTF-8') {
866
            return $str;
867
        }
868
869
        if (function_exists('iconv') && $from != 'UTF-7' && $to != 'UTF-7') {
870
            return @iconv($from, $to.'//IGNORE', $str);
871
        } else {
872
            if (!$from) {
873
                return mb_convert_encoding($str, $to);
874
            }
875
            return mb_convert_encoding($str, $to, $from);
876
        }
877
    }
878
879
    /**
880
     * Get the encoding of a given abject
881
     *
882
     * @param object|string $structure
883
     *
884
     * @return string
885
     */
886
    public function getEncoding($structure) {
887
        if (property_exists($structure, 'parameters')) {
888
            foreach ($structure->parameters as $parameter) {
889
                if (strtolower($parameter->attribute) == "charset") {
890
                    return EncodingAliases::get($parameter->value);
891
                }
892
            }
893
        }elseif (is_string($structure) === true){
894
            return mb_detect_encoding($structure);
895
        }
896
897
        return 'UTF-8';
898
    }
899
900
    /**
901
     * Find the folder containing this message.
902
     *
903
     * @param null|Folder $folder where to start searching from (top-level inbox by default)
904
     *
905
     * @return null|Folder
906
     * @throws Exceptions\ConnectionFailedException
907
     * @throws InvalidMessageDateException
908
     * @throws MaskNotFoundException
909
     */
910
    public function getContainingFolder(Folder $folder = null) {
911
        $folder = $folder ?: $this->client->getFolders()->first();
912
        $this->client->checkConnection();
913
914
        // Try finding the message by uid in the current folder
915
        $client = new Client;
916
        $client->openFolder($folder);
917
        $uidMatches = imap_fetch_overview($client->getConnection(), $this->uid, IMAP::FT_UID);
0 ignored issues
show
Bug introduced by
It seems like $client->getConnection() can also be of type true; however, parameter $imap_stream of imap_fetch_overview() does only seem to accept resource, 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

917
        $uidMatches = imap_fetch_overview(/** @scrutinizer ignore-type */ $client->getConnection(), $this->uid, IMAP::FT_UID);
Loading history...
918
        $uidMatch = count($uidMatches)
919
            ? new Message($uidMatches[0]->uid, $uidMatches[0]->msgno, $client)
920
            : null;
921
        $client->disconnect();
922
923
        // imap_fetch_overview() on a parent folder will return the matching message
924
        // even when the message is in a child folder so we need to recursively
925
        // search the children
926
        foreach ($folder->children as $child) {
927
            $childFolder = $this->getContainingFolder($child);
928
929
            if ($childFolder) {
930
                return $childFolder;
931
            }
932
        }
933
934
        // before returning the parent
935
        if ($this->is($uidMatch)) {
936
            return $folder;
937
        }
938
939
        // or signalling that the message was not found in any folder
940
        return null;
941
    }
942
943
    /**
944
     * Move the Message into an other Folder
945
     * @param string $mailbox
946
     *
947
     * @return bool
948
     * @throws Exceptions\ConnectionFailedException
949
     */
950
    public function moveToFolder($mailbox = 'INBOX') {
951
        $this->client->createFolder($mailbox);
952
953
        return imap_mail_move($this->client->getConnection(), $this->uid, $mailbox, IMAP::CP_UID);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_mail_move() does only seem to accept resource, 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

953
        return imap_mail_move(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, $mailbox, IMAP::CP_UID);
Loading history...
954
    }
955
956
    /**
957
     * Delete the current Message
958
     * @param bool $expunge
959
     *
960
     * @return bool
961
     * @throws Exceptions\ConnectionFailedException
962
     */
963
    public function delete($expunge = true) {
964
        $status = imap_delete($this->client->getConnection(), $this->uid, IMAP::FT_UID);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_delete() does only seem to accept resource, 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

964
        $status = imap_delete(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, IMAP::FT_UID);
Loading history...
965
        if($expunge) $this->client->expunge();
966
967
        return $status;
968
    }
969
970
    /**
971
     * Restore a deleted Message
972
     * @param boolean $expunge
973
     *
974
     * @return bool
975
     * @throws Exceptions\ConnectionFailedException
976
     */
977
    public function restore($expunge = true) {
978
        $status = imap_undelete($this->client->getConnection(), $this->uid, IMAP::FT_UID);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_undelete() does only seem to accept resource, 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

978
        $status = imap_undelete(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, IMAP::FT_UID);
Loading history...
979
        if($expunge) $this->client->expunge();
980
981
        return $status;
982
    }
983
984
    /**
985
     * Get all message attachments.
986
     *
987
     * @return AttachmentCollection
988
     */
989
    public function getAttachments() {
990
        return $this->attachments;
991
    }
992
993
    /**
994
     * Checks if there are any attachments present
995
     *
996
     * @return boolean
997
     */
998
    public function hasAttachments() {
999
        return $this->attachments->isEmpty() === false;
1000
    }
1001
1002
    /**
1003
     * Set a given flag
1004
     * @param string|array $flag
1005
     *
1006
     * @return bool
1007
     * @throws Exceptions\ConnectionFailedException
1008
     */
1009
    public function setFlag($flag) {
1010
        $flag = "\\".trim(is_array($flag) ? implode(" \\", $flag) : $flag);
1011
        $status = imap_setflag_full($this->client->getConnection(), $this->getUid(), $flag, SE_UID);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_setflag_full() does only seem to accept resource, 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

1011
        $status = imap_setflag_full(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->getUid(), $flag, SE_UID);
Loading history...
1012
        $this->parseFlags();
1013
1014
        return $status;
1015
    }
1016
1017
    /**
1018
     * Unset a given flag
1019
     * @param string|array $flag
1020
     *
1021
     * @return bool
1022
     * @throws Exceptions\ConnectionFailedException
1023
     */
1024
    public function unsetFlag($flag) {
1025
        $flag = "\\".trim(is_array($flag) ? implode(" \\", $flag) : $flag);
1026
        $status = imap_clearflag_full($this->client->getConnection(), $this->getUid(), $flag, SE_UID);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_clearflag_full() does only seem to accept resource, 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

1026
        $status = imap_clearflag_full(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->getUid(), $flag, SE_UID);
Loading history...
1027
        $this->parseFlags();
1028
1029
        return $status;
1030
    }
1031
1032
    /**
1033
     * @return null|object|string
1034
     * @throws Exceptions\ConnectionFailedException
1035
     */
1036
    public function getRawBody() {
1037
        if ($this->raw_body === null) {
1038
            $this->raw_body = imap_fetchbody($this->client->getConnection(), $this->getUid(), '', $this->fetch_options | IMAP::FT_UID);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_fetchbody() does only seem to accept resource, 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

1038
            $this->raw_body = imap_fetchbody(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->getUid(), '', $this->fetch_options | IMAP::FT_UID);
Loading history...
1039
        }
1040
1041
        return $this->raw_body;
1042
    }
1043
1044
    /**
1045
     * @return string
1046
     */
1047
    public function getHeader() {
1048
        return $this->header;
1049
    }
1050
1051
    /**
1052
     * @return Client
1053
     */
1054
    public function getClient() {
1055
        return $this->client;
1056
    }
1057
1058
    /**
1059
     * @return integer
1060
     */
1061
    public function getFetchOptions() {
1062
        return $this->fetch_options;
1063
    }
1064
1065
    /**
1066
     * @return boolean
1067
     */
1068
    public function getFetchBodyOption() {
1069
        return $this->fetch_body;
1070
    }
1071
1072
    /**
1073
     * @return boolean
1074
     */
1075
    public function getFetchAttachmentOption() {
1076
        return $this->fetch_attachment;
1077
    }
1078
    
1079
    /**
1080
     * @return boolean
1081
     */
1082
    public function getFetchFlagsOption() {
1083
        return $this->fetch_flags;
1084
    }
1085
1086
    /**
1087
     * @return mixed
1088
     */
1089
    public function getBodies() {
1090
        return $this->bodies;
1091
    }
1092
    
1093
    /**
1094
     * @return FlagCollection
1095
     */
1096
    public function getFlags() {
1097
        return $this->flags;
1098
    }
1099
1100
    /**
1101
     * @return object|null
1102
     */
1103
    public function getStructure(){
1104
        return $this->structure;
1105
    }
1106
1107
    /**
1108
     * Does this message match another one?
1109
     *
1110
     * A match means same uid, message id, subject and date/time.
1111
     *
1112
     * @param  null|Message $message
1113
     * @return boolean
1114
     */
1115
    public function is(Message $message = null) {
1116
        if (is_null($message)) {
1117
            return false;
1118
        }
1119
1120
        return $this->uid == $message->uid
1121
            && $this->message_id == $message->message_id
1122
            && $this->subject == $message->subject
1123
            && $this->date->eq($message->date);
1124
    }
1125
1126
    /**
1127
     * @return array
1128
     */
1129
    public function getAttributes(){
1130
        return $this->attributes;
1131
    }
1132
1133
    /**
1134
     * @param $mask
1135
     * @return $this
1136
     */
1137
    public function setMask($mask){
1138
        if(class_exists($mask)){
1139
            $this->mask = $mask;
1140
        }
1141
1142
        return $this;
1143
    }
1144
1145
    /**
1146
     * @return string
1147
     */
1148
    public function getMask(){
1149
        return $this->mask;
1150
    }
1151
1152
    /**
1153
     * Get a masked instance by providing a mask name
1154
     * @param string|null $mask
1155
     *
1156
     * @return mixed
1157
     * @throws MaskNotFoundException
1158
     */
1159
    public function mask($mask = null){
1160
        $mask = $mask !== null ? $mask : $this->mask;
1161
        if(class_exists($mask)){
1162
            return new $mask($this);
1163
        }
1164
1165
        throw new MaskNotFoundException("Unknown mask provided: ".$mask);
1166
    }
1167
}
1168