Completed
Push — master ( 230e50...aad7db )
by Malte
02:32
created

Query::setFetchOrderAsc()   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
                $message_key = ClientManager::get('options.message_key');
210
                $query =& $this;
211
212
                $available_messages->forPage($this->page, $this->limit)->each(function($msgno, $msglist) use(&$messages, $message_key, $query) {
213
                    $message = $query->getMessage($msgno, $msglist);
214
                    switch ($message_key){
215
                        case 'number':
216
                            $message_key = $message->getMessageNo();
217
                            break;
218
                        case 'list':
219
                            $message_key = $msglist;
220
                            break;
221
                        default:
222
                            $message_key = $message->getMessageId();
223
                            break;
224
225
                    }
226
                    $messages->put($message_key, $message);
227
                });
228
            }
229
230
            return $messages;
231
        } catch (\Exception $e) {
232
            throw new GetMessagesFailedException($e->getMessage(),0, $e);
233
        }
234
    }
235
236
    /**
237
     * Get a new Message instance
238
     * @param $msgno
239
     * @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...
240
     *
241
     * @return Message
242
     * @throws ConnectionFailedException
243
     * @throws RuntimeException
244
     * @throws InvalidMessageDateException
245
     * @throws MessageContentFetchingException
246
     * @throws MessageHeaderFetchingException
247
     * @throws \Webklex\PHPIMAP\Exceptions\EventNotFoundException
248
     */
249
    public function getMessage($msgno, $msglist = null){
250
        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

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

519
    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...
520
        return $this->setFetchOrderAsc();
521
    }
522
523
    /**
524
     * @return Query
525
     */
526
    public function setFetchOrderDesc() {
527
        return $this->setFetchOrder('desc');
528
    }
529
530
    /**
531
     * @return Query
532
     */
533
    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

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