Passed
Pull Request — master (#62)
by Sergei
12:43
created

ArrayCollection::withModifier()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 1
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
        $arrays = [];
91
        foreach ($args as $arg) {
92
            $arrays[] = $arg instanceof self ? $arg->data : $arg;
93
        }
94
95
        $collections = [];
96
        foreach ($args as $index => $arg) {
97
            $collection = $arg instanceof self ? $arg : new self($arg);
98
            foreach ($collection->getModifiers() as $modifier) {
99
                if ($modifier instanceof BeforeMergeModifierInterface) {
100
                    $collection->data = $modifier->beforeMerge($arrays, $index);
101
                }
102
            }
103
            $collections[$index] = $collection;
104
        }
105
106
        $collection = $this->merge(...$collections);
107
108
        foreach ($collection->getModifiers() as $modifier) {
109
            if ($modifier instanceof AfterMergeModifierInterface) {
110
                $collection->data = $modifier->afterMerge($collection->data);
111
            }
112
        }
113
114
        return $collection;
115
    }
116
117
    /**
118
     * @param array|self ...$args
119
     * @return self
120
     */
121
    private function merge(...$args): self
122
    {
123
        $collection = new ArrayCollection();
124
125
        while (!empty($args)) {
126
            $array = array_shift($args);
127
128
            if ($array instanceof ArrayCollection) {
129
                $collection->modifiers = array_merge($collection->modifiers, $array->modifiers);
130
                $collection->data = $this->merge($collection->data, $array->data)->data;
131
                continue;
132
            }
133
134
            foreach ($array as $k => $v) {
135
                if (is_int($k)) {
136
                    if (array_key_exists($k, $collection->data)) {
137
                        if ($collection[$k] !== $v) {
138
                            $collection->data[] = $v;
139
                        }
140
                    } else {
141
                        $collection->data[$k] = $v;
142
                    }
143
                } 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

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