Passed
Push — master ( 1bc95e...277078 )
by Malte
03:34
created

Query::search()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 18
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 8
c 0
b 0
f 0
nc 4
nop 0
dl 0
loc 18
rs 10
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
     * Fetch the current query and return all found messages
170
     *
171
     * @return MessageCollection
172
     * @throws GetMessagesFailedException
173
     */
174
    public function get() {
175
        $messages = MessageCollection::make([]);
176
177
        try {
178
            $available_messages = $this->search();
179
            $available_messages_count = $available_messages->count();
180
181
            if ($available_messages_count > 0) {
182
183
                $messages->total($available_messages_count);
184
185
                $options = config('imap.options');
186
187
                if(strtolower($options['fetch_order']) === 'desc'){
188
                    $available_messages = $available_messages->reverse();
189
                }
190
191
                $query =& $this;
192
193
                $available_messages->forPage($this->page, $this->limit)->each(function($msgno, $msglist) use(&$messages, $options, $query) {
194
                    $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

194
                    $oMessage = new Message($msgno, $msglist, $query->getClient(), $query->getFetchOptions(), $query->getFetchBody(), $query->getFetchAttachment(), /** @scrutinizer ignore-type */ $query->getFetchFlags());
Loading history...
195
                    switch ($options['message_key']){
196
                        case 'number':
197
                            $message_key = $oMessage->getMessageNo();
198
                            break;
199
                        case 'list':
200
                            $message_key = $msglist;
201
                            break;
202
                        default:
203
                            $message_key = $oMessage->getMessageId();
204
                            break;
205
206
                    }
207
                    $messages->put($message_key, $oMessage);
208
                });
209
            }
210
211
            return $messages;
212
        } catch (\Exception $e) {
213
            throw new GetMessagesFailedException($e->getMessage());
214
        }
215
    }
216
217
    /**
218
     * Paginate the current query
219
     * @param int $per_page
220
     * @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...
221
     * @param string $page_name
222
     *
223
     * @return \Illuminate\Pagination\LengthAwarePaginator
224
     * @throws GetMessagesFailedException
225
     */
226
    public function paginate($per_page = 5, $page = null, $page_name = 'imap_page'){
227
        $this->page = $page > $this->page ? $page : $this->page;
228
        $this->limit = $per_page;
229
230
        return $this->get()->paginate($per_page, $this->page, $page_name);
231
    }
232
233
    /**
234
     * Get the raw IMAP search query
235
     *
236
     * @return string
237
     */
238
    public function generate_query() {
239
        $query = '';
240
        $this->query->each(function($statement) use(&$query) {
241
            if (count($statement) == 1) {
242
                $query .= $statement[0];
243
            } else {
244
                if($statement[1] === null){
245
                    $query .= $statement[0];
246
                }else{
247
                    $query .= $statement[0].' "'.$statement[1].'"';
248
                }
249
            }
250
            $query .= ' ';
251
252
        });
253
254
        $this->raw_query = trim($query);
255
256
        return $this->raw_query;
257
    }
258
259
    /**
260
     * @return Client
261
     * @throws \Webklex\IMAP\Exceptions\ConnectionFailedException
262
     */
263
    public function getClient() {
264
        $this->client->checkConnection();
265
        return $this->client;
266
    }
267
268
    /**
269
     * Set the limit and page for the current query
270
     * @param int $limit
271
     * @param int $page
272
     *
273
     * @return $this
274
     */
275
    public function limit($limit, $page = 1) {
276
        if($page >= 1) $this->page = $page;
277
        $this->limit = $limit;
278
279
        return $this;
280
    }
281
282
    /**
283
     * @return array
284
     */
285
    public function getQuery() {
286
        return $this->query;
287
    }
288
289
    /**
290
     * @param array $query
291
     * @return Query
292
     */
293
    public function setQuery($query) {
294
        $this->query = $query;
295
        return $this;
296
    }
297
298
    /**
299
     * @return string
300
     */
301
    public function getRawQuery() {
302
        return $this->raw_query;
303
    }
304
305
    /**
306
     * @param string $raw_query
307
     * @return Query
308
     */
309
    public function setRawQuery($raw_query) {
310
        $this->raw_query = $raw_query;
311
        return $this;
312
    }
313
314
    /**
315
     * @return string
316
     */
317
    public function getCharset() {
318
        return $this->charset;
319
    }
320
321
    /**
322
     * @param string $charset
323
     * @return Query
324
     */
325
    public function setCharset($charset) {
326
        $this->charset = $charset;
327
        return $this;
328
    }
329
330
    /**
331
     * @param Client $client
332
     * @return Query
333
     */
334
    public function setClient(Client $client) {
335
        $this->client = $client;
336
        return $this;
337
    }
338
339
    /**
340
     * @return int
341
     */
342
    public function getLimit() {
343
        return $this->limit;
344
    }
345
346
    /**
347
     * @param int $limit
348
     * @return Query
349
     */
350
    public function setLimit($limit) {
351
        $this->limit = $limit <= 0 ? null : $limit;
352
        return $this;
353
    }
354
355
    /**
356
     * @return int
357
     */
358
    public function getPage() {
359
        return $this->page;
360
    }
361
362
    /**
363
     * @param int $page
364
     * @return Query
365
     */
366
    public function setPage($page) {
367
        $this->page = $page;
368
        return $this;
369
    }
370
371
    /**
372
     * @param boolean $fetch_options
373
     * @return Query
374
     */
375
    public function setFetchOptions($fetch_options) {
376
        $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...
377
        return $this;
378
    }
379
380
    /**
381
     * @param boolean $fetch_options
382
     * @return Query
383
     */
384
    public function fetchOptions($fetch_options) {
385
        return $this->setFetchOptions($fetch_options);
386
    }
387
388
    /**
389
     * @return int
390
     */
391
    public function getFetchOptions() {
392
        return $this->fetch_options;
393
    }
394
395
    /**
396
     * @return boolean
397
     */
398
    public function getFetchBody() {
399
        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...
400
    }
401
402
    /**
403
     * @param boolean $fetch_body
404
     * @return Query
405
     */
406
    public function setFetchBody($fetch_body) {
407
        $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...
408
        return $this;
409
    }
410
411
    /**
412
     * @param boolean $fetch_body
413
     * @return Query
414
     */
415
    public function fetchBody($fetch_body) {
416
        return $this->setFetchBody($fetch_body);
417
    }
418
419
    /**
420
     * @return boolean
421
     */
422
    public function getFetchAttachment() {
423
        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...
424
    }
425
426
    /**
427
     * @param boolean $fetch_attachment
428
     * @return Query
429
     */
430
    public function setFetchAttachment($fetch_attachment) {
431
        $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...
432
        return $this;
433
    }
434
435
    /**
436
     * @param boolean $fetch_attachment
437
     * @return Query
438
     */
439
    public function fetchAttachment($fetch_attachment) {
440
        return $this->setFetchAttachment($fetch_attachment);
441
    }
442
443
    /**
444
     * @return int
445
     */
446
    public function getFetchFlags() {
447
        return $this->fetch_flags;
448
    }
449
450
    /**
451
     * @param int $fetch_flags
452
     * @return Query
453
     */
454
    public function setFetchFlags($fetch_flags) {
455
        $this->fetch_flags = $fetch_flags;
456
        return $this;
457
    }
458
}
459