Completed
Push — 1.x ( 9316fc...fa3a77 )
by Pol
01:27
created

Attributes::render()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 5
cts 5
cp 1
rs 9.9
c 0
b 0
f 0
cc 3
nc 3
nop 0
crap 3
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 View Code Duplication
    public function append($key, $value = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
    public function replace($key, $value, $replacement)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
167
    {
168
        $this->storage += array(
169
            $key => new Attribute(
170
                $key,
171
                $this->ensureString($value)
172
            )
173
        );
174
175
        foreach ($this->normalizeValue($replacement) as $replacement_value) {
176
            $this->storage[$key]->replace($value, $replacement_value);
177
        }
178
179
        return $this;
180
    }
181
182
    /**
183
     * Merge attributes.
184
     *
185
     * @param array $data
186
     *   The data to merge.
187
     *
188
     * @return $this
189
     */
190 View Code Duplication
    public function merge(array $data = array())
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
191
    {
192
        foreach ($data as $key => $value) {
193
            $this->storage += array(
194
                $key => new Attribute(
195
                    $key,
196
                    $this->ensureString($value)
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->storage[$key]->contains($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
                $return = iterator_to_array(
401
                    new \RecursiveIteratorIterator(
402
                        new \RecursiveArrayIterator(
403
                            $value
404
                        )
405
                    ),
406
                    false
407
                );
408
                break;
409
410 2
            case 'double':
411 2
            case 'integer':
412
                $return = array($value);
413
                break;
414 2
            case 'object':
415 2
            case 'boolean':
416 2
            case 'resource':
417 2
            case 'NULL':
418
        }
419
420 3
        return $return;
421
    }
422
423
    /**
424
     * Todo.
425
     *
426
     * @param mixed $value
427
     *   Todo.
428
     *
429
     * @return string
430
     *   A string.
431
     */
432 3
    private function ensureString($value)
433
    {
434 3
        $type = gettype($value);
435
436 3
        $return = '';
437
438
        switch ($type) {
439 3
            case 'string':
440 2
                $return = $value;
441 2
                break;
442
443 2
            case 'array':
444
                $return = implode(
445
                    ' ',
446
                    $this->ensureFlatArray($value)
447
                );
448
                break;
449
450 2
            case 'double':
451 2
            case 'integer':
452
                $return = (string) $value;
453
                break;
454 2
            case 'object':
455 2
            case 'boolean':
456 2
            case 'resource':
457 2
            case 'NULL':
458
        }
459
460 3
        return $return;
461
    }
462
}
463