Completed
Push — 1.x ( dbb510...248b44 )
by Pol
11:13
created

Attributes::count()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 2
1
<?php
2
3
namespace drupol\htmltag;
4
5
/**
6
 * Class Attributes.
7
 */
8
class Attributes implements \ArrayAccess, \IteratorAggregate, \Countable
9
{
10
    /**
11
     * Stores the attribute data.
12
     *
13
     * @var \drupol\htmltag\Attribute[]
14
     */
15
    private $storage = array();
16
17
    /**
18
     * {@inheritdoc}
19
     */
20 13
    public function __construct(array $attributes = array())
21
    {
22 13
        foreach ($attributes as $name => $value) {
23
            $this->storage[$name] = new Attribute(
24
                $name,
25
                $this->ensureString($value)
26
            );
27
        }
28 13
    }
29
30
    /**
31
     * {@inheritdoc}
32
     */
33 2
    public function &offsetGet($name)
34
    {
35 2
        if (!isset($this->storage[$name])) {
36 2
            $this->storage[$name] = new Attribute(
37 2
                $name
38
            );
39
        }
40
41 2
        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 7
    public function append($key, $value = null)
82
    {
83
        $this->storage += array(
84 7
            $key => new Attribute(
85 7
                $key,
86 7
                $this->ensureString($value)
87
            )
88
        );
89
90 7
        foreach ($this->normalizeValue($value) as $value_item) {
91 6
            $this->storage[$key]->append($value_item);
92
        }
93
94 7
        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 1
    public function replace($key, $value, $replacement)
167
    {
168
        $this->storage += array(
169 1
            $key => new Attribute(
170 1
                $key,
171 1
                $this->ensureString($value)
172
            )
173
        );
174
175 1
        $this->storage[$key]->remove($value);
176 1
        foreach ($this->normalizeValue($replacement) as $replacement_value) {
177 1
            $this->storage[$key]->append($replacement_value);
178
        }
179
180 1
        return $this;
181
    }
182
183
    /**
184
     * Merge attributes.
185
     *
186
     * @param array $data
187
     *   The data to merge.
188
     *
189
     * @return $this
190
     */
191 1
    public function merge(array $data = array())
192
    {
193 1
        foreach ($data as $key => $value) {
194
            $this->storage += array(
195 1
                $key => new Attribute(
196 1
                    $key
197
                )
198
            );
199
200 1
            $this->storage[$key]->merge(
201 1
                $this->normalizeValue($value)
202
            );
203
        }
204
205 1
        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 1
    public function exists($key, $value = null)
220
    {
221 1
        if (!isset($this->storage[$key])) {
222 1
            return false;
223
        }
224
225 1
        if (null !== $value) {
226 1
            return $this->contains($key, $value);
227
        }
228
229 1
        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 2
    public function contains($key, $value)
244
    {
245 2
        if (!isset($this->storage[$key])) {
246 1
            return false;
247
        }
248
249 2
        return $this->storage[$key]->contains($value);
250
    }
251
252
    /**
253
     * {@inheritdoc}
254
     */
255 8
    public function __toString()
256
    {
257 8
        return $this->render();
258
    }
259
260
    /**
261
     * Render the attributes.
262
     */
263 8
    public function render()
264
    {
265
        // If empty, just return an empty string.
266 8
        if ([] === $this->storage) {
267 3
            return '';
268
        }
269
270 7
        $attributes = implode(' ', $this->prepareValues());
271
272 7
        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 7
    private function prepareValues()
325
    {
326 7
        $attributes = $this->storage;
327
328
        // If empty, just return an empty array.
329 7
        if (empty($attributes)) {
330
            return array();
331
        }
332
333
        // Sort the attributes.
334 7
        ksort($attributes);
335
336 7
        $result = [];
337
338 7
        foreach ($attributes as $attribute_name => $attribute) {
339 7
            switch ($attribute_name) {
340 7
                case 'class':
341 6
                    $classes = $attribute->getValueAsArray();
342 6
                    asort($classes);
343 6
                    $result[$attribute->getName()] = $attribute->set(
344 6
                        implode(' ', $classes)
345
                    );
346 6
                    break;
347
348
                default:
349 7
                    $result[$attribute->getName()] = $attribute;
350
            }
351
        }
352
353 7
        return $result;
354
    }
355
356
    /**
357
     * {@inheritdoc}
358
     */
359
    public function count() {
360
        return count($this->storage);
361
    }
362
363
    /**
364
     * Normalize a value.
365 7
     *
366
     * @param mixed $value
367 7
     *  The value to normalize.
368
     *
369
     * @return array
370
     *   The value normalized.
371
     */
372
    private function normalizeValue($value)
373
    {
374
        return $this->ensureFlatArray($value);
375
    }
376
377
    /**
378
     * Todo.
379 7
     *
380
     * @param mixed $value
381 7
     *   Todo.
382
     *
383 7
     * @return array
384
     *   The array, flattened.
385 7
     */
386 7
    private function ensureFlatArray($value)
387 6
    {
388 6
        $type = gettype($value);
389 6
390
        $return = array();
391 6
392
        switch ($type) {
393 6
            case 'string':
394 1
                $return = explode(
395 1
                    ' ',
396 1
                    $this->ensureString($value)
397 1
                );
398
                break;
399
400 1
            case 'array':
401
                $flat_array = iterator_to_array(
402
                    new \RecursiveIteratorIterator(
403 1
                        new \RecursiveArrayIterator(
404 1
                            $value
405 1
                        )
406 1
                    ),
407 1
                    false
408
                );
409
410 1
                $return = [];
411
                foreach ($flat_array as $item) {
412 6
                    $return = array_merge(
413 6
                        $return,
414
                        $this->normalizeValue($item)
415
                    );
416 6
                }
417 6
                break;
418 6
419 6
            case 'double':
420
            case 'integer':
421
                $return = array($value);
422 7
                break;
423
            case 'object':
424
            case 'boolean':
425
            case 'resource':
426
            case 'NULL':
427
        }
428
429
        return $return;
430
    }
431
432
    /**
433
     * Todo.
434 7
     *
435
     * @param mixed $value
436 7
     *   Todo.
437
     *
438 7
     * @return string
439
     *   A string.
440 7
     */
441 7
    private function ensureString($value)
442 6
    {
443 6
        $type = gettype($value);
444
445 6
        $return = '';
446
447
        switch ($type) {
448
            case 'string':
449
                $return = $value;
450
                break;
451
452 6
            case 'array':
453 6
                $return = implode(
454
                    ' ',
455
                    $this->ensureFlatArray($value)
456 6
                );
457 6
                break;
458 6
459 6
            case 'double':
460
            case 'integer':
461
                $return = (string) $value;
462 7
                break;
463
            case 'object':
464
            case 'boolean':
465
            case 'resource':
466
            case 'NULL':
467
        }
468
469
        return $return;
470
    }
471
}
472