Completed
Push — v0.7 ( 7c9b41...5c4e91 )
by Nate
02:30
created

ArrayTypeAdapter::read()   D

Complexity

Conditions 18
Paths 20

Size

Total Lines 64
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 35
CRAP Score 18

Importance

Changes 0
Metric Value
eloc 37
c 0
b 0
f 0
dl 0
loc 64
ccs 35
cts 35
cp 1
rs 4.8666
cc 18
nc 20
nop 2
crap 18

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 stdClass;
13
use Tebru\Gson\Exception\JsonSyntaxException;
14
use Tebru\Gson\Internal\TypeAdapterProvider;
15
use Tebru\Gson\ReaderContext;
16
use Tebru\Gson\TypeAdapter;
17
use Tebru\Gson\WriterContext;
18
use Tebru\PhpType\TypeToken;
19
20
/**
21
 * Class ArrayTypeAdapter
22
 *
23
 * @author Nate Brunette <[email protected]>
24
 */
25
class ArrayTypeAdapter extends TypeAdapter
26
{
27
    /**
28
     * @var TypeAdapterProvider
29
     */
30
    private $typeAdapterProvider;
31
32
    /**
33
     * @var TypeToken
34
     */
35
    private $keyType;
36
37
    /**
38
     * @var TypeAdapter
39
     */
40
    private $valueTypeAdapter;
41
42
    /**
43
     * @var int
44
     */
45
    private $numberOfGenerics;
46
47
    /**
48
     * Constructor
49
     *
50
     * @param TypeAdapterProvider $typeAdapterProvider
51
     * @param TypeToken $keyType
52
     * @param TypeAdapter $valueTypeAdapter
53
     * @param int $numberOfGenerics
54
     */
55
    public function __construct(
56 24
        TypeAdapterProvider $typeAdapterProvider,
57
        TypeToken $keyType,
58
        TypeAdapter $valueTypeAdapter,
59
        int $numberOfGenerics
60
    ) {
61
        $this->typeAdapterProvider = $typeAdapterProvider;
62 24
        $this->keyType = $keyType;
63 24
        $this->valueTypeAdapter = $valueTypeAdapter;
64 24
        $this->numberOfGenerics = $numberOfGenerics;
65 24
    }
66 24
67
    /**
68
     * Read the next value, convert it to its type and return it
69
     *
70
     * @param array|null $value
71
     * @param ReaderContext $context
72
     * @return array|null
73
     */
74
    public function read($value, ReaderContext $context): ?array
75
    {
76 14
        if ($value === null) {
77
            return null;
78 14
        }
79 14
80 1
        $array = [];
81 1
82
        if ($this->numberOfGenerics > 2) {
83
            throw new LogicException('Array may not have more than 2 generic types');
84 13
        }
85
86 13
        $arrayIsObject = $this->isArrayObject($value);
87 1
        $enableScalarAdapters = $context->enableScalarAdapters();
88
89
        foreach ($value as $key => $item) {
90
            if (!$enableScalarAdapters && is_scalar($item)) {
91 12
                $array[$arrayIsObject ? (string)$key : (int)$key] = $item;
92 7
                continue;
93
            }
94 7
95 7
            $itemValue = null;
96
            switch ($this->numberOfGenerics) {
97 7
                case 0:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
98
99 7
                    if (!$arrayIsObject) {
100
                        $itemValue = $this->valueTypeAdapter->read($item, $context);
101
                        break;
102
                    }
103 3
104 1
                    // By now we know that we're deserializing a json object to an array.
105 3
                    // If there is a nested object, continue deserializing to an array,
106
                    // otherwise guess the type using the wildcard
107 3
                    $type = is_array($item)
108 3
                        ? TypeToken::create(TypeToken::HASH)
109 3
                        : TypeToken::create(TypeToken::WILDCARD);
110
111 5
                    $adapter = $this->typeAdapterProvider->getAdapter($type);
112 1
                    $itemValue = $adapter->read($item, $context);
113 1
                    break;
114
                case 1:
115 4
                    $itemValue = $this->valueTypeAdapter->read($item, $context);
116 4
                    break;
117 1
                case 2:
118
                    if (!$arrayIsObject) {
119
                        throw new LogicException('An array may only specify a generic type for the value');
120 3
                    }
121 2
122 1
                    if ($this->keyType->phpType !== TypeToken::STRING && $this->keyType->phpType !== TypeToken::INTEGER) {
123
                        throw new LogicException('Array keys must be strings or integers');
124
                    }
125 1
126
                    if (($this->keyType->phpType === TypeToken::INTEGER) && !ctype_digit($key)) {
127
                        throw new JsonSyntaxException('Expected integer, but found string for key');
128 2
                    }
129
130 2
                    $itemValue = $this->valueTypeAdapter->read($item, $context);
131
                    break;
132
            }
133
134 5
            $array[$arrayIsObject ? (string)$key : (int)$key] = $itemValue;
135
        }
136 5
137 5
        return $array;
138 4
    }
139
140 4
    /**
141 4
     * Write the value to the writer for the type
142
     *
143 4
     * @param array|stdClass|null $value
144 3
     * @param WriterContext $context
145 3
     * @return array|null
146
     */
147 3
    public function write($value, WriterContext $context): ?array
148
    {
149 1
        if ($value === null) {
150
            return null;
151
        }
152
153 3
        if ($this->numberOfGenerics > 2) {
154
            throw new LogicException('Array may not have more than 2 generic types');
155 3
        }
156
157 1
        $arrayIsObject = $this->isArrayObject($value);
158
        $enableScalarAdapters = $context->enableScalarAdapters();
159
        $result = [];
160 8
161
        foreach ($value as $key => $item) {
162
            if (!$enableScalarAdapters && is_scalar($item)) {
163
                $result[$arrayIsObject ? (string)$key : (int)$key] = $item;
164
                continue;
165
            }
166
167
            $itemValue = null;
168
            switch ($this->numberOfGenerics) {
169
                // no generics specified
170
                case 0:
171 10
                    $adapter = $this->typeAdapterProvider->getAdapter(TypeToken::createFromVariable($item));
172
                    $itemValue = $adapter->write($item, $context);
173 10
                    break;
174 1
                // generic for value specified
175
                case 1:
176 1
                case 2:
177
                    $itemValue = $this->valueTypeAdapter->write($item, $context);
178
                    break;
179 9
            }
180 1
181
            $result[$arrayIsObject ? (string)$key : (int)$key] = $itemValue;
182
        }
183 8
184
        return $result;
185 8
    }
186 4
187
    /**
188 4
     * Returns true if the array is acting like an object
189
     * @param array|stdClass $array
190
     * @return bool
191 8
     */
192 8
    private function isArrayObject($array): bool
193
    {
194 8
        if ($this->numberOfGenerics === 2) {
195 5
            return true;
196 2
        }
197
198
        return is_string(key($array));
0 ignored issues
show
Bug introduced by
It seems like $array can also be of type stdClass; however, parameter $array of key() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

198
        return is_string(key(/** @scrutinizer ignore-type */ $array));
Loading history...
199 5
    }
200
}
201