Passed
Branch master (f07ed3)
by Nate
02:39
created

JsonDecodeReader   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 172
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 72
dl 0
loc 172
ccs 81
cts 81
cp 1
rs 10
c 0
b 0
f 0
wmc 24

8 Methods

Rating   Name   Duplication   Size   Complexity  
B updateStack() 0 33 7
A beginObject() 0 14 3
A nextString() 0 13 3
A __construct() 0 11 2
A nextDouble() 0 9 2
A beginArray() 0 13 3
A nextBoolean() 0 9 2
A nextInteger() 0 9 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\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
            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
            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
            case 'string':
65 23
                $this->stack[$this->stackSize] = $decodedJson;
66 23
                $this->stackTypes[$this->stackSize++] = JsonToken::STRING;
67 23
                break;
68 50
            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
            case 'double':
74 40
                $this->stack[$this->stackSize] = $decodedJson;
75 40
                $this->stackTypes[$this->stackSize++] = JsonToken::NUMBER;
76 40
                break;
77
            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
    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
    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
    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