Completed
Push — master ( 2eeef0...12dd9c )
by Malte
01:50
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 string $date_format */
66
    protected $date_format;
67
68
    /**
69
     * Query constructor.
70
     * @param Client $client
71
     * @param string $charset
72
     */
73
    public function __construct(Client $client, $charset = 'UTF-8') {
74
        $this->setClient($client);
75
76
        if(ClientManager::get('options.fetch') === IMAP::FT_PEEK) $this->leaveUnread();
77
78
        $this->date_format = ClientManager::get('date_format', 'd M y');
79
80
        $this->charset = $charset;
81
        $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...
82
        $this->boot();
83
    }
84
85
    /**
86
     * Instance boot method for additional functionality
87
     */
88
    protected function boot(){}
89
90
    /**
91
     * Parse a given value
92
     * @param mixed $value
93
     *
94
     * @return string
95
     */
96
    protected function parse_value($value){
97
        switch(true){
98
            case $value instanceof \Carbon\Carbon:
99
                $value = $value->format($this->date_format);
100
                break;
101
        }
102
103
        return (string) $value;
104
    }
105
106
    /**
107
     * Check if a given date is a valid carbon object and if not try to convert it
108
     * @param $date
109
     *
110
     * @return Carbon
111
     * @throws MessageSearchValidationException
112
     */
113
    protected function parse_date($date) {
114
        if($date instanceof \Carbon\Carbon) return $date;
115
116
        try {
117
            $date = Carbon::parse($date);
118
        } catch (\Exception $e) {
119
            throw new MessageSearchValidationException();
120
        }
121
122
        return $date;
123
    }
124
125
    /**
126
     * Don't mark messages as read when fetching
127
     *
128
     * @return $this
129
     */
130
    public function leaveUnread() {
131
        $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

131
        $this->setFetchOptions(/** @scrutinizer ignore-type */ IMAP::FT_PEEK);
Loading history...
132
133
        return $this;
134
    }
135
136
    /**
137
     * Mark all messages as read when fetching
138
     *
139
     * @return $this
140
     */
141
    public function markAsRead() {
142
        $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

142
        $this->setFetchOptions(/** @scrutinizer ignore-type */ IMAP::FT_UID);
Loading history...
143
144
        return $this;
145
    }
146
147
    /**
148
     * Perform an imap search request
149
     *
150
     * @return Collection
151
     * @throws GetMessagesFailedException
152
     */
153
    protected function search(){
154
        $this->generate_query();
155
156
        try {
157
            $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

157
            $available_messages = $this->client->getConnection()->/** @scrutinizer ignore-call */ search([$this->getRawQuery()]);
Loading history...
158
        } catch (RuntimeException $e) {
159
            $available_messages = false;
160
        } catch (ConnectionFailedException $e) {
161
            throw new GetMessagesFailedException("failed to fetch messages", 0, $e);
162
        }
163
164
        if ($available_messages !== false) {
165
            return collect($available_messages);
166
        }
167
168
        return collect();
169
    }
170
171
    /**
172
     * Count all available messages matching the current search criteria
173
     *
174
     * @return int
175
     * @throws GetMessagesFailedException
176
     */
177
    public function count() {
178
        return $this->search()->count();
179
    }
180
181
    /**
182
     * Fetch the current query and return all found messages
183
     *
184
     * @return MessageCollection
185
     * @throws GetMessagesFailedException
186
     */
187
    public function get() {
188
        $messages = MessageCollection::make([]);
189
190
        $available_messages = $this->search();
191
        try {
192
            if (($available_messages_count = $available_messages->count()) > 0) {
193
194
                $messages->total($available_messages_count);
195
196
                $options = ClientManager::get('options');
197
198
                if(strtolower($options['fetch_order']) === 'desc'){
199
                    $available_messages = $available_messages->reverse();
200
                }
201
202
                $query =& $this;
203
204
                $available_messages->forPage($this->page, $this->limit)->each(function($msgno, $msglist) use(&$messages, $options, $query) {
205
                    $message = $query->getMessage($msgno, $msglist);
206
                    switch ($options['message_key']){
207
                        case 'number':
208
                            $message_key = $message->getMessageNo();
209
                            break;
210
                        case 'list':
211
                            $message_key = $msglist;
212
                            break;
213
                        default:
214
                            $message_key = $message->getMessageId();
215
                            break;
216
217
                    }
218
                    $messages->put($message_key, $message);
219
                });
220
            }
221
222
            return $messages;
223
        } catch (\Exception $e) {
224
            throw new GetMessagesFailedException($e->getMessage(),0, $e);
225
        }
226
    }
227
228
    /**
229
     * Get a new Message instance
230
     * @param $msgno
231
     * @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...
232
     *
233
     * @return Message
234
     * @throws ConnectionFailedException
235
     * @throws RuntimeException
236
     * @throws InvalidMessageDateException
237
     * @throws MessageContentFetchingException
238
     * @throws MessageHeaderFetchingException
239
     */
240
    public function getMessage($msgno, $msglist = null){
241
        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

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