Passed
Push — master ( 2051c8...8e4f12 )
by Edward
05:16
created

SliceElementMatcher   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 86
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 40
dl 0
loc 86
ccs 44
cts 44
cp 1
rs 10
c 1
b 0
f 1
wmc 26

7 Methods

Rating   Name   Duplication   Size   Complexity  
A isOnStep() 0 3 2
A match() 0 15 5
A __construct() 0 7 1
A findArrayLength() 0 9 4
A detectStart() 0 11 4
A isInRange() 0 5 4
A detectEnd() 0 13 6
1
<?php
2
declare(strict_types=1);
3
4
namespace Remorhaz\JSON\Path\Runtime\Matcher;
5
6
use Remorhaz\JSON\Data\Value\ArrayValueInterface;
7
use function is_int;
8
use function iterator_count;
9
use function max;
10
use Remorhaz\JSON\Data\Value\NodeValueInterface;
11
12
final class SliceElementMatcher implements ChildMatcherInterface
13
{
14
15
    private $start;
16
17
    private $end;
18
19
    private $step;
20
21
    private $isReverse;
22
23 28
    public function __construct(?int $start, ?int $end, ?int $step)
24
    {
25 28
        $this->step = $step ?? 1;
26 28
        $this->isReverse = $this->step < 0;
27
28 28
        $this->start = $start;
29 28
        $this->end = $end;
30 28
    }
31
32 28
    public function match($address, NodeValueInterface $value, NodeValueInterface $container): bool
33
    {
34 28
        if (0 == $this->step || !is_int($address)) {
35 3
            return false;
36
        }
37
38 25
        $count = $this->findArrayLength($container);
39 25
        if (!isset($count)) {
40 2
            return false;
41
        }
42
43 23
        $start = $this->detectStart($count);
44 23
        $end = $this->detectEnd($count);
45
46 23
        return $this->isInRange($address, $start, $end) && $this->isOnStep($address, $start);
47
    }
48
49 25
    private function findArrayLength(NodeValueInterface $value): ?int
50
    {
51 25
        $count = $value instanceof ArrayValueInterface
52 24
            ? iterator_count($value->createChildIterator())
53 25
            : null;
54
55 25
        return isset($count) && $count > 0
56 23
            ? $count
57 25
            : null;
58
    }
59
60 23
    private function detectStart(int $count): int
61
    {
62 23
        $start = $this->start;
63 23
        if (!isset($start)) {
64 14
            $start = $this->isReverse ? -1 : 0;
65
        }
66 23
        if ($start < 0) {
67 7
            $start = max($start + $count, 0);
68
        }
69
70 23
        return $start;
71
    }
72
73 23
    private function detectEnd(int $count): int
74
    {
75 23
        $end = $this->end;
76 23
        if (!isset($end)) {
77 15
            $end = $this->isReverse ? -$count - 1 : $count;
78
        }
79 23
        if ($end > $count) {
80 1
            return $count;
81
        }
82
83 22
        return $end < 0
84 7
            ? max($end + $count, $this->isReverse ? -1 : 0)
85 22
            : $end;
86
    }
87
88 23
    private function isInRange(int $address, int $start, int $end): bool
89
    {
90 23
        return $this->isReverse
91 10
            ? $address <= $start && $address > $end
92 23
            : $address >= $start && $address < $end;
93
    }
94
95 11
    private function isOnStep(int $address, int $start): bool
96
    {
97 11
        return 0 == ($this->isReverse ? $start - $address : $address - $start) % $this->step;
98
    }
99
}
100