ArrayTypeAdapter   A
last analyzed

Complexity

Total Complexity 41

Size/Duplication

Total Lines 185
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 41
eloc 84
dl 0
loc 185
ccs 82
cts 82
cp 1
rs 9.1199
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
C write() 0 53 17
D read() 0 67 23

How to fix   Complexity   

Complex Class

Complex classes like ArrayTypeAdapter often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ArrayTypeAdapter, and based on these observations, apply Extract Interface, too.

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