Completed
Push — 6.0 ( 9f7271...939c37 )
by liu
06:28
created

Collection   F

Complexity

Total Complexity 107

Size/Duplication

Total Lines 620
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 146
dl 0
loc 620
ccs 0
cts 189
cp 0
rs 2
c 0
b 0
f 0
wmc 107

46 Methods

Rating   Name   Duplication   Size   Complexity  
A diff() 0 18 6
A push() 0 6 2
A shuffle() 0 7 1
B dictionary() 0 17 9
A make() 0 3 1
A column() 0 3 1
A intersect() 0 18 6
A shift() 0 3 1
A keys() 0 3 1
A toArray() 0 5 3
A map() 0 3 1
A chunk() 0 9 2
A whereLike() 0 3 1
A merge() 0 3 1
A whereNotLike() 0 3 1
A whereNotBetween() 0 3 1
A filter() 0 7 2
A whereNotIn() 0 3 1
A __construct() 0 3 1
A each() 0 13 4
D where() 0 50 29
A pop() 0 3 1
A sort() 0 12 4
A all() 0 3 1
A values() 0 3 1
A reduce() 0 3 1
A reverse() 0 3 1
A whereIn() 0 3 1
A whereBetween() 0 3 1
A flip() 0 3 1
A order() 0 7 2
A unshift() 0 6 2
A isEmpty() 0 3 1
A offsetSet() 0 6 2
A __toString() 0 3 1
A convertToArray() 0 7 2
A last() 0 3 1
A offsetUnset() 0 3 1
A jsonSerialize() 0 3 1
A toJson() 0 3 1
A getIterator() 0 3 1
A count() 0 3 1
A offsetExists() 0 3 1
A slice() 0 3 1
A offsetGet() 0 3 1
A first() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Collection 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 Collection, and based on these observations, apply Extract Interface, too.

1
<?php
2
// +----------------------------------------------------------------------
3
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
6
// +----------------------------------------------------------------------
7
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
8
// +----------------------------------------------------------------------
9
// | Author: zhangyajun <[email protected]>
10
// +----------------------------------------------------------------------
11
declare (strict_types = 1);
12
13
namespace think;
14
15
use ArrayAccess;
16
use ArrayIterator;
17
use Countable;
18
use IteratorAggregate;
19
use JsonSerializable;
20
21
/**
22
 * 数据集管理类
23
 */
24
class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
25
{
26
    /**
27
     * 数据集数据
28
     * @var array
29
     */
30
    protected $items = [];
31
32
    public function __construct($items = [])
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __construct()
Loading history...
33
    {
34
        $this->items = $this->convertToArray($items);
35
    }
36
37
    public static function make($items = [])
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function make()
Loading history...
38
    {
39
        return new static($items);
40
    }
41
42
    /**
43
     * 是否为空
44
     * @access public
45
     * @return bool
46
     */
47
    public function isEmpty(): bool
48
    {
49
        return empty($this->items);
50
    }
51
52
    public function toArray(): array
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function toArray()
Loading history...
53
    {
54
        return array_map(function ($value) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
55
            return ($value instanceof Model || $value instanceof self) ? $value->toArray() : $value;
56
        }, $this->items);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
57
    }
58
59
    public function all(): array
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function all()
Loading history...
60
    {
61
        return $this->items;
62
    }
63
64
    /**
65
     * 合并数组
66
     *
67
     * @access public
68
     * @param  mixed $items 数据
69
     * @return static
70
     */
71
    public function merge($items)
72
    {
73
        return new static(array_merge($this->items, $this->convertToArray($items)));
74
    }
75
76
    /**
77
     * 按指定键整理数据
78
     *
79
     * @access public
80
     * @param  mixed  $items    数据
81
     * @param  string $indexKey 键名
82
     * @return array
83
     */
84
    public function dictionary($items = null, string &$indexKey = null)
85
    {
86
        if ($items instanceof self || $items instanceof Paginator) {
87
            $items = $items->all();
88
        }
89
90
        $items = is_null($items) ? $this->items : $items;
91
92
        if ($items && empty($indexKey)) {
93
            $indexKey = is_array($items[0]) ? 'id' : $items[0]->getPk();
94
        }
95
96
        if (isset($indexKey) && is_string($indexKey)) {
97
            return array_column($items, null, $indexKey);
98
        }
99
100
        return $items;
101
    }
102
103
    /**
104
     * 比较数组,返回差集
105
     *
106
     * @access public
107
     * @param  mixed  $items    数据
108
     * @param  string $indexKey 指定比较的键名
109
     * @return static
110
     */
111
    public function diff($items, string $indexKey = null)
112
    {
113
        if ($this->isEmpty() || is_scalar($this->items[0])) {
114
            return new static(array_diff($this->items, $this->convertToArray($items)));
115
        }
116
117
        $diff       = [];
118
        $dictionary = $this->dictionary($items, $indexKey);
119
120
        if (is_string($indexKey)) {
121
            foreach ($this->items as $item) {
122
                if (!isset($dictionary[$item[$indexKey]])) {
123
                    $diff[] = $item;
124
                }
125
            }
126
        }
127
128
        return new static($diff);
129
    }
130
131
    /**
132
     * 比较数组,返回交集
133
     *
134
     * @access public
135
     * @param  mixed  $items    数据
136
     * @param  string $indexKey 指定比较的键名
137
     * @return static
138
     */
139
    public function intersect($items, string $indexKey = null)
140
    {
141
        if ($this->isEmpty() || is_scalar($this->items[0])) {
142
            return new static(array_diff($this->items, $this->convertToArray($items)));
143
        }
144
145
        $intersect  = [];
146
        $dictionary = $this->dictionary($items, $indexKey);
147
148
        if (is_string($indexKey)) {
149
            foreach ($this->items as $item) {
150
                if (isset($dictionary[$item[$indexKey]])) {
151
                    $intersect[] = $item;
152
                }
153
            }
154
        }
155
156
        return new static($intersect);
157
    }
158
159
    /**
160
     * 交换数组中的键和值
161
     *
162
     * @access public
163
     * @return static
164
     */
165
    public function flip()
166
    {
167
        return new static(array_flip($this->items));
168
    }
169
170
    /**
171
     * 返回数组中所有的键名
172
     *
173
     * @access public
174
     * @return static
175
     */
176
    public function keys()
177
    {
178
        return new static(array_keys($this->items));
179
    }
180
181
    /**
182
     * 返回数组中所有的值组成的新 Collection 实例
183
     * @access public
184
     * @return static
185
     */
186
    public function values()
187
    {
188
        return new static(array_values($this->items));
189
    }
190
191
    /**
192
     * 删除数组的最后一个元素(出栈)
193
     *
194
     * @access public
195
     * @return mixed
196
     */
197
    public function pop()
198
    {
199
        return array_pop($this->items);
200
    }
201
202
    /**
203
     * 通过使用用户自定义函数,以字符串返回数组
204
     *
205
     * @access public
206
     * @param  callable $callback 调用方法
207
     * @param  mixed    $initial
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
208
     * @return mixed
209
     */
210
    public function reduce(callable $callback, $initial = null)
211
    {
212
        return array_reduce($this->items, $callback, $initial);
213
    }
214
215
    /**
216
     * 以相反的顺序返回数组。
217
     *
218
     * @access public
219
     * @return static
220
     */
221
    public function reverse()
222
    {
223
        return new static(array_reverse($this->items));
224
    }
225
226
    /**
227
     * 删除数组中首个元素,并返回被删除元素的值
228
     *
229
     * @access public
230
     * @return mixed
231
     */
232
    public function shift()
233
    {
234
        return array_shift($this->items);
235
    }
236
237
    /**
238
     * 在数组结尾插入一个元素
239
     * @access public
240
     * @param  mixed  $value 元素
241
     * @param  string $key KEY
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 1 found
Loading history...
242
     * @return void
243
     */
244
    public function push($value, string $key = null): void
245
    {
246
        if (is_null($key)) {
247
            $this->items[] = $value;
248
        } else {
249
            $this->items[$key] = $value;
250
        }
251
    }
252
253
    /**
254
     * 把一个数组分割为新的数组块.
255
     *
256
     * @access public
257
     * @param  int  $size 块大小
0 ignored issues
show
Coding Style introduced by
Expected 9 spaces after parameter name; 1 found
Loading history...
258
     * @param  bool $preserveKeys
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
259
     * @return static
260
     */
261
    public function chunk(int $size, bool $preserveKeys = false)
262
    {
263
        $chunks = [];
264
265
        foreach (array_chunk($this->items, $size, $preserveKeys) as $chunk) {
266
            $chunks[] = new static($chunk);
267
        }
268
269
        return new static($chunks);
270
    }
271
272
    /**
273
     * 在数组开头插入一个元素
274
     * @access public
275
     * @param mixed  $value 元素
276
     * @param string $key KEY
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 1 found
Loading history...
277
     * @return void
278
     */
279
    public function unshift($value, string $key = null): void
280
    {
281
        if (is_null($key)) {
282
            array_unshift($this->items, $value);
283
        } else {
284
            $this->items = [$key => $value] + $this->items;
285
        }
286
    }
287
288
    /**
289
     * 给每个元素执行个回调
290
     *
291
     * @access public
292
     * @param  callable $callback 回调
293
     * @return $this
294
     */
295
    public function each(callable $callback)
296
    {
297
        foreach ($this->items as $key => $item) {
298
            $result = $callback($item, $key);
299
300
            if (false === $result) {
301
                break;
302
            } elseif (!is_object($item)) {
303
                $this->items[$key] = $result;
304
            }
305
        }
306
307
        return $this;
308
    }
309
310
    /**
311
     * 用回调函数处理数组中的元素
312
     * @access public
313
     * @param  callable|null $callback 回调
314
     * @return static
315
     */
316
    public function map(callable $callback)
317
    {
318
        return new static(array_map($callback, $this->items));
319
    }
320
321
    /**
322
     * 用回调函数过滤数组中的元素
323
     * @access public
324
     * @param  callable|null $callback 回调
325
     * @return static
326
     */
327
    public function filter(callable $callback = null)
328
    {
329
        if ($callback) {
330
            return new static(array_filter($this->items, $callback));
331
        }
332
333
        return new static(array_filter($this->items));
334
    }
335
336
    /**
337
     * 根据字段条件过滤数组中的元素
338
     * @access public
339
     * @param  string $field 字段名
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces after parameter name; 1 found
Loading history...
340
     * @param  mixed  $operator 操作符
341
     * @param  mixed  $value 数据
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces after parameter name; 1 found
Loading history...
342
     * @return static
343
     */
344
    public function where(string $field, $operator, $value = null)
345
    {
346
        if (is_null($value)) {
347
            $value    = $operator;
348
            $operator = '=';
349
        }
350
351
        return $this->filter(function ($data) use ($field, $operator, $value) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
352
            if (strpos($field, '.')) {
353
                list($field, $relation) = explode('.', $field);
354
355
                $result = $data[$field][$relation] ?? null;
356
            } else {
357
                $result = $data[$field] ?? null;
358
            }
359
360
            switch ($operator) {
361
                case '===':
362
                    return $result === $value;
363
                case '!==':
364
                    return $result !== $value;
365
                case '!=':
366
                case '<>':
367
                    return $result != $value;
368
                case '>':
369
                    return $result > $value;
370
                case '>=':
371
                    return $result >= $value;
372
                case '<':
373
                    return $result < $value;
374
                case '<=':
375
                    return $result <= $value;
376
                case 'like':
377
                    return is_string($result) && false !== strpos($result, $value);
378
                case 'not like':
379
                    return is_string($result) && false === strpos($result, $value);
380
                case 'in':
381
                    return is_scalar($result) && in_array($result, $value, true);
382
                case 'not in':
383
                    return is_scalar($result) && !in_array($result, $value, true);
384
                case 'between':
385
                    list($min, $max) = is_string($value) ? explode(',', $value) : $value;
386
                    return is_scalar($result) && $result >= $min && $result <= $max;
387
                case 'not between':
388
                    list($min, $max) = is_string($value) ? explode(',', $value) : $value;
389
                    return is_scalar($result) && $result > $max || $result < $min;
390
                case '==':
391
                case '=':
392
                default:
393
                    return $result == $value;
394
            }
395
        });
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
396
    }
397
398
    /**
399
     * LIKE过滤
400
     * @access public
401
     * @param  string $field 字段名
402
     * @param  string $value 数据
403
     * @return static
404
     */
405
    public function whereLike(string $field, string $value)
406
    {
407
        return $this->where($field, 'like', $value);
408
    }
409
410
    /**
411
     * NOT LIKE过滤
412
     * @access public
413
     * @param  string $field 字段名
414
     * @param  string $value 数据
415
     * @return static
416
     */
417
    public function whereNotLike(string $field, string $value)
418
    {
419
        return $this->where($field, 'not like', $value);
420
    }
421
422
    /**
423
     * IN过滤
424
     * @access public
425
     * @param  string $field 字段名
426
     * @param  array  $value 数据
427
     * @return static
428
     */
429
    public function whereIn(string $field, array $value)
430
    {
431
        return $this->where($field, 'in', $value);
432
    }
433
434
    /**
435
     * NOT IN过滤
436
     * @access public
437
     * @param  string $field 字段名
438
     * @param  array  $value 数据
439
     * @return static
440
     */
441
    public function whereNotIn(string $field, array $value)
442
    {
443
        return $this->where($field, 'not in', $value);
444
    }
445
446
    /**
447
     * BETWEEN 过滤
448
     * @access public
449
     * @param  string $field 字段名
450
     * @param  mixed  $value 数据
451
     * @return static
452
     */
453
    public function whereBetween(string $field, $value)
454
    {
455
        return $this->where($field, 'between', $value);
456
    }
457
458
    /**
459
     * NOT BETWEEN 过滤
460
     * @access public
461
     * @param  string $field 字段名
462
     * @param  mixed  $value 数据
463
     * @return static
464
     */
465
    public function whereNotBetween(string $field, $value)
466
    {
467
        return $this->where($field, 'not between', $value);
468
    }
469
470
    /**
471
     * 返回数据中指定的一列
472
     * @access public
473
     * @param string $columnKey 键名
474
     * @param string $indexKey  作为索引值的列
475
     * @return array
476
     */
477
    public function column(string $columnKey, string $indexKey = null)
478
    {
479
        return array_column($this->items, $columnKey, $indexKey);
480
    }
481
482
    /**
483
     * 对数组排序
484
     *
485
     * @access public
486
     * @param  callable|null $callback 回调
487
     * @return static
488
     */
489
    public function sort(callable $callback = null)
490
    {
491
        $items = $this->items;
492
493
        $callback = $callback ?: function ($a, $b) {
494
            return $a == $b ? 0 : (($a < $b) ? -1 : 1);
495
496
        };
497
498
        uasort($items, $callback);
499
500
        return new static($items);
501
    }
502
503
    /**
504
     * 指定字段排序
505
     * @access public
506
     * @param  string $field 排序字段
507
     * @param  string $order 排序
508
     * @return $this
509
     */
510
    public function order(string $field, string $order = null)
511
    {
512
        return $this->sort(function ($a, $b) use ($field, $order) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
513
            $fieldA = $a[$field] ?? null;
514
            $fieldB = $b[$field] ?? null;
515
516
            return 'desc' == strtolower($order) ? strcmp($fieldB, $fieldA) : strcmp($fieldA, $fieldB);
517
        });
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
518
    }
519
520
    /**
521
     * 将数组打乱
522
     *
523
     * @access public
524
     * @return static
525
     */
526
    public function shuffle()
527
    {
528
        $items = $this->items;
529
530
        shuffle($items);
531
532
        return new static($items);
533
    }
534
535
    /**
536
     * 获取最后一个单元数据
537
     *
538
     * @access public
539
     * @return mixed
540
     */
541
    public function first()
542
    {
543
        return reset($this->items);
544
    }
545
546
    /**
547
     * 获取第一个单元数据
548
     *
549
     * @access public
550
     * @return mixed
551
     */
552
    public function last()
553
    {
554
        return end($this->items);
555
    }
556
557
    /**
558
     * 截取数组
559
     *
560
     * @access public
561
     * @param  int  $offset 起始位置
0 ignored issues
show
Coding Style introduced by
Expected 7 spaces after parameter name; 1 found
Loading history...
562
     * @param  int  $length 截取长度
0 ignored issues
show
Coding Style introduced by
Expected 7 spaces after parameter name; 1 found
Loading history...
563
     * @param  bool $preserveKeys preserveKeys
564
     * @return static
565
     */
566
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
567
    {
568
        return new static(array_slice($this->items, $offset, $length, $preserveKeys));
569
    }
570
571
    // ArrayAccess
572
    public function offsetExists($offset)
0 ignored issues
show
Coding Style introduced by
You must use "/**" style comments for a function comment
Loading history...
573
    {
574
        return array_key_exists($offset, $this->items);
575
    }
576
577
    public function offsetGet($offset)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function offsetGet()
Loading history...
578
    {
579
        return $this->items[$offset];
580
    }
581
582
    public function offsetSet($offset, $value)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function offsetSet()
Loading history...
583
    {
584
        if (is_null($offset)) {
585
            $this->items[] = $value;
586
        } else {
587
            $this->items[$offset] = $value;
588
        }
589
    }
590
591
    public function offsetUnset($offset)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function offsetUnset()
Loading history...
592
    {
593
        unset($this->items[$offset]);
594
    }
595
596
    //Countable
597
    public function count()
0 ignored issues
show
Coding Style introduced by
You must use "/**" style comments for a function comment
Loading history...
598
    {
599
        return count($this->items);
600
    }
601
602
    //IteratorAggregate
603
    public function getIterator()
0 ignored issues
show
Coding Style introduced by
You must use "/**" style comments for a function comment
Loading history...
604
    {
605
        return new ArrayIterator($this->items);
606
    }
607
608
    //JsonSerializable
609
    public function jsonSerialize()
0 ignored issues
show
Coding Style introduced by
You must use "/**" style comments for a function comment
Loading history...
610
    {
611
        return $this->toArray();
612
    }
613
614
    /**
615
     * 转换当前数据集为JSON字符串
616
     * @access public
617
     * @param  integer $options json参数
618
     * @return string
619
     */
620
    public function toJson(int $options = JSON_UNESCAPED_UNICODE)
621
    {
622
        return json_encode($this->toArray(), $options);
623
    }
624
625
    public function __toString()
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __toString()
Loading history...
626
    {
627
        return $this->toJson();
628
    }
629
630
    /**
631
     * 转换成数组
632
     *
633
     * @access public
634
     * @param  mixed $items 数据
635
     * @return array
636
     */
637
    protected function convertToArray($items): array
638
    {
639
        if ($items instanceof self) {
640
            return $items->all();
641
        }
642
643
        return (array) $items;
644
    }
645
}
646