Passed
Branch feature/first-release (4212f2)
by Andrea Marco
10:46
created

Pointer::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 5
ccs 4
cts 4
cp 1
crap 1
rs 10
1
<?php
2
3
namespace Cerbero\JsonParser\Pointers;
4
5
use Cerbero\JsonParser\Exceptions\InvalidPointerException;
6
use Cerbero\JsonParser\Tree;
7
use Closure;
8
use Stringable;
9
10
use function count;
11
use function call_user_func;
12
use function is_int;
13
use function array_slice;
14
15
/**
16
 * The JSON pointer.
17
 *
18
 */
19
final class Pointer implements Stringable
20
{
21
    /**
22
     * The reference tokens.
23
     *
24
     * @var string[]
25
     */
26
    private array $referenceTokens;
27
28
    /**
29
     * The pointer depth.
30
     *
31
     * @var int
32
     */
33
    private int $depth;
34
35
    /**
36
     * The pointer callback.
37
     *
38
     * @var Closure
39
     */
40
    private ?Closure $callback;
41
42
    /**
43
     * Whether the pointer was found.
44
     *
45
     * @var bool
46
     */
47
    public bool $wasFound = false;
48
49
    /**
50
     * Instantiate the class.
51
     *
52
     * @param string $pointer
53
     * @param Closure|null $callback
54
     */
55 139
    public function __construct(private string $pointer, Closure $callback = null)
56
    {
57 139
        $this->referenceTokens = $this->toReferenceTokens();
58 135
        $this->depth = count($this->referenceTokens);
59 135
        $this->callback = $callback;
60
    }
61
62
    /**
63
     * Turn the JSON pointer into reference tokens
64
     *
65
     * @return string[]
66
     */
67 139
    private function toReferenceTokens(): array
68
    {
69 139
        if (preg_match('#^(?:/(?:(?:[^/~])|(?:~[01]))*)*$#', $this->pointer) === 0) {
70 4
            throw new InvalidPointerException($this->pointer);
71
        }
72
73 135
        $tokens = explode('/', $this->pointer);
74 135
        $referenceTokens = array_map(fn (string $token) => str_replace(['~1', '~0'], ['/', '~'], $token), $tokens);
75
76 135
        return array_slice($referenceTokens, 1);
77
    }
78
79
    /**
80
     * Retrieve the reference tokens
81
     *
82
     * @return string[]
83
     */
84 127
    public function referenceTokens(): array
85
    {
86 127
        return $this->referenceTokens;
87
    }
88
89
    /**
90
     * Retrieve the JSON pointer depth
91
     *
92
     * @return int
93
     */
94 125
    public function depth(): int
95
    {
96 125
        return $this->depth;
97
    }
98
99
    /**
100
     * Call the pointer callback
101
     *
102
     * @param mixed $value
103
     * @param mixed $key
104
     * @return mixed
105
     */
106 92
    public function call(mixed $value, mixed $key): mixed
107
    {
108 92
        if ($this->callback === null) {
109 91
            return $value;
110
        }
111
112 1
        return call_user_func($this->callback, $value, $key) ?? $value;
113
    }
114
115
    /**
116
     * Determine whether the reference token at the given depth matches the provided key
117
     *
118
     * @param int $depth
119
     * @param string|int $key
120
     * @return bool
121
     */
122 27
    public function depthMatchesKey(int $depth, string|int $key): bool
123
    {
124 27
        $referenceToken = $this->referenceTokens[$depth] ?? null;
125
126 27
        return $referenceToken === (string) $key
127 27
            || (is_int($key) && $referenceToken === '-');
128
    }
129
130
    /**
131
     * Determine whether the pointer matches the given tree
132
     *
133
     * @param Tree $tree
134
     * @return bool
135
     */
136 125
    public function matchesTree(Tree $tree): bool
137
    {
138 125
        return $this->referenceTokens == []
139 125
            || $this->referenceTokens == $tree->original()
140 125
            || $this->referenceTokens == $tree->wildcarded();
141
    }
142
143
    /**
144
     * Determine whether the pointer includes the given tree
145
     *
146
     * @param Tree $tree
147
     * @return bool
148
     */
149 67
    public function includesTree(Tree $tree): bool
150
    {
151 67
        if ($this->pointer == '') {
152 4
            return true;
153
        }
154
155 63
        return is_int($firstNest = array_search('-', $this->referenceTokens))
156 63
            && array_slice($this->referenceTokens, 0, $firstNest) === array_slice($tree->original(), 0, $firstNest);
157
    }
158
159
    /**
160
     * Retrieve the underlying JSON pointer
161
     *
162
     * @return string
163
     */
164 135
    public function __toString(): string
165
    {
166 135
        return $this->pointer;
167
    }
168
}
169