Completed
Push — master ( 3be2b4...9bcc1d )
by Paweł
02:33
created

AbstractItem   B

Complexity

Total Complexity 48

Size/Duplication

Total Lines 202
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 1 Features 0
Metric Value
eloc 105
c 2
b 1
f 0
dl 0
loc 202
ccs 89
cts 89
cp 1
rs 8.5599
wmc 48

4 Methods

Rating   Name   Duplication   Size   Complexity  
C toArray() 0 51 15
C grabData() 0 53 13
B __call() 0 36 10
B __construct() 0 27 10

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
                ) {
62 380
                    $this->{$property->getName()} = new ArrayType($property->getName(), $data['dataType']);
63 380
                    $this->{$property->getName()}->getBaseDataType()->addAttributes($data['attributes']);
64
                } else {
65 478
                    $this->{$property->getName()} = new $data['type']($property->getName());
66 487
                    $this->{$property->getName()}->addAttributes($data['attributes']);
67
                }
68
            }
69
        }
70 498
    }
71
72
    /**
73
     * @param mixed $name
74
     * @param mixed $arguments
75
     *
76
     * @return mixed
77
     *
78
     * @throws \Error
79
     */
80 496
    public function __call($name, $arguments)
81
    {
82 496
        $operation = mb_substr($name, 0, 3);
83 496
        $property = lcfirst(mb_substr($name, 3));
84
85
        if (
86 496
            property_exists($this, $property) &&
87 496
            in_array($operation, ['add', 'set', 'get'], true) &&
88 496
            ($this->{$property} instanceof DataType)
89
        ) {
90 248
            switch ($operation) {
91 496
                case 'add':
92 48
                    if (method_exists($this->{$property}, 'addValue')) {
93 46
                        $this->{$property}->addValue($arguments[0], array_slice($arguments, 1));
94
95 44
                        return $this;
96
                    }
97
98 2
                    break;
99 482
                case 'set':
100 478
                    $this->{$property}->setValue($arguments[0], array_slice($arguments, 1));
101
102 474
                    return $this;
103 436
                case 'get':
104
                    if (
105 436
                        method_exists($this->{$property}, 'setDomain') &&
106 436
                        null !== $this->getDomain()
107
                    ) {
108 80
                        $this->{$property}->setDomain($this->getDomain());
109
                    }
110
111 436
                    return $this->{$property}->getValue();
112
            }
113
        }
114
115 4
        throw new Error('Call to undefined method ' . __CLASS__ . '::' . $name . '()');
116
    }
117
118
    /**
119
     * @return array
120
     */
121 20
    public function toArray(): array
122
    {
123 20
        $array = [];
124
125 20
        if (static::NAMESPACE_NAME && static::ELEMENT_NAME) {
126
            $array = [
127 12
                '_namespace' => static::NAMESPACE_NAME,
128 12
                '_element' => static::ELEMENT_NAME,
129
            ];
130
        }
131
132 20
        $array[static::ELEMENT_NAME] = [];
133
134 20
        foreach (array_keys(get_object_vars($this)) as $property) {
135 20
            if (is_object($this->{$property})) {
136 18
                $method = 'get' . ucfirst($property);
137 18
                preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $property, $matches);
138 18
                $property = $matches[0];
139
140 18
                foreach ($property as &$match) {
141 18
                    $match = $match == mb_strtoupper($match) ? mb_strtolower($match) : lcfirst($match);
142
                }
143
144 18
                $property = implode('_', $property);
145 18
                $data = $this->{$method}();
146
147 18
                if (is_array($data)) {
148 4
                    if ($this->isAssoc($data)) {
149 4
                        $item = array_key_first($data);
150
151 4
                        if (null !== $item) {
152 4
                            $array[static::ELEMENT_NAME][$property]['_value'] = $item;
153
154 4
                            if (array_key_exists($item, $data)) {
155 4
                                foreach ($data[$item] as $attr => $val) {
156 4
                                    $array[static::ELEMENT_NAME][$property]['_attributes'][$attr] = $val;
157
                                }
158
                            }
159
                        }
160
                    } else {
161 4
                        foreach ($data as $element) {
162 4
                            $array[static::ELEMENT_NAME][$property][] = $element;
163
                        }
164
                    }
165 18
                } elseif (null !== $data && '' !== $data) {
166 19
                    $array[static::ELEMENT_NAME][$property] = $data;
167
                }
168
            }
169
        }
170
171 20
        return $array;
172
    }
173
174
    /**
175
     * @param \ReflectionProperty $property
176
     *
177
     * @return null|array
178
     */
179 496
    private function grabData(ReflectionProperty $property): ?array
180
    {
181 496
        if (false === $property->getDocComment()) {
182
            // @codeCoverageIgnoreStart
183
            return null;
184
            // @codeCoverageIgnoreEnd
185
        }
186
187 496
        preg_match_all(
188 496
            '/
189
                        @var\s+(?\'type\'[^\s]+)|
190
                        @dataType\s+(?\'dataType\'[^\s]+)|
191
                        @attribute\s+(?\'attribute\'[^\s]+)|
192
                        @attributeDataType\s+(?\'attributeDataType\'[^\s]+)
193
                    /mx',
194 496
            $property->getDocComment(),
195 496
            $matches
196
        );
197
198
        $results = [
199 496
            'type' => null,
200
            'dataType' => null,
201
            'attributes' => [],
202
        ];
203
204 496
        foreach ($matches['type'] as $match) {
205 496
            if ('' !== $match && null !== $match) {
206 496
                $results['type'] = $match;
207
208 496
                break;
209
            }
210
        }
211
212 496
        foreach ($matches['dataType'] as $match) {
213 496
            if ('' !== $match && null !== $match) {
214 380
                $results['dataType'] = $match;
215
216 438
                break;
217
            }
218
        }
219
220 496
        foreach ($matches['attribute'] as $key => $match) {
221
            if (
222 496
                '' !== $match &&
223 496
                null !== $match &&
224 496
                isset($matches['attributeDataType'][$key + 1]) &&
225 496
                '' !== $matches['attributeDataType'][$key + 1]
226
            ) {
227 408
                $results['attributes'][$match] = $matches['attributeDataType'][$key + 1];
228
            }
229
        }
230
231 496
        return $results;
232
    }
233
}
234