Completed
Push — master ( 9afc27...035c9e )
by Chris
04:26
created

Pointer::__toString()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4.016

Importance

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