Passed
Push — master ( 5545f1...83deac )
by Kirill
03:22
created

Aggregator   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 214
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 49
c 1
b 0
f 0
dl 0
loc 214
rs 9.84
wmc 32

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __get() 0 3 1
A find() 0 9 4
A getIterator() 0 3 1
A offsetGet() 0 3 1
A replace() 0 9 3
A has() 0 9 4
A isEmpty() 0 3 1
A offsetExists() 0 3 1
A render() 0 9 2
A offsetSet() 0 3 1
A count() 0 3 1
A offsetUnset() 0 3 1
A add() 0 20 5
A remove() 0 9 4
A __construct() 0 4 1
A get() 0 3 1
1
<?php
2
3
/**
4
 * Spiral Framework.
5
 *
6
 * @license   MIT
7
 * @author    Anton Titov (Wolfy-J)
8
 */
9
10
declare(strict_types=1);
11
12
namespace Spiral\Reactor;
13
14
use ArrayAccess;
15
use ArrayIterator;
16
use Countable;
17
use IteratorAggregate;
18
use ReflectionObject;
19
use Spiral\Reactor\Exception\ReactorException;
20
21
/**
22
 * Provides ability to aggregate specific set of elements (type constrained), render them or
23
 * apply set of operations.
24
 */
25
class Aggregator extends AbstractDeclaration implements
26
    ArrayAccess,
27
    IteratorAggregate,
28
    Countable,
29
    ReplaceableInterface
30
{
31
    /**
32
     * @var array
33
     */
34
    private $allowed;
35
36
    /**
37
     * @var DeclarationInterface[]
38
     */
39
    private $elements;
40
41
    /**
42
     * @param array $allowed
43
     * @param array $elements
44
     */
45
    public function __construct(array $allowed, array $elements = [])
46
    {
47
        $this->allowed = $allowed;
48
        $this->elements = $elements;
49
    }
50
51
    /**
52
     * Get element by it's name.
53
     *
54
     * @param string $name
55
     * @return DeclarationInterface
56
     * @throws ReactorException
57
     */
58
    public function __get($name)
59
    {
60
        return $this->get($name);
61
    }
62
63
    /**
64
     * @return bool
65
     */
66
    public function isEmpty(): bool
67
    {
68
        return empty($this->elements);
69
    }
70
71
    /**
72
     * @return int
73
     */
74
    public function count(): int
75
    {
76
        return count($this->elements);
77
    }
78
79
    /**
80
     * Check if aggregation has named element with given name.
81
     *
82
     * @param string $name
83
     * @return bool
84
     */
85
    public function has(string $name): bool
86
    {
87
        foreach ($this->elements as $element) {
88
            if ($element instanceof NamedInterface && $element->getName() === $name) {
89
                return true;
90
            }
91
        }
92
93
        return false;
94
    }
95
96
    /**
97
     * Add new element.
98
     *
99
     * @param DeclarationInterface $element
100
     * @return self
101
     * @throws ReactorException
102
     */
103
    public function add(DeclarationInterface $element): Aggregator
104
    {
105
        $reflector = new ReflectionObject($element);
106
107
        $allowed = false;
108
        foreach ($this->allowed as $class) {
109
            if ($reflector->isSubclassOf($class) || get_class($element) === $class) {
110
                $allowed = true;
111
                break;
112
            }
113
        }
114
115
        if (!$allowed) {
116
            $type = get_class($element);
117
            throw new ReactorException("Elements with type '{$type}' are not allowed");
118
        }
119
120
        $this->elements[] = $element;
121
122
        return $this;
123
    }
124
125
    /**
126
     * Get named element by it's name.
127
     *
128
     * @param string $name
129
     * @return DeclarationInterface
130
     * @throws ReactorException
131
     */
132
    public function get(string $name)
133
    {
134
        return $this->find($name);
135
    }
136
137
    /**
138
     * Remove element by it's name.
139
     *
140
     * @param string $name
141
     * @return self
142
     */
143
    public function remove(string $name): Aggregator
144
    {
145
        foreach ($this->elements as $index => $element) {
146
            if ($element instanceof NamedInterface && $element->getName() === $name) {
147
                unset($this->elements[$index]);
148
            }
149
        }
150
151
        return $this;
152
    }
153
154
    /**
155
     * @return ArrayIterator
156
     */
157
    public function getIterator(): ArrayIterator
158
    {
159
        return new ArrayIterator($this->elements);
160
    }
161
162
    /**
163
     * {@inheritdoc}
164
     */
165
    public function offsetExists($offset)
166
    {
167
        return $this->has($offset);
168
    }
169
170
    /**
171
     * {@inheritdoc}
172
     */
173
    public function offsetGet($offset)
174
    {
175
        return $this->get($offset);
176
    }
177
178
    /**
179
     * {@inheritdoc}
180
     */
181
    public function offsetSet($offset, $value): void
182
    {
183
        $this->remove($offset)->add($value);
184
    }
185
186
    /**
187
     * {@inheritdoc}
188
     */
189
    public function offsetUnset($offset): void
190
    {
191
        $this->remove($offset);
192
    }
193
194
    /**
195
     * {@inheritdoc}
196
     * @return self
197
     */
198
    public function replace($search, $replace): Aggregator
199
    {
200
        foreach ($this->elements as $element) {
201
            if ($element instanceof ReplaceableInterface) {
202
                $element->replace($search, $replace);
203
            }
204
        }
205
206
        return $this;
207
    }
208
209
    /**
210
     * {@inheritdoc}
211
     */
212
    public function render(int $indentLevel = 0): string
213
    {
214
        $result = '';
215
216
        foreach ($this->elements as $element) {
217
            $result .= $element->render($indentLevel) . "\n\n";
218
        }
219
220
        return rtrim($result, "\n");
221
    }
222
223
    /**
224
     * Find element by it's name (NamedDeclarations only).
225
     *
226
     * @param string $name
227
     * @return DeclarationInterface
228
     * @throws ReactorException When unable to find.
229
     */
230
    protected function find(string $name): DeclarationInterface
231
    {
232
        foreach ($this->elements as $element) {
233
            if ($element instanceof NamedInterface && $element->getName() === $name) {
234
                return $element;
235
            }
236
        }
237
238
        throw new ReactorException("Unable to find element '{$name}'");
239
    }
240
}
241