Passed
Push — master ( d418f0...22ef42 )
by Malte
02:51
created

Client::getUnseenMessages()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 6
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
<?php
2
/*
3
* File:     Client.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 Webklex\IMAP\Exceptions\ConnectionFailedException;
16
use Webklex\IMAP\Exceptions\GetMessagesFailedException;
17
use Webklex\IMAP\Exceptions\MessageSearchValidationException;
18
use Webklex\IMAP\Support\FolderCollection;
19
use Webklex\IMAP\Support\MessageCollection;
20
21
/**
22
 * Class Client
23
 *
24
 * @package Webklex\IMAP
25
 */
26
class Client {
27
28
    /**
29
     * @var boolean|resource
30
     */
31
    public $connection = false;
32
33
    /**
34
     * Server hostname.
35
     *
36
     * @var string
37
     */
38
    public $host;
39
40
    /**
41
     * Server port.
42
     *
43
     * @var int
44
     */
45
    public $port;
46
47
    /**
48
     * Service protocol.
49
     *
50
     * @var int
51
     */
52
    public $protocol;
53
54
    /**
55
     * Server encryption.
56
     * Supported: none, ssl or tls.
57
     *
58
     * @var string
59
     */
60
    public $encryption;
61
62
    /**
63
     * If server has to validate cert.
64
     *
65
     * @var mixed
66
     */
67
    public $validate_cert;
68
69
    /**
70
     * Account username/
71
     *
72
     * @var mixed
73
     */
74
    public $username;
75
76
    /**
77
     * Account password.
78
     *
79
     * @var string
80
     */
81
    public $password;
82
83
    /**
84
     * Read only parameter.
85
     *
86
     * @var bool
87
     */
88
    protected $read_only = false;
89
90
    /**
91
     * Active folder.
92
     *
93
     * @var Folder
94
     */
95
    protected $activeFolder = false;
96
97
    /**
98
     * Connected parameter
99
     *
100
     * @var bool
101
     */
102
    protected $connected = false;
103
104
    /**
105
     * IMAP errors that might have ben occurred
106
     *
107
     * @var array $errors
108
     */
109
    protected $errors = [];
110
111
    /**
112
     * All valid and available account config parameters
113
     *
114
     * @var array $validConfigKeys
115
     */
116
    protected $validConfigKeys = ['host', 'port', 'encryption', 'validate_cert', 'username', 'password','protocol'];
117
118
    /**
119
     * Client constructor.
120
     *
121
     * @param array $config
122
     */
123
    public function __construct($config = []) {
124
        $this->setConfig($config);
125
    }
126
127
    /**
128
     * Client destructor
129
     */
130
    public function __destruct() {
131
        $this->disconnect();
132
    }
133
134
    /**
135
     * Set the Client configuration
136
     *
137
     * @param array $config
138
     *
139
     * @return self
140
     */
141
    public function setConfig(array $config) {
142
        $defaultAccount = config('imap.default');
143
        $defaultConfig  = config("imap.accounts.$defaultAccount");
144
145
        foreach ($this->validConfigKeys as $key) {
146
            $this->$key = isset($config[$key]) ? $config[$key] : $defaultConfig[$key];
147
        }
148
149
        return $this;
150
    }
151
152
    /**
153
     * Get the current imap resource
154
     *
155
     * @return bool|resource
156
     * @throws ConnectionFailedException
157
     */
158
    public function getConnection() {
159
        $this->checkConnection();
160
        return $this->connection;
161
    }
162
163
    /**
164
     * Set read only property and reconnect if it's necessary.
165
     *
166
     * @param bool $readOnly
167
     *
168
     * @return self
169
     */
170
    public function setReadOnly($readOnly = true) {
171
        $this->read_only = $readOnly;
172
173
        return $this;
174
    }
175
176
    /**
177
     * Determine if connection was established.
178
     *
179
     * @return bool
180
     */
181
    public function isConnected() {
182
        return $this->connected;
183
    }
184
185
    /**
186
     * Determine if connection is in read only mode.
187
     *
188
     * @return bool
189
     */
190
    public function isReadOnly() {
191
        return $this->read_only;
192
    }
193
194
    /**
195
     * Determine if connection was established and connect if not.
196
     *
197
     * @throws ConnectionFailedException
198
     */
199
    public function checkConnection() {
200
        if (!$this->isConnected() || $this->connection === false) {
201
            $this->connect();
202
        }
203
    }
204
205
    /**
206
     * Connect to server.
207
     *
208
     * @param int $attempts
209
     *
210
     * @return $this
211
     * @throws ConnectionFailedException
212
     */
213
    public function connect($attempts = 3) {
214
        $this->disconnect();
215
216
        try {
217
            $this->connection = imap_open(
218
                $this->getAddress(),
219
                $this->username,
220
                $this->password,
221
                $this->getOptions(),
222
                $attempts,
223
                config('imap.options.open')
224
            );
225
            $this->connected = !!$this->connection;
226
        } catch (\ErrorException $e) {
227
            $errors = imap_errors();
228
            $message = $e->getMessage().'. '.implode("; ", (is_array($errors) ? $errors : array()));
0 ignored issues
show
introduced by
The condition is_array($errors) is always true.
Loading history...
229
230
            throw new ConnectionFailedException($message);
231
        }
232
233
        return $this;
234
    }
235
236
    /**
237
     * Disconnect from server.
238
     *
239
     * @return $this
240
     */
241
    public function disconnect() {
242
        if ($this->isConnected() && $this->connection !== false && is_integer($this->connection) === false) {
243
            $this->errors = array_merge($this->errors, imap_errors() ?: []);
244
            $this->connected = !imap_close($this->connection, CL_EXPUNGE);
0 ignored issues
show
Bug introduced by
$this->connection of type integer is incompatible with the type resource expected by parameter $imap_stream of imap_close(). ( Ignorable by Annotation )

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

244
            $this->connected = !imap_close(/** @scrutinizer ignore-type */ $this->connection, CL_EXPUNGE);
Loading history...
245
        }
246
247
        return $this;
248
    }
249
250
    /**
251
     * Get a folder instance by a folder name
252
     * ---------------------------------------------
253
     * PLEASE NOTE: This is an experimental function
254
     * ---------------------------------------------
255
     * @param string        $folder_name
256
     * @param int           $attributes
257
     * @param null|string   $delimiter
258
     *
259
     * @return Folder
260
     */
261
    public function getFolder($folder_name, $attributes = 32, $delimiter = null) {
262
263
        $delimiter = $delimiter === null ? config('imap.options.delimiter', '/') : $delimiter;
264
265
        $oFolder = new Folder($this, (object) [
266
            'name'       => $this->getAddress().$folder_name,
267
            'attributes' => $attributes,
268
            'delimiter'  => $delimiter
269
        ]);
270
271
        return $oFolder;
272
    }
273
274
    /**
275
     * Get folders list.
276
     * If hierarchical order is set to true, it will make a tree of folders, otherwise it will return flat array.
277
     *
278
     * @param boolean     $hierarchical
279
     * @param string|null $parent_folder
280
     *
281
     * @return FolderCollection
282
     * @throws ConnectionFailedException
283
     */
284
    public function getFolders($hierarchical = true, $parent_folder = null) {
285
        $this->checkConnection();
286
        $folders = FolderCollection::make([]);
287
288
        $pattern = $parent_folder.($hierarchical ? '%' : '*');
289
290
        $items = imap_getmailboxes($this->connection, $this->getAddress(), $pattern);
0 ignored issues
show
Bug introduced by
It seems like $this->connection can also be of type true; however, parameter $imap_stream of imap_getmailboxes() 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

290
        $items = imap_getmailboxes(/** @scrutinizer ignore-type */ $this->connection, $this->getAddress(), $pattern);
Loading history...
291
        foreach ($items as $item) {
292
            $folder = new Folder($this, $item);
293
294
            if ($hierarchical && $folder->hasChildren()) {
295
                $pattern = $folder->fullName.$folder->delimiter.'%';
296
297
                $children = $this->getFolders(true, $pattern);
298
                $folder->setChildren($children);
299
            }
300
301
            $folders->push($folder);
302
        }
303
304
        return $folders;
305
    }
306
307
    /**
308
     * Open folder.
309
     *
310
     * @param Folder $folder
311
     * @param int    $attempts
312
     *
313
     * @throws ConnectionFailedException
314
     */
315
    public function openFolder(Folder $folder, $attempts = 3) {
316
        $this->checkConnection();
317
318
        if ($this->activeFolder !== $folder) {
319
            $this->activeFolder = $folder;
320
321
            imap_reopen($this->getConnection(), $folder->path, $this->getOptions(), $attempts);
0 ignored issues
show
Bug introduced by
It seems like $this->getConnection() can also be of type true; however, parameter $imap_stream of imap_reopen() 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

321
            imap_reopen(/** @scrutinizer ignore-type */ $this->getConnection(), $folder->path, $this->getOptions(), $attempts);
Loading history...
322
        }
323
    }
324
325
    /**
326
     * Create a new Folder
327
     * @param string $name
328
     * @param boolean $expunge
329
     *
330
     * @return bool
331
     * @throws ConnectionFailedException
332
     */
333
    public function createFolder($name, $expunge = true) {
334
        $this->checkConnection();
335
        $status = imap_createmailbox($this->getConnection(), $this->getAddress() . imap_utf7_encode($name));
0 ignored issues
show
Bug introduced by
It seems like $this->getConnection() can also be of type true; however, parameter $imap_stream of imap_createmailbox() 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

335
        $status = imap_createmailbox(/** @scrutinizer ignore-type */ $this->getConnection(), $this->getAddress() . imap_utf7_encode($name));
Loading history...
336
        if($expunge) $this->expunge();
337
338
        return $status;
339
    }
340
    
341
    /**
342
     * Rename Folder
343
     * @param string  $old_name
344
     * @param string  $new_name
345
     * @param boolean $expunge
346
     *
347
     * @return bool
348
     * @throws ConnectionFailedException
349
     */
350
    public function renameFolder($old_name, $new_name, $expunge = true) {
351
        $this->checkConnection();
352
        $status = imap_renamemailbox($this->getConnection(), $this->getAddress() . imap_utf7_encode($old_name), $this->getAddress() . imap_utf7_encode($new_name));
0 ignored issues
show
Bug introduced by
It seems like $this->getConnection() can also be of type true; however, parameter $imap_stream of imap_renamemailbox() 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

352
        $status = imap_renamemailbox(/** @scrutinizer ignore-type */ $this->getConnection(), $this->getAddress() . imap_utf7_encode($old_name), $this->getAddress() . imap_utf7_encode($new_name));
Loading history...
353
        if($expunge) $this->expunge();
354
355
        return $status;
356
    }
357
    
358
     /**
359
     * Delete Folder
360
     * @param string $name
361
      * @param boolean $expunge
362
     *
363
     * @return bool
364
     * @throws ConnectionFailedException
365
     */
366
    public function deleteFolder($name, $expunge = true) {
367
        $this->checkConnection();
368
        $status = imap_deletemailbox($this->getConnection(), $this->getAddress() . imap_utf7_encode($name));
0 ignored issues
show
Bug introduced by
It seems like $this->getConnection() can also be of type true; however, parameter $imap_stream of imap_deletemailbox() 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

368
        $status = imap_deletemailbox(/** @scrutinizer ignore-type */ $this->getConnection(), $this->getAddress() . imap_utf7_encode($name));
Loading history...
369
        if($expunge) $this->expunge();
370
371
        return $status;
372
    }
373
374
    /**
375
     * Get messages from folder.
376
     *
377
     * @param Folder   $folder
378
     * @param string   $criteria
379
     * @param int|null $fetch_options
380
     * @param boolean  $fetch_body
381
     * @param boolean  $fetch_attachment
382
     *
383
     * @return MessageCollection
384
     * @throws ConnectionFailedException
385
     * @throws GetMessagesFailedException
386
     * @throws MessageSearchValidationException
387
     *
388
     * @deprecated 1.0.5.2:2.0.0 No longer needed. Use Folder::getMessages() instead
389
     * @see Folder::getMessages()
390
     */
391
    public function getMessages(Folder $folder, $criteria = 'ALL', $fetch_options = null, $fetch_body = true, $fetch_attachment = true, $fetch_flags = false) {
392
        return $folder->getMessages($criteria, $fetch_options, $fetch_body, $fetch_attachment, $fetch_flags);
393
    }
394
395
    /**
396
     * Get all unseen messages from folder
397
     *
398
     * @param Folder   $folder
399
     * @param string   $criteria
400
     * @param int|null $fetch_options
401
     * @param boolean  $fetch_body
402
     * @param boolean  $fetch_attachment
403
     *
404
     * @return MessageCollection
405
     * @throws ConnectionFailedException
406
     * @throws GetMessagesFailedException
407
     * @throws MessageSearchValidationException
408
     *
409
     * @deprecated 1.0.5:2.0.0 No longer needed. Use Folder::getMessages('UNSEEN') instead
410
     * @see Folder::getMessages()
411
     */
412
    public function getUnseenMessages(Folder $folder, $criteria = 'UNSEEN', $fetch_options = null, $fetch_body = true, $fetch_attachment = true, $fetch_flags = false) {
413
        return $folder->getUnseenMessages($criteria, $fetch_options, $fetch_body, $fetch_attachment, $fetch_flags);
0 ignored issues
show
Deprecated Code introduced by
The function Webklex\IMAP\Folder::getUnseenMessages() has been deprecated: 1.0.5:2.0.0 No longer needed. Use Folder::getMessages('UNSEEN') instead ( Ignorable by Annotation )

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

413
        return /** @scrutinizer ignore-deprecated */ $folder->getUnseenMessages($criteria, $fetch_options, $fetch_body, $fetch_attachment, $fetch_flags);

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...
414
    }
415
416
    /**
417
     * Search messages by a given search criteria
418
     *
419
     * @param array    $where
420
     * @param Folder   $folder
421
     * @param int|null $fetch_options
422
     * @param boolean  $fetch_body
423
     * @param string   $charset
424
     * @param boolean  $fetch_attachment
425
     *
426
     * @return MessageCollection
427
     * @throws ConnectionFailedException
428
     * @throws GetMessagesFailedException
429
     * @throws MessageSearchValidationException
430
     *
431
     * @deprecated 1.0.5:2.0.0 No longer needed. Use Folder::searchMessages() instead
432
     * @see Folder::searchMessages()
433
     *
434
     */
435
    public function searchMessages(array $where, Folder $folder, $fetch_options = null, $fetch_body = true, $charset = "UTF-8", $fetch_attachment = true, $fetch_flags = false) {
436
        return $folder->searchMessages($where, $fetch_options, $fetch_body, $charset, $fetch_attachment, $fetch_flags);
0 ignored issues
show
Bug introduced by
$charset of type string is incompatible with the type boolean expected by parameter $fetch_attachment of Webklex\IMAP\Folder::searchMessages(). ( Ignorable by Annotation )

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

436
        return $folder->searchMessages($where, $fetch_options, $fetch_body, /** @scrutinizer ignore-type */ $charset, $fetch_attachment, $fetch_flags);
Loading history...
Deprecated Code introduced by
The function Webklex\IMAP\Folder::searchMessages() has been deprecated: 1.2.1:2.0.0 No longer needed. Use Folder::query() instead ( Ignorable by Annotation )

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

436
        return /** @scrutinizer ignore-deprecated */ $folder->searchMessages($where, $fetch_options, $fetch_body, $charset, $fetch_attachment, $fetch_flags);

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...
437
    }
438
439
    /**
440
     * Get option for imap_open and imap_reopen.
441
     * It supports only isReadOnly feature.
442
     *
443
     * @return int
444
     */
445
    protected function getOptions() {
446
        return ($this->isReadOnly()) ? OP_READONLY : 0;
447
    }
448
449
    /**
450
     * Get full address of mailbox.
451
     *
452
     * @return string
453
     */
454
    protected function getAddress() {
455
        $address = "{".$this->host.":".$this->port."/".($this->protocol ? $this->protocol : 'imap');
456
        if (!$this->validate_cert) {
457
            $address .= '/novalidate-cert';
458
        }
459
        if (in_array($this->encryption,['tls','ssl'])) {
460
            $address .= '/'.$this->encryption;
461
        }
462
        $address .= '}';
463
464
        return $address;
465
    }
466
467
    /**
468
     * Retrieve the quota level settings, and usage statics per mailbox
469
     *
470
     * @return array
471
     * @throws ConnectionFailedException
472
     */
473
    public function getQuota() {
474
        $this->checkConnection();
475
        return imap_get_quota($this->getConnection(), 'user.'.$this->username);
0 ignored issues
show
Bug introduced by
It seems like $this->getConnection() can also be of type true; however, parameter $imap_stream of imap_get_quota() 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

475
        return imap_get_quota(/** @scrutinizer ignore-type */ $this->getConnection(), 'user.'.$this->username);
Loading history...
476
    }
477
478
    /**
479
     * Retrieve the quota settings per user
480
     *
481
     * @param string $quota_root
482
     *
483
     * @return array
484
     * @throws ConnectionFailedException
485
     */
486
    public function getQuotaRoot($quota_root = 'INBOX') {
487
        $this->checkConnection();
488
        return imap_get_quotaroot($this->getConnection(), $quota_root);
0 ignored issues
show
Bug introduced by
It seems like $this->getConnection() can also be of type true; however, parameter $imap_stream of imap_get_quotaroot() 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

488
        return imap_get_quotaroot(/** @scrutinizer ignore-type */ $this->getConnection(), $quota_root);
Loading history...
489
    }
490
491
    /**
492
     * Gets the number of messages in the current mailbox
493
     *
494
     * @return int
495
     * @throws ConnectionFailedException
496
     */
497
    public function countMessages() {
498
        $this->checkConnection();
499
        return imap_num_msg($this->connection);
0 ignored issues
show
Bug introduced by
It seems like $this->connection can also be of type true; however, parameter $imap_stream of imap_num_msg() 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

499
        return imap_num_msg(/** @scrutinizer ignore-type */ $this->connection);
Loading history...
500
    }
501
502
    /**
503
     * Gets the number of recent messages in current mailbox
504
     *
505
     * @return int
506
     * @throws ConnectionFailedException
507
     */
508
    public function countRecentMessages() {
509
        $this->checkConnection();
510
        return imap_num_recent($this->connection);
0 ignored issues
show
Bug introduced by
It seems like $this->connection can also be of type true; however, parameter $imap_stream of imap_num_recent() 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

510
        return imap_num_recent(/** @scrutinizer ignore-type */ $this->connection);
Loading history...
511
    }
512
513
    /**
514
     * Returns all IMAP alert messages that have occurred
515
     *
516
     * @return array
517
     */
518
    public function getAlerts() {
519
        return imap_alerts();
520
    }
521
522
    /**
523
     * Returns all of the IMAP errors that have occurred
524
     *
525
     * @return array
526
     */
527
    public function getErrors() {
528
        $this->errors = array_merge($this->errors, imap_errors() ?: []);
529
530
        return $this->errors;
531
    }
532
533
    /**
534
     * Gets the last IMAP error that occurred during this page request
535
     *
536
     * @return string
537
     */
538
    public function getLastError() {
539
        return imap_last_error();
540
    }
541
542
    /**
543
     * Delete all messages marked for deletion
544
     *
545
     * @return bool
546
     * @throws ConnectionFailedException
547
     */
548
    public function expunge() {
549
        $this->checkConnection();
550
        return imap_expunge($this->connection);
0 ignored issues
show
Bug introduced by
It seems like $this->connection can also be of type true; however, parameter $imap_stream of imap_expunge() 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

550
        return imap_expunge(/** @scrutinizer ignore-type */ $this->connection);
Loading history...
551
    }
552
553
    /**
554
     * Check current mailbox
555
     *
556
     * @return object {
557
     *      Date    [string(37) "Wed, 8 Mar 2017 22:17:54 +0100 (CET)"]             current system time formatted according to » RFC2822
558
     *      Driver  [string(4) "imap"]                                              protocol used to access this mailbox: POP3, IMAP, NNTP
559
     *      Mailbox ["{[email protected]:993/imap/user="[email protected]"}INBOX"]    the mailbox name
560
     *      Nmsgs   [int(1)]                                                        number of messages in the mailbox
561
     *      Recent  [int(0)]                                                        number of recent messages in the mailbox
562
     * }
563
     * @throws ConnectionFailedException
564
     */
565
    public function checkCurrentMailbox() {
566
        $this->checkConnection();
567
        return imap_check($this->connection);
0 ignored issues
show
Bug introduced by
It seems like $this->connection can also be of type true; however, parameter $imap_stream of imap_check() 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

567
        return imap_check(/** @scrutinizer ignore-type */ $this->connection);
Loading history...
568
    }
569
}
570