cerbero90 /
json-parser
| 1 | <?php |
||
| 2 | |||
| 3 | namespace Cerbero\JsonParser\Pointers; |
||
| 4 | |||
| 5 | use Cerbero\JsonParser\Exceptions\InvalidPointerException; |
||
| 6 | use Cerbero\JsonParser\ValueObjects\Tree; |
||
| 7 | use Closure; |
||
| 8 | use Stringable; |
||
| 9 | |||
| 10 | use function count; |
||
| 11 | use function is_int; |
||
| 12 | use function array_slice; |
||
| 13 | |||
| 14 | /** |
||
| 15 | * The JSON pointer. |
||
| 16 | * |
||
| 17 | */ |
||
| 18 | final class Pointer implements Stringable |
||
| 19 | { |
||
| 20 | /** |
||
| 21 | * The reference tokens. |
||
| 22 | * |
||
| 23 | * @var string[] |
||
| 24 | */ |
||
| 25 | public readonly array $referenceTokens; |
||
| 26 | |||
| 27 | /** |
||
| 28 | * The pointer depth. |
||
| 29 | * |
||
| 30 | * @var int |
||
| 31 | */ |
||
| 32 | public readonly int $depth; |
||
| 33 | |||
| 34 | /** |
||
| 35 | * Whether the pointer was found. |
||
| 36 | * |
||
| 37 | * @var bool |
||
| 38 | */ |
||
| 39 | public bool $wasFound = false; |
||
| 40 | |||
| 41 | /** |
||
| 42 | * Instantiate the class. |
||
| 43 | * |
||
| 44 | * @param string $pointer |
||
| 45 | * @param bool $isLazy |
||
| 46 | * @param Closure|null $callback |
||
| 47 | */ |
||
| 48 | 369 | public function __construct( |
|
| 49 | private readonly string $pointer, |
||
| 50 | public readonly bool $isLazy = false, |
||
| 51 | private readonly ?Closure $callback = null, |
||
| 52 | ) { |
||
| 53 | 369 | $this->referenceTokens = $this->toReferenceTokens(); |
|
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||
| 54 | 365 | $this->depth = count($this->referenceTokens); |
|
|
0 ignored issues
–
show
|
|||
| 55 | } |
||
| 56 | |||
| 57 | /** |
||
| 58 | * Turn the JSON pointer into reference tokens |
||
| 59 | * |
||
| 60 | * @return string[] |
||
| 61 | */ |
||
| 62 | 369 | private function toReferenceTokens(): array |
|
| 63 | { |
||
| 64 | 369 | if (preg_match('#^(?:/(?:(?:[^/~])|(?:~[01]))*)*$#', $this->pointer) === 0) { |
|
| 65 | 4 | throw new InvalidPointerException($this->pointer); |
|
| 66 | } |
||
| 67 | |||
| 68 | 365 | $tokens = explode('/', $this->pointer); |
|
| 69 | 365 | $referenceTokens = array_map(fn (string $token) => str_replace(['~1', '~0'], ['/', '~'], $token), $tokens); |
|
| 70 | |||
| 71 | 365 | return array_slice($referenceTokens, 1); |
|
| 72 | } |
||
| 73 | |||
| 74 | /** |
||
| 75 | * Call the pointer callback |
||
| 76 | * |
||
| 77 | * @param mixed $value |
||
| 78 | * @param mixed $key |
||
| 79 | * @return mixed |
||
| 80 | */ |
||
| 81 | 264 | public function call(mixed $value, mixed &$key): mixed |
|
| 82 | { |
||
| 83 | 264 | if ($this->callback === null) { |
|
| 84 | 260 | return $value; |
|
| 85 | } |
||
| 86 | |||
| 87 | 4 | return ($this->callback)($value, $key) ?? $value; |
|
| 88 | } |
||
| 89 | |||
| 90 | /** |
||
| 91 | * Determine whether the reference token at the given depth matches the provided key |
||
| 92 | * |
||
| 93 | * @param int $depth |
||
| 94 | * @param string|int $key |
||
| 95 | * @return bool |
||
| 96 | */ |
||
| 97 | 84 | public function depthMatchesKey(int $depth, string|int $key): bool |
|
| 98 | { |
||
| 99 | 84 | $referenceToken = $this->referenceTokens[$depth] ?? null; |
|
| 100 | |||
| 101 | 84 | return $referenceToken === (string) $key |
|
| 102 | 84 | || (is_int($key) && $referenceToken === '-'); |
|
| 103 | } |
||
| 104 | |||
| 105 | /** |
||
| 106 | * Determine whether the pointer matches the given tree |
||
| 107 | * |
||
| 108 | * @param Tree $tree |
||
| 109 | * @return bool |
||
| 110 | */ |
||
| 111 | 355 | public function matchesTree(Tree $tree): bool |
|
| 112 | { |
||
| 113 | 355 | return $this->referenceTokens == [] |
|
| 114 | 355 | || $this->referenceTokens == $tree->original() |
|
| 115 | 355 | || $this->referenceTokens == $tree->wildcarded(); |
|
| 116 | } |
||
| 117 | |||
| 118 | /** |
||
| 119 | * Determine whether the pointer includes the given tree |
||
| 120 | * |
||
| 121 | * @param Tree $tree |
||
| 122 | * @return bool |
||
| 123 | */ |
||
| 124 | 214 | public function includesTree(Tree $tree): bool |
|
| 125 | { |
||
| 126 | 214 | if ($this->pointer == '') { |
|
| 127 | 28 | return true; |
|
| 128 | } |
||
| 129 | |||
| 130 | 197 | return is_int($firstNest = array_search('-', $this->referenceTokens)) |
|
| 131 | 197 | && array_slice($this->referenceTokens, 0, $firstNest) === array_slice($tree->original(), 0, $firstNest); |
|
| 132 | } |
||
| 133 | |||
| 134 | /** |
||
| 135 | * Retrieve the underlying JSON pointer |
||
| 136 | * |
||
| 137 | * @return string |
||
| 138 | */ |
||
| 139 | 365 | public function __toString(): string |
|
| 140 | { |
||
| 141 | 365 | return $this->pointer; |
|
| 142 | } |
||
| 143 | } |
||
| 144 |