Passed
Push — master ( 1972a3...17da55 )
by Malte
02:19
created

Query::setSequence()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
c 0
b 0
f 0
dl 0
loc 4
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 = 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...
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
        } catch (RuntimeException $e) {
189
            $available_messages = false;
190
        } catch (ConnectionFailedException $e) {
191
            throw new GetMessagesFailedException("failed to fetch messages", 0, $e);
192
        }
193
194
        if ($available_messages !== false) {
195
            return collect($available_messages);
196
        }
197
198
        return collect();
199
    }
200
201
    /**
202
     * Count all available messages matching the current search criteria
203
     *
204
     * @return int
205
     * @throws GetMessagesFailedException
206
     */
207
    public function count() {
208
        return $this->search()->count();
209
    }
210
211
    /**
212
     * Fetch a given id collection
213
     * @param Collection $available_messages
214
     *
215
     * @return array
216
     * @throws ConnectionFailedException
217
     * @throws RuntimeException
218
     */
219
    protected function fetch($available_messages) {
220
        if ($this->fetch_order === 'desc') {
221
            $available_messages = $available_messages->reverse();
222
        }
223
224
        $uids = $available_messages->forPage($this->page, $this->limit)->toArray();
225
        $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

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

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

230
            $contents = $this->client->getConnection()->/** @scrutinizer ignore-call */ content($uids, "RFC822", $this->sequence == IMAP::ST_UID);
Loading history...
231
        }
232
233
        return [
234
            "uids"     => $uids,
235
            "flags"    => $flags,
236
            "headers"  => $headers,
237
            "contents" => $contents,
238
        ];
239
    }
240
241
    /**
242
     * Populate a given id collection and receive a fully fetched message collection
243
     * @param Collection $available_messages
244
     *
245
     * @return MessageCollection
246
     * @throws ConnectionFailedException
247
     * @throws EventNotFoundException
248
     * @throws GetMessagesFailedException
249
     * @throws ReflectionException
250
     * @throws RuntimeException
251
     */
252
    protected function populate($available_messages) {
253
        $messages = MessageCollection::make([]);
254
255
        $messages->total($available_messages->count());
256
257
        $message_key = ClientManager::get('options.message_key');
258
259
        $raw_messages = $this->fetch($available_messages);
260
        $flags = $raw_messages["flags"];
261
        $headers = $raw_messages["headers"];
262
        $contents = $raw_messages["contents"];
263
264
        $msglist = 0;
265
        foreach ($headers as $uid => $header) {
266
            $content = isset($contents[$uid]) ? $contents[$uid] : "";
267
            $flag = isset($flags[$uid]) ? $flags[$uid] : [];
268
            $error = null;
269
270
            try {
271
                $message = Message::make($uid, $msglist, $this->getClient(), $header, $content, $flag, $this->getFetchOptions(), $this->sequence);
0 ignored issues
show
Bug introduced by
It seems like $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

271
                $message = Message::make($uid, $msglist, $this->getClient(), $header, $content, /** @scrutinizer ignore-type */ $flag, $this->getFetchOptions(), $this->sequence);
Loading history...
272
273
                switch ($message_key) {
274
                    case 'number':
275
                        $key = $message->getMessageNo();
276
                        break;
277
                    case 'list':
278
                        $key = $msglist;
279
                        break;
280
                    case 'uid':
281
                        $key = $message->getUid();
282
                        break;
283
                    default:
284
                        $key = $message->getMessageId();
285
                        break;
286
287
                }
288
                $messages->put("$key", $message);
289
                $msglist++;
290
            }catch (MessageNotFoundException $e) {
291
                $this->setError($uid, $e);
292
            }catch (RuntimeException $e) {
293
                $this->setError($uid, $e);
294
            }catch (MessageFlagException $e) {
295
                $this->setError($uid, $e);
296
            }catch (InvalidMessageDateException $e) {
297
                $this->setError($uid, $e);
298
            }catch (MessageContentFetchingException $e) {
299
                $this->setError($uid, $e);
300
            }
301
302
            if ($this->soft_fail === false && $this->hasError($uid)) {
303
                $error = $this->getError($uid);
304
                throw new GetMessagesFailedException($error->getMessage(), 0, $error);
305
            }
306
        }
307
308
        return $messages;
309
    }
310
311
    /**
312
     * Fetch the current query and return all found messages
313
     *
314
     * @return MessageCollection
315
     * @throws GetMessagesFailedException
316
     */
317
    public function get() {
318
        $available_messages = $this->search();
319
320
        try {
321
            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...
322
                return $this->populate($available_messages);
323
            }
324
            return MessageCollection::make([]);
325
        } catch (Exception $e) {
326
            throw new GetMessagesFailedException($e->getMessage(), 0, $e);
327
        }
328
    }
329
330
    /**
331
     * Fetch the current query as chunked requests
332
     * @param callable $callback
333
     * @param int $chunk_size
334
     * @param int $start_chunk
335
     *
336
     * @throws GetMessagesFailedException
337
     */
338
    public function chunked($callback, $chunk_size = 10, $start_chunk = 1) {
339
        $available_messages = $this->search();
340
        if (($available_messages_count = $available_messages->count()) > 0) {
341
            $old_limit = $this->limit;
342
            $old_page = $this->page;
343
344
            $this->limit = $chunk_size;
345
            $this->page = $start_chunk;
346
            while ($this->limit * $this->page <= $available_messages_count) {
347
                try {
348
                    $messages = $this->populate($available_messages);
349
                    $callback($messages, $this->page);
350
                } catch (Exception $e) {
351
                    if ($this->soft_fail === false) {
352
                        throw new GetMessagesFailedException($e->getMessage(), 0, $e);
353
                    }
354
                }
355
                $this->page++;
356
            }
357
            $this->limit = $old_limit;
358
            $this->page = $old_page;
359
        }
360
    }
361
362
    /**
363
     * Paginate the current query
364
     * @param int $per_page Results you which to receive per page
365
     * @param int|null $page The current page you are on (e.g. 0, 1, 2, ...) use `null` to enable auto mode
366
     * @param string $page_name The page name / uri parameter used for the generated links and the auto mode
367
     *
368
     * @return LengthAwarePaginator
369
     * @throws GetMessagesFailedException
370
     */
371
    public function paginate($per_page = 5, $page = null, $page_name = 'imap_page') {
372
        if (
373
            $page === null
374
            && isset($_GET[$page_name])
375
            && $_GET[$page_name] > 0
376
        ) {
377
            $this->page = intval($_GET[$page_name]);
378
        } elseif ($page > 0) {
379
            $this->page = $page;
380
        }
381
382
        $this->limit = $per_page;
383
384
        return $this->get()->paginate($per_page, $this->page, $page_name, true);
385
    }
386
387
    /**
388
     * Get a new Message instance
389
     * @param int $uid
390
     * @param int|null $msglist
391
     * @param int|null $sequence
392
     *
393
     * @return Message
394
     * @throws ConnectionFailedException
395
     * @throws RuntimeException
396
     * @throws InvalidMessageDateException
397
     * @throws MessageContentFetchingException
398
     * @throws MessageHeaderFetchingException
399
     * @throws EventNotFoundException
400
     * @throws MessageFlagException
401
     * @throws MessageNotFoundException
402
     */
403
    public function getMessage($uid, $msglist = null, $sequence = null) {
404
        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

404
        return new Message($uid, $msglist, $this->getClient(), $this->getFetchOptions(), $this->getFetchBody(), /** @scrutinizer ignore-type */ $this->getFetchFlags(), $sequence ? $sequence : $this->sequence);
Loading history...
405
    }
406
407
    /**
408
     * Get a message by its message number
409
     * @param $msgn
410
     * @param int|null $msglist
411
     *
412
     * @return Message
413
     * @throws ConnectionFailedException
414
     * @throws InvalidMessageDateException
415
     * @throws MessageContentFetchingException
416
     * @throws MessageHeaderFetchingException
417
     * @throws RuntimeException
418
     * @throws EventNotFoundException
419
     * @throws MessageFlagException
420
     * @throws MessageNotFoundException
421
     */
422
    public function getMessageByMsgn($msgn, $msglist = null) {
423
        return $this->getMessage($msgn, $msglist, IMAP::ST_MSGN);
424
    }
425
426
    /**
427
     * Get a message by its uid
428
     * @param $uid
429
     *
430
     * @return Message
431
     * @throws ConnectionFailedException
432
     * @throws InvalidMessageDateException
433
     * @throws MessageContentFetchingException
434
     * @throws MessageHeaderFetchingException
435
     * @throws RuntimeException
436
     * @throws EventNotFoundException
437
     * @throws MessageFlagException
438
     * @throws MessageNotFoundException
439
     */
440
    public function getMessageByUid($uid) {
441
        return $this->getMessage($uid, null, IMAP::ST_UID);
442
    }
443
444
    /**
445
     * Don't mark messages as read when fetching
446
     *
447
     * @return $this
448
     */
449
    public function leaveUnread() {
450
        $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

450
        $this->setFetchOptions(/** @scrutinizer ignore-type */ IMAP::FT_PEEK);
Loading history...
451
452
        return $this;
453
    }
454
455
    /**
456
     * Mark all messages as read when fetching
457
     *
458
     * @return $this
459
     */
460
    public function markAsRead() {
461
        $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

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