Passed
Pull Request — master (#62)
by Sergei
10:47
created

ArrayCollection::getModifiers()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
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
    /**
28
     * @param array $data
29
     * @param ModifierInterface|ModifierInterface[] $modifiers
30
     */
31
    public function __construct(array $data = [], $modifiers = [])
32
    {
33
        $this->data = $data;
34
        $this->modifiers = is_array($modifiers) ? $modifiers : [$modifiers];
35
    }
36
37
    public function withData(array $data): self
38
    {
39
        $new = clone $this;
40
        $new->data = $data;
41
        return $new;
42
    }
43
44
    /**
45
     * @return ModifierInterface[]
46
     */
47
    public function getModifiers(): array
48
    {
49
        return $this->modifiers;
50
    }
51
52
    public function withModifier(ModifierInterface ...$modifiers): self
53
    {
54
        return $this->withModifiers($modifiers);
55
    }
56
57
    /**
58
     * @param ModifierInterface[] $modifiers
59
     * @return self
60
     */
61
    public function withModifiers(array $modifiers): self
62
    {
63
        $new = clone $this;
64
        $new->modifiers = $modifiers;
65
        return $new;
66
    }
67
68
    public function withAddedModifier(ModifierInterface ...$modifiers): self
69
    {
70
        return $this->withAddedModifiers($modifiers);
71
    }
72
73
    /**
74
     * @param ModifierInterface[] $modifiers
75
     * @return self
76
     */
77
    public function withAddedModifiers(array $modifiers): self
78
    {
79
        $new = clone $this;
80
        $new->modifiers = array_merge($new->modifiers, $modifiers);
81
        return $new;
82
    }
83
84
    /**
85
     * @param array|self ...$args
86
     * @return self
87
     */
88
    public function mergeWith(...$args): self
89
    {
90
        array_unshift($args, $this);
91
92
        $arrays = [];
93
        foreach ($args as $arg) {
94
            $arrays[] = $arg instanceof self ? $arg->data : $arg;
95
        }
96
97
        $collections = [];
98
        foreach ($args as $index => $arg) {
99
            $collection = $arg instanceof self ? $arg : new self($arg);
100
            foreach ($collection->getModifiers() as $modifier) {
101
                if ($modifier instanceof BeforeMergeModifierInterface) {
102
                    $collection->data = $modifier->beforeMerge($arrays, $index);
103
                }
104
            }
105
            $collections[$index] = $collection;
106
        }
107
108
        $collection = $this->merge(...$collections);
109
110
        foreach ($collection->getModifiers() as $modifier) {
111
            if ($modifier instanceof AfterMergeModifierInterface) {
112
                $collection->data = $modifier->afterMerge($collection->data);
113
            }
114
        }
115
116
        return $collection;
117
    }
118
119
    /**
120
     * @param array|self ...$args
121
     * @return self
122
     */
123
    private function merge(...$args): self
124
    {
125
        $collection = new ArrayCollection();
126
127
        while (!empty($args)) {
128
            $array = array_shift($args);
129
130
            if ($array instanceof ArrayCollection) {
131
                $collection->modifiers = array_merge($collection->modifiers, $array->modifiers);
132
                $collection->data = $this->merge($collection->data, $array->data)->data;
133
                continue;
134
            }
135
136
            foreach ($array as $k => $v) {
137
                if (is_int($k)) {
138
                    if (array_key_exists($k, $collection->data)) {
139
                        if ($collection[$k] !== $v) {
140
                            $collection->data[] = $v;
141
                        }
142
                    } else {
143
                        $collection->data[$k] = $v;
144
                    }
145
                } 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

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