Completed
Pull Request — master (#12)
by
unknown
02:19
created

Client::getFolder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 2
rs 10
cc 1
nc 1
nop 1
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\PHPIMAP;
14
15
use Webklex\PHPIMAP\Connection\Protocols\ImapProtocol;
16
use Webklex\PHPIMAP\Connection\Protocols\LegacyProtocol;
17
use Webklex\PHPIMAP\Connection\Protocols\Protocol;
18
use Webklex\PHPIMAP\Connection\Protocols\ProtocolInterface;
19
use Webklex\PHPIMAP\Exceptions\ConnectionFailedException;
20
use Webklex\PHPIMAP\Exceptions\FolderFetchingException;
21
use Webklex\PHPIMAP\Exceptions\MaskNotFoundException;
22
use Webklex\PHPIMAP\Exceptions\ProtocolNotSupportedException;
23
use Webklex\PHPIMAP\Support\FolderCollection;
24
use Webklex\PHPIMAP\Support\Masks\AttachmentMask;
25
use Webklex\PHPIMAP\Support\Masks\MessageMask;
26
use Webklex\PHPIMAP\Traits\HasEvents;
27
28
/**
29
 * Class Client
30
 *
31
 * @package Webklex\PHPIMAP
32
 */
33
class Client {
34
    use HasEvents;
35
36
    /**
37
     * Connection resource
38
     *
39
     * @var boolean|Protocol
40
     */
41
    public $connection = false;
42
43
    /**
44
     * Server hostname.
45
     *
46
     * @var string
47
     */
48
    public $host;
49
50
    /**
51
     * Server port.
52
     *
53
     * @var int
54
     */
55
    public $port;
56
57
    /**
58
     * Service protocol.
59
     *
60
     * @var int
61
     */
62
    public $protocol;
63
64
    /**
65
     * Server encryption.
66
     * Supported: none, ssl, tls, or notls.
67
     *
68
     * @var string
69
     */
70
    public $encryption;
71
72
    /**
73
     * If server has to validate cert.
74
     *
75
     * @var mixed
76
     */
77
    public $validate_cert;
78
79
    /**
80
     * Account username/
81
     *
82
     * @var mixed
83
     */
84
    public $username;
85
86
    /**
87
     * Account password.
88
     *
89
     * @var string
90
     */
91
    public $password;
92
93
    /**
94
     * Account authentication method.
95
     *
96
     * @var string
97
     */
98
    public $authentication;
99
100
    /**
101
     * Active folder.
102
     *
103
     * @var Folder
104
     */
105
    protected $active_folder = false;
106
107
    /**
108
     * Default message mask
109
     *
110
     * @var string $default_message_mask
111
     */
112
    protected $default_message_mask = MessageMask::class;
113
114
    /**
115
     * Default attachment mask
116
     *
117
     * @var string $default_attachment_mask
118
     */
119
    protected $default_attachment_mask = AttachmentMask::class;
120
121
    /**
122
     * Used default account values
123
     *
124
     * @var array $default_account_config
125
     */
126
    protected $default_account_config = [
127
        'host' => 'localhost',
128
        'port' => 993,
129
        'protocol'  => 'imap',
130
        'encryption' => 'ssl',
131
        'validate_cert' => true,
132
        'username' => '',
133
        'password' => '',
134
        'authentication' => null,
135
    ];
136
137
    /**
138
     * Client constructor.
139
     * @param array $config
140
     *
141
     * @throws MaskNotFoundException
142
     */
143
    public function __construct($config = []) {
144
        $this->setConfig($config);
145
        $this->setMaskFromConfig($config);
146
        $this->setEventsFromConfig($config);
147
    }
148
149
    /**
150
     * Client destructor
151
     */
152
    public function __destruct() {
153
        $this->disconnect();
154
    }
155
156
    /**
157
     * Set the Client configuration
158
     * @param array $config
159
     *
160
     * @return self
161
     */
162
    public function setConfig(array $config) {
163
        $default_account = ClientManager::get('default');
164
        $default_config  = ClientManager::get("accounts.$default_account");
165
166
        foreach ($this->default_account_config as $key => $value) {
167
            $this->setAccountConfig($key, $config, $default_config);
168
        }
169
170
        return $this;
171
    }
172
173
    /**
174
     * Set a specific account config
175
     * @param string $key
176
     * @param array $config
177
     * @param array $default_config
178
     */
179
    private function setAccountConfig($key, $config, $default_config){
180
        $value = $this->default_account_config[$key];
181
        if(isset($config[$key])) {
182
            $value = $config[$key];
183
        }elseif(isset($default_config[$key])) {
184
            $value = $default_config[$key];
185
        }
186
        $this->$key = $value;
187
    }
188
189
    /**
190
     * Look for a possible events in any available config
191
     * @param $config
192
     */
193
    protected function setEventsFromConfig($config) {
194
        $this->events = ClientManager::get("events");
195
        if(isset($config['events'])){
196
            if(isset($config['events'])) {
197
                foreach($config['events'] as $section => $events) {
198
                    $this->events[$section] = array_merge($this->events[$section], $events);
199
                }
200
            }
201
        }
202
    }
203
204
    /**
205
     * Look for a possible mask in any available config
206
     * @param $config
207
     *
208
     * @throws MaskNotFoundException
209
     */
210
    protected function setMaskFromConfig($config) {
211
        $default_config  = ClientManager::get("masks");
212
213
        if(isset($config['masks'])){
214
            if(isset($config['masks']['message'])) {
215
                if(class_exists($config['masks']['message'])) {
216
                    $this->default_message_mask = $config['masks']['message'];
217
                }else{
218
                    throw new MaskNotFoundException("Unknown mask provided: ".$config['masks']['message']);
219
                }
220
            }else{
221
                if(class_exists($default_config['message'])) {
222
                    $this->default_message_mask = $default_config['message'];
223
                }else{
224
                    throw new MaskNotFoundException("Unknown mask provided: ".$default_config['message']);
225
                }
226
            }
227
            if(isset($config['masks']['attachment'])) {
228
                if(class_exists($config['masks']['attachment'])) {
229
                    $this->default_message_mask = $config['masks']['attachment'];
230
                }else{
231
                    throw new MaskNotFoundException("Unknown mask provided: ".$config['masks']['attachment']);
232
                }
233
            }else{
234
                if(class_exists($default_config['attachment'])) {
235
                    $this->default_message_mask = $default_config['attachment'];
236
                }else{
237
                    throw new MaskNotFoundException("Unknown mask provided: ".$default_config['attachment']);
238
                }
239
            }
240
        }else{
241
            if(class_exists($default_config['message'])) {
242
                $this->default_message_mask = $default_config['message'];
243
            }else{
244
                throw new MaskNotFoundException("Unknown mask provided: ".$default_config['message']);
245
            }
246
247
            if(class_exists($default_config['attachment'])) {
248
                $this->default_message_mask = $default_config['attachment'];
249
            }else{
250
                throw new MaskNotFoundException("Unknown mask provided: ".$default_config['attachment']);
251
            }
252
        }
253
254
    }
255
256
    /**
257
     * Get the current imap resource
258
     *
259
     * @return bool|Protocol|ProtocolInterface
260
     * @throws ConnectionFailedException
261
     */
262
    public function getConnection() {
263
        $this->checkConnection();
264
        return $this->connection;
265
    }
266
267
    /**
268
     * Determine if connection was established.
269
     *
270
     * @return bool
271
     */
272
    public function isConnected() {
273
        return $this->connection ? $this->connection->connected() : false;
0 ignored issues
show
Bug introduced by
The method connected() 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

273
        return $this->connection ? $this->connection->/** @scrutinizer ignore-call */ connected() : false;
Loading history...
274
    }
275
276
    /**
277
     * Determine if connection was established and connect if not.
278
     *
279
     * @throws ConnectionFailedException
280
     */
281
    public function checkConnection() {
282
        if (!$this->isConnected()) {
283
            $this->connect();
284
        }
285
    }
286
287
    /**
288
     * Force a reconnect
289
     *
290
     * @throws ConnectionFailedException
291
     */
292
    public function reconnect() {
293
        if ($this->isConnected()) {
294
            $this->disconnect();
295
        }
296
        $this->connect();
297
    }
298
299
    /**
300
     * Connect to server.
301
     *
302
     * @return $this
303
     * @throws ConnectionFailedException
304
     */
305
    public function connect() {
306
        $this->disconnect();
307
        $protocol = strtolower($this->protocol);
308
309
        if ($protocol == "imap") {
310
            $timeout = $this->connection !== false ? $this->connection->getConnectionTimeout() : null;
311
            $this->connection = new ImapProtocol($this->validate_cert);
312
            $this->connection->setConnectionTimeout($timeout);
313
        }else{
314
            if (extension_loaded('imap') === false) {
315
                throw new ConnectionFailedException("connection setup failed", 0, new ProtocolNotSupportedException($protocol." is an unsupported protocol"));
316
            }
317
            $this->connection = new LegacyProtocol($this->validate_cert);
318
            if (strpos($protocol, "legacy-") === 0) {
319
                $protocol = substr($protocol, 7);
320
            }
321
            $this->connection->setProtocol($protocol);
322
        }
323
324
        $this->connection->connect($this->host, $this->port, $this->encryption);
0 ignored issues
show
Bug introduced by
$this->encryption of type string is incompatible with the type boolean expected by parameter $encryption of Webklex\PHPIMAP\Connecti...gacyProtocol::connect(). ( Ignorable by Annotation )

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

324
        $this->connection->connect($this->host, $this->port, /** @scrutinizer ignore-type */ $this->encryption);
Loading history...
325
        $this->authenticate();
326
327
        return $this;
328
    }
329
330
    /**
331
     * Authenticate the current session
332
     *
333
     * @throws ConnectionFailedException
334
     */
335
    protected function authenticate() {
336
        try {
337
            if ($this->authentication == "oauth") {
338
                $this->connection->authenticate($this->username, $this->password);
0 ignored issues
show
Bug introduced by
The method authenticate() 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

338
                $this->connection->/** @scrutinizer ignore-call */ 
339
                                   authenticate($this->username, $this->password);
Loading history...
339
            }else{
340
                $this->connection->login($this->username, $this->password);
0 ignored issues
show
Bug introduced by
The method login() 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

340
                $this->connection->/** @scrutinizer ignore-call */ 
341
                                   login($this->username, $this->password);
Loading history...
341
            }
342
        } catch (\Exception $e) {
343
            throw new ConnectionFailedException("connection setup failed", 0, $e);
344
        }
345
    }
346
347
    /**
348
     * Disconnect from server.
349
     *
350
     * @return $this
351
     */
352
    public function disconnect() {
353
        if ($this->isConnected() && $this->connection !== false) {
354
            $this->connection->logout();
0 ignored issues
show
Bug introduced by
The method logout() 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

354
            $this->connection->/** @scrutinizer ignore-call */ 
355
                               logout();
Loading history...
355
        }
356
357
        return $this;
358
    }
359
360
    /**
361
     * Get a folder instance by a folder name
362
     * @param $folder_name
363
     *
364
     * @return mixed
365
     * @throws ConnectionFailedException
366
     * @throws FolderFetchingException
367
     */
368
    public function getFolder($folder_name) {
369
        return $this->getFolders(false)->where("name", $folder_name)->first();
370
    }
371
372
    /**
373
     * Get a folder instance by a folder path
374
     * @param $folder_path
375
     *
376
     * @return mixed
377
     * @throws ConnectionFailedException
378
     * @throws FolderFetchingException
379
     */
380
    public function getFolderByPath($folder_path) {
381
        return $this->getFolders(false)->where("path", $folder_path)->first();
382
    }
383
384
    /**
385
     * Get folders list.
386
     * If hierarchical order is set to true, it will make a tree of folders, otherwise it will return flat array.
387
     *
388
     * @param boolean     $hierarchical
389
     * @param string|null $parent_folder
390
     *
391
     * @return FolderCollection
392
     * @throws ConnectionFailedException
393
     * @throws FolderFetchingException
394
     */
395
    public function getFolders($hierarchical = true, $parent_folder = null) {
396
        $this->checkConnection();
397
        $folders = FolderCollection::make([]);
398
399
        $pattern = $parent_folder.($hierarchical ? '%' : '*');
400
        $items = $this->connection->folders('', $pattern);
0 ignored issues
show
Bug introduced by
The method folders() 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

400
        /** @scrutinizer ignore-call */ 
401
        $items = $this->connection->folders('', $pattern);
Loading history...
401
402
        if(is_array($items)){
403
            foreach ($items as $folder_name => $item) {
404
                $folder = new Folder($this, $folder_name, $item["delimiter"], $item["flags"]);
405
406
                if ($hierarchical && $folder->hasChildren()) {
407
                    $pattern = $folder->full_name.$folder->delimiter.'%';
408
409
                    $children = $this->getFolders(true, $pattern);
410
                    $folder->setChildren($children);
411
                }
412
413
                $folders->push($folder);
414
            }
415
416
            return $folders;
417
        }else{
418
            throw new FolderFetchingException("failed to fetch any folders");
419
        }
420
    }
421
422
    /**
423
     * Open folder.
424
     * @param string $folder
425
     *
426
     * @return mixed
427
     * @throws ConnectionFailedException
428
     */
429
    public function openFolder($folder) {
430
        if ($this->active_folder == $folder && $this->isConnected()) {
0 ignored issues
show
introduced by
The condition $this->active_folder == $folder is always false.
Loading history...
431
            return true;
432
        }
433
        $this->checkConnection();
434
        $this->active_folder = $folder;
0 ignored issues
show
Documentation Bug introduced by
It seems like $folder of type string is incompatible with the declared type Webklex\PHPIMAP\Folder of property $active_folder.

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...
435
        return $this->connection->selectFolder($folder);
0 ignored issues
show
Bug introduced by
The method selectFolder() 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

435
        return $this->connection->/** @scrutinizer ignore-call */ selectFolder($folder);
Loading history...
436
    }
437
438
    /**
439
     * Create a new Folder
440
     * @param string $folder
441
     * @param boolean $expunge
442
     *
443
     * @return bool
444
     * @throws ConnectionFailedException
445
     * @throws FolderFetchingException
446
     * @throws Exceptions\EventNotFoundException
447
     */
448
    public function createFolder($folder, $expunge = true) {
449
        $this->checkConnection();
450
        $status = $this->connection->createFolder($folder);
0 ignored issues
show
Bug introduced by
The method createFolder() 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

450
        /** @scrutinizer ignore-call */ 
451
        $status = $this->connection->createFolder($folder);
Loading history...
451
        if($expunge) $this->expunge();
452
453
        $folder = $this->getFolder($folder);
454
        if($status && $folder) {
455
            $event = $this->getEvent("folder", "new");
456
            $event::dispatch($folder);
457
        }
458
459
        return $folder;
460
    }
461
462
    /**
463
     * Check a given folder
464
     * @param $folder
465
     *
466
     * @return false|object
467
     * @throws ConnectionFailedException
468
     */
469
    public function checkFolder($folder) {
470
        $this->checkConnection();
471
        return $this->connection->examineFolder($folder);
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

471
        return $this->connection->/** @scrutinizer ignore-call */ examineFolder($folder);
Loading history...
472
    }
473
474
    /**
475
     * Get the current active folder
476
     *
477
     * @return Folder
478
     */
479
    public function getFolderPath(){
480
        return $this->active_folder;
481
    }
482
483
    /**
484
     * Retrieve the quota level settings, and usage statics per mailbox
485
     *
486
     * @return array
487
     * @throws ConnectionFailedException
488
     */
489
    public function getQuota() {
490
        $this->checkConnection();
491
        return $this->connection->getQuota($this->username);
0 ignored issues
show
Bug introduced by
The method getQuota() 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

491
        return $this->connection->/** @scrutinizer ignore-call */ getQuota($this->username);
Loading history...
492
    }
493
494
    /**
495
     * Retrieve the quota settings per user
496
     * @param string $quota_root
497
     *
498
     * @return array
499
     * @throws ConnectionFailedException
500
     */
501
    public function getQuotaRoot($quota_root = 'INBOX') {
502
        $this->checkConnection();
503
        return $this->connection->getQuotaRoot($quota_root);
0 ignored issues
show
Bug introduced by
The method getQuotaRoot() 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

503
        return $this->connection->/** @scrutinizer ignore-call */ getQuotaRoot($quota_root);
Loading history...
504
    }
505
506
    /**
507
     * Delete all messages marked for deletion
508
     *
509
     * @return bool
510
     * @throws ConnectionFailedException
511
     */
512
    public function expunge() {
513
        $this->checkConnection();
514
        return $this->connection->expunge();
0 ignored issues
show
Bug introduced by
The method expunge() 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

514
        return $this->connection->/** @scrutinizer ignore-call */ expunge();
Loading history...
515
    }
516
517
    /**
518
     * Set the imap timeout for a given operation type
519
     * @param $timeout
520
     *
521
     * @return Protocol
522
     */
523
    public function setTimeout($timeout) {
524
        return $this->connection->setConnectionTimeout($timeout);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->connection...ectionTimeout($timeout) also could return the type boolean which is incompatible with the documented return type Webklex\PHPIMAP\Connection\Protocols\Protocol.
Loading history...
525
    }
526
527
    /**
528
     * Get the timeout for a certain operation
529
     * @param $type
530
     *
531
     * @return int
532
     */
533
    public function getTimeout($type){
0 ignored issues
show
Unused Code introduced by
The parameter $type is not used and could be removed. ( Ignorable by Annotation )

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

533
    public function getTimeout(/** @scrutinizer ignore-unused */ $type){

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
534
        return $this->connection->getConnectionTimeout();
535
    }
536
537
    /**
538
     * Get the default message mask
539
     *
540
     * @return string
541
     */
542
    public function getDefaultMessageMask(){
543
        return $this->default_message_mask;
544
    }
545
546
    /**
547
     * Get the default events for a given section
548
     * @param $section
549
     *
550
     * @return array
551
     */
552
    public function getDefaultEvents($section){
553
        return $this->events[$section];
554
    }
555
556
    /**
557
     * Set the default message mask
558
     * @param $mask
559
     *
560
     * @return $this
561
     * @throws MaskNotFoundException
562
     */
563
    public function setDefaultMessageMask($mask) {
564
        if(class_exists($mask)) {
565
            $this->default_message_mask = $mask;
566
567
            return $this;
568
        }
569
570
        throw new MaskNotFoundException("Unknown mask provided: ".$mask);
571
    }
572
573
    /**
574
     * Get the default attachment mask
575
     *
576
     * @return string
577
     */
578
    public function getDefaultAttachmentMask(){
579
        return $this->default_attachment_mask;
580
    }
581
582
    /**
583
     * Set the default attachment mask
584
     * @param $mask
585
     *
586
     * @return $this
587
     * @throws MaskNotFoundException
588
     */
589
    public function setDefaultAttachmentMask($mask) {
590
        if(class_exists($mask)) {
591
            $this->default_attachment_mask = $mask;
592
593
            return $this;
594
        }
595
596
        throw new MaskNotFoundException("Unknown mask provided: ".$mask);
597
    }
598
}
599