Passed
Pull Request — master (#62)
by Sergei
14:17
created

ArrayCollection::merge()   B

Complexity

Conditions 10
Paths 3

Size

Total Lines 31
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 19
c 1
b 0
f 0
dl 0
loc 31
rs 7.6666
cc 10
nc 3
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Arrays\Collection;
6
7
use ArrayAccess;
8
use Countable;
9
use IteratorAggregate;
10
use Yiisoft\Arrays\ArrayAccessTrait;
11
use Yiisoft\Arrays\Collection\Modifier\ModifierInterface\AfterMergeModifierInterface;
12
use Yiisoft\Arrays\Collection\Modifier\ModifierInterface\BeforeMergeModifierInterface;
13
use Yiisoft\Arrays\Collection\Modifier\ModifierInterface\DataModifierInterface;
14
use Yiisoft\Arrays\Collection\Modifier\ModifierInterface\ModifierInterface;
15
16
final class ArrayCollection implements ArrayAccess, IteratorAggregate, Countable
17
{
18
    use ArrayAccessTrait;
19
20
    private array $data;
21
22
    /**
23
     * @var ModifierInterface[]
24
     */
25
    private array $modifiers = [];
26
27
    public function __construct(array $data = [])
28
    {
29
        $this->data = $data;
30
    }
31
32
    public function withData(array $data): self
33
    {
34
        $new = clone $this;
35
        $new->data = $data;
36
        return $new;
37
    }
38
39
    /**
40
     * @return ModifierInterface[]
41
     */
42
    public function getModifiers(): array
43
    {
44
        return $this->modifiers;
45
    }
46
47
    public function withModifier(ModifierInterface ...$modifiers): self
48
    {
49
        return $this->withModifiers($modifiers);
50
    }
51
52
    /**
53
     * @param ModifierInterface[] $modifiers
54
     * @return self
55
     */
56
    public function withModifiers(array $modifiers): self
57
    {
58
        $new = clone $this;
59
        $new->modifiers = $modifiers;
60
        return $new;
61
    }
62
63
    public function withAddedModifier(ModifierInterface ...$modifiers): self
64
    {
65
        return $this->withAddedModifiers($modifiers);
66
    }
67
68
    /**
69
     * @param ModifierInterface[] $modifiers
70
     * @return self
71
     */
72
    public function withAddedModifiers(array $modifiers): self
73
    {
74
        $new = clone $this;
75
        $new->modifiers = array_merge($new->modifiers, $modifiers);
76
        return $new;
77
    }
78
79
    /**
80
     * @param array|self ...$args
81
     * @return self
82
     */
83
    public function mergeWith(...$args): self
84
    {
85
        $arrays = [];
86
        foreach ($args as $arg) {
87
            $arrays[] = $arg instanceof self ? $arg->data : $arg;
88
        }
89
90
        $collections = [];
91
        foreach ($args as $index => $arg) {
92
            $collection = $arg instanceof self ? $arg : new self($arg);
93
            foreach ($collection->getModifiers() as $modifier) {
94
                if ($modifier instanceof BeforeMergeModifierInterface) {
95
                    $collection->data = $modifier->beforeMerge($arrays, $index);
96
                }
97
            }
98
            $collections[$index] = $collection;
99
        }
100
101
        $collection = $this->merge(...$collections);
102
103
        foreach ($collection->getModifiers() as $modifier) {
104
            if ($modifier instanceof AfterMergeModifierInterface) {
105
                $collection->data = $modifier->afterMerge($collection->data);
106
            }
107
        }
108
109
        return $collection;
110
    }
111
112
    /**
113
     * @param array|self ...$args
114
     * @return self
115
     */
116
    private function merge(...$args): self
117
    {
118
        $collection = new ArrayCollection();
119
120
        while (!empty($args)) {
121
            $array = array_shift($args);
122
123
            if ($array instanceof ArrayCollection) {
124
                $collection->modifiers = array_merge($collection->modifiers, $array->modifiers);
125
                $collection->data = $this->merge($collection->data, $array->data)->data;
126
                continue;
127
            }
128
129
            foreach ($array as $k => $v) {
130
                if (is_int($k)) {
131
                    if (array_key_exists($k, $collection->data)) {
132
                        if ($collection[$k] !== $v) {
133
                            $collection->data[] = $v;
134
                        }
135
                    } else {
136
                        $collection->data[$k] = $v;
137
                    }
138
                } elseif (static::isMergable($v) && isset($collection[$k]) && static::isMergable($collection[$k])) {
0 ignored issues
show
Bug Best Practice introduced by
The method Yiisoft\Arrays\Collectio...ollection::isMergable() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

138
                } elseif (static::/** @scrutinizer ignore-call */ isMergable($v) && isset($collection[$k]) && static::isMergable($collection[$k])) {
Loading history...
139
                    $collection->data[$k] = $this->merge($collection[$k], $v)->data;
140
                } else {
141
                    $collection->data[$k] = $v;
142
                }
143
            }
144
        }
145
146
        return $collection;
147
    }
148
149
    /**
150
     * @param mixed $value
151
     * @return bool
152
     */
153
    private function isMergable($value): bool
154
    {
155
        return is_array($value) || $value instanceof self;
156
    }
157
158
    public function toArray(): array
159
    {
160
        $array = $this->performArray($this->getIterator()->getArrayCopy());
161
162
        foreach ($this->modifiers as $modifier) {
163
            if ($modifier instanceof DataModifierInterface) {
164
                $array = $modifier->apply($array);
165
            }
166
        }
167
168
        return $array;
169
    }
170
171
    private function performArray(array $array): array
172
    {
173
        foreach ($array as $k => $v) {
174
            if ($v instanceof ArrayCollection) {
175
                $array[$k] = $v->toArray();
176
            } elseif (is_array($v)) {
177
                $array[$k] = $this->performArray($v);
178
            } else {
179
                $array[$k] = $v;
180
            }
181
        }
182
        return $array;
183
    }
184
185
    /**
186
     * @param mixed $offset the offset to set element
187
     * @param mixed $value the element value
188
     */
189
    public function offsetSet($offset, $value): void
190
    {
191
        throw new ArrayCollectionIsImmutableException();
192
    }
193
194
    /**
195
     * @param mixed $offset the offset to unset element
196
     */
197
    public function offsetUnset($offset): void
198
    {
199
        throw new ArrayCollectionIsImmutableException();
200
    }
201
}
202