Test Failed
Push — master ( 940bcf...a6b4b4 )
by Chris
02:36
created

Pointer::createFromParameters()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 14
ccs 6
cts 6
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 7
nc 2
nop 3
crap 2
1
<?php declare(strict_types=1);
2
3
namespace DaveRandom\Jom;
4
5
use DaveRandom\Jom\Exceptions\InvalidPointerException;
6
7
final class Pointer
8
{
9
    private $path = [];
10
    private $relativeLevels = null;
11
    private $keyLookup = false;
12
13
    private $string;
14
15 20
    /**
16
     * @throws InvalidPointerException
17 20
     */
18
    private static function decodePath(string $path): array
19 20
    {
20 20
        if ($path !== '' && $path[0] !== '/') {
21
            throw new InvalidPointerException('JSON pointer path must be the empty string or begin with /');
22
        }
23 20
24
        $result = [];
25
26 22
        foreach (\explode('/', \substr($path, 1)) as $component) {
27
            $result[] = \str_replace(['~1', '~0'], ['/', '~'], $component);
28 22
        }
29
30 22
        return $result;
31 14
    }
32
33
    private static function encodePath(array $path): string
34 22
    {
35
        $result = '';
36
37
        foreach ($path as $component) {
38
            $result .= '/' . \str_replace(['~', '/'], ['~0', '~1'], $component);
39 49
        }
40
41 49
        return $result;
42 42
    }
43 49
44
    private static function splitRelativePointerComponents(string $pointer): array
45
    {
46
        return \preg_match('/^(0|[1-9][0-9]*)($|[^0-9].*)/i', $pointer, $match)
47
            ? [$match[2], (int)$match[1]]
48
            : [$pointer, null];
49 49
    }
50
51 49
    /**
52
     * @throws InvalidPointerException
53 49
     */
54
    private static function validateRelativePointerParams(?int $relativeLevels, ?bool $isKeyLookup): void
55 49
    {
56
        if ($relativeLevels < 0) {
57 49
            throw new InvalidPointerException('Relative levels must be positive');
58 29
        }
59
60
        if ($isKeyLookup && $relativeLevels === null) {
61 20
            throw new InvalidPointerException('Key lookups are only valid for relative pointers');
62
        }
63
    }
64
65 20
    private function __construct() { }
66
67 20
    /**
68
     * @throws InvalidPointerException
69
     */
70
    public static function createFromString(string $pointer): self
71
    {
72
        $result = new self();
73
74 11
        [$path, $result->relativeLevels] = self::splitRelativePointerComponents($pointer);
75
76 11
        $result->keyLookup = $result->relativeLevels !== null && $path === '#';
77
78
        if (!$result->keyLookup) {
79
            $result->path = self::decodePath($path);
80 11
        }
81
82
        return $result;
83
    }
84 11
85
    /**
86 11
     * @param string[] $path
87 7
     * @throws InvalidPointerException
88
     */
89
    public static function createFromParameters(array $path, ?int $relativeLevels = null, ?bool $isKeyLookup = false): self
90 11
    {
91 11
        self::validateRelativePointerParams($relativeLevels, $isKeyLookup);
92
93 11
        $result = new self();
94
95
        $result->relativeLevels = $relativeLevels;
96 37
        $result->keyLookup = $isKeyLookup ?? false;
97
98 37
        foreach ($path as $component) {
99
            $result->path[] = (string)$component;
100
        }
101 42
102
        return $result;
103 42
    }
104
105
    public function getPath(): array
106 49
    {
107
        return $this->path;
108 49
    }
109
110
    public function getRelativeLevels(): ?int
111 30
    {
112
        return $this->relativeLevels;
113 30
    }
114
115
    public function isRelative(): bool
116 26
    {
117
        return $this->relativeLevels !== null;
118 26
    }
119
120
    public function isKeyLookup(): bool
121
    {
122 26
        return $this->keyLookup;
123
    }
124 26
125 19
    public function __toString(): string
126
    {
127
        if (isset($this->string)) {
128 26
            return $this->string;
129 4
        }
130 22
131
        $this->string = '';
132 26
133
        if ($this->relativeLevels !== null) {
134
            $this->string .= $this->relativeLevels;
135
        }
136
137
        $this->string .= $this->keyLookup
138
            ? '#'
139
            : self::encodePath($this->path);
140
141
        return $this->string;
142
    }
143
}
144