Passed
Push — master ( abd91e...c0bdb9 )
by Malte
02:08
created

Query::setRawQuery()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 2
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 1
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 $sequence */
66
    protected $sequence = IMAP::NIL;
67
68
    /** @var string $fetch_order */
69
    protected $fetch_order;
70
71
    /** @var string $date_format */
72
    protected $date_format;
73
74
    /**
75
     * Query constructor.
76
     * @param Client $client
77
     * @param string $charset
78
     */
79
    public function __construct(Client $client, $charset = 'UTF-8') {
80
        $this->setClient($client);
81
82
        $this->sequence = ClientManager::get('options.sequence', IMAP::ST_MSGN);
83
        if(ClientManager::get('options.fetch') === IMAP::FT_PEEK) $this->leaveUnread();
84
85
        if (ClientManager::get('options.fetch_order') === 'desc') {
86
            $this->fetch_order = 'desc';
87
        } else {
88
            $this->fetch_order = 'asc';
89
        }
90
91
        $this->date_format = ClientManager::get('date_format', 'd M y');
92
93
        $this->charset = $charset;
94
        $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...
95
        $this->boot();
96
    }
97
98
    /**
99
     * Instance boot method for additional functionality
100
     */
101
    protected function boot(){}
102
103
    /**
104
     * Parse a given value
105
     * @param mixed $value
106
     *
107
     * @return string
108
     */
109
    protected function parse_value($value){
110
        switch(true){
111
            case $value instanceof \Carbon\Carbon:
112
                $value = $value->format($this->date_format);
113
                break;
114
        }
115
116
        return (string) $value;
117
    }
118
119
    /**
120
     * Check if a given date is a valid carbon object and if not try to convert it
121
     * @param $date
122
     *
123
     * @return Carbon
124
     * @throws MessageSearchValidationException
125
     */
126
    protected function parse_date($date) {
127
        if($date instanceof \Carbon\Carbon) return $date;
128
129
        try {
130
            $date = Carbon::parse($date);
131
        } catch (\Exception $e) {
132
            throw new MessageSearchValidationException();
133
        }
134
135
        return $date;
136
    }
137
138
    /**
139
     * Don't mark messages as read when fetching
140
     *
141
     * @return $this
142
     */
143
    public function leaveUnread() {
144
        $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

144
        $this->setFetchOptions(/** @scrutinizer ignore-type */ IMAP::FT_PEEK);
Loading history...
145
146
        return $this;
147
    }
148
149
    /**
150
     * Mark all messages as read when fetching
151
     *
152
     * @return $this
153
     */
154
    public function markAsRead() {
155
        $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

155
        $this->setFetchOptions(/** @scrutinizer ignore-type */ IMAP::FT_UID);
Loading history...
156
157
        return $this;
158
    }
159
160
    /**
161
     * Set the sequence type
162
     * @param int $sequence
163
     *
164
     * @return $this
165
     */
166
    public function setSequence($sequence) {
167
        $this->sequence = $sequence != IMAP::ST_MSGN ? IMAP::ST_UID : $sequence;
168
169
        return $this;
170
    }
171
172
    /**
173
     * Perform an imap search request
174
     *
175
     * @return Collection
176
     * @throws GetMessagesFailedException
177
     */
178
    protected function search(){
179
        $this->generate_query();
180
181
        try {
182
            $available_messages = $this->client->getConnection()->search([$this->getRawQuery()], $this->sequence == IMAP::ST_UID);
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

182
            $available_messages = $this->client->getConnection()->/** @scrutinizer ignore-call */ search([$this->getRawQuery()], $this->sequence == IMAP::ST_UID);
Loading history...
183
        } catch (RuntimeException $e) {
184
            $available_messages = false;
185
        } catch (ConnectionFailedException $e) {
186
            throw new GetMessagesFailedException("failed to fetch messages", 0, $e);
187
        }
188
189
        if ($available_messages !== false) {
190
            return collect($available_messages);
191
        }
192
193
        return collect();
194
    }
195
196
    /**
197
     * Count all available messages matching the current search criteria
198
     *
199
     * @return int
200
     * @throws GetMessagesFailedException
201
     */
202
    public function count() {
203
        return $this->search()->count();
204
    }
205
206
    /**
207
     * Fetch the current query and return all found messages
208
     *
209
     * @return MessageCollection
210
     * @throws GetMessagesFailedException
211
     */
212
    public function get() {
213
        $messages = MessageCollection::make([]);
214
215
        $available_messages = $this->search();
216
        try {
217
            if (($available_messages_count = $available_messages->count()) > 0) {
218
219
                $messages->total($available_messages_count);
220
221
                if ($this->fetch_order === 'desc') {
222
                    $available_messages = $available_messages->reverse();
223
                }
224
225
                $message_key = ClientManager::get('options.message_key');
226
227
                $uids = $available_messages->forPage($this->page, $this->limit)->toArray();
228
229
                $raw_flags = $this->client->getConnection()->flags($uids, $this->sequence == IMAP::ST_UID);
0 ignored issues
show
Bug introduced by
The method flags() 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

229
                $raw_flags = $this->client->getConnection()->/** @scrutinizer ignore-call */ flags($uids, $this->sequence == IMAP::ST_UID);
Loading history...
230
                $raw_headers = $this->client->getConnection()->headers($uids, "RFC822", $this->sequence == IMAP::ST_UID);
0 ignored issues
show
Bug introduced by
The method headers() 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

230
                $raw_headers = $this->client->getConnection()->/** @scrutinizer ignore-call */ headers($uids, "RFC822", $this->sequence == IMAP::ST_UID);
Loading history...
231
232
                $raw_contents = [];
233
                if ($this->getFetchBody()) {
234
                    $raw_contents = $this->client->getConnection()->content($uids, "RFC822", $this->sequence == IMAP::ST_UID);
0 ignored issues
show
Bug introduced by
The method content() 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

234
                    $raw_contents = $this->client->getConnection()->/** @scrutinizer ignore-call */ content($uids, "RFC822", $this->sequence == IMAP::ST_UID);
Loading history...
235
                }
236
237
                $msglist = 0;
238
                foreach ($raw_headers as $uid => $raw_header) {
239
                    $raw_content = isset($raw_contents[$uid]) ? $raw_contents[$uid] : "";
240
                    $raw_flag = isset($raw_flags[$uid]) ? $raw_flags[$uid] : [];
241
242
                    $message = Message::make($uid, $msglist, $this->getClient(), $raw_header, $raw_content, $raw_flag, $this->getFetchOptions(), $this->sequence);
0 ignored issues
show
Bug introduced by
It seems like $raw_flag can also be of type array; however, parameter $raw_flags of Webklex\PHPIMAP\Message::make() does only seem to accept string, 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

242
                    $message = Message::make($uid, $msglist, $this->getClient(), $raw_header, $raw_content, /** @scrutinizer ignore-type */ $raw_flag, $this->getFetchOptions(), $this->sequence);
Loading history...
243
                    switch ($message_key){
244
                        case 'number':
245
                            $message_key = $message->getMessageNo();
246
                            break;
247
                        case 'list':
248
                            $message_key = $msglist;
249
                            break;
250
                        case 'uid':
251
                            $message_key = $message->getUid();
252
                            break;
253
                        default:
254
                            $message_key = $message->getMessageId();
255
                            break;
256
257
                    }
258
                    $messages->put($message_key, $message);
259
                    $msglist++;
260
                }
261
            }
262
263
            return $messages;
264
        } catch (\Exception $e) {
265
            throw new GetMessagesFailedException($e->getMessage(),0, $e);
266
        }
267
    }
268
269
    /**
270
     * Get a new Message instance
271
     * @param int $uid
272
     * @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...
273
     * @param null $sequence
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $sequence is correct as it would always require null to be passed?
Loading history...
274
     *
275
     * @return Message
276
     * @throws ConnectionFailedException
277
     * @throws RuntimeException
278
     * @throws InvalidMessageDateException
279
     * @throws MessageContentFetchingException
280
     * @throws MessageHeaderFetchingException
281
     * @throws \Webklex\PHPIMAP\Exceptions\EventNotFoundException
282
     */
283
    public function getMessage($uid, $msglist = null, $sequence = null){
284
        return new Message($uid, $msglist, $this->getClient(), $this->getFetchOptions(), $this->getFetchBody(), $this->getFetchFlags(), $sequence ? $sequence : $this->sequence);
0 ignored issues
show
introduced by
$sequence is of type null, thus it always evaluated to false.
Loading history...
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

284
        return new Message($uid, $msglist, $this->getClient(), $this->getFetchOptions(), $this->getFetchBody(), /** @scrutinizer ignore-type */ $this->getFetchFlags(), $sequence ? $sequence : $this->sequence);
Loading history...
285
    }
286
287
    /**
288
     * Get a message by its message number
289
     * @param $msgn
290
     * @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...
291
     *
292
     * @return Message
293
     * @throws ConnectionFailedException
294
     * @throws InvalidMessageDateException
295
     * @throws MessageContentFetchingException
296
     * @throws MessageHeaderFetchingException
297
     * @throws RuntimeException
298
     * @throws \Webklex\PHPIMAP\Exceptions\EventNotFoundException
299
     */
300
    public function getMessageByMsgn($msgn, $msglist = null){
301
        return $this->getMessage($msgn, $msglist, IMAP::ST_MSGN);
302
    }
303
304
    /**
305
     * Get a message by its uid
306
     * @param $uid
307
     *
308
     * @return Message
309
     * @throws ConnectionFailedException
310
     * @throws InvalidMessageDateException
311
     * @throws MessageContentFetchingException
312
     * @throws MessageHeaderFetchingException
313
     * @throws RuntimeException
314
     * @throws \Webklex\PHPIMAP\Exceptions\EventNotFoundException
315
     */
316
    public function getMessageByUid($uid){
317
        return $this->getMessage($uid, null, IMAP::ST_UID);
318
    }
319
320
    /**
321
     * Paginate the current query
322
     * @param int $per_page
323
     * @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...
324
     * @param string $page_name
325
     *
326
     * @return LengthAwarePaginator
327
     * @throws GetMessagesFailedException
328
     */
329
    public function paginate($per_page = 5, $page = null, $page_name = 'imap_page'){
330
        if (
331
               $page === null
332
            && isset($_GET[$page_name])
333
            && $_GET[$page_name] > 0
334
        ) {
335
            $this->page = intval($_GET[$page_name]);
336
        } elseif ($page > 0) {
337
            $this->page = $page;
338
        }
339
340
        $this->limit = $per_page;
341
342
        return $this->get()->paginate($per_page, $this->page, $page_name, true);
343
    }
344
345
    /**
346
     * Get the raw IMAP search query
347
     *
348
     * @return string
349
     */
350
    public function generate_query() {
351
        $query = '';
352
        $this->query->each(function($statement) use(&$query) {
353
            if (count($statement) == 1) {
354
                $query .= $statement[0];
355
            } else {
356
                if($statement[1] === null){
357
                    $query .= $statement[0];
358
                }else{
359
                    $query .= $statement[0].' "'.$statement[1].'"';
360
                }
361
            }
362
            $query .= ' ';
363
364
        });
365
366
        $this->raw_query = trim($query);
367
368
        return $this->raw_query;
369
    }
370
371
    /**
372
     * @return Client
373
     * @throws ConnectionFailedException
374
     */
375
    public function getClient() {
376
        $this->client->checkConnection();
377
        return $this->client;
378
    }
379
380
    /**
381
     * Set the limit and page for the current query
382
     * @param int $limit
383
     * @param int $page
384
     *
385
     * @return $this
386
     */
387
    public function limit($limit, $page = 1) {
388
        if($page >= 1) $this->page = $page;
389
        $this->limit = $limit;
390
391
        return $this;
392
    }
393
394
    /**
395
     * @return array
396
     */
397
    public function getQuery() {
398
        return $this->query;
399
    }
400
401
    /**
402
     * @param array $query
403
     * @return Query
404
     */
405
    public function setQuery($query) {
406
        $this->query = $query;
407
        return $this;
408
    }
409
410
    /**
411
     * @return string
412
     */
413
    public function getRawQuery() {
414
        return $this->raw_query;
415
    }
416
417
    /**
418
     * @param string $raw_query
419
     * @return Query
420
     */
421
    public function setRawQuery($raw_query) {
422
        $this->raw_query = $raw_query;
423
        return $this;
424
    }
425
426
    /**
427
     * @return string
428
     */
429
    public function getCharset() {
430
        return $this->charset;
431
    }
432
433
    /**
434
     * @param string $charset
435
     * @return Query
436
     */
437
    public function setCharset($charset) {
438
        $this->charset = $charset;
439
        return $this;
440
    }
441
442
    /**
443
     * @param Client $client
444
     * @return Query
445
     */
446
    public function setClient(Client $client) {
447
        $this->client = $client;
448
        return $this;
449
    }
450
451
    /**
452
     * @return int
453
     */
454
    public function getLimit() {
455
        return $this->limit;
456
    }
457
458
    /**
459
     * @param int $limit
460
     * @return Query
461
     */
462
    public function setLimit($limit) {
463
        $this->limit = $limit <= 0 ? null : $limit;
464
        return $this;
465
    }
466
467
    /**
468
     * @return int
469
     */
470
    public function getPage() {
471
        return $this->page;
472
    }
473
474
    /**
475
     * @param int $page
476
     * @return Query
477
     */
478
    public function setPage($page) {
479
        $this->page = $page;
480
        return $this;
481
    }
482
483
    /**
484
     * @param boolean $fetch_options
485
     * @return Query
486
     */
487
    public function setFetchOptions($fetch_options) {
488
        $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...
489
        return $this;
490
    }
491
492
    /**
493
     * @param boolean $fetch_options
494
     * @return Query
495
     */
496
    public function fetchOptions($fetch_options) {
497
        return $this->setFetchOptions($fetch_options);
498
    }
499
500
    /**
501
     * @return int
502
     */
503
    public function getFetchOptions() {
504
        return $this->fetch_options;
505
    }
506
507
    /**
508
     * @return boolean
509
     */
510
    public function getFetchBody() {
511
        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...
512
    }
513
514
    /**
515
     * @param boolean $fetch_body
516
     * @return Query
517
     */
518
    public function setFetchBody($fetch_body) {
519
        $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...
520
        return $this;
521
    }
522
523
    /**
524
     * @param boolean $fetch_body
525
     * @return Query
526
     */
527
    public function fetchBody($fetch_body) {
528
        return $this->setFetchBody($fetch_body);
529
    }
530
531
    /**
532
     * @return int
533
     */
534
    public function getFetchFlags() {
535
        return $this->fetch_flags;
536
    }
537
538
    /**
539
     * @param int $fetch_flags
540
     * @return Query
541
     */
542
    public function setFetchFlags($fetch_flags) {
543
        $this->fetch_flags = $fetch_flags;
544
        return $this;
545
    }
546
547
    /**
548
     * @param string $fetch_order
549
     * @return Query
550
     */
551
    public function setFetchOrder($fetch_order) {
552
        $fetch_order = strtolower($fetch_order);
553
554
        if (in_array($fetch_order, ['asc', 'desc'])) {
555
            $this->fetch_order = $fetch_order;
556
        }
557
558
        return $this;
559
    }
560
561
    /**
562
     * @param string $fetch_order
563
     * @return Query
564
     */
565
    public function fetchOrder($fetch_order) {
566
        return $this->setFetchOrder($fetch_order);
567
    }
568
569
    /**
570
     * @return string
571
     */
572
    public function getFetchOrder() {
573
        return $this->fetch_order;
574
    }
575
576
    /**
577
     * @return Query
578
     */
579
    public function setFetchOrderAsc() {
580
        return $this->setFetchOrder('asc');
581
    }
582
583
    /**
584
     * @return Query
585
     */
586
    public function fetchOrderAsc() {
587
        return $this->setFetchOrderAsc();
588
    }
589
590
    /**
591
     * @return Query
592
     */
593
    public function setFetchOrderDesc() {
594
        return $this->setFetchOrder('desc');
595
    }
596
597
    /**
598
     * @return Query
599
     */
600
    public function fetchOrderDesc() {
601
        return $this->setFetchOrderDesc();
602
    }
603
}
604