Passed
Push — master ( f57d20...5d5950 )
by Paweł
02:41
created

AbstractItem   C

Complexity

Total Complexity 53

Size/Duplication

Total Lines 218
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 1 Features 0
Metric Value
eloc 114
c 2
b 1
f 0
dl 0
loc 218
ccs 97
cts 97
cp 1
rs 6.96
wmc 53

4 Methods

Rating   Name   Duplication   Size   Complexity  
B __call() 0 36 10
B __construct() 0 28 11
D toArray() 0 66 19
C grabData() 0 53 13

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * This file is part of Wszetko Sitemap.
7
 *
8
 * (c) Paweł Kłopotek-Główczewski <[email protected]>
9
 *
10
 * This source file is subject to the MIT license that is bundled
11
 * with this source code in the file LICENSE.
12
 */
13
14
namespace Wszetko\Sitemap\Items;
15
16
use Error;
17
use ReflectionClass;
18
use ReflectionProperty;
19
use Wszetko\Sitemap\Interfaces\DataType;
20
use Wszetko\Sitemap\Interfaces\Item;
21
use Wszetko\Sitemap\Items\DataTypes\ArrayType;
22
use Wszetko\Sitemap\Traits\Domain;
23
use Wszetko\Sitemap\Traits\IsAssoc;
24
25
/**
26
 * Class AbstractItem.
27
 *
28
 * @package Wszetko\Sitemap\Items
29
 */
30
abstract class AbstractItem implements Item
31
{
32
    use IsAssoc;
33
    use Domain;
34
35
    /**
36
     * AbstractItem constructor.
37
     *
38
     * @throws \ReflectionException
39
     */
40 498
    public function __construct()
41
    {
42 498
        $class = new ReflectionClass($this);
43 498
        $properties = $class->getProperties(ReflectionProperty::IS_PROTECTED);
44
45 498
        foreach ($properties as $property) {
46 496
            $data = $this->grabData($property);
47
48
            if (
49 496
                is_array($data) &&
50
                (
51 496
                    isset($data['type']) &&
52 496
                    '' !== $data['type']
53
                ) &&
54 496
                class_exists($data['type']) &&
55 496
                in_array('Wszetko\Sitemap\Interfaces\DataType', class_implements($data['type']), true)
56
            ) {
57
                if (
58 496
                    isset($data['dataType']) &&
59 496
                    '' !== $data['dataType'] &&
60 496
                    class_exists($data['dataType']) &&
61 496
                    in_array('Wszetko\Sitemap\Interfaces\DataType', class_implements($data['dataType']), true)
62
                ) {
63 380
                    $this->{$property->getName()} = new ArrayType($property->getName(), $data['dataType']);
64 380
                    $this->{$property->getName()}->getBaseDataType()->addAttributes($data['attributes']);
65
                } else {
66 478
                    $this->{$property->getName()} = new $data['type']($property->getName());
67 478
                    $this->{$property->getName()}->addAttributes($data['attributes']);
68
                }
69
            }
70
        }
71 498
    }
72
73
    /**
74
     * @param mixed $name
75
     * @param mixed $arguments
76
     *
77
     * @return mixed
78
     *
79
     * @throws \Error
80
     */
81 496
    public function __call($name, $arguments)
82
    {
83 496
        $operation = mb_substr($name, 0, 3);
84 496
        $property = lcfirst(mb_substr($name, 3));
85
86
        if (
87 496
            property_exists($this, $property) &&
88 496
            in_array($operation, ['add', 'set', 'get'], true) &&
89 496
            ($this->{$property} instanceof DataType)
90
        ) {
91 496
            switch ($operation) {
92 496
                case 'add':
93 48
                    if (method_exists($this->{$property}, 'addValue')) {
94 46
                        $this->{$property}->addValue($arguments[0], array_slice($arguments, 1));
95
96 44
                        return $this;
97
                    }
98
99 2
                    break;
100 482
                case 'set':
101 478
                    $this->{$property}->setValue($arguments[0], array_slice($arguments, 1));
102
103 474
                    return $this;
104 436
                case 'get':
105
                    if (
106 436
                        method_exists($this->{$property}, 'setDomain') &&
107 436
                        null !== $this->getDomain()
108
                    ) {
109 80
                        $this->{$property}->setDomain($this->getDomain());
110
                    }
111
112 436
                    return $this->{$property}->getValue();
113
            }
114
        }
115
116 4
        throw new Error('Call to undefined method ' . __CLASS__ . '::' . $name . '()');
117
    }
118
119
    /**
120
     * @return array
121
     */
122 20
    public function toArray(): array
123
    {
124 20
        $array = [];
125
126 20
        if (static::NAMESPACE_NAME && static::ELEMENT_NAME) {
127
            $array = [
128 12
                '_namespace' => static::NAMESPACE_NAME,
129 12
                '_element' => static::ELEMENT_NAME,
130
            ];
131
        }
132
133 20
        $array[static::ELEMENT_NAME] = [];
134
135 20
        foreach (array_keys(get_object_vars($this)) as $property) {
136 20
            if (is_object($this->{$property})) {
137 18
                $method = 'get' . ucfirst($property);
138 18
                preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $property, $matches);
139 18
                $property = $matches[0];
140
141 18
                foreach ($property as &$match) {
142 18
                    $match = $match == mb_strtoupper($match) ? mb_strtolower($match) : lcfirst($match);
143
                }
144
145 18
                $property = implode('_', $property);
146 18
                $data = $this->{$method}();
147
148 18
                if (is_array($data)) {
149 4
                    if ($this->isAssoc($data)) {
150 4
                        $item = array_key_first($data);
151
152 4
                        if (null !== $item) {
153 4
                            $array[static::ELEMENT_NAME][$property]['_value'] = $item;
154
155 4
                            if (array_key_exists($item, $data)) {
156 4
                                foreach ($data[$item] as $attr => $val) {
157 4
                                    $array[static::ELEMENT_NAME][$property]['_attributes'][$attr] = $val;
158
                                }
159
                            }
160
                        }
161
                    } else {
162 4
                        foreach ($data as $element) {
163 4
                            if (is_array($element) && $this->isAssoc($element)) {
164 4
                                $elementData = [];
165
166 4
                                foreach ($element as $value => $attributes) {
167 4
                                    $elementData['_value'] = $value;
168
169 4
                                    foreach ($attributes as $attr => $val) {
170 4
                                        $elementData['_attributes'][$attr] = $val;
171
                                    }
172
                                }
173
174 4
                                $array[static::ELEMENT_NAME][$property][] = $elementData;
175
176
                            } else {
177 4
                                $array[static::ELEMENT_NAME][$property][] = $element;
178
                            }
179
                        }
180
                    }
181 18
                } elseif (null !== $data && '' !== $data) {
182 18
                    $array[static::ELEMENT_NAME][$property] = $data;
183
                }
184
            }
185
        }
186
187 20
        return $array;
188
    }
189
190
    /**
191
     * @param \ReflectionProperty $property
192
     *
193
     * @return null|array
194
     */
195 496
    private function grabData(ReflectionProperty $property): ?array
196
    {
197 496
        if (false === $property->getDocComment()) {
198
            // @codeCoverageIgnoreStart
199
            return null;
200
            // @codeCoverageIgnoreEnd
201
        }
202
203 496
        preg_match_all(
204 496
            '/
205
                        @var\s+(?\'type\'[^\s]+)|
206
                        @dataType\s+(?\'dataType\'[^\s]+)|
207
                        @attribute\s+(?\'attribute\'[^\s]+)|
208
                        @attributeDataType\s+(?\'attributeDataType\'[^\s]+)
209
                    /mx',
210 496
            $property->getDocComment(),
211 248
            $matches
212
        );
213
214
        $results = [
215 496
            'type' => null,
216
            'dataType' => null,
217
            'attributes' => [],
218
        ];
219
220 496
        foreach ($matches['type'] as $match) {
221 496
            if ('' !== $match && null !== $match) {
222 496
                $results['type'] = $match;
223
224 496
                break;
225
            }
226
        }
227
228 496
        foreach ($matches['dataType'] as $match) {
229 496
            if ('' !== $match && null !== $match) {
230 380
                $results['dataType'] = $match;
231
232 380
                break;
233
            }
234
        }
235
236 496
        foreach ($matches['attribute'] as $key => $match) {
237
            if (
238 496
                '' !== $match &&
239 496
                null !== $match &&
240 496
                isset($matches['attributeDataType'][$key + 1]) &&
241 496
                '' !== $matches['attributeDataType'][$key + 1]
242
            ) {
243 320
                $results['attributes'][$match] = $matches['attributeDataType'][$key + 1];
244
            }
245
        }
246
247 496
        return $results;
248
    }
249
}
250