Completed
Pull Request — master (#14)
by
unknown
02:09
created

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