Completed
Push — 1.x ( 584456...24c051 )
by Pol
15:13 queued 13s
created

Attributes   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 463
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 51.63%

Importance

Changes 0
Metric Value
wmc 58
lcom 1
cbo 1
dl 0
loc 463
ccs 79
cts 153
cp 0.5163
rs 4.5599
c 0
b 0
f 0

22 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 2
A offsetGet() 0 10 2
A offsetSet() 0 7 1
A offsetUnset() 0 4 1
A offsetExists() 0 4 1
A remove() 0 12 3
A delete() 0 8 2
A without() 0 6 1
A exists() 0 12 3
A contains() 0 8 2
A __toString() 0 4 1
A render() 0 11 3
A toArray() 0 17 3
A getStorage() 0 4 1
A getIterator() 0 4 1
A prepareValues() 0 37 5
A normalizeValue() 0 4 1
A append() 0 15 2
A replace() 0 16 2
A merge() 0 16 2
B ensureFlatArray() 0 45 10
B ensureString() 0 30 9

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
namespace drupol\htmltag;
4
5
/**
6
 * Class Attributes.
7
 */
8
class Attributes implements \ArrayAccess, \IteratorAggregate
9
{
10
    /**
11
     * Stores the attribute data.
12
     *
13
     * @var \drupol\htmltag\Attribute[]
14
     */
15
    private $storage = array();
16
17
    /**
18
     * {@inheritdoc}
19
     */
20 7
    public function __construct(array $attributes = array())
21
    {
22 7
        foreach ($attributes as $name => $value) {
23
            $this->storage[$name] = new Attribute(
24
                $name,
25
                $this->ensureString($value)
26
            );
27
        }
28 7
    }
29
30
    /**
31
     * {@inheritdoc}
32
     */
33 1
    public function &offsetGet($name)
34
    {
35 1
        if (!isset($this->storage[$name])) {
36 1
            $this->storage[$name] = new Attribute(
37 1
                $name
38
            );
39
        }
40
41 1
        return $this->storage[$name];
42
    }
43
44
    /**
45
     * {@inheritdoc}
46
     */
47
    public function offsetSet($name, $value = null)
48
    {
49
        $this->storage[$name] = new Attribute(
50
            $name,
51
            $this->ensureString($value)
52
        );
53
    }
54
55
    /**
56
     * {@inheritdoc}
57
     */
58
    public function offsetUnset($name)
59
    {
60
        unset($this->storage[$name]);
61
    }
62
63
    /**
64
     * {@inheritdoc}
65
     */
66
    public function offsetExists($name)
67
    {
68
        return isset($this->storage[$name]);
69
    }
70
71
    /**
72
     * Append a value into an attribute.
73
     *
74
     * @param string $key
75
     *   The attribute's name.
76
     * @param string|array|bool $value
77
     *   The attribute's value.
78
     *
79
     * @return $this
80
     */
81 3
    public function append($key, $value = null)
82
    {
83
        $this->storage += array(
84 3
            $key => new Attribute(
85 3
                $key,
86 3
                $this->ensureString($value)
87
            )
88
        );
89
90 3
        foreach ($this->normalizeValue($value) as $value_item) {
91 2
            $this->storage[$key]->append($value_item);
92
        }
93
94 3
        return $this;
95
    }
96
97
    /**
98
     * Remove a value from a specific attribute.
99
     *
100
     * @param string $key
101
     *   The attribute's name.
102
     * @param string|array|bool $value
103
     *   The attribute's value.
104
     *
105
     * @return $this
106
     */
107 2
    public function remove($key, $value = false)
108
    {
109 2
        if (!isset($this->storage[$key])) {
110 1
            return $this;
111
        }
112
113 1
        foreach ($this->normalizeValue($value) as $value_item) {
114 1
            $this->storage[$key]->remove($value_item);
115
        }
116
117 1
        return $this;
118
    }
119
120
    /**
121
     * Delete an attribute.
122
     *
123
     * @param string|array $name
124
     *   The name of the attribute key to delete.
125
     *
126
     * @return $this
127
     */
128 1
    public function delete($name = array())
129
    {
130 1
        foreach ($this->normalizeValue($name) as $attribute_name) {
131 1
            unset($this->storage[$attribute_name]);
132
        }
133
134 1
        return $this;
135
    }
136
137
    /**
138
     * Return the attributes.
139
     *
140
     * @param string $key
141
     *   The attributes's name.
142
     * @param array|string $value
143
     *   The attribute's value.
144
     *
145
     * @return \drupol\htmltag\Attributes
146
     */
147
    public function without($key, $value)
148
    {
149
        $attributes = clone $this;
150
151
        return $attributes->remove($key, $value);
152
    }
153
154
    /**
155
     * Replace a value with another.
156
     *
157
     * @param string $key
158
     *   The attributes's name.
159
     * @param string $value
160
     *   The attribute's value.
161
     * @param array|string $replacement
162
     *   The replacement value.
163
     *
164
     * @return $this
165
     */
166
    public function replace($key, $value, $replacement)
167
    {
168
        $this->storage += array(
169
            $key => new Attribute(
170
                $key,
171
                $this->ensureString($value)
172
            )
173
        );
174
175
        $this->storage[$key]->remove($value);
176
        foreach ($this->normalizeValue($replacement) as $replacement_value) {
177
            $this->storage[$key]->append($replacement_value);
178
        }
179
180
        return $this;
181
    }
182
183
    /**
184
     * Merge attributes.
185
     *
186
     * @param array $data
187
     *   The data to merge.
188
     *
189
     * @return $this
190
     */
191
    public function merge(array $data = array())
192
    {
193
        foreach ($data as $key => $value) {
194
            $this->storage += array(
195
                $key => new Attribute(
196
                    $key
197
                )
198
            );
199
200
            $this->storage[$key]->merge(
201
                $this->normalizeValue($value)
202
            );
203
        }
204
205
        return $this;
206
    }
207
208
    /**
209
     * Check if an attribute exists and if a value if provided check it as well.
210
     *
211
     * @param string $key
212
     *   Attribute name.
213
     * @param string $value
214
     *   Todo.
215
     *
216
     * @return bool
217
     *   Whereas an attribute exists.
218
     */
219
    public function exists($key, $value = null)
220
    {
221
        if (!isset($this->storage[$key])) {
222
            return false;
223
        }
224
225
        if (null !== $value) {
226
            return $this->contains($key, $value);
227
        }
228
229
        return true;
230
    }
231
232
    /**
233
     * Check if attribute contains a value.
234
     *
235
     * @param string $key
236
     *   Attribute name.
237
     * @param string $value
238
     *   Attribute value.
239
     *
240
     * @return bool
241
     *   Whereas an attribute contains a value.
242
     */
243
    public function contains($key, $value)
244
    {
245
        if (!isset($this->storage[$key])) {
246
            return false;
247
        }
248
249
        return $this->storage[$key]->contains($value);
250
    }
251
252
    /**
253
     * {@inheritdoc}
254
     */
255 4
    public function __toString()
256
    {
257 4
        return $this->render();
258
    }
259
260
    /**
261
     * Render the attributes.
262
     */
263 4
    public function render()
264
    {
265
        // If empty, just return an empty string.
266 4
        if ([] === $this->storage) {
267 2
            return '';
268
        }
269
270 4
        $attributes = implode(' ', $this->prepareValues());
271
272 4
        return $attributes ? ' ' . $attributes : '';
273
    }
274
275
    /**
276
     * Returns all storage elements as an array.
277
     *
278
     * @return array
279
     *   An associative array of attributes.
280
     */
281
    public function toArray()
282
    {
283
        $attributes = $this->storage;
284
285
        // If empty, just return an empty array.
286
        if (empty($attributes)) {
287
            return array();
288
        }
289
290
        $result = [];
291
292
        foreach ($this->prepareValues() as $attribute) {
293
            $result[$attribute->getName()] = $attribute->getValueAsArray();
294
        }
295
296
        return $result;
297
    }
298
299
    /**
300
     * Get storage.
301
     *
302
     * @return \drupol\htmltag\Attribute[]
303
     *   Todo.
304
     */
305
    public function getStorage()
306
    {
307
        return $this->storage;
308
    }
309
310
    /**
311
     * {@inheritdoc}
312
     */
313
    public function getIterator()
314
    {
315
        return new \ArrayIterator($this->toArray());
316
    }
317
318
    /**
319
     * Returns all storage elements as an array.
320
     *
321
     * @return \drupol\htmltag\Attribute[]
322
     *   An associative array of attributes.
323
     */
324 4
    private function prepareValues()
325
    {
326 4
        $attributes = $this->storage;
327
328
        // If empty, just return an empty array.
329 4
        if (empty($attributes)) {
330
            return array();
331
        }
332
333
        // Sort the attributes.
334 4
        ksort($attributes);
335
336 4
        $result = [];
337
338 4
        foreach ($attributes as $attribute_name => $attribute) {
339
            switch ($attribute_name) {
340 4
                case 'class':
341 3
                    $classes = $attribute->getValueAsArray();
342 3
                    asort($classes);
343 3
                    $result[$attribute->getName()] = $attribute->set(
344 3
                        implode(' ', $classes)
345
                    );
346 3
                    break;
347
348 2
                case 'placeholder':
349
                    $result[$attribute->getName()] = $attribute->set(
350
                        strip_tags($attribute->getValueAsString())
351
                    );
352
                    break;
353
354
                default:
355 4
                    $result[$attribute->getName()] = $attribute;
356
            }
357
        }
358
359 4
        return $result;
360
    }
361
362
    /**
363
     * Normalize a value.
364
     *
365
     * @param mixed $value
366
     *  The value to normalize.
367
     *
368
     * @return array
369
     *   The value normalized.
370
     */
371 3
    private function normalizeValue($value)
372
    {
373 3
        return $this->ensureFlatArray($value);
374
    }
375
376
    /**
377
     * Todo.
378
     *
379
     * @param mixed $value
380
     *   Todo.
381
     *
382
     * @return array
383
     *   The array, flattened.
384
     */
385 3
    private function ensureFlatArray($value)
386
    {
387 3
        $type = gettype($value);
388
389 3
        $return = array();
390
391
        switch ($type) {
392 3
            case 'string':
393 2
                $return = explode(
394 2
                    ' ',
395 2
                    $this->ensureString($value)
396
                );
397 2
                break;
398
399 2
            case 'array':
400
                $flat_array = iterator_to_array(
401
                    new \RecursiveIteratorIterator(
402
                        new \RecursiveArrayIterator(
403
                            $value
404
                        )
405
                    ),
406
                    false
407
                );
408
409
                $return = [];
410 2
                foreach ($flat_array as $item) {
411 2
                    $return = array_merge(
412
                        $return,
413
                        $this->normalizeValue($item)
414 2
                    );
415 2
                }
416 2
                break;
417 2
418
            case 'double':
419
            case 'integer':
420 3
                $return = array($value);
421
                break;
422
            case 'object':
423
            case 'boolean':
424
            case 'resource':
425
            case 'NULL':
426
        }
427
428
        return $return;
429
    }
430
431
    /**
432 3
     * Todo.
433
     *
434 3
     * @param mixed $value
435
     *   Todo.
436 3
     *
437
     * @return string
438
     *   A string.
439 3
     */
440 2
    private function ensureString($value)
441 2
    {
442
        $type = gettype($value);
443 2
444
        $return = '';
445
446
        switch ($type) {
447
            case 'string':
448
                $return = $value;
449
                break;
450 2
451 2
            case 'array':
452
                $return = implode(
453
                    ' ',
454 2
                    $this->ensureFlatArray($value)
455 2
                );
456 2
                break;
457 2
458
            case 'double':
459
            case 'integer':
460 3
                $return = (string) $value;
461
                break;
462
            case 'object':
463
            case 'boolean':
464
            case 'resource':
465
            case 'NULL':
466
        }
467
468
        return $return;
469
    }
470
}
471