Passed
Pull Request — master (#41)
by
unknown
02:49
created

Query::setFetchOrderDesc()   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 0
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\PHPIMAP\Query;
14
15
use Carbon\Carbon;
16
use Illuminate\Pagination\LengthAwarePaginator;
17
use Illuminate\Support\Collection;
18
use Webklex\PHPIMAP\Client;
19
use Webklex\PHPIMAP\ClientManager;
20
use Webklex\PHPIMAP\Exceptions\ConnectionFailedException;
21
use Webklex\PHPIMAP\Exceptions\GetMessagesFailedException;
22
use Webklex\PHPIMAP\Exceptions\InvalidMessageDateException;
23
use Webklex\PHPIMAP\Exceptions\MessageContentFetchingException;
24
use Webklex\PHPIMAP\Exceptions\MessageHeaderFetchingException;
25
use Webklex\PHPIMAP\Exceptions\MessageSearchValidationException;
26
use Webklex\PHPIMAP\Exceptions\RuntimeException;
27
use Webklex\PHPIMAP\IMAP;
28
use Webklex\PHPIMAP\Message;
29
use Webklex\PHPIMAP\Support\MessageCollection;
30
31
/**
32
 * Class Query
33
 *
34
 * @package Webklex\PHPIMAP\Query
35
 */
36
class Query {
37
38
    /** @var array $query */
39
    protected $query;
40
41
    /** @var string $raw_query  */
42
    protected $raw_query;
43
44
    /** @var string $charset */
45
    protected $charset;
46
47
    /** @var Client $client */
48
    protected $client;
49
50
    /** @var int $limit */
51
    protected $limit = null;
52
53
    /** @var int $page */
54
    protected $page = 1;
55
56
    /** @var int $fetch_options */
57
    protected $fetch_options = null;
58
59
    /** @var int $fetch_body */
60
    protected $fetch_body = true;
61
62
    /** @var int $fetch_flags */
63
    protected $fetch_flags = true;
64
65
    /** @var int $fetch_order */
66
    protected $fetch_order;
67
68
    /** @var string $date_format */
69
    protected $date_format;
70
71
    /**
72
     * Query constructor.
73
     * @param Client $client
74
     * @param string $charset
75
     */
76
    public function __construct(Client $client, $charset = 'UTF-8') {
77
        $this->setClient($client);
78
79
        if(ClientManager::get('options.fetch') === IMAP::FT_PEEK) $this->leaveUnread();
80
81
        if (ClientManager::get('options.fetch_order') === 'desc') {
82
            $this->fetch_order = 'desc';
0 ignored issues
show
Documentation Bug introduced by
The property $fetch_order was declared of type integer, but 'desc' is of type string. 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...
83
        } else {
84
            $this->fetch_order = 'asc';
85
        }
86
87
        $this->date_format = ClientManager::get('date_format', 'd M y');
88
89
        $this->charset = $charset;
90
        $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...
91
        $this->boot();
92
    }
93
94
    /**
95
     * Instance boot method for additional functionality
96
     */
97
    protected function boot(){}
98
99
    /**
100
     * Parse a given value
101
     * @param mixed $value
102
     *
103
     * @return string
104
     */
105
    protected function parse_value($value){
106
        switch(true){
107
            case $value instanceof \Carbon\Carbon:
108
                $value = $value->format($this->date_format);
109
                break;
110
        }
111
112
        return (string) $value;
113
    }
114
115
    /**
116
     * Check if a given date is a valid carbon object and if not try to convert it
117
     * @param $date
118
     *
119
     * @return Carbon
120
     * @throws MessageSearchValidationException
121
     */
122
    protected function parse_date($date) {
123
        if($date instanceof \Carbon\Carbon) return $date;
124
125
        try {
126
            $date = Carbon::parse($date);
127
        } catch (\Exception $e) {
128
            throw new MessageSearchValidationException();
129
        }
130
131
        return $date;
132
    }
133
134
    /**
135
     * Don't mark messages as read when fetching
136
     *
137
     * @return $this
138
     */
139
    public function leaveUnread() {
140
        $this->setFetchOptions(IMAP::FT_PEEK);
0 ignored issues
show
Bug introduced by
Webklex\PHPIMAP\IMAP::FT_PEEK of type integer is incompatible with the type boolean expected by parameter $fetch_options of Webklex\PHPIMAP\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

140
        $this->setFetchOptions(/** @scrutinizer ignore-type */ IMAP::FT_PEEK);
Loading history...
141
142
        return $this;
143
    }
144
145
    /**
146
     * Mark all messages as read when fetching
147
     *
148
     * @return $this
149
     */
150
    public function markAsRead() {
151
        $this->setFetchOptions(IMAP::FT_UID);
0 ignored issues
show
Bug introduced by
Webklex\PHPIMAP\IMAP::FT_UID of type integer is incompatible with the type boolean expected by parameter $fetch_options of Webklex\PHPIMAP\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

151
        $this->setFetchOptions(/** @scrutinizer ignore-type */ IMAP::FT_UID);
Loading history...
152
153
        return $this;
154
    }
155
156
    /**
157
     * Perform an imap search request
158
     *
159
     * @return Collection
160
     * @throws GetMessagesFailedException
161
     */
162
    protected function search(){
163
        $this->generate_query();
164
165
        try {
166
            $available_messages = $this->client->getConnection()->search([$this->getRawQuery()]);
0 ignored issues
show
Bug introduced by
The method search() 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

166
            $available_messages = $this->client->getConnection()->/** @scrutinizer ignore-call */ search([$this->getRawQuery()]);
Loading history...
167
        } catch (RuntimeException $e) {
168
            $available_messages = false;
169
        } catch (ConnectionFailedException $e) {
170
            throw new GetMessagesFailedException("failed to fetch messages", 0, $e);
171
        }
172
173
        if ($available_messages !== false) {
174
            return collect($available_messages);
175
        }
176
177
        return collect();
178
    }
179
180
    /**
181
     * Count all available messages matching the current search criteria
182
     *
183
     * @return int
184
     * @throws GetMessagesFailedException
185
     */
186
    public function count() {
187
        return $this->search()->count();
188
    }
189
190
    /**
191
     * Fetch the current query and return all found messages
192
     *
193
     * @return MessageCollection
194
     * @throws GetMessagesFailedException
195
     */
196
    public function get() {
197
        $messages = MessageCollection::make([]);
198
199
        $available_messages = $this->search();
200
        try {
201
            if (($available_messages_count = $available_messages->count()) > 0) {
202
203
                $messages->total($available_messages_count);
204
205
                if ($this->fetch_order === 'desc') {
0 ignored issues
show
introduced by
The condition $this->fetch_order === 'desc' is always false.
Loading history...
206
                    $available_messages = $available_messages->reverse();
207
                }
208
209
                $query =& $this;
210
211
                $available_messages->forPage($this->page, $this->limit)->each(function($msgno, $msglist) use(&$messages, $options, $query) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $options seems to be never defined.
Loading history...
212
                    $message = $query->getMessage($msgno, $msglist);
213
                    switch ($options['message_key']){
214
                        case 'number':
215
                            $message_key = $message->getMessageNo();
216
                            break;
217
                        case 'list':
218
                            $message_key = $msglist;
219
                            break;
220
                        default:
221
                            $message_key = $message->getMessageId();
222
                            break;
223
224
                    }
225
                    $messages->put($message_key, $message);
226
                });
227
            }
228
229
            return $messages;
230
        } catch (\Exception $e) {
231
            throw new GetMessagesFailedException($e->getMessage(),0, $e);
232
        }
233
    }
234
235
    /**
236
     * Get a new Message instance
237
     * @param $msgno
238
     * @param null $msglist
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $msglist is correct as it would always require null to be passed?
Loading history...
239
     *
240
     * @return Message
241
     * @throws ConnectionFailedException
242
     * @throws RuntimeException
243
     * @throws InvalidMessageDateException
244
     * @throws MessageContentFetchingException
245
     * @throws MessageHeaderFetchingException
246
     * @throws \Webklex\PHPIMAP\Exceptions\EventNotFoundException
247
     */
248
    public function getMessage($msgno, $msglist = null){
249
        return new Message($msgno, $msglist, $this->getClient(), $this->getFetchOptions(), $this->getFetchBody(), $this->getFetchFlags());
0 ignored issues
show
Bug introduced by
$this->getFetchFlags() of type integer is incompatible with the type boolean expected by parameter $fetch_flags of Webklex\PHPIMAP\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

249
        return new Message($msgno, $msglist, $this->getClient(), $this->getFetchOptions(), $this->getFetchBody(), /** @scrutinizer ignore-type */ $this->getFetchFlags());
Loading history...
250
    }
251
252
    /**
253
     * Paginate the current query
254
     * @param int $per_page
255
     * @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...
256
     * @param string $page_name
257
     *
258
     * @return LengthAwarePaginator
259
     * @throws GetMessagesFailedException
260
     */
261
    public function paginate($per_page = 5, $page = null, $page_name = 'imap_page'){
262
        if (
263
               $page === null
264
            && isset($_GET[$page_name])
265
            && $_GET[$page_name] > 0
266
        ) {
267
            $this->page = intval($_GET[$page_name]);
268
        } elseif ($page > 0) {
269
            $this->page = $page;
270
        }
271
272
        $this->limit = $per_page;
273
274
        return $this->get()->paginate($per_page, $this->page, $page_name);
275
    }
276
277
    /**
278
     * Get the raw IMAP search query
279
     *
280
     * @return string
281
     */
282
    public function generate_query() {
283
        $query = '';
284
        $this->query->each(function($statement) use(&$query) {
285
            if (count($statement) == 1) {
286
                $query .= $statement[0];
287
            } else {
288
                if($statement[1] === null){
289
                    $query .= $statement[0];
290
                }else{
291
                    $query .= $statement[0].' "'.$statement[1].'"';
292
                }
293
            }
294
            $query .= ' ';
295
296
        });
297
298
        $this->raw_query = trim($query);
299
300
        return $this->raw_query;
301
    }
302
303
    /**
304
     * @return Client
305
     * @throws ConnectionFailedException
306
     */
307
    public function getClient() {
308
        $this->client->checkConnection();
309
        return $this->client;
310
    }
311
312
    /**
313
     * Set the limit and page for the current query
314
     * @param int $limit
315
     * @param int $page
316
     *
317
     * @return $this
318
     */
319
    public function limit($limit, $page = 1) {
320
        if($page >= 1) $this->page = $page;
321
        $this->limit = $limit;
322
323
        return $this;
324
    }
325
326
    /**
327
     * @return array
328
     */
329
    public function getQuery() {
330
        return $this->query;
331
    }
332
333
    /**
334
     * @param array $query
335
     * @return Query
336
     */
337
    public function setQuery($query) {
338
        $this->query = $query;
339
        return $this;
340
    }
341
342
    /**
343
     * @return string
344
     */
345
    public function getRawQuery() {
346
        return $this->raw_query;
347
    }
348
349
    /**
350
     * @param string $raw_query
351
     * @return Query
352
     */
353
    public function setRawQuery($raw_query) {
354
        $this->raw_query = $raw_query;
355
        return $this;
356
    }
357
358
    /**
359
     * @return string
360
     */
361
    public function getCharset() {
362
        return $this->charset;
363
    }
364
365
    /**
366
     * @param string $charset
367
     * @return Query
368
     */
369
    public function setCharset($charset) {
370
        $this->charset = $charset;
371
        return $this;
372
    }
373
374
    /**
375
     * @param Client $client
376
     * @return Query
377
     */
378
    public function setClient(Client $client) {
379
        $this->client = $client;
380
        return $this;
381
    }
382
383
    /**
384
     * @return int
385
     */
386
    public function getLimit() {
387
        return $this->limit;
388
    }
389
390
    /**
391
     * @param int $limit
392
     * @return Query
393
     */
394
    public function setLimit($limit) {
395
        $this->limit = $limit <= 0 ? null : $limit;
396
        return $this;
397
    }
398
399
    /**
400
     * @return int
401
     */
402
    public function getPage() {
403
        return $this->page;
404
    }
405
406
    /**
407
     * @param int $page
408
     * @return Query
409
     */
410
    public function setPage($page) {
411
        $this->page = $page;
412
        return $this;
413
    }
414
415
    /**
416
     * @param boolean $fetch_options
417
     * @return Query
418
     */
419
    public function setFetchOptions($fetch_options) {
420
        $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...
421
        return $this;
422
    }
423
424
    /**
425
     * @param boolean $fetch_options
426
     * @return Query
427
     */
428
    public function fetchOptions($fetch_options) {
429
        return $this->setFetchOptions($fetch_options);
430
    }
431
432
    /**
433
     * @return int
434
     */
435
    public function getFetchOptions() {
436
        return $this->fetch_options;
437
    }
438
439
    /**
440
     * @return boolean
441
     */
442
    public function getFetchBody() {
443
        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...
444
    }
445
446
    /**
447
     * @param boolean $fetch_body
448
     * @return Query
449
     */
450
    public function setFetchBody($fetch_body) {
451
        $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...
452
        return $this;
453
    }
454
455
    /**
456
     * @param boolean $fetch_body
457
     * @return Query
458
     */
459
    public function fetchBody($fetch_body) {
460
        return $this->setFetchBody($fetch_body);
461
    }
462
463
    /**
464
     * @return int
465
     */
466
    public function getFetchFlags() {
467
        return $this->fetch_flags;
468
    }
469
470
    /**
471
     * @param int $fetch_flags
472
     * @return Query
473
     */
474
    public function setFetchFlags($fetch_flags) {
475
        $this->fetch_flags = $fetch_flags;
476
        return $this;
477
    }
478
479
    /**
480
     * @param string $fetch_order
481
     * @return Query
482
     */
483
    public function setFetchOrder($fetch_order) {
484
        $fetch_order = strtolower($fetch_order);
485
486
        if (in_array($fetch_order, ['asc', 'desc'])) {
487
            $this->fetch_order = $fetch_order;
0 ignored issues
show
Documentation Bug introduced by
The property $fetch_order was declared of type integer, but $fetch_order is of type string. 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...
488
        }
489
490
        return $this;
491
    }
492
493
    /**
494
     * @param string $fetch_order
495
     * @return Query
496
     */
497
    public function fetchOrder($fetch_order) {
498
        return $this->setFetchOrder($fetch_order);
499
    }
500
501
    /**
502
     * @return string
503
     */
504
    public function getFetchOrder() {
505
        return $this->fetch_order;
506
    }
507
508
    /**
509
     * @return Query
510
     */
511
    public function setFetchOrderAsc() {
512
        return $this->setFetchOrder('asc');
513
    }
514
515
    /**
516
     * @return Query
517
     */
518
    public function fetchOrderAsc($fetch_order) {
0 ignored issues
show
Unused Code introduced by
The parameter $fetch_order 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

518
    public function fetchOrderAsc(/** @scrutinizer ignore-unused */ $fetch_order) {

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...
519
        return $this->setFetchOrderAsc();
520
    }
521
522
    /**
523
     * @return Query
524
     */
525
    public function setFetchOrderDesc() {
526
        return $this->setFetchOrder('desc');
527
    }
528
529
    /**
530
     * @return Query
531
     */
532
    public function fetchOrderDesc($fetch_order) {
0 ignored issues
show
Unused Code introduced by
The parameter $fetch_order 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

532
    public function fetchOrderDesc(/** @scrutinizer ignore-unused */ $fetch_order) {

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...
533
        return $this->setFetchOrderDesc();
534
    }
535
}
536