Passed
Push — master ( 7eda73...e84ff4 )
by Malte
04:28
created

Query   B

Complexity

Total Complexity 51

Size/Duplication

Total Lines 439
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 120
dl 0
loc 439
rs 7.92
c 1
b 0
f 0
wmc 51

35 Methods

Rating   Name   Duplication   Size   Complexity  
A boot() 0 1 1
A search() 0 18 3
A leaveUnread() 0 4 1
A markAsRead() 0 4 1
A parse_date() 0 10 3
A __construct() 0 10 2
A parse_value() 0 8 2
A setLimit() 0 3 2
A limit() 0 5 2
A getFetchFlags() 0 2 1
A setQuery() 0 3 1
A getCharset() 0 2 1
A setPage() 0 3 1
A getQuery() 0 2 1
A setCharset() 0 3 1
A getRawQuery() 0 2 1
A getClient() 0 3 1
B get() 0 40 6
A setClient() 0 3 1
A setFetchAttachment() 0 3 1
A getFetchBody() 0 2 1
A fetchOptions() 0 2 1
A setFetchOptions() 0 3 1
A setFetchFlags() 0 3 1
A setRawQuery() 0 3 1
A generate_query() 0 19 3
A setFetchBody() 0 3 1
A getLimit() 0 2 1
A getFetchOptions() 0 2 1
A fetchBody() 0 2 1
A fetchAttachment() 0 2 1
A paginate() 0 5 2
A getFetchAttachment() 0 2 1
A getPage() 0 2 1
A count() 0 2 1

How to fix   Complexity   

Complex Class

Complex classes like Query often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Query, and based on these observations, apply Extract Interface, too.

1
<?php
2
/*
3
* File:     Query.php
4
* Category: -
5
* Author:   M. Goldenbaum
6
* Created:  21.07.18 18:54
7
* Updated:  -
8
*
9
* Description:
10
*  -
11
*/
12
13
namespace Webklex\IMAP\Query;
14
15
use Carbon\Carbon;
16
use Webklex\IMAP\Client;
17
use Webklex\IMAP\Exceptions\GetMessagesFailedException;
18
use Webklex\IMAP\Exceptions\MessageSearchValidationException;
19
use Webklex\IMAP\IMAP;
20
use Webklex\IMAP\Message;
21
use Webklex\IMAP\Support\MessageCollection;
22
23
/**
24
 * Class Query
25
 *
26
 * @package Webklex\IMAP\Query
27
 */
28
class Query {
29
30
    /** @var array $query */
31
    protected $query;
32
33
    /** @var string $raw_query  */
34
    protected $raw_query;
35
36
    /** @var string $charset */
37
    protected $charset;
38
39
    /** @var Client $client */
40
    protected $client;
41
42
    /** @var int $limit */
43
    protected $limit = null;
44
45
    /** @var int $page */
46
    protected $page = 1;
47
48
    /** @var int $fetch_options */
49
    protected $fetch_options = null;
50
51
    /** @var int $fetch_body */
52
    protected $fetch_body = true;
53
54
    /** @var int $fetch_attachment */
55
    protected $fetch_attachment = true;
56
57
    /** @var int $fetch_flags */
58
    protected $fetch_flags = true;
59
60
    /** @var string $date_format */
61
    protected $date_format;
62
63
    /**
64
     * Query constructor.
65
     * @param Client $client
66
     * @param string $charset
67
     */
68
    public function __construct(Client $client, $charset = 'UTF-8') {
69
        $this->setClient($client);
70
71
        if(config('imap.options.fetch') === IMAP::FT_PEEK) $this->leaveUnread();
72
73
        $this->date_format = config('imap.date_format', 'd M y');
74
75
        $this->charset = $charset;
76
        $this->query = collect();
0 ignored issues
show
Documentation Bug introduced by
It seems like collect() of type Illuminate\Support\Collection is incompatible with the declared type array of property $query.

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...
77
        $this->boot();
78
    }
79
80
    /**
81
     * Instance boot method for additional functionality
82
     */
83
    protected function boot(){}
84
85
    /**
86
     * Parse a given value
87
     * @param mixed $value
88
     *
89
     * @return string
90
     */
91
    protected function parse_value($value){
92
        switch(true){
93
            case $value instanceof \Carbon\Carbon:
94
                $value = $value->format($this->date_format);
95
                break;
96
        }
97
98
        return (string) $value;
99
    }
100
101
    /**
102
     * Check if a given date is a valid carbon object and if not try to convert it
103
     * @param $date
104
     *
105
     * @return Carbon
106
     * @throws MessageSearchValidationException
107
     */
108
    protected function parse_date($date) {
109
        if($date instanceof \Carbon\Carbon) return $date;
110
111
        try {
112
            $date = Carbon::parse($date);
113
        } catch (\Exception $e) {
114
            throw new MessageSearchValidationException();
115
        }
116
117
        return $date;
118
    }
119
120
    /**
121
     * Don't mark messages as read when fetching
122
     *
123
     * @return $this
124
     */
125
    public function leaveUnread() {
126
        $this->setFetchOptions(IMAP::FT_PEEK);
0 ignored issues
show
Bug introduced by
Webklex\IMAP\IMAP::FT_PEEK of type integer is incompatible with the type boolean expected by parameter $fetch_options of Webklex\IMAP\Query\Query::setFetchOptions(). ( Ignorable by Annotation )

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

126
        $this->setFetchOptions(/** @scrutinizer ignore-type */ IMAP::FT_PEEK);
Loading history...
127
128
        return $this;
129
    }
130
131
    /**
132
     * Mark all messages as read when fetching
133
     *
134
     * @return $this
135
     */
136
    public function markAsRead() {
137
        $this->setFetchOptions(IMAP::FT_UID);
0 ignored issues
show
Bug introduced by
Webklex\IMAP\IMAP::FT_UID of type integer is incompatible with the type boolean expected by parameter $fetch_options of Webklex\IMAP\Query\Query::setFetchOptions(). ( Ignorable by Annotation )

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

137
        $this->setFetchOptions(/** @scrutinizer ignore-type */ IMAP::FT_UID);
Loading history...
138
139
        return $this;
140
    }
141
142
    /**
143
     * Perform an imap search request
144
     *
145
     * @return \Illuminate\Support\Collection
146
     * @throws \Webklex\IMAP\Exceptions\ConnectionFailedException
147
     */
148
    protected function search(){
149
        $this->generate_query();
150
151
        /**
152
         * Don't set the charset if it isn't used - prevent strange outlook mail server errors
153
         * @see https://github.com/Webklex/laravel-imap/issues/100
154
         */
155
        if($this->getCharset() === null){
0 ignored issues
show
introduced by
The condition $this->getCharset() === null is always false.
Loading history...
156
            $available_messages = imap_search($this->getClient()->getConnection(), $this->getRawQuery(), IMAP::SE_UID);
157
        }else{
158
            $available_messages = imap_search($this->getClient()->getConnection(), $this->getRawQuery(), IMAP::SE_UID, $this->getCharset());
0 ignored issues
show
Bug introduced by
It seems like $this->getClient()->getConnection() can also be of type true; however, parameter $imap_stream of imap_search() 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

158
            $available_messages = imap_search(/** @scrutinizer ignore-type */ $this->getClient()->getConnection(), $this->getRawQuery(), IMAP::SE_UID, $this->getCharset());
Loading history...
159
        }
160
161
        if ($available_messages !== false) {
162
            return collect($available_messages);
163
        }
164
165
        return collect();
166
    }
167
168
    /**
169
     * Count all available messages matching the current search criteria
170
     *
171
     * @return int
172
     * @throws \Webklex\IMAP\Exceptions\ConnectionFailedException
173
     */
174
    public function count() {
175
        return $this->search()->count();
176
    }
177
178
    /**
179
     * Fetch the current query and return all found messages
180
     *
181
     * @return MessageCollection
182
     * @throws GetMessagesFailedException
183
     */
184
    public function get() {
185
        $messages = MessageCollection::make([]);
186
187
        try {
188
            $available_messages = $this->search();
189
            $available_messages_count = $available_messages->count();
190
191
            if ($available_messages_count > 0) {
192
193
                $messages->total($available_messages_count);
194
195
                $options = config('imap.options');
196
197
                if(strtolower($options['fetch_order']) === 'desc'){
198
                    $available_messages = $available_messages->reverse();
199
                }
200
201
                $query =& $this;
202
203
                $available_messages->forPage($this->page, $this->limit)->each(function($msgno, $msglist) use(&$messages, $options, $query) {
204
                    $oMessage = new Message($msgno, $msglist, $query->getClient(), $query->getFetchOptions(), $query->getFetchBody(), $query->getFetchAttachment(), $query->getFetchFlags());
0 ignored issues
show
Bug introduced by
$query->getFetchFlags() of type integer is incompatible with the type boolean expected by parameter $fetch_flags of Webklex\IMAP\Message::__construct(). ( Ignorable by Annotation )

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

204
                    $oMessage = new Message($msgno, $msglist, $query->getClient(), $query->getFetchOptions(), $query->getFetchBody(), $query->getFetchAttachment(), /** @scrutinizer ignore-type */ $query->getFetchFlags());
Loading history...
205
                    switch ($options['message_key']){
206
                        case 'number':
207
                            $message_key = $oMessage->getMessageNo();
208
                            break;
209
                        case 'list':
210
                            $message_key = $msglist;
211
                            break;
212
                        default:
213
                            $message_key = $oMessage->getMessageId();
214
                            break;
215
216
                    }
217
                    $messages->put($message_key, $oMessage);
218
                });
219
            }
220
221
            return $messages;
222
        } catch (\Exception $e) {
223
            throw new GetMessagesFailedException($e->getMessage());
224
        }
225
    }
226
227
    /**
228
     * Paginate the current query
229
     * @param int $per_page
230
     * @param null $page
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $page is correct as it would always require null to be passed?
Loading history...
231
     * @param string $page_name
232
     *
233
     * @return \Illuminate\Pagination\LengthAwarePaginator
234
     * @throws GetMessagesFailedException
235
     */
236
    public function paginate($per_page = 5, $page = null, $page_name = 'imap_page'){
237
        $this->page = $page > $this->page ? $page : $this->page;
238
        $this->limit = $per_page;
239
240
        return $this->get()->paginate($per_page, $this->page, $page_name);
241
    }
242
243
    /**
244
     * Get the raw IMAP search query
245
     *
246
     * @return string
247
     */
248
    public function generate_query() {
249
        $query = '';
250
        $this->query->each(function($statement) use(&$query) {
251
            if (count($statement) == 1) {
252
                $query .= $statement[0];
253
            } else {
254
                if($statement[1] === null){
255
                    $query .= $statement[0];
256
                }else{
257
                    $query .= $statement[0].' "'.$statement[1].'"';
258
                }
259
            }
260
            $query .= ' ';
261
262
        });
263
264
        $this->raw_query = trim($query);
265
266
        return $this->raw_query;
267
    }
268
269
    /**
270
     * @return Client
271
     * @throws \Webklex\IMAP\Exceptions\ConnectionFailedException
272
     */
273
    public function getClient() {
274
        $this->client->checkConnection();
275
        return $this->client;
276
    }
277
278
    /**
279
     * Set the limit and page for the current query
280
     * @param int $limit
281
     * @param int $page
282
     *
283
     * @return $this
284
     */
285
    public function limit($limit, $page = 1) {
286
        if($page >= 1) $this->page = $page;
287
        $this->limit = $limit;
288
289
        return $this;
290
    }
291
292
    /**
293
     * @return array
294
     */
295
    public function getQuery() {
296
        return $this->query;
297
    }
298
299
    /**
300
     * @param array $query
301
     * @return Query
302
     */
303
    public function setQuery($query) {
304
        $this->query = $query;
305
        return $this;
306
    }
307
308
    /**
309
     * @return string
310
     */
311
    public function getRawQuery() {
312
        return $this->raw_query;
313
    }
314
315
    /**
316
     * @param string $raw_query
317
     * @return Query
318
     */
319
    public function setRawQuery($raw_query) {
320
        $this->raw_query = $raw_query;
321
        return $this;
322
    }
323
324
    /**
325
     * @return string
326
     */
327
    public function getCharset() {
328
        return $this->charset;
329
    }
330
331
    /**
332
     * @param string $charset
333
     * @return Query
334
     */
335
    public function setCharset($charset) {
336
        $this->charset = $charset;
337
        return $this;
338
    }
339
340
    /**
341
     * @param Client $client
342
     * @return Query
343
     */
344
    public function setClient(Client $client) {
345
        $this->client = $client;
346
        return $this;
347
    }
348
349
    /**
350
     * @return int
351
     */
352
    public function getLimit() {
353
        return $this->limit;
354
    }
355
356
    /**
357
     * @param int $limit
358
     * @return Query
359
     */
360
    public function setLimit($limit) {
361
        $this->limit = $limit <= 0 ? null : $limit;
362
        return $this;
363
    }
364
365
    /**
366
     * @return int
367
     */
368
    public function getPage() {
369
        return $this->page;
370
    }
371
372
    /**
373
     * @param int $page
374
     * @return Query
375
     */
376
    public function setPage($page) {
377
        $this->page = $page;
378
        return $this;
379
    }
380
381
    /**
382
     * @param boolean $fetch_options
383
     * @return Query
384
     */
385
    public function setFetchOptions($fetch_options) {
386
        $this->fetch_options = $fetch_options;
0 ignored issues
show
Documentation Bug introduced by
The property $fetch_options was declared of type integer, but $fetch_options is of type boolean. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
387
        return $this;
388
    }
389
390
    /**
391
     * @param boolean $fetch_options
392
     * @return Query
393
     */
394
    public function fetchOptions($fetch_options) {
395
        return $this->setFetchOptions($fetch_options);
396
    }
397
398
    /**
399
     * @return int
400
     */
401
    public function getFetchOptions() {
402
        return $this->fetch_options;
403
    }
404
405
    /**
406
     * @return boolean
407
     */
408
    public function getFetchBody() {
409
        return $this->fetch_body;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->fetch_body returns the type integer which is incompatible with the documented return type boolean.
Loading history...
410
    }
411
412
    /**
413
     * @param boolean $fetch_body
414
     * @return Query
415
     */
416
    public function setFetchBody($fetch_body) {
417
        $this->fetch_body = $fetch_body;
0 ignored issues
show
Documentation Bug introduced by
The property $fetch_body was declared of type integer, but $fetch_body is of type boolean. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
418
        return $this;
419
    }
420
421
    /**
422
     * @param boolean $fetch_body
423
     * @return Query
424
     */
425
    public function fetchBody($fetch_body) {
426
        return $this->setFetchBody($fetch_body);
427
    }
428
429
    /**
430
     * @return boolean
431
     */
432
    public function getFetchAttachment() {
433
        return $this->fetch_attachment;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->fetch_attachment returns the type integer which is incompatible with the documented return type boolean.
Loading history...
434
    }
435
436
    /**
437
     * @param boolean $fetch_attachment
438
     * @return Query
439
     */
440
    public function setFetchAttachment($fetch_attachment) {
441
        $this->fetch_attachment = $fetch_attachment;
0 ignored issues
show
Documentation Bug introduced by
The property $fetch_attachment was declared of type integer, but $fetch_attachment is of type boolean. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
442
        return $this;
443
    }
444
445
    /**
446
     * @param boolean $fetch_attachment
447
     * @return Query
448
     */
449
    public function fetchAttachment($fetch_attachment) {
450
        return $this->setFetchAttachment($fetch_attachment);
451
    }
452
453
    /**
454
     * @return int
455
     */
456
    public function getFetchFlags() {
457
        return $this->fetch_flags;
458
    }
459
460
    /**
461
     * @param int $fetch_flags
462
     * @return Query
463
     */
464
    public function setFetchFlags($fetch_flags) {
465
        $this->fetch_flags = $fetch_flags;
466
        return $this;
467
    }
468
}
469