-
Notifications
You must be signed in to change notification settings - Fork 95
/
Copy pathJsonPointer.php
168 lines (153 loc) · 5.57 KB
/
JsonPointer.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
<?php
/**
* @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors
* @license https://github.com/cebe/php-openapi/blob/master/LICENSE
*/
namespace cebe\openapi\json;
/**
* Represents a JSON Pointer (RFC 6901)
*
* A JSON Pointer only works in the context of a single JSON document,
* if you need to reference values in external documents, use [[JsonReference]] instead.
*
* @link https://tools.ietf.org/html/rfc6901
* @see JsonReference
*/
final class JsonPointer
{
/**
* @var string
*/
private $_pointer;
/**
* JSON Pointer constructor.
* @param string $pointer The JSON Pointer.
* Must be either an empty string (for referencing the whole document), or a string starting with `/`.
* @throws InvalidJsonPointerSyntaxException in case an invalid JSON pointer string is passed
*/
public function __construct(string $pointer)
{
if (!preg_match('~^(/[^/]*)*$~', $pointer)) {
throw new InvalidJsonPointerSyntaxException("Invalid JSON Pointer syntax: $pointer");
}
$this->_pointer = $pointer;
}
public function __toString()
{
return $this->_pointer;
}
/**
* @return string returns the JSON Pointer.
*/
public function getPointer(): string
{
return $this->_pointer;
}
/**
* @return array the JSON pointer path as array.
*/
public function getPath(): array
{
if ($this->_pointer === '') {
return [];
}
$pointer = substr($this->_pointer, 1);
return array_map([get_class($this), 'decode'], explode('/', $pointer));
}
/**
* Append a new part to the JSON path.
* @param string $subpath the path element to append.
* @return JsonPointer a new JSON pointer pointing to the subpath.
*/
public function append(string $subpath): JsonPointer
{
return new JsonPointer($this->_pointer . '/' . static::encode($subpath));
}
/**
* Returns a JSON pointer to the parent path element of this pointer.
* @return JsonPointer|null a new JSON pointer pointing to the parent element
* or null if this pointer already points to the document root.
*/
public function parent(): ?JsonPointer
{
$path = $this->getPath();
if (empty($path)) {
return null;
}
array_pop($path);
if (empty($path)) {
return new JsonPointer('');
}
return new JsonPointer('/' . implode('/', array_map([get_class($this), 'encode'], $path)));
}
/**
* Evaluate the JSON Pointer on the provided document.
*
* Note that this does only resolve the JSON Pointer, it will not load external
* documents by URI. Loading the Document from the URI is supposed to be done outside of this class.
*
* @param mixed $jsonDocument
* @return mixed
* @throws NonexistentJsonPointerReferenceException
*/
public function evaluate($jsonDocument)
{
$currentReference = $jsonDocument;
$currentPath = '';
foreach ($this->getPath() as $part) {
if (is_array($currentReference)) {
// if (!preg_match('~^([1-9]*[0-9]|-)$~', $part)) {
// throw new NonexistentJsonPointerReferenceException(
// "Failed to evaluate pointer '$this->_pointer'. Invalid pointer path '$part' for Array at path '$currentPath'."
// );
// }
if ($part === '-' || !array_key_exists($part, $currentReference)) {
throw new NonexistentJsonPointerReferenceException(
"Failed to evaluate pointer '$this->_pointer'. Array has no member $part at path '$currentPath'."
);
}
$currentReference = $currentReference[$part];
} elseif ($currentReference instanceof \ArrayAccess) {
if (!$currentReference->offsetExists($part)) {
throw new NonexistentJsonPointerReferenceException(
"Failed to evaluate pointer '$this->_pointer'. Array has no member $part at path '$currentPath'."
);
}
$currentReference = $currentReference[$part];
} elseif (is_object($currentReference)) {
if (!isset($currentReference->$part) && !property_exists($currentReference, $part)) {
throw new NonexistentJsonPointerReferenceException(
"Failed to evaluate pointer '$this->_pointer'. Object has no member $part at path '$currentPath'."
);
}
$currentReference = $currentReference->$part;
} else {
throw new NonexistentJsonPointerReferenceException(
"Failed to evaluate pointer '$this->_pointer'. Value at path '$currentPath' is neither an array nor an object."
);
}
$currentPath = "$currentPath/$part";
}
return $currentReference;
}
/**
* Encodes a string for use inside of a JSON pointer.
*/
public static function encode(string $string): string
{
return strtr($string, [
'~' => '~0',
'/' => '~1',
]);
}
/**
* Decodes a string used inside of a JSON pointer.
*/
public static function decode(string $string): string
{
return strtr($string, [
'~1' => '/',
'~0' => '~',
]);
}
}