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

JsonEncodeWriter::topIsObjectStart()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8

Duplication

Lines 8
Ratio 100 %

Code Coverage

Tests 4
CRAP Score 3

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 8
loc 8
ccs 4
cts 4
cp 1
rs 10
cc 3
nc 3
nop 0
crap 3
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 LogicException;
12
use stdClass;
13
use Tebru\Gson\JsonWritable;
14
15
/**
16
 * Class JsonEncodeWriter
17
 *
18
 * @author Nate Brunette <[email protected]>
19
 */
20
final class JsonEncodeWriter implements JsonWritable
21
{
22
    /**
23
     * True if we should serialize nulls
24
     *
25
     * @var bool
26
     */
27
    private $serializeNull = false;
28
29
    /**
30
     * Stack of values to be written
31
     *
32
     * @var array
33
     */
34
    private $stack = [];
35
36
    /**
37
     * Size of the stack array
38
     *
39
     * @var int
40
     */
41
    private $stackSize = 0;
42
43
    /**
44
     * A cache of the parsing state corresponding to the stack
45
     *
46
     * @var int[]
47
     */
48
    private $stackStates = [];
49
50
    /**
51
     * When serializing an object, store the name that should be serialized
52
     *
53
     * @var
54
     */
55
    private $pendingName;
56
57
    /**
58
     * The final result that will be json encoded
59
     *
60
     * @var mixed
61
     */
62
    private $result;
63
    
64
    /**
65
     * Begin writing array
66
     *
67
     * @return JsonWritable
68
     * @throws \LogicException
69
     */
70 6
    public function beginArray(): JsonWritable
71
    {
72 6
        if ($this->stackSize > 0 && $this->stackStates[$this->stackSize - 1] === self::STATE_OBJECT_NAME) {
73 1
            throw new LogicException('Cannot call beginArray() before name() during object serialization');
74
        }
75
76 5
        $array = [];
77 5
        $this->push($array);
78 5
        $this->stack[$this->stackSize] = &$array;
79 5
        $this->stackStates[$this->stackSize] = self::STATE_ARRAY;
80 5
        $this->stackSize++;
81
82 5
        return $this;
83
    }
84
85
    /**
86
     * End writing array
87
     *
88
     * @return JsonWritable
89
     * @throws \LogicException
90
     */
91 4
    public function endArray(): JsonWritable
92
    {
93 4
        if ($this->stackSize === 0 || $this->stackStates[$this->stackSize - 1] !== self::STATE_ARRAY) {
94 2
            throw new LogicException('Cannot call endArray() if not serializing array');
95
        }
96
97 2
        \array_pop($this->stack);
98 2
        $this->stackSize--;
99
100 2
        return $this;
101
    }
102
103
    /**
104
     * Begin writing object
105
     *
106
     * @return JsonWritable
107
     * @throws \LogicException
108
     */
109 16
    public function beginObject(): JsonWritable
110
    {
111 16
        if ($this->stackSize > 0 && $this->stackStates[$this->stackSize - 1] === self::STATE_OBJECT_NAME) {
112 1
            throw new LogicException('Cannot call beginObject() before name() during object serialization');
113
        }
114
115 16
        $class = new stdClass();
116 16
        $this->push($class);
117 16
        $this->stack[$this->stackSize] = $class;
118 16
        $this->stackStates[$this->stackSize] = self::STATE_OBJECT_NAME;
119 16
        $this->stackSize++;
120
121 16
        return $this;
122
    }
123
124
    /**
125
     * End writing object
126
     *
127
     * @return JsonWritable
128
     * @throws \LogicException
129
     */
130 6
    public function endObject(): JsonWritable
131
    {
132 6
        if ($this->stackSize === 0 || $this->stackStates[$this->stackSize - 1] !== self::STATE_OBJECT_NAME) {
133 2
            throw new LogicException('Cannot call endObject() if not serializing object');
134
        }
135
136 4
        \array_pop($this->stack);
137 4
        $this->stackSize--;
138
139 4
        return $this;
140
    }
141
142
    /**
143
     * Writes a property name
144
     *
145
     * @param string $name
146
     * @return JsonWritable
147
     * @throws \LogicException
148
     */
149 5
    public function name(string $name): JsonWritable
150
    {
151 5
        if ($this->stackStates[$this->stackSize - 1] !== self::STATE_OBJECT_NAME) {
152 1
            throw new LogicException('Cannot call name() at this point.  Either name() has already been called or object serialization has not been started');
153
        }
154
155 5
        $this->pendingName = $name;
156 5
        $this->stackStates[$this->stackSize - 1] = self::STATE_OBJECT_VALUE;
157
158 5
        return $this;
159
    }
160
161
    /**
162
     * Write an integer value
163
     *
164
     * @param int $value
165
     * @return JsonWritable
166
     * @throws \LogicException
167
     */
168 4
    public function writeInteger(int $value): JsonWritable
169
    {
170 4
        if ($this->stackSize > 0 && $this->stackStates[$this->stackSize - 1] === self::STATE_OBJECT_NAME) {
171 1
            throw new LogicException('Cannot call writeInteger() before name() during object serialization');
172
        }
173
174 3
        return $this->push($value);
175
    }
176
177
    /**
178
     * Write a float value
179
     *
180
     * @param float $value
181
     * @return JsonWritable
182
     * @throws \LogicException
183
     */
184 3
    public function writeFloat(float $value): JsonWritable
185
    {
186 3
        if ($this->stackSize > 0 && $this->stackStates[$this->stackSize - 1] === self::STATE_OBJECT_NAME) {
187 1
            throw new LogicException('Cannot call writeFloat() before name() during object serialization');
188
        }
189
190 2
        return $this->push($value);
191
    }
192
193
    /**
194
     * Write a string value
195
     *
196
     * @param string $value
197
     * @return JsonWritable
198
     * @throws \LogicException
199
     */
200 4
    public function writeString(string $value): JsonWritable
201
    {
202 4
        if ($this->stackSize > 0 && $this->stackStates[$this->stackSize - 1] === self::STATE_OBJECT_NAME) {
203 1
            throw new LogicException('Cannot call writeString() before name() during object serialization');
204
        }
205
206 3
        return $this->push($value);
207
    }
208
209
    /**
210
     * Write a boolean value
211
     *
212
     * @param boolean $value
213
     * @return JsonWritable
214
     * @throws \LogicException
215
     */
216 3
    public function writeBoolean(bool $value): JsonWritable
217
    {
218 3
        if ($this->stackSize > 0 && $this->stackStates[$this->stackSize - 1] === self::STATE_OBJECT_NAME) {
219 1
            throw new LogicException('Cannot call writeBoolean() before name() during object serialization');
220
        }
221
222 2
        return $this->push($value);
223
    }
224
225
    /**
226
     * Write a null value if we are serializing nulls, otherwise
227
     * skip the value.  If this is a property value, that property
228
     * should be skipped as well.
229
     *
230
     * @return JsonWritable
231
     * @throws \LogicException
232
     */
233 5
    public function writeNull(): JsonWritable
234
    {
235 5
        if ($this->stackSize > 0 && $this->stackStates[$this->stackSize - 1] === self::STATE_OBJECT_NAME) {
236 1
            throw new LogicException('Cannot call writeNull() before name() during object serialization');
237
        }
238
239 4
        if ($this->serializeNull) {
240 2
            $null = null;
241 2
            return $this->push($null);
242
        }
243
244
        // if we're not serializing nulls
245 2
        if (null !== $this->pendingName) {
246 1
            $this->stackStates[$this->stackSize - 1] = self::STATE_OBJECT_NAME;
247 1
            $this->pendingName = null;
248
        }
249
250 2
        return $this;
251
    }
252
253
    /**
254
     * Sets whether nulls are serialized
255
     *
256
     * @param bool $serializeNull
257
     * @return void
258
     */
259 2
    public function setSerializeNull(bool $serializeNull): void
260
    {
261 2
        $this->serializeNull = $serializeNull;
262 2
    }
263
264
    /**
265
     * Convert the writer to json
266
     *
267
     * @return string
268
     */
269 17
    public function __toString(): string
270
    {
271 17
        return \json_encode($this->result);
272
    }
273
274
    /**
275
     * Push a value to the result or current array/object
276
     *
277
     * @param mixed $value
278
     * @return JsonWritable
279
     * @throws \LogicException
280
     */
281 28
    private function push(&$value): JsonWritable
282
    {
283 28
        if (0 === $this->stackSize) {
284 28
            if (null !== $this->result) {
285 2
                throw new LogicException('Attempting to write two different types');
286
            }
287
288 28
            $this->result = &$value;
289
290 28
            return $this;
291
        }
292
293 5
        switch ($this->stackStates[$this->stackSize - 1]) {
294 5
            case self::STATE_OBJECT_VALUE:
295 3
                $this->stack[$this->stackSize - 1]->{$this->pendingName} = &$value;
296 3
                $this->stackStates[$this->stackSize - 1] = self::STATE_OBJECT_NAME;
297 3
                $this->pendingName = null;
298 3
                break;
299 2
            case self::STATE_ARRAY:
300 2
                $this->stack[$this->stackSize - 1][] = &$value;
301 2
                $this->stackStates[$this->stackSize - 1] = self::STATE_ARRAY;
302 2
                break;
303
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
304
        }
305
306 5
        return $this;
307
    }
308
}
309