|
1
|
|
|
<?php |
|
2
|
|
|
/* |
|
3
|
|
|
* Copyright (c) Nate Brunette. |
|
4
|
|
|
* Distributed under the MIT License (http://opensource.org/licenses/MIT) |
|
5
|
|
|
*/ |
|
6
|
|
|
|
|
7
|
|
|
declare(strict_types=1); |
|
8
|
|
|
|
|
9
|
|
|
namespace Tebru\Gson\Internal; |
|
10
|
|
|
|
|
11
|
|
|
use Tebru\Gson\Exception\JsonParseException; |
|
12
|
|
|
use Tebru\Gson\ReaderContext; |
|
13
|
|
|
use Tebru\Gson\JsonToken; |
|
14
|
|
|
|
|
15
|
|
|
/** |
|
16
|
|
|
* Class JsonDecodeReader |
|
17
|
|
|
* |
|
18
|
|
|
* @author Nate Brunette <[email protected]> |
|
19
|
|
|
*/ |
|
20
|
|
|
final class JsonDecodeReader extends JsonReader |
|
21
|
|
|
{ |
|
22
|
|
|
/** |
|
23
|
|
|
* Constructor |
|
24
|
|
|
* |
|
25
|
|
|
* @param string $json |
|
26
|
|
|
* @param ReaderContext $context |
|
27
|
|
|
* @throws \Tebru\Gson\Exception\JsonParseException If the json cannot be decoded |
|
28
|
|
|
*/ |
|
29
|
91 |
|
public function __construct(string $json, ReaderContext $context) |
|
30
|
|
|
{ |
|
31
|
91 |
|
$decodedJson = \json_decode($json); |
|
32
|
|
|
|
|
33
|
91 |
|
if (\json_last_error() !== JSON_ERROR_NONE) { |
|
34
|
1 |
|
throw new JsonParseException(\sprintf('Could not decode json, the error message was: "%s"', \json_last_error_msg())); |
|
35
|
|
|
} |
|
36
|
|
|
|
|
37
|
90 |
|
$this->payload = $decodedJson; |
|
38
|
90 |
|
$this->context = $context; |
|
39
|
90 |
|
$this->updateStack($decodedJson); |
|
40
|
90 |
|
} |
|
41
|
|
|
|
|
42
|
|
|
/** |
|
43
|
|
|
* Update internal stack and stack types, appending values |
|
44
|
|
|
* |
|
45
|
|
|
* @param mixed $decodedJson |
|
46
|
|
|
*/ |
|
47
|
90 |
|
private function updateStack($decodedJson): void |
|
48
|
|
|
{ |
|
49
|
90 |
|
$type = \gettype($decodedJson); |
|
50
|
|
|
|
|
51
|
|
|
switch ($type) { |
|
52
|
90 |
View Code Duplication |
case 'object': |
|
|
|
|
|
|
53
|
36 |
|
$this->stack[$this->stackSize] = null; |
|
54
|
36 |
|
$this->stackTypes[$this->stackSize++] = JsonToken::END_OBJECT; |
|
55
|
36 |
|
$this->stack[$this->stackSize] = $decodedJson; |
|
56
|
36 |
|
$this->stackTypes[$this->stackSize++] = JsonToken::BEGIN_OBJECT; |
|
57
|
36 |
|
break; |
|
58
|
81 |
View Code Duplication |
case 'array': |
|
|
|
|
|
|
59
|
26 |
|
$this->stack[$this->stackSize] = null; |
|
60
|
26 |
|
$this->stackTypes[$this->stackSize++] = JsonToken::END_ARRAY; |
|
61
|
26 |
|
$this->stack[$this->stackSize] = $decodedJson; |
|
62
|
26 |
|
$this->stackTypes[$this->stackSize++] = JsonToken::BEGIN_ARRAY; |
|
63
|
26 |
|
break; |
|
64
|
71 |
View Code Duplication |
case 'string': |
|
|
|
|
|
|
65
|
23 |
|
$this->stack[$this->stackSize] = $decodedJson; |
|
66
|
23 |
|
$this->stackTypes[$this->stackSize++] = JsonToken::STRING; |
|
67
|
23 |
|
break; |
|
68
|
50 |
View Code Duplication |
case 'boolean': |
|
|
|
|
|
|
69
|
9 |
|
$this->stack[$this->stackSize] = $decodedJson; |
|
70
|
9 |
|
$this->stackTypes[$this->stackSize++] = JsonToken::BOOLEAN; |
|
71
|
9 |
|
break; |
|
72
|
42 |
|
case 'integer': |
|
73
|
3 |
View Code Duplication |
case 'double': |
|
|
|
|
|
|
74
|
40 |
|
$this->stack[$this->stackSize] = $decodedJson; |
|
75
|
40 |
|
$this->stackTypes[$this->stackSize++] = JsonToken::NUMBER; |
|
76
|
40 |
|
break; |
|
77
|
|
View Code Duplication |
default: |
|
|
|
|
|
|
78
|
2 |
|
$this->stack[$this->stackSize] = null; |
|
79
|
2 |
|
$this->stackTypes[$this->stackSize++] = JsonToken::NULL; |
|
80
|
|
|
} |
|
81
|
90 |
|
} |
|
82
|
|
|
|
|
83
|
|
|
/** |
|
84
|
|
|
* Consumes the next token and asserts it's the beginning of a new array |
|
85
|
|
|
* |
|
86
|
|
|
* @return void |
|
87
|
|
|
*/ |
|
88
|
24 |
View Code Duplication |
public function beginArray(): void |
|
|
|
|
|
|
89
|
|
|
{ |
|
90
|
24 |
|
$this->pathIndices[$this->pathIndex++]++; |
|
91
|
24 |
|
$this->pathIndices[$this->pathIndex] = -1; |
|
92
|
|
|
|
|
93
|
24 |
|
if ($this->stackTypes[$this->stackSize - 1] !== JsonToken::BEGIN_ARRAY) { |
|
94
|
1 |
|
$this->assertionFailed(JsonToken::BEGIN_ARRAY); |
|
95
|
|
|
} |
|
96
|
|
|
|
|
97
|
23 |
|
$array = $this->stack[--$this->stackSize]; |
|
98
|
23 |
|
$size = \count($array); |
|
99
|
23 |
|
for ($i = $size - 1; $i >= 0; $i--) { |
|
100
|
18 |
|
$this->updateStack($array[$i]); |
|
101
|
|
|
} |
|
102
|
23 |
|
} |
|
103
|
|
|
|
|
104
|
|
|
/** |
|
105
|
|
|
* Consumes the next token and asserts it's the beginning of a new object |
|
106
|
|
|
* |
|
107
|
|
|
* @return void |
|
108
|
|
|
*/ |
|
109
|
29 |
View Code Duplication |
public function beginObject(): void |
|
|
|
|
|
|
110
|
|
|
{ |
|
111
|
29 |
|
$this->pathIndices[$this->pathIndex++]++; |
|
112
|
29 |
|
$this->pathIndices[$this->pathIndex] = -1; |
|
113
|
|
|
|
|
114
|
29 |
|
if ($this->stackTypes[$this->stackSize - 1] !== JsonToken::BEGIN_OBJECT) { |
|
115
|
1 |
|
$this->assertionFailed(JsonToken::BEGIN_OBJECT); |
|
116
|
|
|
} |
|
117
|
|
|
|
|
118
|
28 |
|
$vars = \array_reverse(\get_object_vars($this->stack[--$this->stackSize]), true); |
|
119
|
28 |
|
foreach ($vars as $key => $value) { |
|
120
|
25 |
|
$this->updateStack($value); |
|
121
|
25 |
|
$this->stack[$this->stackSize] = $key; |
|
122
|
25 |
|
$this->stackTypes[$this->stackSize++] = JsonToken::NAME; |
|
123
|
|
|
} |
|
124
|
28 |
|
} |
|
125
|
|
|
|
|
126
|
|
|
/** |
|
127
|
|
|
* Consumes the value of the next token, asserts it's a boolean and returns it |
|
128
|
|
|
* |
|
129
|
|
|
* @return bool |
|
130
|
|
|
*/ |
|
131
|
6 |
|
public function nextBoolean(): bool |
|
132
|
|
|
{ |
|
133
|
6 |
|
$this->pathIndices[$this->pathIndex]++; |
|
134
|
|
|
|
|
135
|
6 |
|
if ($this->stackTypes[$this->stackSize - 1] !== JsonToken::BOOLEAN) { |
|
136
|
1 |
|
$this->assertionFailed(JsonToken::BOOLEAN); |
|
137
|
|
|
} |
|
138
|
|
|
|
|
139
|
6 |
|
return (bool)$this->stack[--$this->stackSize]; |
|
140
|
|
|
} |
|
141
|
|
|
|
|
142
|
|
|
/** |
|
143
|
|
|
* Consumes the value of the next token, asserts it's a double and returns it |
|
144
|
|
|
* |
|
145
|
|
|
* @return double |
|
146
|
|
|
*/ |
|
147
|
3 |
|
public function nextDouble(): float |
|
148
|
|
|
{ |
|
149
|
3 |
|
$this->pathIndices[$this->pathIndex]++; |
|
150
|
|
|
|
|
151
|
3 |
|
if ($this->stackTypes[$this->stackSize - 1] !== JsonToken::NUMBER) { |
|
152
|
1 |
|
$this->assertionFailed(JsonToken::NUMBER); |
|
153
|
|
|
} |
|
154
|
|
|
|
|
155
|
2 |
|
return (float)$this->stack[--$this->stackSize]; |
|
156
|
|
|
} |
|
157
|
|
|
|
|
158
|
|
|
/** |
|
159
|
|
|
* Consumes the value of the next token, asserts it's an int and returns it |
|
160
|
|
|
* |
|
161
|
|
|
* @return int |
|
162
|
|
|
*/ |
|
163
|
15 |
|
public function nextInteger(): int |
|
164
|
|
|
{ |
|
165
|
15 |
|
$this->pathIndices[$this->pathIndex]++; |
|
166
|
|
|
|
|
167
|
15 |
|
if ($this->stackTypes[$this->stackSize - 1] !== JsonToken::NUMBER) { |
|
168
|
1 |
|
$this->assertionFailed(JsonToken::NUMBER); |
|
169
|
|
|
} |
|
170
|
|
|
|
|
171
|
14 |
|
return (int)$this->stack[--$this->stackSize]; |
|
172
|
|
|
} |
|
173
|
|
|
|
|
174
|
|
|
/** |
|
175
|
|
|
* Consumes the value of the next token, asserts it's a string and returns it |
|
176
|
|
|
* |
|
177
|
|
|
* @return string |
|
178
|
|
|
*/ |
|
179
|
16 |
View Code Duplication |
public function nextString(): string |
|
|
|
|
|
|
180
|
|
|
{ |
|
181
|
16 |
|
$this->pathIndices[$this->pathIndex]++; |
|
182
|
|
|
|
|
183
|
16 |
|
if ($this->stackTypes[$this->stackSize - 1] !== JsonToken::STRING) { |
|
184
|
2 |
|
if ($this->stackTypes[$this->stackSize - 1] === JsonToken::NAME) { |
|
185
|
1 |
|
return $this->nextName(); |
|
186
|
|
|
} |
|
187
|
|
|
|
|
188
|
1 |
|
$this->assertionFailed(JsonToken::STRING); |
|
189
|
|
|
} |
|
190
|
|
|
|
|
191
|
14 |
|
return (string)$this->stack[--$this->stackSize]; |
|
192
|
|
|
} |
|
193
|
|
|
} |
|
194
|
|
|
|
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.