Passed
Push — master ( 543752...de50ce )
by Malte
03:39
created

Folder   A

Complexity

Total Complexity 37

Size/Duplication

Total Lines 410
Duplicated Lines 0 %

Importance

Changes 9
Bugs 2 Features 2
Metric Value
wmc 37
eloc 92
c 9
b 2
f 2
dl 0
loc 410
rs 9.44

21 Methods

Rating   Name   Duplication   Size   Complexity  
A getSimpleName() 0 4 1
A __construct() 0 12 1
A messages() 0 2 1
A delete() 0 8 2
A query() 0 5 1
A rename() 0 2 1
A overview() 0 5 2
A parseAttributes() 0 6 6
A subscribe() 0 3 1
A search() 0 2 1
A unsubscribe() 0 3 1
A appendMessage() 0 14 3
A decodeName() 0 2 1
A hasChildren() 0 2 1
A setChildren() 0 4 1
A move() 0 10 2
A setDelimiter() 0 6 2
B idle() 0 33 6
A getStatus() 0 2 1
A getClient() 0 2 1
A examine() 0 2 1
1
<?php
2
/*
3
* File:     Folder.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\PHPIMAP;
14
15
use Carbon\Carbon;
16
use Webklex\PHPIMAP\Exceptions\ConnectionFailedException;
17
use Webklex\PHPIMAP\Exceptions\GetMessagesFailedException;
18
use Webklex\PHPIMAP\Exceptions\MessageSearchValidationException;
19
use Webklex\PHPIMAP\Query\WhereQuery;
20
use Webklex\PHPIMAP\Support\FolderCollection;
21
use Webklex\PHPIMAP\Support\MessageCollection;
22
use Webklex\PHPIMAP\Traits\HasEvents;
23
24
/**
25
 * Class Folder
26
 *
27
 * @package Webklex\PHPIMAP
28
 */
29
class Folder {
30
    use HasEvents;
31
32
    /**
33
     * Client instance
34
     *
35
     * @var \Webklex\PHPIMAP\Client
36
     */
37
    protected $client;
38
39
    /**
40
     * Folder full path
41
     *
42
     * @var string
43
     */
44
    public $path;
45
46
    /**
47
     * Folder name
48
     *
49
     * @var string
50
     */
51
    public $name;
52
53
    /**
54
     * Folder fullname
55
     *
56
     * @var string
57
     */
58
    public $full_name;
59
60
    /**
61
     * Children folders
62
     *
63
     * @var FolderCollection|array
64
     */
65
    public $children = [];
66
67
    /**
68
     * Delimiter for folder
69
     *
70
     * @var string
71
     */
72
    public $delimiter;
73
74
    /**
75
     * Indicates if folder can't containg any "children".
76
     * CreateFolder won't work on this folder.
77
     *
78
     * @var boolean
79
     */
80
    public $no_inferiors;
81
82
    /**
83
     * Indicates if folder is only container, not a mailbox - you can't open it.
84
     *
85
     * @var boolean
86
     */
87
    public $no_select;
88
89
    /**
90
     * Indicates if folder is marked. This means that it may contain new messages since the last time it was checked.
91
     * Not provided by all IMAP servers.
92
     *
93
     * @var boolean
94
     */
95
    public $marked;
96
97
    /**
98
     * Indicates if folder containg any "children".
99
     * Not provided by all IMAP servers.
100
     *
101
     * @var boolean
102
     */
103
    public $has_children;
104
105
    /**
106
     * Indicates if folder refers to other.
107
     * Not provided by all IMAP servers.
108
     *
109
     * @var boolean
110
     */
111
    public $referral;
112
113
    /**
114
     * Folder constructor.
115
     * @param Client $client
116
     * @param string $folder_name
117
     * @param string $delimiter
118
     * @param string[] $attributes
119
     */
120
    public function __construct(Client $client, $folder_name, $delimiter, $attributes) {
121
        $this->client = $client;
122
123
        $this->events["message"] = $client->getDefaultEvents("message");
124
        $this->events["folder"] = $client->getDefaultEvents("folder");
125
126
        $this->setDelimiter($delimiter);
127
        $this->path      = $folder_name;
128
        $this->full_name  = $this->decodeName($folder_name);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->decodeName($folder_name) can also be of type array. However, the property $full_name is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
129
        $this->name      = $this->getSimpleName($this->delimiter, $this->full_name);
130
131
        $this->parseAttributes($attributes);
132
    }
133
134
    /**
135
     * Get a new search query instance
136
     * @param string $charset
137
     *
138
     * @return WhereQuery
139
     * @throws Exceptions\ConnectionFailedException
140
     * @throws Exceptions\RuntimeException
141
     */
142
    public function query($charset = 'UTF-8'){
143
        $this->getClient()->checkConnection();
144
        $this->getClient()->openFolder($this->path);
145
146
        return new WhereQuery($this->getClient(), $charset);
147
    }
148
149
    /**
150
     * @inheritdoc self::query($charset = 'UTF-8')
151
     * @throws Exceptions\ConnectionFailedException
152
     * @throws Exceptions\RuntimeException
153
     */
154
    public function search($charset = 'UTF-8'){
155
        return $this->query($charset);
156
    }
157
158
    /**
159
     * @inheritdoc self::query($charset = 'UTF-8')
160
     * @throws Exceptions\ConnectionFailedException
161
     * @throws Exceptions\RuntimeException
162
     */
163
    public function messages($charset = 'UTF-8'){
164
        return $this->query($charset);
165
    }
166
167
    /**
168
     * Determine if folder has children.
169
     *
170
     * @return bool
171
     */
172
    public function hasChildren() {
173
        return $this->has_children;
174
    }
175
176
    /**
177
     * Set children.
178
     * @param FolderCollection|array $children
179
     *
180
     * @return self
181
     */
182
    public function setChildren($children = []) {
183
        $this->children = $children;
184
185
        return $this;
186
    }
187
188
    /**
189
     * Decode name.
190
     * It converts UTF7-IMAP encoding to UTF-8.
191
     * @param $name
192
     *
193
     * @return mixed|string
194
     */
195
    protected function decodeName($name) {
196
        return mb_convert_encoding($name, "UTF-8", "UTF7-IMAP");
197
    }
198
199
    /**
200
     * Get simple name (without parent folders).
201
     * @param $delimiter
202
     * @param $full_name
203
     *
204
     * @return mixed
205
     */
206
    protected function getSimpleName($delimiter, $full_name) {
207
        $arr = explode($delimiter, $full_name);
208
209
        return end($arr);
210
    }
211
212
    /**
213
     * Parse attributes and set it to object properties.
214
     * @param $attributes
215
     */
216
    protected function parseAttributes($attributes) {
217
        $this->no_inferiors = in_array('\NoInferiors', $attributes) ? true : false;
218
        $this->no_select    = in_array('\NoSelect', $attributes) ? true : false;
219
        $this->marked       = in_array('\Marked', $attributes) ? true : false;
220
        $this->referral     = in_array('\Referral', $attributes) ? true : false;
221
        $this->has_children = in_array('\HasChildren', $attributes) ? true : false;
222
    }
223
224
    /**
225
     * Move or rename the current folder
226
     * @param string $new_name
227
     * @param boolean $expunge
228
     *
229
     * @return bool
230
     * @throws ConnectionFailedException
231
     * @throws Exceptions\EventNotFoundException
232
     * @throws Exceptions\FolderFetchingException
233
     * @throws Exceptions\RuntimeException
234
     */
235
    public function move($new_name, $expunge = true) {
236
        $this->client->checkConnection();
237
        $status = $this->client->getConnection()->renameFolder($this->full_name, $new_name);
0 ignored issues
show
Bug introduced by
The method renameFolder() does not exist on Webklex\PHPIMAP\Connection\Protocols\Protocol. Since it exists in all sub-types, consider adding an abstract or default implementation to Webklex\PHPIMAP\Connection\Protocols\Protocol. ( Ignorable by Annotation )

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

237
        $status = $this->client->getConnection()->/** @scrutinizer ignore-call */ renameFolder($this->full_name, $new_name);
Loading history...
238
        if($expunge) $this->client->expunge();
239
240
        $folder = $this->client->getFolder($new_name);
241
        $event = $this->getEvent("folder", "moved");
242
        $event::dispatch($this, $folder);
243
244
        return $status;
245
    }
246
247
    /**
248
     * Get a message overview
249
     * @param string|null $sequence uid sequence
250
     *
251
     * @return array
252
     * @throws ConnectionFailedException
253
     * @throws Exceptions\RuntimeException
254
     */
255
    public function overview($sequence = null){
256
        $this->client->openFolder($this->path);
257
        $sequence = $sequence === null ? "1:*" : $sequence;
258
        $uid = ClientManager::get('options.sequence', IMAP::ST_MSGN) == IMAP::ST_UID;
259
        return $this->client->getConnection()->overview($sequence, $uid);
0 ignored issues
show
Bug introduced by
The method overview() does not exist on Webklex\PHPIMAP\Connection\Protocols\Protocol. Since it exists in all sub-types, consider adding an abstract or default implementation to Webklex\PHPIMAP\Connection\Protocols\Protocol. ( Ignorable by Annotation )

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

259
        return $this->client->getConnection()->/** @scrutinizer ignore-call */ overview($sequence, $uid);
Loading history...
260
    }
261
262
    /**
263
     * Append a string message to the current mailbox
264
     * @param string $message
265
     * @param string $options
266
     * @param string $internal_date
267
     *
268
     * @return bool
269
     * @throws Exceptions\ConnectionFailedException
270
     * @throws Exceptions\RuntimeException
271
     */
272
    public function appendMessage($message, $options = null, $internal_date = null) {
273
        /**
274
         * Check if $internal_date is parsed. If it is null it should not be set. Otherwise the message can't be stored.
275
         * If this parameter is set, it will set the INTERNALDATE on the appended message. The parameter should be a
276
         * date string that conforms to the rfc2060 specifications for a date_time value or be a Carbon object.
277
         */
278
279
        if ($internal_date != null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $internal_date of type null|string against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
280
            if ($internal_date instanceof Carbon){
0 ignored issues
show
introduced by
$internal_date is never a sub-type of Carbon\Carbon.
Loading history...
281
                $internal_date = $internal_date->format('d-M-Y H:i:s O');
282
            }
283
        }
284
285
        return $this->client->getConnection()->appendMessage($this->full_name, $message, $options, $internal_date);
0 ignored issues
show
Bug introduced by
The method appendMessage() does not exist on Webklex\PHPIMAP\Connection\Protocols\Protocol. Since it exists in all sub-types, consider adding an abstract or default implementation to Webklex\PHPIMAP\Connection\Protocols\Protocol. ( Ignorable by Annotation )

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

285
        return $this->client->getConnection()->/** @scrutinizer ignore-call */ appendMessage($this->full_name, $message, $options, $internal_date);
Loading history...
Bug introduced by
It seems like $options can also be of type string; however, parameter $flags of Webklex\PHPIMAP\Connecti...erface::appendMessage() does only seem to accept array, 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

285
        return $this->client->getConnection()->appendMessage($this->full_name, $message, /** @scrutinizer ignore-type */ $options, $internal_date);
Loading history...
286
    }
287
288
    /**
289
     * Rename the current folder
290
     * @param string $new_name
291
     * @param boolean $expunge
292
     *
293
     * @return bool
294
     * @throws ConnectionFailedException
295
     * @throws Exceptions\EventNotFoundException
296
     * @throws Exceptions\FolderFetchingException
297
     * @throws Exceptions\RuntimeException
298
     */
299
    public function rename($new_name, $expunge = true) {
300
        return $this->move($new_name, $expunge);
301
    }
302
303
    /**
304
     * Delete the current folder
305
     * @param boolean $expunge
306
     *
307
     * @return bool
308
     * @throws Exceptions\ConnectionFailedException
309
     * @throws Exceptions\RuntimeException
310
     * @throws Exceptions\EventNotFoundException
311
     */
312
    public function delete($expunge = true) {
313
        $status = $this->client->getConnection()->deleteFolder($this->path);
0 ignored issues
show
Bug introduced by
The method deleteFolder() does not exist on Webklex\PHPIMAP\Connection\Protocols\Protocol. Since it exists in all sub-types, consider adding an abstract or default implementation to Webklex\PHPIMAP\Connection\Protocols\Protocol. ( Ignorable by Annotation )

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

313
        $status = $this->client->getConnection()->/** @scrutinizer ignore-call */ deleteFolder($this->path);
Loading history...
314
        if($expunge) $this->client->expunge();
315
316
        $event = $this->getEvent("folder", "deleted");
317
        $event::dispatch($this);
318
319
        return $status;
320
    }
321
322
    /**
323
     * Subscribe the current folder
324
     *
325
     * @return bool
326
     * @throws Exceptions\ConnectionFailedException
327
     * @throws Exceptions\RuntimeException
328
     */
329
    public function subscribe() {
330
        $this->client->openFolder($this->path);
331
        return $this->client->getConnection()->subscribeFolder($this->path);
0 ignored issues
show
Bug introduced by
The method subscribeFolder() does not exist on Webklex\PHPIMAP\Connection\Protocols\Protocol. Since it exists in all sub-types, consider adding an abstract or default implementation to Webklex\PHPIMAP\Connection\Protocols\Protocol. ( Ignorable by Annotation )

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

331
        return $this->client->getConnection()->/** @scrutinizer ignore-call */ subscribeFolder($this->path);
Loading history...
332
    }
333
334
    /**
335
     * Unsubscribe the current folder
336
     *
337
     * @return bool
338
     * @throws Exceptions\ConnectionFailedException
339
     * @throws Exceptions\RuntimeException
340
     */
341
    public function unsubscribe() {
342
        $this->client->openFolder($this->path);
343
        return $this->client->getConnection()->unsubscribeFolder($this->path);
0 ignored issues
show
Bug introduced by
The method unsubscribeFolder() does not exist on Webklex\PHPIMAP\Connecti...ocols\ProtocolInterface. Did you maybe mean subscribeFolder()? ( Ignorable by Annotation )

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

343
        return $this->client->getConnection()->/** @scrutinizer ignore-call */ unsubscribeFolder($this->path);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method unsubscribeFolder() does not exist on Webklex\PHPIMAP\Connection\Protocols\Protocol. Since it exists in all sub-types, consider adding an abstract or default implementation to Webklex\PHPIMAP\Connection\Protocols\Protocol. ( Ignorable by Annotation )

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

343
        return $this->client->getConnection()->/** @scrutinizer ignore-call */ unsubscribeFolder($this->path);
Loading history...
344
    }
345
346
    /**
347
     * Idle the current connection
348
     * @param callable $callback
349
     * @param integer $timeout max 1740 seconds - recommended by rfc2177 §3
350
     * @param boolean $auto_reconnect try to reconnect on connection close
351
     *
352
     * @throws ConnectionFailedException
353
     * @throws Exceptions\InvalidMessageDateException
354
     * @throws Exceptions\MessageContentFetchingException
355
     * @throws Exceptions\MessageHeaderFetchingException
356
     * @throws Exceptions\RuntimeException
357
     * @throws Exceptions\EventNotFoundException
358
     * @throws Exceptions\MessageFlagException
359
     */
360
    public function idle(callable $callback, $timeout = 1200, $auto_reconnect = false) {
361
        $this->client->getConnection()->setConnectionTimeout($timeout);
0 ignored issues
show
Bug introduced by
The method setConnectionTimeout() does not exist on Webklex\PHPIMAP\Connecti...ocols\ProtocolInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Webklex\PHPIMAP\Connecti...ocols\ProtocolInterface. ( Ignorable by Annotation )

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

361
        $this->client->getConnection()->/** @scrutinizer ignore-call */ setConnectionTimeout($timeout);
Loading history...
362
363
        $this->client->reconnect();
364
        $this->client->openFolder($this->path, true);
365
        $connection = $this->client->getConnection();
366
367
        $sequence = ClientManager::get('options.sequence', IMAP::ST_MSGN);
368
        $connection->idle();
0 ignored issues
show
Bug introduced by
The method idle() does not exist on Webklex\PHPIMAP\Connection\Protocols\Protocol. Since it exists in all sub-types, consider adding an abstract or default implementation to Webklex\PHPIMAP\Connection\Protocols\Protocol. ( Ignorable by Annotation )

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

368
        $connection->/** @scrutinizer ignore-call */ 
369
                     idle();
Loading history...
Bug introduced by
The method idle() does not exist on Webklex\PHPIMAP\Connecti...ocols\ProtocolInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Webklex\PHPIMAP\Connecti...ocols\ProtocolInterface. ( Ignorable by Annotation )

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

368
        $connection->/** @scrutinizer ignore-call */ 
369
                     idle();
Loading history...
369
370
        while (true) {
371
            try {
372
                $line = $connection->nextLine();
0 ignored issues
show
Bug introduced by
The method nextLine() does not exist on Webklex\PHPIMAP\Connection\Protocols\Protocol. It seems like you code against a sub-type of Webklex\PHPIMAP\Connection\Protocols\Protocol such as Webklex\PHPIMAP\Connection\Protocols\ImapProtocol. ( Ignorable by Annotation )

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

372
                /** @scrutinizer ignore-call */ 
373
                $line = $connection->nextLine();
Loading history...
Bug introduced by
The method nextLine() does not exist on Webklex\PHPIMAP\Connecti...ocols\ProtocolInterface. It seems like you code against a sub-type of Webklex\PHPIMAP\Connecti...ocols\ProtocolInterface such as Webklex\PHPIMAP\Connection\Protocols\ImapProtocol. ( Ignorable by Annotation )

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

372
                /** @scrutinizer ignore-call */ 
373
                $line = $connection->nextLine();
Loading history...
373
                if (($pos = strpos($line, "EXISTS")) !== false) {
374
                    $msgn = (int) substr($line, 2, $pos -2);
375
                    $connection->done();
0 ignored issues
show
Bug introduced by
The method done() does not exist on Webklex\PHPIMAP\Connection\Protocols\Protocol. Since it exists in all sub-types, consider adding an abstract or default implementation to Webklex\PHPIMAP\Connection\Protocols\Protocol. ( Ignorable by Annotation )

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

375
                    $connection->/** @scrutinizer ignore-call */ 
376
                                 done();
Loading history...
Bug introduced by
The method done() does not exist on Webklex\PHPIMAP\Connecti...ocols\ProtocolInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Webklex\PHPIMAP\Connecti...ocols\ProtocolInterface. ( Ignorable by Annotation )

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

375
                    $connection->/** @scrutinizer ignore-call */ 
376
                                 done();
Loading history...
376
377
                    $this->client->openFolder($this->path, true);
378
                    $message = $this->query()->getMessageByMsgn($msgn);
379
                    $message->setSequence($sequence);
380
                    $callback($message);
381
382
                    $event = $this->getEvent("message", "new");
383
                    $event::dispatch($message);
384
385
                    $connection->idle();
386
                }
387
            }catch (Exceptions\RuntimeException $e) {
388
                if(strpos($e->getMessage(), "connection closed") === false) {
389
                    throw $e;
390
                }
391
                if ($auto_reconnect === true) {
392
                    $this->client->reconnect();
393
                }
394
            }
395
        }
396
    }
397
398
    /**
399
     * Get folder status information
400
     *
401
     * @return array|bool
402
     * @throws Exceptions\ConnectionFailedException
403
     * @throws Exceptions\RuntimeException
404
     */
405
    public function getStatus() {
406
        return $this->examine();
407
    }
408
409
    /**
410
     * Examine the current folder
411
     *
412
     * @return array
413
     * @throws Exceptions\ConnectionFailedException
414
     * @throws Exceptions\RuntimeException
415
     */
416
    public function examine() {
417
        return $this->client->getConnection()->examineFolder($this->path);
0 ignored issues
show
Bug introduced by
The method examineFolder() does not exist on Webklex\PHPIMAP\Connection\Protocols\Protocol. Since it exists in all sub-types, consider adding an abstract or default implementation to Webklex\PHPIMAP\Connection\Protocols\Protocol. ( Ignorable by Annotation )

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

417
        return $this->client->getConnection()->/** @scrutinizer ignore-call */ examineFolder($this->path);
Loading history...
Bug Best Practice introduced by
The expression return $this->client->ge...mineFolder($this->path) also could return the type boolean which is incompatible with the documented return type array.
Loading history...
418
    }
419
420
    /**
421
     * Get the current Client instance
422
     *
423
     * @return Client
424
     */
425
    public function getClient() {
426
        return $this->client;
427
    }
428
429
    /**
430
     * Set the delimiter
431
     * @param $delimiter
432
     */
433
    public function setDelimiter($delimiter){
434
        if(in_array($delimiter, [null, '', ' ', false]) === true) {
435
            $delimiter = ClientManager::get('options.delimiter', '/');
436
        }
437
438
        $this->delimiter = $delimiter;
439
    }
440
}
441