AbstractObject   B
last analyzed

Complexity

Total Complexity 44

Size/Duplication

Total Lines 293
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 44
eloc 64
dl 0
loc 293
c 0
b 0
f 0
rs 8.8798

19 Methods

Rating   Name   Duplication   Size   Complexity  
A isFieldRequired() 0 4 1
A __toString() 0 4 1
A getUpDownInstance() 0 8 2
A toJson() 0 4 1
A __call() 0 15 6
A setArgs() 0 19 6
A throwErrors() 0 5 2
A setData() 0 4 1
A __get() 0 9 3
A getClassName() 0 4 1
A toArray() 0 13 4
A setUpDown() 0 4 1
A getCleanClassName() 0 4 1
A getNamespace() 0 4 1
A __set() 0 6 1
A getPropertyType() 0 4 2
A __construct() 0 5 1
A addError() 0 6 1
B parseFieldValue() 0 14 8

How to fix   Complexity   

Complex Class

Complex classes like AbstractObject 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 AbstractObject, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Copyright (c) 2019 - present
4
 * updown - AbstractObject.php
5
 * author: Roberto Belotti - [email protected]
6
 * web : robertobelotti.com, github.com/biscolab
7
 * Initial version created on: 15/2/2019
8
 * MIT license: https://github.com/biscolab/updown-php/blob/master/LICENSE
9
 */
10
11
namespace Biscolab\UpDown\Abstracts;
12
13
use Biscolab\UpDown\Enum\UpDownFieldType;
14
use Biscolab\UpDown\Exception\Exception;
15
use Biscolab\UpDown\UpDown;
16
use function Biscolab\UpDown\camel2Snake;
17
18
/**
19
 * Class AbstractObject
20
 * @package Biscolab\UpDown\Abstracts
21
 */
22
abstract class AbstractObject
23
{
24
25
    /**
26
     * @var array
27
     */
28
    protected $typeCheck = [];
29
30
    /**
31
     * @var array
32
     */
33
    protected $required = [];
34
35
    /**
36
     * @var array
37
     */
38
    protected $attributes = [];
39
40
    /**
41
     * @var array
42
     */
43
    protected $errors = [];
44
45
    /**
46
     * @var null|UpDown
47
     */
48
    protected $updown = null;
49
50
    /**
51
     * AbstractObject constructor.
52
     *
53
     * @param array $args
54
     */
55
    public function __construct($args = [])
56
    {
57
58
        $this->setUpDown();
59
        $this->setArgs($args);
60
    }
61
62
    /**
63
     * Set UpDown instance
64
     */
65
    protected function setUpDown()
66
    {
67
68
        $this->updown = static::getUpDownInstance(UpDown::instance());
69
70
    }
71
72
    /**
73
     * @param UpDown|null $instance
74
     *
75
     * @return UpDown
76
     * @throws Exception
77
     */
78
    protected static function getUpDownInstance(UpDown $instance = null): UpDown
79
    {
80
81
        if (!$instance instanceof UpDown) {
82
            throw new Exception('Invalid UpDown instance');
83
        }
84
85
        return $instance;
86
87
    }
88
89
    /**
90
     * @param array|null $args
91
     *
92
     * @return AbstractObject
93
     */
94
    protected function setArgs(?array $args = []): AbstractObject
95
    {
96
97
        if (is_null($args)) {
0 ignored issues
show
introduced by Roberto
The condition is_null($args) is always false.
Loading history...
98
            $args = [];
99
        }
100
101
        foreach ($this->typeCheck as $field_name => $field_type) {
102
            if (empty($args[$field_name]) || is_null($args[$field_name])) {
103
                if ($this->isFieldRequired($field_name)) {
104
                    $this->addError('Missing "' . $field_name . '" in ' . static::class);
105
                }
106
            } else {
107
                $this->attributes[$field_name] = $this->parseFieldValue($field_type, $args[$field_name]);
108
            }
109
        }
110
        $this->throwErrors();
111
112
        return $this;
113
    }
114
115
    /**
116
     * @param string $field_name
117
     *
118
     * @return bool
119
     */
120
    protected function isFieldRequired(string $field_name): bool
121
    {
122
123
        return in_array($field_name, $this->required);
124
    }
125
126
    /**
127
     * @param string $error
128
     *
129
     * @return array
130
     */
131
    protected function addError(string $error): array
132
    {
133
134
        array_push($this->errors, $error);
135
136
        return $this->errors;
137
    }
138
139
    /**
140
     * @param string $field_type
141
     * @param string $field_value
142
     *
143
     * @return mixed
144
     */
145
    protected function parseFieldValue(string $field_type, $field_value)
146
    {
147
148
        switch ($field_type) {
149
            case UpDownFieldType::STRING:
150
            case UpDownFieldType::INT:
151
            case UpDownFieldType::FLOAT:
152
            case UpDownFieldType::ARRAY:
153
            case UpDownFieldType::BOOL:
154
                return $field_value;
155
            case UpDownFieldType::DATETIME:
156
                return strtotime($field_value);
157
            default:
158
                return ($field_value instanceof $field_type) ? $field_value : new $field_type($field_value);
159
        }
160
    }
161
162
    /**
163
     * @throws Exception
164
     */
165
    protected function throwErrors()
166
    {
167
168
        if (count($this->errors)) {
169
            throw new Exception(implode(', ', $this->errors));
170
        }
171
    }
172
173
    /**
174
     * @param array|null $args
175
     *
176
     * @return AbstractObject
177
     */
178
    public function setData(?array $args = []): AbstractObject
179
    {
180
181
        return $this->setArgs($args);
182
    }
183
184
    /**
185
     * @return string
186
     */
187
    public function toJson(): string
188
    {
189
190
        return json_encode($this->toArray());
191
    }
192
193
    /**
194
     * @return array
195
     */
196
    public function toArray(): array
197
    {
198
199
        $fields = $this->attributes;
200
201
        foreach ($fields as $field_name => $field_value) {
202
203
            if (!is_scalar($field_value) && method_exists($field_value, 'toJson')) {
204
                $fields[$field_name] = $field_value->toArray();
205
            }
206
        }
207
208
        return $fields;
209
    }
210
211
    /**
212
     * @return string
213
     */
214
    public function __toString(): string
215
    {
216
217
        return implode(',', $this->toArray());
218
    }
219
220
    /**
221
     * @param $name
222
     * @param $arguments
223
     *
224
     * @return mixed
225
     */
226
    public function __call($name, $arguments)
227
    {
228
229
        preg_match('/(?<=(g|s)et)([A-Za-z0-9])\w+/', $name, $match);
230
231
        $camel_field = (empty($match[0])) ? '' : $match[0];
232
233
        $snake_field = ($camel_field) ? camel2Snake($camel_field) : $name;
234
235
        if (!empty($match[1])) {
236
            switch ($match[1]) {
237
                case 's':
238
                    return $this->__set($snake_field, current($arguments));
239
                case 'g':
240
                    return $this->__get($snake_field);
241
            }
242
        }
243
244
    }
245
246
    /**
247
     * @param string $snake_field
248
     *
249
     * @return mixed|null
250
     * @throws Exception
251
     */
252
    public function __get(string $snake_field)
253
    {
254
255
        $field_type = $this->getPropertyType($snake_field);
256
        if (!$field_type) {
257
            throw new Exception('property ' . $snake_field . ' doesn’t exist');
258
        }
259
260
        return (empty($this->attributes[$snake_field])) ? null : $this->attributes[$snake_field];
261
262
    }
263
264
    /**
265
     * @param string $snake_field
266
     * @param        $value
267
     *
268
     * @return mixed
269
     */
270
    public function __set(string $snake_field, $value)
271
    {
272
273
        $field_type = $this->getPropertyType($snake_field);
274
275
        return $this->attributes[$snake_field] = $this->parseFieldValue($field_type, $value);
0 ignored issues
show
Bug introduced by Roberto
It seems like $field_type can also be of type null; however, parameter $field_type of Biscolab\UpDown\Abstract...ject::parseFieldValue() does only seem to accept string, 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

275
        return $this->attributes[$snake_field] = $this->parseFieldValue(/** @scrutinizer ignore-type */ $field_type, $value);
Loading history...
276
277
    }
278
279
    /**
280
     * @param string $snake_field
281
     *
282
     * @return null|string
283
     */
284
    protected function getPropertyType(string $snake_field): ?string
285
    {
286
287
        return (empty($this->typeCheck[$snake_field])) ? null : $this->typeCheck[$snake_field];
288
    }
289
290
    /**
291
     * @return string
292
     */
293
    public function getClassName()
294
    {
295
296
        return get_called_class();
297
    }
298
299
    /**
300
     * @return mixed
301
     */
302
    public function getCleanClassName()
303
    {
304
305
        return str_replace($this->getNamespace() . '\\', '', get_called_class());
306
    }
307
308
    /**
309
     * @return string
310
     */
311
    public function getNamespace()
312
    {
313
314
        return __NAMESPACE__;
315
    }
316
317
}