Passed
Push — master ( 17da55...52e12c )
by Malte
02:19
created

Query::setLimit()   A

Complexity

Conditions 2
Paths 2

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 2
nc 2
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 Exception;
17
use Illuminate\Pagination\LengthAwarePaginator;
18
use Illuminate\Support\Collection;
19
use ReflectionException;
20
use Webklex\PHPIMAP\Client;
21
use Webklex\PHPIMAP\ClientManager;
22
use Webklex\PHPIMAP\Exceptions\ConnectionFailedException;
23
use Webklex\PHPIMAP\Exceptions\EventNotFoundException;
24
use Webklex\PHPIMAP\Exceptions\GetMessagesFailedException;
25
use Webklex\PHPIMAP\Exceptions\InvalidMessageDateException;
26
use Webklex\PHPIMAP\Exceptions\MessageContentFetchingException;
27
use Webklex\PHPIMAP\Exceptions\MessageFlagException;
28
use Webklex\PHPIMAP\Exceptions\MessageHeaderFetchingException;
29
use Webklex\PHPIMAP\Exceptions\MessageNotFoundException;
30
use Webklex\PHPIMAP\Exceptions\MessageSearchValidationException;
31
use Webklex\PHPIMAP\Exceptions\RuntimeException;
32
use Webklex\PHPIMAP\IMAP;
33
use Webklex\PHPIMAP\Message;
34
use Webklex\PHPIMAP\Support\MessageCollection;
35
36
/**
37
 * Class Query
38
 *
39
 * @package Webklex\PHPIMAP\Query
40
 */
41
class Query {
42
43
    /** @var array $query */
44
    protected $query;
45
46
    /** @var string $raw_query */
47
    protected $raw_query;
48
49
    /** @var string $charset */
50
    protected $charset;
51
52
    /** @var Client $client */
53
    protected $client;
54
55
    /** @var int $limit */
56
    protected $limit = null;
57
58
    /** @var int $page */
59
    protected $page = 1;
60
61
    /** @var int $fetch_options */
62
    protected $fetch_options = null;
63
64
    /** @var int $fetch_body */
65
    protected $fetch_body = true;
66
67
    /** @var int $fetch_flags */
68
    protected $fetch_flags = true;
69
70
    /** @var int $sequence */
71
    protected $sequence = IMAP::NIL;
72
73
    /** @var string $fetch_order */
74
    protected $fetch_order;
75
76
    /** @var string $date_format */
77
    protected $date_format;
78
79
    /** @var bool $soft_fail */
80
    protected $soft_fail = false;
81
82
    /** @var array $errors */
83
    protected $errors = [];
84
85
    /**
86
     * Query constructor.
87
     * @param Client $client
88
     * @param string $charset
89
     */
90
    public function __construct(Client $client, $charset = 'UTF-8') {
91
        $this->setClient($client);
92
93
        $this->sequence = ClientManager::get('options.sequence', IMAP::ST_MSGN);
94
        if (ClientManager::get('options.fetch') === IMAP::FT_PEEK) $this->leaveUnread();
95
96
        if (ClientManager::get('options.fetch_order') === 'desc') {
97
            $this->fetch_order = 'desc';
98
        } else {
99
            $this->fetch_order = 'asc';
100
        }
101
102
        $this->date_format = ClientManager::get('date_format', 'd M y');
103
        $this->soft_fail = ClientManager::get('options.soft_fail', false);
104
105
        $this->charset = $charset;
106
        $this->query = new Collection();
0 ignored issues
show
Documentation Bug introduced by
It seems like new Illuminate\Support\Collection() 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...
107
        $this->boot();
108
    }
109
110
    /**
111
     * Instance boot method for additional functionality
112
     */
113
    protected function boot() {
114
    }
115
116
    /**
117
     * Parse a given value
118
     * @param mixed $value
119
     *
120
     * @return string
121
     */
122
    protected function parse_value($value) {
123
        switch (true) {
124
            case $value instanceof Carbon:
125
                $value = $value->format($this->date_format);
126
                break;
127
        }
128
129
        return (string)$value;
130
    }
131
132
    /**
133
     * Check if a given date is a valid carbon object and if not try to convert it
134
     * @param string|Carbon $date
135
     *
136
     * @return Carbon
137
     * @throws MessageSearchValidationException
138
     */
139
    protected function parse_date($date) {
140
        if ($date instanceof Carbon) return $date;
141
142
        try {
143
            $date = Carbon::parse($date);
144
        } catch (Exception $e) {
145
            throw new MessageSearchValidationException();
146
        }
147
148
        return $date;
149
    }
150
151
    /**
152
     * Get the raw IMAP search query
153
     *
154
     * @return string
155
     */
156
    public function generate_query() {
157
        $query = '';
158
        $this->query->each(function($statement) use (&$query) {
159
            if (count($statement) == 1) {
160
                $query .= $statement[0];
161
            } else {
162
                if ($statement[1] === null) {
163
                    $query .= $statement[0];
164
                } else {
165
                    $query .= $statement[0] . ' "' . $statement[1] . '"';
166
                }
167
            }
168
            $query .= ' ';
169
170
        });
171
172
        $this->raw_query = trim($query);
173
174
        return $this->raw_query;
175
    }
176
177
    /**
178
     * Perform an imap search request
179
     *
180
     * @return Collection
181
     * @throws GetMessagesFailedException
182
     */
183
    protected function search() {
184
        $this->generate_query();
185
186
        try {
187
            $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

187
            $available_messages = $this->client->getConnection()->/** @scrutinizer ignore-call */ search([$this->getRawQuery()], $this->sequence == IMAP::ST_UID);
Loading history...
188
            return new Collection($available_messages);
189
        } catch (RuntimeException $e) {
190
            throw new GetMessagesFailedException("failed to fetch messages", 0, $e);
191
        } catch (ConnectionFailedException $e) {
192
            throw new GetMessagesFailedException("failed to fetch messages", 0, $e);
193
        }
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 a given id collection
208
     * @param Collection $available_messages
209
     *
210
     * @return array
211
     * @throws ConnectionFailedException
212
     * @throws RuntimeException
213
     */
214
    protected function fetch($available_messages) {
215
        if ($this->fetch_order === 'desc') {
216
            $available_messages = $available_messages->reverse();
217
        }
218
219
        $uids = $available_messages->forPage($this->page, $this->limit)->toArray();
220
        $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

220
        $flags = $this->client->getConnection()->/** @scrutinizer ignore-call */ flags($uids, $this->sequence == IMAP::ST_UID);
Loading history...
221
        $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

221
        $headers = $this->client->getConnection()->/** @scrutinizer ignore-call */ headers($uids, "RFC822", $this->sequence == IMAP::ST_UID);
Loading history...
222
223
        $contents = [];
224
        if ($this->getFetchBody()) {
225
            $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

225
            $contents = $this->client->getConnection()->/** @scrutinizer ignore-call */ content($uids, "RFC822", $this->sequence == IMAP::ST_UID);
Loading history...
226
        }
227
228
        return [
229
            "uids"     => $uids,
230
            "flags"    => $flags,
231
            "headers"  => $headers,
232
            "contents" => $contents,
233
        ];
234
    }
235
236
    /**
237
     * Make a new message from given raw components
238
     * @param integer $uid
239
     * @param integer $msglist
240
     * @param string $header
241
     * @param string $content
242
     * @param array $flags
243
     *
244
     * @return Message|null
245
     * @throws ConnectionFailedException
246
     * @throws EventNotFoundException
247
     * @throws GetMessagesFailedException
248
     * @throws ReflectionException
249
     */
250
    protected function make($uid, $msglist, $header, $content, $flags){
251
        try {
252
            return Message::make($uid, $msglist, $this->getClient(), $header, $content, $flags, $this->getFetchOptions(), $this->sequence);
253
        }catch (MessageNotFoundException $e) {
254
            $this->setError($uid, $e);
255
        }catch (RuntimeException $e) {
256
            $this->setError($uid, $e);
257
        }catch (MessageFlagException $e) {
258
            $this->setError($uid, $e);
259
        }catch (InvalidMessageDateException $e) {
260
            $this->setError($uid, $e);
261
        }catch (MessageContentFetchingException $e) {
262
            $this->setError($uid, $e);
263
        }
264
265
        $this->handleException($uid);
266
267
        return null;
268
    }
269
270
    /**
271
     * Get the message key for a given message
272
     * @param string $message_key
273
     * @param integer $msglist
274
     * @param Message $message
275
     *
276
     * @return string
277
     */
278
    protected function getMessageKey($message_key, $msglist, $message){
279
        switch ($message_key) {
280
            case 'number':
281
                $key = $message->getMessageNo();
282
                break;
283
            case 'list':
284
                $key = $msglist;
285
                break;
286
            case 'uid':
287
                $key = $message->getUid();
288
                break;
289
            default:
290
                $key = $message->getMessageId();
291
                break;
292
        }
293
        return (string)$key;
294
    }
295
296
    /**
297
     * Populate a given id collection and receive a fully fetched message collection
298
     * @param Collection $available_messages
299
     *
300
     * @return MessageCollection
301
     * @throws ConnectionFailedException
302
     * @throws EventNotFoundException
303
     * @throws GetMessagesFailedException
304
     * @throws ReflectionException
305
     * @throws RuntimeException
306
     */
307
    protected function populate($available_messages) {
308
        $messages = MessageCollection::make([]);
309
310
        $messages->total($available_messages->count());
311
312
        $message_key = ClientManager::get('options.message_key');
313
314
        $raw_messages = $this->fetch($available_messages);
315
316
        $msglist = 0;
317
        foreach ($raw_messages["headers"] as $uid => $header) {
318
            $content = isset($raw_messages["contents"][$uid]) ? $raw_messages["contents"][$uid] : "";
319
            $flag = isset($raw_messages["flags"][$uid]) ? $raw_messages["flags"][$uid] : [];
320
321
            $message = $this->make($uid, $msglist, $header, $content, $flag);
322
            if ($message !== null) {
323
                $key = $this->getMessageKey($message_key, $msglist, $message);
324
                $messages->put("$key", $message);
325
            }
326
            $msglist++;
327
        }
328
329
        return $messages;
330
    }
331
332
    /**
333
     * Fetch the current query and return all found messages
334
     *
335
     * @return MessageCollection
336
     * @throws GetMessagesFailedException
337
     */
338
    public function get() {
339
        $available_messages = $this->search();
340
341
        try {
342
            if (($available_messages_count = $available_messages->count()) > 0) {
0 ignored issues
show
Unused Code introduced by
The assignment to $available_messages_count is dead and can be removed.
Loading history...
343
                return $this->populate($available_messages);
344
            }
345
            return MessageCollection::make([]);
346
        } catch (Exception $e) {
347
            throw new GetMessagesFailedException($e->getMessage(), 0, $e);
348
        }
349
    }
350
351
    /**
352
     * Fetch the current query as chunked requests
353
     * @param callable $callback
354
     * @param int $chunk_size
355
     * @param int $start_chunk
356
     *
357
     * @throws ConnectionFailedException
358
     * @throws EventNotFoundException
359
     * @throws GetMessagesFailedException
360
     * @throws ReflectionException
361
     * @throws RuntimeException
362
     */
363
    public function chunked($callback, $chunk_size = 10, $start_chunk = 1) {
364
        $available_messages = $this->search();
365
        if (($available_messages_count = $available_messages->count()) > 0) {
366
            $old_limit = $this->limit;
367
            $old_page = $this->page;
368
369
            $this->limit = $chunk_size;
370
            $this->page = $start_chunk;
371
            while ($this->limit * $this->page <= $available_messages_count) {
372
                $messages = $this->populate($available_messages);
373
                $callback($messages, $this->page);
374
                $this->page++;
375
            }
376
            $this->limit = $old_limit;
377
            $this->page = $old_page;
378
        }
379
    }
380
381
    /**
382
     * Paginate the current query
383
     * @param int $per_page Results you which to receive per page
384
     * @param int|null $page The current page you are on (e.g. 0, 1, 2, ...) use `null` to enable auto mode
385
     * @param string $page_name The page name / uri parameter used for the generated links and the auto mode
386
     *
387
     * @return LengthAwarePaginator
388
     * @throws GetMessagesFailedException
389
     */
390
    public function paginate($per_page = 5, $page = null, $page_name = 'imap_page') {
391
        if (
392
            $page === null
393
            && isset($_GET[$page_name])
394
            && $_GET[$page_name] > 0
395
        ) {
396
            $this->page = intval($_GET[$page_name]);
397
        } elseif ($page > 0) {
398
            $this->page = $page;
399
        }
400
401
        $this->limit = $per_page;
402
403
        return $this->get()->paginate($per_page, $this->page, $page_name, true);
404
    }
405
406
    /**
407
     * Get a new Message instance
408
     * @param int $uid
409
     * @param int|null $msglist
410
     * @param int|null $sequence
411
     *
412
     * @return Message
413
     * @throws ConnectionFailedException
414
     * @throws RuntimeException
415
     * @throws InvalidMessageDateException
416
     * @throws MessageContentFetchingException
417
     * @throws MessageHeaderFetchingException
418
     * @throws EventNotFoundException
419
     * @throws MessageFlagException
420
     * @throws MessageNotFoundException
421
     */
422
    public function getMessage($uid, $msglist = null, $sequence = null) {
423
        return new Message($uid, $msglist, $this->getClient(), $this->getFetchOptions(), $this->getFetchBody(), $this->getFetchFlags(), $sequence ? $sequence : $this->sequence);
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

423
        return new Message($uid, $msglist, $this->getClient(), $this->getFetchOptions(), $this->getFetchBody(), /** @scrutinizer ignore-type */ $this->getFetchFlags(), $sequence ? $sequence : $this->sequence);
Loading history...
424
    }
425
426
    /**
427
     * Get a message by its message number
428
     * @param $msgn
429
     * @param int|null $msglist
430
     *
431
     * @return Message
432
     * @throws ConnectionFailedException
433
     * @throws InvalidMessageDateException
434
     * @throws MessageContentFetchingException
435
     * @throws MessageHeaderFetchingException
436
     * @throws RuntimeException
437
     * @throws EventNotFoundException
438
     * @throws MessageFlagException
439
     * @throws MessageNotFoundException
440
     */
441
    public function getMessageByMsgn($msgn, $msglist = null) {
442
        return $this->getMessage($msgn, $msglist, IMAP::ST_MSGN);
443
    }
444
445
    /**
446
     * Get a message by its uid
447
     * @param $uid
448
     *
449
     * @return Message
450
     * @throws ConnectionFailedException
451
     * @throws InvalidMessageDateException
452
     * @throws MessageContentFetchingException
453
     * @throws MessageHeaderFetchingException
454
     * @throws RuntimeException
455
     * @throws EventNotFoundException
456
     * @throws MessageFlagException
457
     * @throws MessageNotFoundException
458
     */
459
    public function getMessageByUid($uid) {
460
        return $this->getMessage($uid, null, IMAP::ST_UID);
461
    }
462
463
    /**
464
     * Don't mark messages as read when fetching
465
     *
466
     * @return $this
467
     */
468
    public function leaveUnread() {
469
        $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

469
        $this->setFetchOptions(/** @scrutinizer ignore-type */ IMAP::FT_PEEK);
Loading history...
470
471
        return $this;
472
    }
473
474
    /**
475
     * Mark all messages as read when fetching
476
     *
477
     * @return $this
478
     */
479
    public function markAsRead() {
480
        $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

480
        $this->setFetchOptions(/** @scrutinizer ignore-type */ IMAP::FT_UID);
Loading history...
481
482
        return $this;
483
    }
484
485
    /**
486
     * Set the sequence type
487
     * @param int $sequence
488
     *
489
     * @return $this
490
     */
491
    public function setSequence($sequence) {
492
        $this->sequence = $sequence != IMAP::ST_MSGN ? IMAP::ST_UID : $sequence;
493
494
        return $this;
495
    }
496
497
    /**
498
     * @return Client
499
     * @throws ConnectionFailedException
500
     */
501
    public function getClient() {
502
        $this->client->checkConnection();
503
        return $this->client;
504
    }
505
506
    /**
507
     * Set the limit and page for the current query
508
     * @param int $limit
509
     * @param int $page
510
     *
511
     * @return $this
512
     */
513
    public function limit($limit, $page = 1) {
514
        if ($page >= 1) $this->page = $page;
515
        $this->limit = $limit;
516
517
        return $this;
518
    }
519
520
    /**
521
     * @return array
522
     */
523
    public function getQuery() {
524
        return $this->query;
525
    }
526
527
    /**
528
     * @param array $query
529
     * @return Query
530
     */
531
    public function setQuery($query) {
532
        $this->query = $query;
533
        return $this;
534
    }
535
536
    /**
537
     * @return string
538
     */
539
    public function getRawQuery() {
540
        return $this->raw_query;
541
    }
542
543
    /**
544
     * @param string $raw_query
545
     * @return Query
546
     */
547
    public function setRawQuery($raw_query) {
548
        $this->raw_query = $raw_query;
549
        return $this;
550
    }
551
552
    /**
553
     * @return string
554
     */
555
    public function getCharset() {
556
        return $this->charset;
557
    }
558
559
    /**
560
     * @param string $charset
561
     * @return Query
562
     */
563
    public function setCharset($charset) {
564
        $this->charset = $charset;
565
        return $this;
566
    }
567
568
    /**
569
     * @param Client $client
570
     * @return Query
571
     */
572
    public function setClient(Client $client) {
573
        $this->client = $client;
574
        return $this;
575
    }
576
577
    /**
578
     * @return int
579
     */
580
    public function getLimit() {
581
        return $this->limit;
582
    }
583
584
    /**
585
     * @param int $limit
586
     * @return Query
587
     */
588
    public function setLimit($limit) {
589
        $this->limit = $limit <= 0 ? null : $limit;
590
        return $this;
591
    }
592
593
    /**
594
     * @return int
595
     */
596
    public function getPage() {
597
        return $this->page;
598
    }
599
600
    /**
601
     * @param int $page
602
     * @return Query
603
     */
604
    public function setPage($page) {
605
        $this->page = $page;
606
        return $this;
607
    }
608
609
    /**
610
     * @param boolean $fetch_options
611
     * @return Query
612
     */
613
    public function setFetchOptions($fetch_options) {
614
        $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...
615
        return $this;
616
    }
617
618
    /**
619
     * @param boolean $fetch_options
620
     * @return Query
621
     */
622
    public function fetchOptions($fetch_options) {
623
        return $this->setFetchOptions($fetch_options);
624
    }
625
626
    /**
627
     * @return int
628
     */
629
    public function getFetchOptions() {
630
        return $this->fetch_options;
631
    }
632
633
    /**
634
     * @return boolean
635
     */
636
    public function getFetchBody() {
637
        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...
638
    }
639
640
    /**
641
     * @param boolean $fetch_body
642
     * @return Query
643
     */
644
    public function setFetchBody($fetch_body) {
645
        $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...
646
        return $this;
647
    }
648
649
    /**
650
     * @param boolean $fetch_body
651
     * @return Query
652
     */
653
    public function fetchBody($fetch_body) {
654
        return $this->setFetchBody($fetch_body);
655
    }
656
657
    /**
658
     * @return int
659
     */
660
    public function getFetchFlags() {
661
        return $this->fetch_flags;
662
    }
663
664
    /**
665
     * @param int $fetch_flags
666
     * @return Query
667
     */
668
    public function setFetchFlags($fetch_flags) {
669
        $this->fetch_flags = $fetch_flags;
670
        return $this;
671
    }
672
673
    /**
674
     * @param string $fetch_order
675
     * @return Query
676
     */
677
    public function setFetchOrder($fetch_order) {
678
        $fetch_order = strtolower($fetch_order);
679
680
        if (in_array($fetch_order, ['asc', 'desc'])) {
681
            $this->fetch_order = $fetch_order;
682
        }
683
684
        return $this;
685
    }
686
687
    /**
688
     * @param string $fetch_order
689
     * @return Query
690
     */
691
    public function fetchOrder($fetch_order) {
692
        return $this->setFetchOrder($fetch_order);
693
    }
694
695
    /**
696
     * @return string
697
     */
698
    public function getFetchOrder() {
699
        return $this->fetch_order;
700
    }
701
702
    /**
703
     * @return Query
704
     */
705
    public function setFetchOrderAsc() {
706
        return $this->setFetchOrder('asc');
707
    }
708
709
    /**
710
     * @return Query
711
     */
712
    public function fetchOrderAsc() {
713
        return $this->setFetchOrderAsc();
714
    }
715
716
    /**
717
     * @return Query
718
     */
719
    public function setFetchOrderDesc() {
720
        return $this->setFetchOrder('desc');
721
    }
722
723
    /**
724
     * @return Query
725
     */
726
    public function fetchOrderDesc() {
727
        return $this->setFetchOrderDesc();
728
    }
729
730
    /**
731
     * @var boolean $state
732
     *
733
     * @return Query
734
     */
735
    public function softFail($state = true) {
736
        return $this->setSoftFail($state);
737
    }
738
739
    /**
740
     * @var boolean $state
741
     *
742
     * @return Query
743
     */
744
    public function setSoftFail($state = true) {
745
        $this->soft_fail = $state;
746
747
        return $this;
748
    }
749
750
    /**
751
     * @return boolean
752
     */
753
    public function getSoftFail() {
754
        return $this->soft_fail;
755
    }
756
757
    /**
758
     * Handle the exception for a given uid
759
     * @param integer $uid
760
     *
761
     * @throws GetMessagesFailedException
762
     */
763
    protected function handleException($uid) {
764
        if ($this->soft_fail === false && $this->hasError($uid)) {
765
            $error = $this->getError($uid);
766
            throw new GetMessagesFailedException($error->getMessage(), 0, $error);
767
        }
768
    }
769
770
    /**
771
     * Add a new error to the error holder
772
     * @param integer $uid
773
     * @param Exception $error
774
     */
775
    protected function setError($uid, $error) {
776
        $this->errors[$uid] = $error;
777
    }
778
779
    /**
780
     * Check if there are any errors / exceptions present
781
     * @var integer|null $uid
782
     *
783
     * @return boolean
784
     */
785
    public function hasErrors($uid = null){
786
        if ($uid !== null) {
787
            return $this->hasError($uid);
788
        }
789
        return count($this->errors) > 0;
790
    }
791
792
    /**
793
     * Check if there is an error / exception present
794
     * @var integer $uid
795
     *
796
     * @return boolean
797
     */
798
    public function hasError($uid){
799
        return isset($this->errors[$uid]);
800
    }
801
802
    /**
803
     * Get all available errors / exceptions
804
     *
805
     * @return array
806
     */
807
    public function errors(){
808
        return $this->getErrors();
809
    }
810
811
    /**
812
     * Get all available errors / exceptions
813
     *
814
     * @return array
815
     */
816
    public function getErrors(){
817
        return $this->errors;
818
    }
819
820
    /**
821
     * Get a specific error / exception
822
     * @var integer $uid
823
     *
824
     * @return Exception|null
825
     */
826
    public function error($uid){
827
        return $this->getError($uid);
828
    }
829
830
    /**
831
     * Get a specific error / exception
832
     * @var integer $uid
833
     *
834
     * @return Exception|null
835
     */
836
    public function getError($uid){
837
        if ($this->hasError($uid)) {
838
            return $this->errors[$uid];
839
        }
840
        return null;
841
    }
842
}
843