Completed
Push — master ( 0cdf45...d59ceb )
by Edward
04:37
created

SliceElementMatcher   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 89
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 42
dl 0
loc 89
ccs 0
cts 44
cp 0
rs 10
c 1
b 0
f 1
wmc 25

7 Methods

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