Passed
Push — master ( 10361c...9be6d3 )
by Gabriel
03:47
created

Definition   C

Complexity

Total Complexity 53

Size/Duplication

Total Lines 380
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 93.33%

Importance

Changes 0
Metric Value
wmc 53
lcom 1
cbo 3
dl 0
loc 380
ccs 126
cts 135
cp 0.9333
rs 6.96
c 0
b 0
f 0

31 Methods

Rating   Name   Duplication   Size   Complexity  
A getItem() 0 11 2
A getItems() 0 8 2
A initItems() 0 11 3
A getItemsNames() 0 6 2
A getItemsNamesFromManager() 0 9 2
A getName() 0 8 2
A setName() 0 4 1
A initName() 0 5 1
A getField() 0 4 1
A setField() 0 4 1
A getManager() 0 4 1
A setManager() 0 4 1
A getItemsNamesFromFiles() 0 13 3
A getItemsDirectory() 0 8 2
A initItemsDirectory() 0 4 1
A generateItemsDirectory() 0 9 2
A generateManagerDirectory() 0 6 1
A generatePropertyDirectory() 0 4 1
A getLabel() 0 8 2
A setLabel() 0 4 1
A initLabel() 0 5 1
A isAbstractItemName() 0 11 3
A newStatus() 0 10 1
A getItemClass() 0 6 2
A getDefaultValue() 0 8 2
A setDefaultValue() 0 4 1
A initDefaultValue() 0 11 3
A getDefaultValueFromManager() 0 9 2
A hasItem() 0 6 1
A getPropertyItemsRootNamespace() 0 9 2
A getValues() 0 16 3

How to fix   Complexity   

Complex Class

Complex classes like Definition 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 Definition, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace ByTIC\Models\SmartProperties\Properties\Definitions;
4
5
use ByTIC\Models\SmartProperties\Properties\AbstractProperty\Generic as Property;
6
use ByTIC\Common\Records\Traits\HasSmartProperties\RecordsTrait;
7
use Exception;
8
use Nip\Records\RecordManager;
9
use Nip_File_System as FileSystem;
10
11
/**
12
 * Class Definition
13
 * @package ByTIC\Models\SmartProperties\Properties\Definitions
14
 */
15
class Definition
16
{
17
18
    /**
19
     * @var RecordManager|RecordsTrait
20
     */
21
    protected $manager;
22
23
    /**
24
     * @var string
25
     */
26
    protected $name = null;
27
28
    /**
29
     * @var string
30
     */
31
    protected $label = null;
32
33
    /**
34
     * @var string
35
     */
36
    protected $field;
37
38
    protected $items = null;
39
40
    protected $itemsDirectory = null;
41
42
    protected $defaultValue = null;
43
44
    /**
45
     * @param $name
46
     *
47
     * @return Property
48
     * @throws Exception
49
     */
50 7
    public function getItem($name)
51
    {
52 7
        $items = $this->getItems();
53 7
        if ( ! $this->hasItem($name)) {
54
            throw new Exception(
55
                'Bad Item [' . $name . '] for smart property 
56
                [' . $this->getManager()->getController() . '][' . $this->getName() . ']');
57
        }
58
59 7
        return $items[$name];
60
    }
61
62
    /**
63
     * @return null|Property[]
64
     */
65 11
    public function getItems()
66
    {
67 11
        if ($this->items == null) {
68 11
            $this->initItems();
69
        }
70
71 11
        return $this->items;
72
    }
73
74 11
    public function initItems()
75
    {
76 11
        $names       = $this->getItemsNames();
77 11
        $this->items = [];
78 11
        foreach ($names as $name) {
0 ignored issues
show
Bug introduced by
The expression $names of type array|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
79 11
            if ( ! $this->isAbstractItemName($name)) {
80 11
                $object                          = $this->newStatus($name);
81 11
                $this->items[$object->getName()] = $object;
82
            }
83
        }
84 11
    }
85
86
    /**
87
     * @return array
88
     */
89 11
    public function getItemsNames()
90
    {
91 11
        $names = $this->getItemsNamesFromManager();
92
93 11
        return $names ? $names : $this->getItemsNamesFromFiles();
94
    }
95
96
    /**
97
     * @return array|boolean
98
     */
99 11
    protected function getItemsNamesFromManager()
100
    {
101 11
        $methodName = 'get' . $this->getName() . 'Names';
102 11
        if (method_exists($this->getManager(), $methodName)) {
103
            return $this->getManager()->$methodName();
104
        }
105
106 11
        return false;
107
    }
108
109
    /**
110
     * @return mixed
111
     */
112 15
    public function getName()
113
    {
114 15
        if ($this->name === null) {
115 15
            $this->initName();
116
        }
117
118 15
        return $this->name;
119
    }
120
121
    /**
122
     * @param mixed $name
123
     */
124 15
    public function setName($name)
125
    {
126 15
        $this->name = $name;
127 15
    }
128
129 15
    protected function initName()
130
    {
131 15
        $name = inflector()->classify($this->getField());
132 15
        $this->setName($name);
133 15
    }
134
135
    /**
136
     * @return mixed
137
     */
138 15
    public function getField()
139
    {
140 15
        return $this->field;
141
    }
142
143
    /**
144
     * @param mixed $field
145
     */
146 15
    public function setField($field)
147
    {
148 15
        $this->field = $field;
149 15
    }
150
151
    /**
152
     * @return RecordManager
153
     */
154 13
    public function getManager()
155
    {
156 13
        return $this->manager;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->manager; of type Nip\Records\RecordManage...Properties\RecordsTrait adds the type ByTIC\Common\Records\Tra...Properties\RecordsTrait to the return on line 156 which is incompatible with the return type documented by ByTIC\Models\SmartProper...\Definition::getManager of type Nip\Records\RecordManager.
Loading history...
157
    }
158
159
    /**
160
     * @param RecordManager|RecordsTrait $manager
161
     */
162 15
    public function setManager($manager)
163
    {
164 15
        $this->manager = $manager;
165 15
    }
166
167
    /**
168
     * @return array
169
     */
170 11
    protected function getItemsNamesFromFiles()
171
    {
172 11
        $files = scandir($this->getItemsDirectory());
173 11
        foreach ($files as $key=>&$name) {
174 11
            if (in_array($name, ['.', '..'])) {
175 11
                unset($files[$key]);
176
            } else {
177 11
                $name = str_replace('.php', '', $name);
178
            }
179
        }
180
181 11
        return $files;
182
    }
183
184
    /**
185
     * @return null|string
186
     */
187 13
    public function getItemsDirectory()
188
    {
189 13
        if ($this->itemsDirectory == null) {
190 13
            $this->initItemsDirectory();
191
        }
192
193 13
        return $this->itemsDirectory;
194
    }
195
196 13
    public function initItemsDirectory()
197
    {
198 13
        $this->itemsDirectory = $this->generateItemsDirectory();
199 13
    }
200
201
    /**
202
     * @return string
203
     */
204 13
    public function generateItemsDirectory()
205
    {
206 13
        $methodName = 'get' . $this->getName() . 'ItemsDirectory';
207 13
        if (method_exists($this->getManager(), $methodName)) {
208
            return $this->getManager()->$methodName();
209
        }
210
211 13
        return $this->generateManagerDirectory() . DIRECTORY_SEPARATOR . $this->generatePropertyDirectory();
212
    }
213
214
    /**
215
     * @return string
216
     */
217 13
    protected function generateManagerDirectory()
218
    {
219 13
        $reflector = new \ReflectionObject($this->getManager());
220
221 13
        return dirname($reflector->getFileName());
222
    }
223
224
    /**
225
     * @return string
226
     */
227 13
    protected function generatePropertyDirectory()
228
    {
229 13
        return $this->getLabel();
230
    }
231
232
    /**
233
     * @return string
234
     */
235 13
    public function getLabel()
236
    {
237 13
        if ($this->label === null) {
238 13
            $this->initLabel();
239
        }
240
241 13
        return $this->label;
242
    }
243
244
    /**
245
     * @param string $label
246
     */
247 13
    public function setLabel($label)
248
    {
249 13
        $this->label = $label;
250 13
    }
251
252 13
    protected function initLabel()
253
    {
254 13
        $name = inflector()->pluralize($this->getName());
255 13
        $this->setLabel($name);
256 13
    }
257
258
    /**
259
     * @param string $name
260
     *
261
     * @return bool
262
     */
263 11
    public function isAbstractItemName($name)
264
    {
265 11
        if (in_array($name, ['Abstract', 'Generic'])) {
266
            return true;
267
        }
268 11
        if (strpos($name, 'Abstract') === 0) {
269 11
            return true;
270
        }
271
272 11
        return false;
273
    }
274
275
    /**
276
     * @param string $type
277
     *
278
     * @return Property
279
     */
280 11
    public function newStatus($type = null)
281
    {
282 11
        $className = $this->getItemClass($type);
0 ignored issues
show
Bug introduced by
It seems like $type defined by parameter $type on line 280 can also be of type string; however, ByTIC\Models\SmartProper...inition::getItemClass() does only seem to accept null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
283 11
        $object    = new $className();
284
        /** @var Property $object */
285 11
        $object->setManager($this->getManager());
286 11
        $object->setField($this->getField());
287
288 11
        return $object;
289
    }
290
291
    /**
292
     * @param null $type
293
     *
294
     * @return string
295
     */
296 11
    public function getItemClass($type = null)
297
    {
298 11
        $type = $type ? $type : $this->getDefaultValue();
299
300 11
        return $this->getPropertyItemsRootNamespace() . inflector()->classify($type);
301
    }
302
303
    /**
304
     * @return string
305
     */
306 2
    public function getDefaultValue()
307
    {
308 2
        if ($this->defaultValue === null) {
309 2
            $this->initDefaultValue();
310
        }
311
312 2
        return $this->defaultValue;
313
    }
314
315
    /**
316
     * @param null $defaultValue
317
     */
318 2
    public function setDefaultValue($defaultValue)
319
    {
320 2
        $this->defaultValue = $defaultValue;
321 2
    }
322
323 2
    protected function initDefaultValue()
324
    {
325 2
        $managerDefaultValue = $this->getDefaultValueFromManager();
326 2
        if ($managerDefaultValue && $this->hasItem($managerDefaultValue)) {
327
            $defaultValue = $managerDefaultValue;
328
        } else {
329 2
            $keys         = array_keys($this->getItems());
330 2
            $defaultValue = reset($keys);
331
        }
332 2
        $this->setDefaultValue($defaultValue);
333 2
    }
334
335
    /**
336
     * @return bool|string
337
     */
338 2
    protected function getDefaultValueFromManager()
339
    {
340 2
        $method = 'getDefault' . $this->getName();
341 2
        if (method_exists($this->getManager(), $method)) {
342
            return $this->getManager()->{$method}();
343
        }
344
345 2
        return false;
346
    }
347
348
    /**
349
     * @param $name
350
     *
351
     * @return bool
352
     */
353 7
    public function hasItem($name)
354
    {
355 7
        $items = $this->getItems();
356
357 7
        return isset($items[$name]);
358
    }
359
360
    /**
361
     * @return string
362
     */
363 11
    protected function getPropertyItemsRootNamespace()
364
    {
365 11
        $method = 'get' . $this->getName() . 'ItemsRootNamespace';
366 11
        if (method_exists($this->getManager(), $method)) {
367
            return $this->getManager()->{$method}();
368
        }
369
370 11
        return $this->getManager()->getModelNamespace() . $this->getLabel() . '\\';
371
    }
372
373
    /**
374
     * @param $name
375
     *
376
     * @return array
377
     */
378 2
    public function getValues($name)
379
    {
380 2
        $return = [];
381 2
        $items  = $this->getItems();
382
383 2
        foreach ($items as $type) {
0 ignored issues
show
Bug introduced by
The expression $items of type null|array<integer,objec...tractProperty\Generic>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
384 2
            $method = 'get' . ucfirst($name);
385 2
            if (method_exists($type, $method)) {
386 2
                $return[] = $type->$method();
387
            } else {
388 2
                $return[] = $type->{$name};
389
            }
390
        }
391
392 2
        return $return;
393
    }
394
}
395