ArrayTypeAdapter::write()   C
last analyzed

Complexity

Conditions 17
Paths 40

Size

Total Lines 53
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 33
CRAP Score 17

Importance

Changes 0
Metric Value
eloc 32
dl 0
loc 53
ccs 33
cts 33
cp 1
rs 5.2166
c 0
b 0
f 0
cc 17
nc 40
nop 2
crap 17

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\TypeAdapter;
10
11
use LogicException;
12
use Tebru\Gson\Context\ReaderContext;
13
use Tebru\Gson\Context\WriterContext;
14
use Tebru\Gson\Exception\JsonSyntaxException;
15
use Tebru\Gson\Internal\TypeAdapterProvider;
16
use Tebru\Gson\TypeAdapter;
17
use Tebru\PhpType\TypeToken;
18
19
/**
20
 * Class ArrayTypeAdapter
21
 *
22
 * @author Nate Brunette <[email protected]>
23
 */
24
class ArrayTypeAdapter extends TypeAdapter
25
{
26
    /**
27
     * @var TypeAdapterProvider
28
     */
29
    protected $typeAdapterProvider;
30
31
    /**
32
     * @var TypeToken
33
     */
34
    protected $keyType;
35
36
    /**
37
     * @var TypeAdapter
38
     */
39
    protected $valueTypeAdapter;
40
41
    /**
42
     * @var int
43
     */
44
    protected $numberOfGenerics;
45
46
    /**
47
     * A TypeAdapter cache keyed by raw type
48
     *
49
     * @var TypeAdapter[]
50
     */
51
    protected $adapters = [];
52
53
    /**
54
     * Constructor
55
     *
56
     * @param TypeAdapterProvider $typeAdapterProvider
57
     * @param TypeToken $keyType
58
     * @param TypeAdapter $valueTypeAdapter
59
     * @param int $numberOfGenerics
60
     */
61 30
    public function __construct(
62
        TypeAdapterProvider $typeAdapterProvider,
63
        TypeToken $keyType,
64
        TypeAdapter $valueTypeAdapter,
65
        int $numberOfGenerics
66
    ) {
67 30
        $this->typeAdapterProvider = $typeAdapterProvider;
68 30
        $this->keyType = $keyType;
69 30
        $this->valueTypeAdapter = $valueTypeAdapter;
70 30
        $this->numberOfGenerics = $numberOfGenerics;
71 30
    }
72
73
    /**
74
     * Read the next value, convert it to its type and return it
75
     *
76
     * @param array|null $value
77
     * @param ReaderContext $context
78
     * @return array|null
79
     */
80 18
    public function read($value, ReaderContext $context): ?array
81
    {
82 18
        if ($value === null) {
83 1
            return null;
84
        }
85
86 17
        if (!is_array($value)) {
0 ignored issues
show
introduced by
The condition is_array($value) is always true.
Loading history...
87 1
            throw new JsonSyntaxException(sprintf('Could not parse json, expected array or object but found "%s"', gettype($value)));
88
        }
89
90 16
        $result = [];
91
92 16
        if ($this->numberOfGenerics > 2) {
93 1
            throw new LogicException('Array may not have more than 2 generic types');
94
        }
95
96 15
        if ($this->keyType->phpType !== TypeToken::WILDCARD
97 15
            && $this->keyType->phpType !== TypeToken::STRING
98 15
            && $this->keyType->phpType !== TypeToken::INTEGER
99
        ) {
100 1
            throw new LogicException('Array keys must be strings or integers');
101
        }
102
103 14
        $arrayIsObject = $this->numberOfGenerics === 2 || is_string(key($value));
104 14
        $enableScalarAdapters = $context->enableScalarAdapters();
105
106 14
        foreach ($value as $key => $item) {
107 13
            $itemValue = null;
108 13
            switch ($this->numberOfGenerics) {
109 13
                case 0:
110 7
                    if (!$enableScalarAdapters && ($item === null || is_scalar($item))) {
111 3
                        $itemValue = $item;
112 3
                        break;
113
                    }
114
115 4
                    if (!$arrayIsObject) {
116 1
                        $itemValue = $this->valueTypeAdapter->read($item, $context);
117 1
                        break;
118
                    }
119
120 3
                    if (is_array($item)) {
121 1
                        $itemValue = $this->read($item, $context);
122 1
                        break;
123
                    }
124
125 3
                    $type = TypeToken::createFromVariable($item);
126 3
                    $adapter = $this->adapters[$type->rawType] ?? $this->adapters[$type->rawType] = $this->typeAdapterProvider->getAdapter($type);
127 3
                    $itemValue = $adapter->read($item, $context);
128 3
                    break;
129 7
                case 1:
130 2
                    $itemValue = $this->valueTypeAdapter->read($item, $context);
131 2
                    break;
132 5
                case 2:
133 5
                    if (($this->keyType->phpType === TypeToken::INTEGER) && !ctype_digit((string)$key)) {
134 1
                        throw new JsonSyntaxException('Expected integer, but found string for key');
135
                    }
136
137 4
                    $itemValue = (!$enableScalarAdapters && ($item ===  null || is_scalar($item)))
138 2
                        ? $item
139 4
                        : $this->valueTypeAdapter->read($item, $context);
140 4
                    break;
141
            }
142
143 12
            $result[$arrayIsObject ? (string)$key : (int)$key] = $itemValue;
144
        }
145
146 13
        return $result;
147
    }
148
149
    /**
150
     * Write the value to the writer for the type
151
     *
152
     * @param array|null $value
153
     * @param WriterContext $context
154
     * @return array|null
155
     */
156 12
    public function write($value, WriterContext $context): ?array
157
    {
158 12
        if ($value === null) {
159 1
            return null;
160
        }
161
162 11
        if ($this->numberOfGenerics > 2) {
163 1
            throw new LogicException('Array may not have more than 2 generic types');
164
        }
165
166 10
        $arrayIsObject = $this->numberOfGenerics === 2 || is_string(key($value));
167 10
        $enableScalarAdapters = $context->enableScalarAdapters();
168 10
        $serializeNull = $context->serializeNull();
169 10
        $result = [];
170
171 10
        foreach ($value as $key => $item) {
172 9
            if ($item === null && !$serializeNull) {
173 1
                continue;
174
            }
175
176 9
            if (!$enableScalarAdapters && is_scalar($item)) {
177 2
                $result[$arrayIsObject ? (string)$key : (int)$key] = $item;
178 2
                continue;
179
            }
180
181 8
            $itemValue = null;
182 8
            switch ($this->numberOfGenerics) {
183
                // no generics specified
184 8
                case 0:
185 7
                    if (is_array($item)) {
186 1
                        $itemValue = $this->write($item, $context);
187 1
                        break;
188
                    }
189
190 6
                    $type = TypeToken::createFromVariable($item);
191 6
                    $adapter = $this->adapters[$type->rawType] ?? $this->adapters[$type->rawType] = $this->typeAdapterProvider->getAdapter($type);
192 6
                    $itemValue = $adapter->write($item, $context);
193 6
                    break;
194
                // generic for value specified
195 1
                case 1:
196 1
                case 2:
197 1
                    $itemValue = $this->valueTypeAdapter->write($item, $context);
198 1
                    break;
199
            }
200
201 8
            if ($itemValue === null && !$serializeNull) {
202 1
                continue;
203
            }
204
205 7
            $result[$arrayIsObject ? (string)$key : (int)$key] = $itemValue;
206
        }
207
208 10
        return $result;
209
    }
210
}
211