WhereQuery   F
last analyzed

Complexity

Total Complexity 61

Size/Duplication

Total Lines 489
Duplicated Lines 0 %

Importance

Changes 7
Bugs 0 Features 0
Metric Value
wmc 61
eloc 92
c 7
b 0
f 0
dl 0
loc 489
rs 3.52

41 Methods

Rating   Name   Duplication   Size   Complexity  
A __call() 0 21 4
A validate_criteria() 0 10 3
A whereRecent() 0 2 1
A whereCc() 0 2 1
A whereIsXSpam() 0 2 1
A whereNot() 0 2 1
A whereAll() 0 2 1
A whereKeyword() 0 2 1
A whereInReplyTo() 0 2 1
A when() 0 8 5
A whereSince() 0 3 1
A whereSeen() 0 2 1
A whereUnflagged() 0 2 1
A andWhere() 0 5 2
A whereText() 0 2 1
A whereHeader() 0 2 1
A push_search_criteria() 0 8 3
A whereOld() 0 2 1
A whereUid() 0 2 1
A whereNew() 0 2 1
A whereFlagged() 0 2 1
A whereBody() 0 2 1
A whereDeleted() 0 2 1
A whereUnanswered() 0 2 1
A whereTo() 0 2 1
A whereBcc() 0 2 1
A whereMessageId() 0 2 1
A unless() 0 8 5
A whereUndeleted() 0 2 1
A whereFrom() 0 2 1
A whereOn() 0 3 1
A whereAnswered() 0 2 1
A whereUidIn() 0 3 1
A orWhere() 0 5 2
A whereSubject() 0 2 1
A whereUnkeyword() 0 2 1
A where() 0 14 4
A whereLanguage() 0 2 1
A whereBefore() 0 3 1
A whereUnseen() 0 2 1
A whereNoXSpam() 0 2 1

How to fix   Complexity   

Complex Class

Complex classes like WhereQuery often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use WhereQuery, and based on these observations, apply Extract Interface, too.

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 Closure;
16
use Illuminate\Support\Str;
17
use Webklex\PHPIMAP\Exceptions\InvalidWhereQueryCriteriaException;
18
use Webklex\PHPIMAP\Exceptions\MethodNotFoundException;
19
use Webklex\PHPIMAP\Exceptions\MessageSearchValidationException;
20
21
/**
22
 * Class WhereQuery
23
 *
24
 * @package Webklex\PHPIMAP\Query
25
 *
26
 * @method WhereQuery all()
27
 * @method WhereQuery answered()
28
 * @method WhereQuery deleted()
29
 * @method WhereQuery new()
30
 * @method WhereQuery old()
31
 * @method WhereQuery recent()
32
 * @method WhereQuery seen()
33
 * @method WhereQuery unanswered()
34
 * @method WhereQuery undeleted()
35
 * @method WhereQuery unflagged()
36
 * @method WhereQuery unseen()
37
 * @method WhereQuery not()
38
 * @method WhereQuery unkeyword($value)
39
 * @method WhereQuery to($value)
40
 * @method WhereQuery text($value)
41
 * @method WhereQuery subject($value)
42
 * @method WhereQuery since($date)
43
 * @method WhereQuery on($date)
44
 * @method WhereQuery keyword($value)
45
 * @method WhereQuery from($value)
46
 * @method WhereQuery flagged()
47
 * @method WhereQuery cc($value)
48
 * @method WhereQuery body($value)
49
 * @method WhereQuery before($date)
50
 * @method WhereQuery bcc($value)
51
 * @method WhereQuery inReplyTo($value)
52
 * @method WhereQuery messageId($value)
53
 *
54
 * @mixin Query
55
 */
56
class WhereQuery extends Query {
57
58
    /**
59
     * @var array $available_criteria
60
     */
61
    protected $available_criteria = [
62
        'OR', 'AND',
63
        'ALL', 'ANSWERED', 'BCC', 'BEFORE', 'BODY', 'CC', 'DELETED', 'FLAGGED', 'FROM', 'KEYWORD',
64
        'NEW', 'NOT', 'OLD', 'ON', 'RECENT', 'SEEN', 'SINCE', 'SUBJECT', 'TEXT', 'TO',
65
        'UNANSWERED', 'UNDELETED', 'UNFLAGGED', 'UNKEYWORD', 'UNSEEN', 'UID'
66
    ];
67
68
    /**
69
     * Magic method in order to allow alias usage of all "where" methods in an optional connection with "NOT"
70
     * @param string $name
71
     * @param array|null $arguments
72
     *
73
     * @return mixed
74
     * @throws InvalidWhereQueryCriteriaException
75
     * @throws MethodNotFoundException
76
     */
77
    public function __call(string $name, $arguments) {
78
        $that = $this;
79
80
        $name = Str::camel($name);
81
82
        if (strtolower(substr($name, 0, 3)) === 'not') {
83
            $that = $that->whereNot();
84
            $name = substr($name, 3);
85
        }
86
87
        if (strpos(strtolower($name), "where") === false) {
88
            $method = 'where' . ucfirst($name);
89
        } else {
90
            $method = lcfirst($name);
91
        }
92
93
        if (method_exists($this, $method) === true) {
94
            return call_user_func_array([$that, $method], $arguments);
0 ignored issues
show
Bug introduced by
It seems like $arguments can also be of type null; however, parameter $args of call_user_func_array() does only seem to accept array, 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

94
            return call_user_func_array([$that, $method], /** @scrutinizer ignore-type */ $arguments);
Loading history...
95
        }
96
97
        throw new MethodNotFoundException("Method " . self::class . '::' . $method . '() is not supported');
98
    }
99
100
    /**
101
     * Validate a given criteria
102
     * @param $criteria
103
     *
104
     * @return string
105
     * @throws InvalidWhereQueryCriteriaException
106
     */
107
    protected function validate_criteria($criteria): string {
108
        $command = strtoupper($criteria);
109
        if (substr($command, 0, 7) === "CUSTOM ") {
110
            return substr($criteria, 7);
111
        }
112
        if (in_array($command, $this->available_criteria) === false) {
113
            throw new InvalidWhereQueryCriteriaException("Invalid imap search criteria: $command");
114
        }
115
116
        return $criteria;
117
    }
118
119
    /**
120
     * Register search parameters
121
     * @param mixed $criteria
122
     * @param null $value
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $value is correct as it would always require null to be passed?
Loading history...
123
     *
124
     * @return $this
125
     * @throws InvalidWhereQueryCriteriaException
126
     *
127
     * Examples:
128
     * $query->from("[email protected]")->seen();
129
     * $query->whereFrom("[email protected]")->whereSeen();
130
     * $query->where([["FROM" => "[email protected]"], ["SEEN"]]);
131
     * $query->where(["FROM" => "[email protected]"])->where(["SEEN"]);
132
     * $query->where(["FROM" => "[email protected]", "SEEN"]);
133
     * $query->where("FROM", "[email protected]")->where("SEEN");
134
     */
135
    public function where($criteria, $value = null): WhereQuery {
136
        if (is_array($criteria)) {
137
            foreach ($criteria as $key => $value) {
138
                if (is_numeric($key)) {
139
                    $this->where($value);
140
                }else{
141
                    $this->where($key, $value);
142
                }
143
            }
144
        } else {
145
            $this->push_search_criteria($criteria, $value);
146
        }
147
148
        return $this;
149
    }
150
151
    /**
152
     * Push a given search criteria and value pair to the search query
153
     * @param $criteria string
154
     * @param $value mixed
155
     *
156
     * @throws InvalidWhereQueryCriteriaException
157
     */
158
    protected function push_search_criteria(string $criteria, $value){
159
        $criteria = $this->validate_criteria($criteria);
160
        $value = $this->parse_value($value);
161
162
        if ($value === null || $value === '') {
163
            $this->query->push([$criteria]);
164
        } else {
165
            $this->query->push([$criteria, $value]);
166
        }
167
    }
168
169
    /**
170
     * @param Closure $closure
171
     *
172
     * @return $this
173
     */
174
    public function orWhere(Closure $closure = null): WhereQuery {
175
        $this->query->push(['OR']);
176
        if ($closure !== null) $closure($this);
177
178
        return $this;
179
    }
180
181
    /**
182
     * @param Closure $closure
183
     *
184
     * @return $this
185
     */
186
    public function andWhere(Closure $closure = null): WhereQuery {
187
        $this->query->push(['AND']);
188
        if ($closure !== null) $closure($this);
189
190
        return $this;
191
    }
192
193
    /**
194
     * @return WhereQuery
195
     * @throws InvalidWhereQueryCriteriaException
196
     */
197
    public function whereAll(): WhereQuery {
198
        return $this->where('ALL');
199
    }
200
201
    /**
202
     * @return WhereQuery
203
     * @throws InvalidWhereQueryCriteriaException
204
     */
205
    public function whereAnswered(): WhereQuery {
206
        return $this->where('ANSWERED');
207
    }
208
209
    /**
210
     * @param string $value
211
     *
212
     * @return WhereQuery
213
     * @throws InvalidWhereQueryCriteriaException
214
     */
215
    public function whereBcc(string $value): WhereQuery {
216
        return $this->where('BCC', $value);
217
    }
218
219
    /**
220
     * @param mixed $value
221
     * @return WhereQuery
222
     * @throws InvalidWhereQueryCriteriaException
223
     * @throws MessageSearchValidationException
224
     */
225
    public function whereBefore($value): WhereQuery {
226
        $date = $this->parse_date($value);
227
        return $this->where('BEFORE', $date);
228
    }
229
230
    /**
231
     * @param string $value
232
     *
233
     * @return WhereQuery
234
     * @throws InvalidWhereQueryCriteriaException
235
     */
236
    public function whereBody(string $value): WhereQuery {
237
        return $this->where('BODY', $value);
238
    }
239
240
    /**
241
     * @param string $value
242
     *
243
     * @return WhereQuery
244
     * @throws InvalidWhereQueryCriteriaException
245
     */
246
    public function whereCc(string $value): WhereQuery {
247
        return $this->where('CC', $value);
248
    }
249
250
    /**
251
     * @return WhereQuery
252
     * @throws InvalidWhereQueryCriteriaException
253
     */
254
    public function whereDeleted(): WhereQuery {
255
        return $this->where('DELETED');
256
    }
257
258
    /**
259
     * @param string $value
260
     *
261
     * @return WhereQuery
262
     * @throws InvalidWhereQueryCriteriaException
263
     */
264
    public function whereFlagged(string $value): WhereQuery {
265
        return $this->where('FLAGGED', $value);
266
    }
267
268
    /**
269
     * @param string $value
270
     *
271
     * @return WhereQuery
272
     * @throws InvalidWhereQueryCriteriaException
273
     */
274
    public function whereFrom(string $value): WhereQuery {
275
        return $this->where('FROM', $value);
276
    }
277
278
    /**
279
     * @param string $value
280
     *
281
     * @return WhereQuery
282
     * @throws InvalidWhereQueryCriteriaException
283
     */
284
    public function whereKeyword(string $value): WhereQuery {
285
        return $this->where('KEYWORD', $value);
286
    }
287
288
    /**
289
     * @return WhereQuery
290
     * @throws InvalidWhereQueryCriteriaException
291
     */
292
    public function whereNew(): WhereQuery {
293
        return $this->where('NEW');
294
    }
295
296
    /**
297
     * @return WhereQuery
298
     * @throws InvalidWhereQueryCriteriaException
299
     */
300
    public function whereNot(): WhereQuery {
301
        return $this->where('NOT');
302
    }
303
304
    /**
305
     * @return WhereQuery
306
     * @throws InvalidWhereQueryCriteriaException
307
     */
308
    public function whereOld(): WhereQuery {
309
        return $this->where('OLD');
310
    }
311
312
    /**
313
     * @param mixed $value
314
     *
315
     * @return WhereQuery
316
     * @throws MessageSearchValidationException
317
     * @throws InvalidWhereQueryCriteriaException
318
     */
319
    public function whereOn($value): WhereQuery {
320
        $date = $this->parse_date($value);
321
        return $this->where('ON', $date);
322
    }
323
324
    /**
325
     * @return WhereQuery
326
     * @throws InvalidWhereQueryCriteriaException
327
     */
328
    public function whereRecent(): WhereQuery {
329
        return $this->where('RECENT');
330
    }
331
332
    /**
333
     * @return WhereQuery
334
     * @throws InvalidWhereQueryCriteriaException
335
     */
336
    public function whereSeen(): WhereQuery {
337
        return $this->where('SEEN');
338
    }
339
340
    /**
341
     * @param mixed $value
342
     *
343
     * @return WhereQuery
344
     * @throws MessageSearchValidationException
345
     * @throws InvalidWhereQueryCriteriaException
346
     */
347
    public function whereSince($value): WhereQuery {
348
        $date = $this->parse_date($value);
349
        return $this->where('SINCE', $date);
350
    }
351
352
    /**
353
     * @param string $value
354
     *
355
     * @return WhereQuery
356
     * @throws InvalidWhereQueryCriteriaException
357
     */
358
    public function whereSubject(string $value): WhereQuery {
359
        return $this->where('SUBJECT', $value);
360
    }
361
362
    /**
363
     * @param string $value
364
     *
365
     * @return WhereQuery
366
     * @throws InvalidWhereQueryCriteriaException
367
     */
368
    public function whereText(string $value): WhereQuery {
369
        return $this->where('TEXT', $value);
370
    }
371
372
    /**
373
     * @param string $value
374
     *
375
     * @return WhereQuery
376
     * @throws InvalidWhereQueryCriteriaException
377
     */
378
    public function whereTo(string $value): WhereQuery {
379
        return $this->where('TO', $value);
380
    }
381
382
    /**
383
     * @param string $value
384
     *
385
     * @return WhereQuery
386
     * @throws InvalidWhereQueryCriteriaException
387
     */
388
    public function whereUnkeyword(string $value): WhereQuery {
389
        return $this->where('UNKEYWORD', $value);
390
    }
391
392
    /**
393
     * @return WhereQuery
394
     * @throws InvalidWhereQueryCriteriaException
395
     */
396
    public function whereUnanswered(): WhereQuery {
397
        return $this->where('UNANSWERED');
398
    }
399
400
    /**
401
     * @return WhereQuery
402
     * @throws InvalidWhereQueryCriteriaException
403
     */
404
    public function whereUndeleted(): WhereQuery {
405
        return $this->where('UNDELETED');
406
    }
407
408
    /**
409
     * @return WhereQuery
410
     * @throws InvalidWhereQueryCriteriaException
411
     */
412
    public function whereUnflagged(): WhereQuery {
413
        return $this->where('UNFLAGGED');
414
    }
415
416
    /**
417
     * @return WhereQuery
418
     * @throws InvalidWhereQueryCriteriaException
419
     */
420
    public function whereUnseen(): WhereQuery {
421
        return $this->where('UNSEEN');
422
    }
423
424
    /**
425
     * @return WhereQuery
426
     * @throws InvalidWhereQueryCriteriaException
427
     */
428
    public function whereNoXSpam(): WhereQuery {
429
        return $this->where("CUSTOM X-Spam-Flag NO");
430
    }
431
432
    /**
433
     * @return WhereQuery
434
     * @throws InvalidWhereQueryCriteriaException
435
     */
436
    public function whereIsXSpam(): WhereQuery {
437
        return $this->where("CUSTOM X-Spam-Flag YES");
438
    }
439
440
    /**
441
     * Search for a specific header value
442
     * @param $header
443
     * @param $value
444
     *
445
     * @return WhereQuery
446
     * @throws InvalidWhereQueryCriteriaException
447
     */
448
    public function whereHeader($header, $value): WhereQuery {
449
        return $this->where("CUSTOM HEADER $header $value");
450
    }
451
452
    /**
453
     * Search for a specific message id
454
     * @param $messageId
455
     *
456
     * @return WhereQuery
457
     * @throws InvalidWhereQueryCriteriaException
458
     */
459
    public function whereMessageId($messageId): WhereQuery {
460
        return $this->whereHeader("Message-ID", $messageId);
461
    }
462
463
    /**
464
     * Search for a specific message id
465
     * @param $messageId
466
     *
467
     * @return WhereQuery
468
     * @throws InvalidWhereQueryCriteriaException
469
     */
470
    public function whereInReplyTo($messageId): WhereQuery {
471
        return $this->whereHeader("In-Reply-To", $messageId);
472
    }
473
474
    /**
475
     * @param $country_code
476
     *
477
     * @return WhereQuery
478
     * @throws InvalidWhereQueryCriteriaException
479
     */
480
    public function whereLanguage($country_code): WhereQuery {
481
        return $this->where("Content-Language $country_code");
482
    }
483
484
    /**
485
     * Get message be it UID.
486
     *
487
     * @param int|string $uid
488
     *
489
     * @return WhereQuery
490
     * @throws InvalidWhereQueryCriteriaException
491
     */
492
    public function whereUid($uid): WhereQuery {
493
        return $this->where('UID', $uid);
494
    }
495
496
    /**
497
     * Get messages by their UIDs.
498
     *
499
     * @param array<int, int> $uids
500
     *
501
     * @return WhereQuery
502
     * @throws InvalidWhereQueryCriteriaException
503
     */
504
    public function whereUidIn(array $uids): WhereQuery {
505
        $uids = implode(',', $uids);
506
        return $this->where('UID', $uids);
507
    }
508
509
    /**
510
     * Apply the callback if the given "value" is truthy.
511
     * copied from @url https://github.com/laravel/framework/blob/8.x/src/Illuminate/Support/Traits/Conditionable.php
512
     *
513
     * @param mixed $value
514
     * @param callable $callback
515
     * @param callable|null $default
516
     * @return $this|mixed
517
     */
518
    public function when($value, callable $callback, $default = null) {
519
        if ($value) {
520
            return $callback($this, $value) ?: $this;
521
        } elseif ($default) {
522
            return $default($this, $value) ?: $this;
523
        }
524
525
        return $this;
526
    }
527
528
    /**
529
     * Apply the callback if the given "value" is falsy.
530
     * copied from @url https://github.com/laravel/framework/blob/8.x/src/Illuminate/Support/Traits/Conditionable.php
531
     *
532
     * @param mixed $value
533
     * @param callable $callback
534
     * @param callable|null $default
535
     * @return $this|mixed
536
     */
537
    public function unless($value, callable $callback, $default = null) {
538
        if (!$value) {
539
            return $callback($this, $value) ?: $this;
540
        } elseif ($default) {
541
            return $default($this, $value) ?: $this;
542
        }
543
544
        return $this;
545
    }
546
}