Passed
Push — master ( 4b6ca8...7b77e0 )
by Malte
02:34
created

Query::getQuery()   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 string $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';
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') {
206
                    $available_messages = $available_messages->reverse();
207
                }
208
209
                $message_key = ClientManager::get('options.message_key');
210
211
                $msgnos = $available_messages->forPage($this->page, $this->limit)->toArray();
212
213
                $raw_headers = $this->client->getConnection()->headers($msgnos);
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

213
                $raw_headers = $this->client->getConnection()->/** @scrutinizer ignore-call */ headers($msgnos);
Loading history...
214
                $raw_contents = [];
215
                $raw_flags = [];
216
217
                if ($this->getFetchBody()) {
218
                    $raw_contents = $this->client->getConnection()->content($msgnos);
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

218
                    $raw_contents = $this->client->getConnection()->/** @scrutinizer ignore-call */ content($msgnos);
Loading history...
219
                }
220
                if ($this->getFetchFlags()) {
221
                    $raw_flags = $this->client->getConnection()->flags($msgnos);
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

221
                    $raw_flags = $this->client->getConnection()->/** @scrutinizer ignore-call */ flags($msgnos);
Loading history...
222
                }
223
224
                $msglist = 0;
225
                foreach ($raw_headers as $msgno => $raw_header) {
226
                    $raw_content = isset($raw_contents[$msgno]) ? $raw_contents[$msgno] : "";
227
                    $raw_flag = isset($raw_flags[$msgno]) ? $raw_flags[$msgno] : [];
228
229
                    $message = Message::make($msgno, $msglist, $this->getClient(), $raw_header, $raw_content, $raw_flag, $this->getFetchOptions());
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

229
                    $message = Message::make($msgno, $msglist, $this->getClient(), $raw_header, $raw_content, /** @scrutinizer ignore-type */ $raw_flag, $this->getFetchOptions());
Loading history...
230
                    switch ($message_key){
231
                        case 'number':
232
                            $message_key = $message->getMessageNo();
233
                            break;
234
                        case 'list':
235
                            $message_key = $msglist;
236
                            break;
237
                        default:
238
                            $message_key = $message->getMessageId();
239
                            break;
240
241
                    }
242
                    $messages->put($message_key, $message);
243
                    $msglist++;
244
                }
245
            }
246
247
            return $messages;
248
        } catch (\Exception $e) {
249
            throw new GetMessagesFailedException($e->getMessage(),0, $e);
250
        }
251
    }
252
253
    /**
254
     * Get a new Message instance
255
     * @param $msgno
256
     * @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...
257
     *
258
     * @return Message
259
     * @throws ConnectionFailedException
260
     * @throws RuntimeException
261
     * @throws InvalidMessageDateException
262
     * @throws MessageContentFetchingException
263
     * @throws MessageHeaderFetchingException
264
     * @throws \Webklex\PHPIMAP\Exceptions\EventNotFoundException
265
     */
266
    public function getMessage($msgno, $msglist = null){
267
        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

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

536
    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...
537
        return $this->setFetchOrderAsc();
538
    }
539
540
    /**
541
     * @return Query
542
     */
543
    public function setFetchOrderDesc() {
544
        return $this->setFetchOrder('desc');
545
    }
546
547
    /**
548
     * @return Query
549
     */
550
    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

550
    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...
551
        return $this->setFetchOrderDesc();
552
    }
553
}
554