Passed
Pull Request — master (#26)
by Nate
07:26
created

JsonReader::expect()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 14
ccs 6
cts 6
cp 1
rs 9.7998
cc 2
nc 2
nop 1
crap 2
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\JsonSyntaxException;
12
use Tebru\Gson\JsonReadable;
13
use Tebru\Gson\ReaderContext;
14
use Tebru\Gson\JsonToken;
15
16
/**
17
 * Class JsonReader
18
 *
19
 * @author Nate Brunette <[email protected]>
20
 */
21
abstract class JsonReader implements JsonReadable
22
{
23
    /**
24
     * A stack representing the next element to be consumed
25
     *
26
     * @var array
27
     */
28
    protected $stack = [null];
29
30
    /**
31
     * An array of types that map to the position in the stack
32
     *
33
     * @var array
34
     */
35
    protected $stackTypes = [JsonToken::END_DOCUMENT];
36
37
    /**
38
     * The current size of the stack
39
     *
40
     * @var int
41
     */
42
    protected $stackSize = 1;
43
44
    /**
45
     * An array of path names that correspond to the current stack
46
     *
47
     * @var array
48
     */
49
    protected $pathNames = [];
50
51
    /**
52
     * An array of path indices that correspond to the current stack. This array could contain invalid
53
     * values at indexes outside the current stack. It could also contain incorrect values at indexes
54
     * where a path name is used. Data should only be fetched by referencing the $pathIndex
55
     *
56
     * @var int[]
57
     */
58
    protected $pathIndices = [-1];
59
60
    /**
61
     * The current path index corresponding to the pathIndices array
62
     *
63
     * @var int
64
     */
65
    protected  $pathIndex = 0;
66
67
    /**
68
     * A cache of the current [@see JsonToken].  This should get nulled out
69
     * whenever a new token should be returned with the subsequent call
70
     * to [@see JsonDecodeReader::peek]
71
     *
72
     * @var int|null
73
     */
74
    protected $currentToken;
75
76
    /**
77
     * The original payload
78
     *
79
     * @var mixed
80
     */
81
    protected $payload;
82
83
    /**
84
     * Runtime context to be used while reading
85
     *
86
     * @var ReaderContext
87
     */
88
    protected $context;
89
90
    /**
91
     * Consumes the next token and asserts it's the end of an array
92
     *
93
     * @return void
94
     */
95 13 View Code Duplication
    public function endArray(): void
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
96
    {
97 13
        if ($this->stackTypes[$this->stackSize - 1] !== JsonToken::END_ARRAY) {
98 2
            $this->pathIndices[$this->pathIndex]++;
99 2
            $this->assertionFailed(JsonToken::END_ARRAY);
100
        }
101
102 11
        $this->stackSize--;
103 11
        $this->pathIndex--;
104 11
    }
105
106
    /**
107
     * Consumes the next token and asserts it's the end of an object
108
     *
109
     * @return void
110
     */
111 15 View Code Duplication
    public function endObject(): void
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
112
    {
113 15
        if ($this->stackTypes[$this->stackSize - 1] !== JsonToken::END_OBJECT) {
114 2
            $this->assertionFailed(JsonToken::END_OBJECT);
115
        }
116
117 13
        $this->stackSize--;
118 13
        $this->pathNames[$this->pathIndex--] = null;
119 13
    }
120
121
    /**
122
     * Returns true if the array or object has another element
123
     *
124
     * If the current scope is not an array or object, this returns false
125
     *
126
     * @return bool
127
     */
128 14
    public function hasNext(): bool
129
    {
130 14
        $peek = $this->stackTypes[$this->stackSize - 1];
131
132 14
        return $peek !== JsonToken::END_OBJECT && $peek !== JsonToken::END_ARRAY;
133
    }
134
135
    /**
136
     * Consumes the next name and returns it
137
     *
138
     * @return string
139
     */
140 31 View Code Duplication
    public function nextName(): string
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
141
    {
142 31
        if ($this->stackTypes[$this->stackSize - 1] !== JsonToken::NAME) {
143 2
            $this->assertionFailed(JsonToken::NAME);
144
        }
145
146 31
        $name = (string)$this->stack[--$this->stackSize];
147
148 31
        $this->pathNames[$this->pathIndex] = $name;
149
150 31
        return $name;
151
    }
152
153
    /**
154
     * Consumes the value of the next token and asserts it's null
155
     *
156
     * @return void
157
     */
158 4 View Code Duplication
    public function nextNull(): void
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
159
    {
160 4
        $this->pathIndices[$this->pathIndex]++;
161
162 4
        if ($this->stackTypes[$this->stackSize - 1] !== JsonToken::NULL) {
163 2
            $this->assertionFailed(JsonToken::NULL);
164
        }
165
166 2
        $this->stackSize--;
167 2
    }
168
169
    /**
170
     * Skip the next value.  If the next value is an object or array, all children will
171
     * also be skipped.
172
     *
173
     * @return void
174
     */
175 7
    public function skipValue(): void
176
    {
177 7
        $this->stackSize--;
178
179 7
        switch ($this->stackTypes[$this->stackSize]) {
180 7
            case JsonToken::BEGIN_OBJECT:
181 5
            case JsonToken::BEGIN_ARRAY:
182 3
                $this->stackSize--;
183 3
                break;
184
        }
185
186 7
        $this->pathIndices[$this->pathIndex]--;
187 7
    }
188
189
    /**
190
     * Returns the type of the next token without consuming it
191
     *
192
     * @return string
193
     */
194 64
    public function peek(): string
195
    {
196 64
        return $this->stackTypes[$this->stackSize - 1];
197
    }
198
199
    /**
200
     * Get the current read path in json xpath format
201
     *
202
     * @return string
203
     */
204 33
    public function getPath(): string
205
    {
206 33
        $result[] = '$';
0 ignored issues
show
Coding Style Comprehensibility introduced by
$result was never initialized. Although not strictly required by PHP, it is generally a good practice to add $result = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
207
208 33
        for ($index = 1; $index <= $this->pathIndex; $index++) {
209 23
            if (!empty($this->pathNames[$index])) {
210 8
                $result[] .= '.'.$this->pathNames[$index];
211 8
                continue;
212
            }
213
214
            // skip initial value
215 16
            if ($this->pathIndices[$this->pathIndex] === -1) {
216 7
                continue;
217
            }
218
219 9
            $result[] .= '['.$this->pathIndices[$index].']';
220
        }
221
222 33
        return implode($result);
223
    }
224
225
    /**
226
     * Returns the original json after json_decode
227
     *
228
     * @return mixed
229
     */
230 2
    public function getPayload()
231
    {
232 2
        return $this->payload;
233
    }
234
235
    /**
236
     * Get context to be used during deserialization
237
     *
238
     * @return ReaderContext
239
     */
240 5
    public function getContext(): ReaderContext
241
    {
242 5
        return $this->context;
243
    }
244
245
    /**
246
     * Check that the next token equals the expectation
247
     *
248
     * @param string $expectedToken
249
     * @return void
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use NoType.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
250
     * @throws \Tebru\Gson\Exception\JsonSyntaxException If the next token is not the expectation
251
     */
252 20
    protected function assertionFailed(string $expectedToken): void
253
    {
254 20
        throw new JsonSyntaxException(
255 20
            \sprintf(
256 20
                'Expected "%s", but found "%s" at "%s"',
257 20
                $expectedToken,
258 20
                $this->stackTypes[$this->stackSize - 1],
259 20
                $this->getPath()
260
            )
261
        );
262
    }
263
}
264