HandlersList::accepts()   B
last analyzed

Complexity

Conditions 7
Paths 6

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 7.392

Importance

Changes 0
Metric Value
eloc 10
c 0
b 0
f 0
dl 0
loc 18
ccs 8
cts 10
cp 0.8
rs 8.8333
cc 7
nc 6
nop 1
crap 7.392
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Flying\HandlersList;
6
7
use Flying\HandlersList\Exception\InvalidHandlerConstraintException;
8
use Flying\HandlersList\Exception\InvalidHandlerException;
9
10
class HandlersList implements HandlersListInterface
11
{
12
    /**
13
     * @var object[]
14
     */
15
    private array $handlers;
16
    private ?string $constraint = null;
17
18
    /**
19
     * @param iterable<object> $handlers
20
     * @throws InvalidHandlerException
21
     */
22 64
    public function __construct(iterable $handlers = [], ?string $constraint = null)
23
    {
24 64
        $this->setConstraint($constraint);
25 62
        $this->store($handlers);
26 56
        $this->update();
27 56
    }
28
29 30
    public function accepts($class): bool
30
    {
31 30
        if (\is_object($class)) {
32 10
            return $this->constraint ? \is_a($class, $this->constraint, true) : true;
33
        }
34
35 20
        if (\is_string($class) && \class_exists($class)) {
36 10
            if ($this->constraint === null) {
37 4
                return true;
38
            }
39
            try {
40 6
                return (new \ReflectionClass($class))->implementsInterface($this->constraint);
41
            } catch (\ReflectionException $e) {
42
                return false;
43
            }
44
        }
45
46 10
        return false;
47
    }
48
49 4
    public function isEmpty(): bool
50
    {
51 4
        return empty($this->handlers);
52
    }
53
54 9
    public function contains(object $handler): bool
55
    {
56 9
        return \in_array($handler, $this->handlers, true);
57
    }
58
59
    /**
60
     * @return object[]
61
     */
62 2
    public function filter(callable $test): array
63
    {
64 2
        return array_values(array_filter($this->handlers, $test));
65
    }
66
67
    /**
68
     * Find handler by reducing list of available handlers using given test function
69
     *
70
     * @param callable $test Test function should accept object as single argument and return boolean
71
     * @return object|null
72
     */
73 2
    public function find(callable $test): ?object
74
    {
75 2
        return array_reduce(
76 2
            $this->handlers,
77 2
            fn(?object $found, object $current) => $found ?? ($test($current) ? $current : null)
78 2
        );
79
    }
80
81 3
    public function set(iterable $handlers): self
82
    {
83 3
        $this->store($handlers);
84 3
        $this->update();
85 3
        return $this;
86
    }
87
88 5
    public function add(object $handler): self
89
    {
90 5
        if (!$this->contains($handler)) {
91 5
            $this->handlers[] = $this->validate($handler);
92 5
            $this->update();
93
        }
94 5
        return $this;
95
    }
96
97 3
    public function remove(object $handler): self
98
    {
99 3
        $this->handlers = array_filter($this->handlers, fn(object $h) => $h !== $handler);
100 3
        $this->update();
101 3
        return $this;
102
    }
103
104 3
    public function clear(): self
105
    {
106 3
        $this->handlers = [];
107 3
        $this->update();
108 3
        return $this;
109
    }
110
111 6
    public function getConstraint(): ?string
112
    {
113 6
        return $this->constraint;
114
    }
115
116 16
    public function toArray(): array
117
    {
118 16
        return $this->handlers;
119
    }
120
121 4
    public function getIterator(): \Iterator
122
    {
123 4
        return new \ArrayIterator($this->handlers);
124
    }
125
126 12
    public function count(): int
127
    {
128 12
        return \count($this->handlers);
129
    }
130
131 64
    protected function setConstraint(?string $constraint): void
132
    {
133 64
        if (\is_string($constraint)) {
134
            try {
135 30
                new \ReflectionClass($constraint);
136 2
            } catch (\ReflectionException $e) {
137 2
                throw new InvalidHandlerConstraintException(sprintf('Given handler class constraint "%s" does not exists', $constraint));
138
            }
139
        }
140
141 62
        $this->constraint = $constraint;
142 62
    }
143
144 62
    protected function store(iterable $handlers): void
145
    {
146
        /** @noinspection PhpParamsInspection */
147 62
        $this->handlers = array_map(function($h) {
148 27
            return $this->validate($h);
149 62
        }, is_array($handlers) ? $handlers : iterator_to_array($handlers, false));
150 56
    }
151
152
    /**
153
     * @param mixed $handler
154
     * @return object
155
     * @throws InvalidHandlerException
156
     */
157 30
    protected function validate($handler): object
158
    {
159 30
        if (!is_object($handler)) {
160 2
            throw new InvalidHandlerException(sprintf('Handler should be an object, "%s" given instead', gettype($handler)));
161
        }
162
163 28
        if ($this->constraint === null) {
164 16
            return $handler;
165
        }
166
167 12
        if (!is_a($handler, $this->constraint)) {
168 4
            throw new InvalidHandlerException(sprintf('Handler "%s" should be instance of "%s"', get_class($handler), $this->constraint));
169
        }
170
171 10
        return $handler;
172
    }
173
174
    /**
175
     * Update handlers list and related parameters after modification
176
     */
177 56
    protected function update(): void
178
    {
179 56
        usort($this->handlers, static function($a, $b) {
180 16
            $ap = $a instanceof PrioritizedHandlerInterface ? $a->getHandlerPriority() : 0;
181 16
            $bp = $b instanceof PrioritizedHandlerInterface ? $b->getHandlerPriority() : 0;
182 16
            if ($ap > $bp) {
183
                return -1;
184
            }
185 16
            if ($ap < $bp) {
186 2
                return 1;
187
            }
188 14
            return 0;
189 56
        });
190 56
    }
191
}
192