1
|
|
|
<?php |
2
|
|
|
/* |
3
|
|
|
* Copyright (c) Nate Brunette. |
4
|
|
|
* Distributed under the MIT License (http://opensource.org/licenses/MIT) |
5
|
|
|
*/ |
6
|
|
|
|
7
|
|
|
namespace Tebru\Gson\Internal; |
8
|
|
|
|
9
|
|
|
use ArrayIterator; |
10
|
|
|
use Iterator; |
11
|
|
|
use Tebru\Gson\Element\JsonArray; |
12
|
|
|
use Tebru\Gson\Element\JsonElement; |
13
|
|
|
use Tebru\Gson\Element\JsonNull; |
14
|
|
|
use Tebru\Gson\Element\JsonObject; |
15
|
|
|
use Tebru\Gson\Element\JsonPrimitive; |
16
|
|
|
use Tebru\Gson\JsonToken; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* Class JsonElementReader |
20
|
|
|
* |
21
|
|
|
* @author Nate Brunette <[email protected]> |
22
|
|
|
*/ |
23
|
|
|
final class JsonElementReader extends JsonReader |
24
|
|
|
{ |
25
|
|
|
/** |
26
|
|
|
* Constructor |
27
|
|
|
* |
28
|
|
|
* @param JsonElement $jsonElement |
29
|
|
|
*/ |
30
|
69 |
|
public function __construct(JsonElement $jsonElement) |
31
|
|
|
{ |
32
|
69 |
|
$this->push($jsonElement); |
33
|
69 |
|
} |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* Consumes the next token and asserts it's the beginning of a new array |
37
|
|
|
* |
38
|
|
|
* @return void |
39
|
|
|
* @throws \Tebru\Gson\Exception\UnexpectedJsonTokenException If the next token is not BEGIN_ARRAY |
40
|
|
|
*/ |
41
|
15 |
View Code Duplication |
public function beginArray(): void |
|
|
|
|
42
|
|
|
{ |
43
|
15 |
|
$this->expect(JsonToken::BEGIN_ARRAY); |
44
|
|
|
|
45
|
|
|
/** @var JsonArray $jsonArray */ |
46
|
14 |
|
$jsonArray = $this->pop(); |
47
|
14 |
|
$this->push($jsonArray->getIterator(), ArrayIterator::class); |
48
|
14 |
|
$this->pathIndices[$this->stackSize - 1] = 0; |
49
|
14 |
|
} |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* Consumes the next token and asserts it's the beginning of a new object |
53
|
|
|
* |
54
|
|
|
* @return void |
55
|
|
|
* @throws \Tebru\Gson\Exception\UnexpectedJsonTokenException If the next token is not BEGIN_OBJECT |
56
|
|
|
*/ |
57
|
17 |
|
public function beginObject(): void |
58
|
|
|
{ |
59
|
17 |
|
$this->expect(JsonToken::BEGIN_OBJECT); |
60
|
|
|
|
61
|
|
|
/** @var JsonObject $jsonObject */ |
62
|
16 |
|
$jsonObject = $this->pop(); |
63
|
|
|
|
64
|
16 |
|
$this->push(new JsonObjectIterator($jsonObject), JsonObjectIterator::class); |
65
|
16 |
|
} |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Consumes the value of the next token, asserts it's a boolean and returns it |
69
|
|
|
* |
70
|
|
|
* @return bool |
71
|
|
|
* @throws \Tebru\Gson\Exception\UnexpectedJsonTokenException If the next token is not BOOLEAN |
72
|
|
|
*/ |
73
|
6 |
|
public function nextBoolean(): bool |
74
|
|
|
{ |
75
|
6 |
|
$this->expect(JsonToken::BOOLEAN); |
76
|
|
|
|
77
|
|
|
/** @var JsonPrimitive $primitive */ |
78
|
5 |
|
$primitive = $this->pop(); |
79
|
|
|
|
80
|
5 |
|
$this->incrementPathIndex(); |
81
|
|
|
|
82
|
5 |
|
return $primitive->asBoolean(); |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* Consumes the value of the next token, asserts it's a double and returns it |
87
|
|
|
* |
88
|
|
|
* @return double |
89
|
|
|
* @throws \Tebru\Gson\Exception\UnexpectedJsonTokenException If the next token is not NUMBER |
90
|
|
|
*/ |
91
|
3 |
|
public function nextDouble(): float |
92
|
|
|
{ |
93
|
3 |
|
$this->expect(JsonToken::NUMBER); |
94
|
|
|
|
95
|
|
|
/** @var JsonPrimitive $primitive */ |
96
|
2 |
|
$primitive = $this->pop(); |
97
|
|
|
|
98
|
2 |
|
$this->incrementPathIndex(); |
99
|
|
|
|
100
|
2 |
|
return $primitive->asFloat(); |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* Consumes the value of the next token, asserts it's an int and returns it |
105
|
|
|
* |
106
|
|
|
* @return int |
107
|
|
|
* @throws \Tebru\Gson\Exception\UnexpectedJsonTokenException If the next token is not NUMBER |
108
|
|
|
*/ |
109
|
6 |
|
public function nextInteger(): int |
110
|
|
|
{ |
111
|
6 |
|
$this->expect(JsonToken::NUMBER); |
112
|
|
|
|
113
|
|
|
/** @var JsonPrimitive $primitive */ |
114
|
5 |
|
$primitive = $this->pop(); |
115
|
|
|
|
116
|
5 |
|
$this->incrementPathIndex(); |
117
|
|
|
|
118
|
5 |
|
return $primitive->asInteger(); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* Consumes the value of the next token, asserts it's a string and returns it |
123
|
|
|
* |
124
|
|
|
* @return string |
125
|
|
|
* @throws \Tebru\Gson\Exception\UnexpectedJsonTokenException If the next token is not NAME or STRING |
126
|
|
|
*/ |
127
|
14 |
View Code Duplication |
public function nextString(): string |
|
|
|
|
128
|
|
|
{ |
129
|
14 |
|
$peek = $this->peek(); |
130
|
14 |
|
if ($peek === JsonToken::NAME) { |
131
|
1 |
|
return $this->nextName(); |
132
|
|
|
} |
133
|
|
|
|
134
|
13 |
|
$this->expect(JsonToken::STRING); |
135
|
|
|
|
136
|
|
|
/** @var JsonPrimitive $primitive */ |
137
|
12 |
|
$primitive = $this->pop(); |
138
|
|
|
|
139
|
12 |
|
$this->incrementPathIndex(); |
140
|
|
|
|
141
|
12 |
|
return $primitive->asString(); |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* Returns an enum representing the type of the next token without consuming it |
146
|
|
|
* |
147
|
|
|
* @return string |
148
|
|
|
*/ |
149
|
69 |
|
public function peek(): string |
150
|
|
|
{ |
151
|
69 |
|
if (null !== $this->currentToken) { |
152
|
23 |
|
return $this->currentToken; |
153
|
|
|
} |
154
|
|
|
|
155
|
69 |
|
if (0 === $this->stackSize) { |
156
|
1 |
|
$this->currentToken = JsonToken::END_DOCUMENT; |
157
|
|
|
|
158
|
1 |
|
return $this->currentToken; |
159
|
|
|
} |
160
|
|
|
|
161
|
69 |
|
$token = null; |
162
|
69 |
|
$element = $this->stack[$this->stackSize - 1]; |
163
|
|
|
|
164
|
69 |
|
switch ($this->stackTypes[$this->stackSize - 1]) { |
165
|
69 |
|
case JsonArray::class: |
166
|
16 |
|
$token = JsonToken::BEGIN_ARRAY; |
167
|
16 |
|
break; |
168
|
66 |
|
case JsonNull::class: |
169
|
2 |
|
$token = JsonToken::NULL; |
170
|
2 |
|
break; |
171
|
64 |
|
case JsonObject::class: |
172
|
20 |
|
$token = JsonToken::BEGIN_OBJECT; |
173
|
20 |
|
break; |
174
|
61 |
|
case JsonPrimitive::class: |
175
|
|
|
/** @var JsonPrimitive $element */ |
176
|
45 |
|
if ($element->isString()) { |
177
|
18 |
|
$token = JsonToken::STRING; |
178
|
28 |
|
} elseif ($element->isBoolean()) { |
179
|
8 |
|
$token = JsonToken::BOOLEAN; |
180
|
21 |
|
} elseif ($element->isNumber()) { |
181
|
21 |
|
$token = JsonToken::NUMBER; |
182
|
|
|
} |
183
|
|
|
|
184
|
45 |
|
break; |
185
|
27 |
|
case JsonObjectIterator::class: |
186
|
|
|
/** @var JsonObjectIterator $element */ |
187
|
15 |
|
$token = $element->valid() ? JsonToken::NAME : JsonToken::END_OBJECT; |
188
|
|
|
|
189
|
15 |
|
break; |
190
|
13 |
View Code Duplication |
case ArrayIterator::class: |
|
|
|
|
191
|
|
|
/** @var ArrayIterator $element */ |
192
|
13 |
|
if ($element->valid()) { |
193
|
9 |
|
$this->push($element->current()); |
194
|
9 |
|
$element->next(); |
195
|
|
|
|
196
|
9 |
|
$token = $this->peek(); |
197
|
|
|
} else { |
198
|
7 |
|
$token = JsonToken::END_ARRAY; |
199
|
|
|
} |
200
|
|
|
|
201
|
13 |
|
break; |
202
|
|
|
} |
203
|
|
|
|
204
|
69 |
|
$this->currentToken = $token; |
205
|
|
|
|
206
|
69 |
|
return $this->currentToken; |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
/** |
210
|
|
|
* Get the current read path in json xpath format |
211
|
|
|
* |
212
|
|
|
* @return string |
213
|
|
|
*/ |
214
|
10 |
View Code Duplication |
public function getPath(): string |
|
|
|
|
215
|
|
|
{ |
216
|
10 |
|
$result = '$'; |
217
|
10 |
|
foreach ($this->stack as $index => $item) { |
218
|
10 |
|
if ($item instanceof ArrayIterator && isset($this->pathIndices[$index])) { |
219
|
1 |
|
$result .= '['.$this->pathIndices[$index].']'; |
220
|
|
|
} |
221
|
|
|
|
222
|
10 |
|
if ($item instanceof JsonObjectIterator && isset($this->pathNames[$index])) { |
223
|
10 |
|
$result .= '.'.$this->pathNames[$index]; |
224
|
|
|
} |
225
|
|
|
} |
226
|
|
|
|
227
|
10 |
|
return $result; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
/** |
231
|
|
|
* Push an element onto the stack |
232
|
|
|
* |
233
|
|
|
* @param JsonElement|Iterator $element |
234
|
|
|
* @param string $type |
|
|
|
|
235
|
|
|
*/ |
236
|
69 |
View Code Duplication |
protected function push($element, $type = null): void |
|
|
|
|
237
|
|
|
{ |
238
|
69 |
|
if (null === $type) { |
239
|
69 |
|
$type = get_class($element); |
240
|
|
|
} |
241
|
|
|
|
242
|
69 |
|
$this->stack[$this->stackSize] = $element; |
243
|
69 |
|
$this->stackTypes[$this->stackSize] = $type; |
244
|
69 |
|
$this->stackSize++; |
245
|
69 |
|
$this->currentToken = null; |
246
|
69 |
|
} |
247
|
|
|
} |
248
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.