Passed
Push — master ( da4da2...955b82 )
by Nate
04:34
created

JsonDecodeReader::getPath()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 15
Code Lines 8

Duplication

Lines 15
Ratio 100 %

Code Coverage

Tests 8
CRAP Score 6

Importance

Changes 0
Metric Value
dl 15
loc 15
ccs 8
cts 8
cp 1
rs 8.8571
c 0
b 0
f 0
cc 6
eloc 8
nc 5
nop 0
crap 6
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
declare(strict_types=1);
9
10
namespace Tebru\Gson\Internal;
11
12
use ArrayIterator;
13
use stdClass;
14
use Tebru\Gson\Exception\MalformedJsonException;
15
use Tebru\Gson\JsonReadable;
16
use Tebru\Gson\JsonToken;
17
18
/**
19
 * Class JsonDecodeReader
20
 *
21
 * @author Nate Brunette <[email protected]>
22
 */
23
final class JsonDecodeReader extends JsonReader implements JsonReadable
24
{
25
    /**
26
     * Constructor
27
     *
28
     * @param string $json
29
     * @throws \Tebru\Gson\Exception\MalformedJsonException If the json cannot be decoded
30
     */
31 83
    public function __construct(string $json)
32
    {
33 83
        $this->push(json_decode($json));
34
35 83
        if (json_last_error() !== JSON_ERROR_NONE) {
36 1
            throw new MalformedJsonException(sprintf('Could not decode json, the error message was: "%s"', json_last_error_msg()));
37
        }
38 82
    }
39
40
    /**
41
     * Consumes the next token and asserts it's the beginning of a new array
42
     *
43
     * @return void
44
     * @throws \Tebru\Gson\Exception\UnexpectedJsonTokenException If the next token is not BEGIN_ARRAY
45
     */
46 24 View Code Duplication
    public function beginArray(): void
1 ignored issue
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...
47
    {
48 24
        $this->expect(JsonToken::BEGIN_ARRAY);
49
50 23
        $array = $this->pop();
51 23
        $this->push(new ArrayIterator($array), ArrayIterator::class);
52 23
        $this->pathIndices[$this->stackSize - 1] = 0;
53 23
    }
54
55
    /**
56
     * Consumes the next token and asserts it's the beginning of a new object
57
     *
58
     * @return void
59
     * @throws \Tebru\Gson\Exception\UnexpectedJsonTokenException If the next token is not BEGIN_OBJECT
60
     */
61 24
    public function beginObject(): void
62
    {
63 24
        $this->expect(JsonToken::BEGIN_OBJECT);
64
65 23
        $this->push(new StdClassIterator($this->pop()), StdClassIterator::class);
66 23
    }
67
68
    /**
69
     * Consumes the value of the next token, asserts it's a boolean and returns it
70
     *
71
     * @return bool
72
     * @throws \Tebru\Gson\Exception\UnexpectedJsonTokenException If the next token is not BOOLEAN
73
     */
74 6
    public function nextBoolean(): bool
75
    {
76 6
        $this->expect(JsonToken::BOOLEAN);
77
78 6
        $result = (bool)$this->pop();
79
80 6
        $this->incrementPathIndex();
81
82 6
        return $result;
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 2
        $result = (float)$this->pop();
96
97 2
        $this->incrementPathIndex();
98
99 2
        return $result;
100
    }
101
102
    /**
103
     * Consumes the value of the next token, asserts it's an int and returns it
104
     *
105
     * @return int
106
     * @throws \Tebru\Gson\Exception\UnexpectedJsonTokenException If the next token is not NUMBER
107
     */
108 15
    public function nextInteger(): int
109
    {
110 15
        $this->expect(JsonToken::NUMBER);
111
112 14
        $result = (int)$this->pop();
113
114 14
        $this->incrementPathIndex();
115
116 14
        return $result;
117
    }
118
119
    /**
120
     * Consumes the value of the next token, asserts it's a string and returns it
121
     *
122
     * @return string
123
     * @throws \Tebru\Gson\Exception\UnexpectedJsonTokenException If the next token is not NAME or STRING
124
     */
125 13 View Code Duplication
    public function nextString(): string
1 ignored issue
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...
126
    {
127 13
        $peek = $this->peek();
128 13
        if ($peek === JsonToken::NAME) {
129 1
            return $this->nextName();
130
        }
131
132 12
        $this->expect(JsonToken::STRING);
133
134 11
        $result = (string)$this->pop();
135
136 11
        $this->incrementPathIndex();
137
138 11
        return $result;
139
    }
140
141
    /**
142
     * Returns an enum representing the type of the next token without consuming it
143
     *
144
     * @return string
145
     */
146 81
    public function peek(): string
147
    {
148 81
        if (null !== $this->currentToken) {
149 22
            return $this->currentToken;
150
        }
151
152 81
        if (0 === $this->stackSize) {
153 1
            $this->currentToken = JsonToken::END_DOCUMENT;
154
155 1
            return $this->currentToken;
156
        }
157
158 81
        $token = null;
159 81
        $element = $this->stack[$this->stackSize - 1];
160
161 81
        switch ($this->stackTypes[$this->stackSize - 1]) {
162 81
            case 'array':
163 25
                $token = JsonToken::BEGIN_ARRAY;
164 25
                break;
165 77
            case 'string':
166 18
                $token = JsonToken::STRING;
167 18
                break;
168 65
            case 'double':
169 1
                $token = JsonToken::NUMBER;
170 1
                break;
171 64
            case 'integer':
172 30
                $token = JsonToken::NUMBER;
173 30
                break;
174 50
            case 'boolean':
175 8
                return JsonToken::BOOLEAN;
176 46
            case 'NULL':
177 2
                $token = JsonToken::NULL;
178 2
                break;
179 44
            case StdClassIterator::class:
180
                /** @var StdClassIterator $element */
181 21
                $token = $element->valid() ? JsonToken::NAME : JsonToken::END_OBJECT;
182 21
                break;
183 44 View Code Duplication
            case ArrayIterator::class:
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across 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...
184
                /** @var ArrayIterator $element */
185 21
                if ($element->valid()) {
186 17
                    $this->push($element->current());
187 17
                    $element->next();
188
189 17
                    $token = $this->peek();
190
                } else {
191 9
                    $token = JsonToken::END_ARRAY;
192
                }
193 21
                break;
194 27
            case 'object':
195 27
                switch (get_class($element)) {
196 27
                    case stdClass::class:
197 27
                        $token = JsonToken::BEGIN_OBJECT;
198 27
                        break;
199
                }
200 27
                break;
201
        }
202
203 77
        $this->currentToken = $token;
204
205 77
        return $this->currentToken;
206
    }
207
208
    /**
209
     * Get the current read path in json xpath format
210
     *
211
     * @return string
212
     */
213 23 View Code Duplication
    public function getPath(): string
1 ignored issue
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...
214
    {
215 23
        $result = '$';
216 23
        foreach ($this->stack as $index => $item) {
217 21
            if ($item instanceof ArrayIterator && isset($this->pathIndices[$index])) {
218 9
                $result .= '['.$this->pathIndices[$index].']';
219
            }
220
221 21
            if ($item instanceof StdClassIterator && isset($this->pathNames[$index])) {
222 21
                $result .= '.'.$this->pathNames[$index];
223
            }
224
        }
225
226 23
        return $result;
227
    }
228
229
    /**
230
     * Push an element onto the stack
231
     *
232
     * @param mixed $element
233
     * @param string $type
0 ignored issues
show
Documentation introduced by
Should the type for parameter $type not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
234
     */
235 83 View Code Duplication
    protected function push($element, $type = null): void
1 ignored issue
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...
236
    {
237 83
        if (null === $type) {
238 83
            $type = gettype($element);
1 ignored issue
show
Coding Style introduced by
Consider using a different name than the parameter $type. This often makes code more readable.
Loading history...
239
        }
240
241 83
        $this->stack[$this->stackSize] = $element;
242 83
        $this->stackTypes[$this->stackSize] = $type;
243 83
        $this->stackSize++;
244 83
        $this->currentToken = null;
245 83
    }
246
}
247