1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace drupol\htmltag; |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* Class Attributes. |
7
|
|
|
*/ |
8
|
|
|
class Attributes implements AttributesInterface |
9
|
|
|
{ |
10
|
|
|
/** |
11
|
|
|
* Stores the attribute data. |
12
|
|
|
* |
13
|
|
|
* @var \drupol\htmltag\AttributeInterface[] |
14
|
|
|
*/ |
15
|
|
|
private $storage = array(); |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* The attribute factory. |
19
|
|
|
* |
20
|
|
|
* @var \drupol\htmltag\AttributeFactoryInterface |
21
|
|
|
*/ |
22
|
|
|
private $attributeFactory; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Attributes constructor. |
26
|
|
|
* |
27
|
|
|
* @param \drupol\htmltag\AttributeFactoryInterface $attributeFactory |
28
|
|
|
* The attribute factory. |
29
|
|
|
* @param mixed[] $attributes |
30
|
|
|
* The input attributes. |
31
|
|
|
*/ |
32
|
26 |
|
public function __construct(AttributeFactoryInterface $attributeFactory, $attributes = array()) |
33
|
|
|
{ |
34
|
26 |
|
$this->attributeFactory = $attributeFactory; |
35
|
26 |
|
$this->import($attributes); |
36
|
26 |
|
} |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* {@inheritdoc} |
40
|
|
|
*/ |
41
|
26 |
|
public function import($attributes = array()) |
42
|
|
|
{ |
43
|
26 |
|
foreach ($attributes as $name => $value) { |
44
|
4 |
|
$this->set($name, $value); |
45
|
|
|
} |
46
|
|
|
|
47
|
26 |
|
return $this; |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* {@inheritdoc} |
52
|
|
|
*/ |
53
|
8 |
|
public function set($name, $value = null) |
54
|
|
|
{ |
55
|
8 |
|
$this->storage[$name] = ($this->attributeFactory)::build($name, $value); |
56
|
|
|
|
57
|
8 |
|
return $this; |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* {@inheritdoc} |
62
|
|
|
*/ |
63
|
2 |
|
public function offsetGet($name) |
64
|
|
|
{ |
65
|
2 |
|
if (!isset($this->storage[$name])) { |
66
|
2 |
|
$this->set($name); |
67
|
|
|
} |
68
|
|
|
|
69
|
2 |
|
return $this->storage[$name]; |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* {@inheritdoc} |
74
|
|
|
*/ |
75
|
1 |
|
public function offsetSet($name, $value = null) |
76
|
|
|
{ |
77
|
1 |
|
$this->set($name, $value); |
78
|
1 |
|
} |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* {@inheritdoc} |
82
|
|
|
*/ |
83
|
1 |
|
public function offsetUnset($name) |
84
|
|
|
{ |
85
|
1 |
|
unset($this->storage[$name]); |
86
|
1 |
|
} |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* {@inheritdoc} |
90
|
|
|
*/ |
91
|
2 |
|
public function offsetExists($name) |
92
|
|
|
{ |
93
|
2 |
|
return isset($this->storage[$name]); |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* {@inheritdoc} |
98
|
|
|
*/ |
99
|
14 |
|
public function append($key, $value = null) |
100
|
|
|
{ |
101
|
|
|
$this->storage += array( |
102
|
14 |
|
$key => ($this->attributeFactory)::build($key) |
103
|
|
|
); |
104
|
|
|
|
105
|
14 |
|
$this->storage[$key]->append($value); |
|
|
|
|
106
|
|
|
|
107
|
14 |
|
return $this; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* {@inheritdoc} |
112
|
|
|
*/ |
113
|
2 |
|
public function remove($key, $value = '') |
114
|
|
|
{ |
115
|
2 |
|
if (!isset($this->storage[$key])) { |
116
|
1 |
|
return $this; |
117
|
|
|
} |
118
|
|
|
|
119
|
1 |
|
foreach ($this->normalizeValue($value) as $value_item) { |
120
|
1 |
|
$this->storage[$key]->remove($value_item); |
121
|
|
|
} |
122
|
|
|
|
123
|
1 |
|
return $this; |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* {@inheritdoc} |
128
|
|
|
*/ |
129
|
2 |
|
public function delete($name) |
130
|
|
|
{ |
131
|
2 |
|
foreach ($this->normalizeValue($name) as $attribute_name) { |
132
|
2 |
|
unset($this->storage[$attribute_name]); |
133
|
|
|
} |
134
|
|
|
|
135
|
2 |
|
return $this; |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* {@inheritdoc} |
140
|
|
|
*/ |
141
|
1 |
|
public function without($key) |
142
|
|
|
{ |
143
|
1 |
|
$attributes = clone $this; |
144
|
|
|
|
145
|
1 |
|
return $attributes->delete($key); |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
/** |
149
|
|
|
* {@inheritdoc} |
150
|
|
|
*/ |
151
|
1 |
|
public function replace($key, $value, $replacement) |
152
|
|
|
{ |
153
|
1 |
|
if (!isset($this->storage[$key])) { |
154
|
1 |
|
return $this; |
155
|
|
|
} |
156
|
|
|
|
157
|
1 |
|
if (!$this->contains($key, $value)) { |
158
|
1 |
|
return $this; |
159
|
|
|
} |
160
|
|
|
|
161
|
1 |
|
$this->storage[$key]->remove($value); |
162
|
1 |
|
foreach ($this->normalizeValue($replacement) as $replacement_value) { |
163
|
1 |
|
$this->storage[$key]->append($replacement_value); |
164
|
|
|
} |
165
|
|
|
|
166
|
1 |
|
return $this; |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
/** |
170
|
|
|
* {@inheritdoc} |
171
|
|
|
*/ |
172
|
1 |
|
public function merge(array $data = array()) |
173
|
|
|
{ |
174
|
1 |
|
foreach ($data as $key => $value) { |
175
|
|
|
$this->storage += array( |
176
|
1 |
|
$key => ($this->attributeFactory)::build($key) |
177
|
|
|
); |
178
|
|
|
|
179
|
1 |
|
$this->storage[$key]->merge( |
180
|
1 |
|
$this->normalizeValue($value) |
181
|
|
|
); |
182
|
|
|
} |
183
|
|
|
|
184
|
1 |
|
return $this; |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
/** |
188
|
|
|
* {@inheritdoc} |
189
|
|
|
*/ |
190
|
1 |
|
public function exists($key, $value = null) |
191
|
|
|
{ |
192
|
1 |
|
if (!isset($this->storage[$key])) { |
193
|
1 |
|
return false; |
194
|
|
|
} |
195
|
|
|
|
196
|
1 |
|
if (null !== $value) { |
197
|
1 |
|
return $this->contains($key, $value); |
198
|
|
|
} |
199
|
|
|
|
200
|
1 |
|
return true; |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
/** |
204
|
|
|
* {@inheritdoc} |
205
|
|
|
*/ |
206
|
3 |
|
public function contains($key, $value) |
207
|
|
|
{ |
208
|
3 |
|
if (!isset($this->storage[$key])) { |
209
|
1 |
|
return false; |
210
|
|
|
} |
211
|
|
|
|
212
|
3 |
|
return $this->storage[$key]->contains($value); |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
/** |
216
|
|
|
* {@inheritdoc} |
217
|
|
|
*/ |
218
|
4 |
|
public function __toString() |
219
|
|
|
{ |
220
|
4 |
|
return $this->render(); |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
/** |
224
|
|
|
* {@inheritdoc} |
225
|
|
|
*/ |
226
|
15 |
|
public function render() |
227
|
|
|
{ |
228
|
15 |
|
$attributes = implode(' ', $this->prepareValues()); |
229
|
|
|
|
230
|
15 |
|
return $attributes ? ' ' . $attributes : ''; |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
/** |
234
|
|
|
* {@inheritdoc} |
235
|
|
|
*/ |
236
|
2 |
|
public function toArray() |
237
|
|
|
{ |
238
|
2 |
|
$attributes = $this->storage; |
239
|
|
|
|
240
|
|
|
// If empty, just return an empty array. |
241
|
2 |
|
if (empty($attributes)) { |
242
|
2 |
|
return array(); |
243
|
|
|
} |
244
|
|
|
|
245
|
1 |
|
$result = []; |
246
|
|
|
|
247
|
1 |
|
foreach ($this->prepareValues() as $attribute) { |
248
|
1 |
|
$result[$attribute->getName()] = $attribute->getValueAsArray(); |
249
|
|
|
} |
250
|
|
|
|
251
|
1 |
|
return $result; |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
/** |
255
|
|
|
* {@inheritdoc} |
256
|
|
|
*/ |
257
|
1 |
|
public function getStorage() |
258
|
|
|
{ |
259
|
1 |
|
return $this->storage; |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
/** |
263
|
|
|
* {@inheritdoc} |
264
|
|
|
*/ |
265
|
1 |
|
public function getIterator() |
266
|
|
|
{ |
267
|
1 |
|
return new \ArrayIterator($this->toArray()); |
268
|
|
|
} |
269
|
|
|
|
270
|
|
|
/** |
271
|
|
|
* {@inheritdoc} |
272
|
|
|
*/ |
273
|
1 |
|
public function count() |
274
|
|
|
{ |
275
|
1 |
|
return count($this->storage); |
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
/** |
279
|
|
|
* Returns all storage elements as an array. |
280
|
|
|
* |
281
|
|
|
* @return \drupol\htmltag\AttributeInterface[] |
282
|
|
|
* An associative array of attributes. |
283
|
|
|
*/ |
284
|
16 |
|
private function prepareValues() |
285
|
|
|
{ |
286
|
16 |
|
$attributes = $this->storage; |
287
|
|
|
|
288
|
|
|
// If empty, just return an empty array. |
289
|
16 |
|
if (empty($attributes)) { |
290
|
5 |
|
return array(); |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
// Sort the attributes. |
294
|
14 |
|
ksort($attributes); |
295
|
|
|
|
296
|
14 |
|
$result = []; |
297
|
|
|
|
298
|
14 |
|
foreach ($attributes as $attribute_name => $attribute) { |
299
|
|
|
switch ($attribute_name) { |
300
|
14 |
|
case 'class': |
301
|
12 |
|
$classes = $attribute->getValueAsArray(); |
302
|
12 |
|
asort($classes); |
303
|
12 |
|
$result[$attribute->getName()] = $attribute->set($classes); |
304
|
12 |
|
break; |
305
|
|
|
|
306
|
|
|
default: |
307
|
14 |
|
$result[$attribute->getName()] = $attribute; |
308
|
|
|
} |
309
|
|
|
} |
310
|
|
|
|
311
|
14 |
|
return $result; |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
/** |
315
|
|
|
* Normalize a value. |
316
|
|
|
* |
317
|
|
|
* @param mixed $value |
318
|
|
|
* The value to normalize. |
319
|
|
|
* |
320
|
|
|
* @return array |
321
|
|
|
* The value normalized. |
322
|
|
|
*/ |
323
|
4 |
|
private function normalizeValue($value) |
324
|
|
|
{ |
325
|
4 |
|
return $this->ensureFlatArray($value); |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
/** |
329
|
|
|
* Todo. |
330
|
|
|
* |
331
|
|
|
* @param mixed $value |
332
|
|
|
* Todo. |
333
|
|
|
* |
334
|
|
|
* @return array |
335
|
|
|
* The array, flattened. |
336
|
|
|
*/ |
337
|
4 |
View Code Duplication |
private function ensureFlatArray($value) |
|
|
|
|
338
|
|
|
{ |
339
|
4 |
|
switch (gettype($value)) { |
340
|
4 |
|
case 'string': |
341
|
4 |
|
$return = explode( |
342
|
4 |
|
' ', |
343
|
4 |
|
$value |
344
|
|
|
); |
345
|
4 |
|
break; |
346
|
|
|
|
347
|
1 |
|
case 'array': |
348
|
1 |
|
$flat_array = iterator_to_array( |
349
|
1 |
|
new \RecursiveIteratorIterator( |
350
|
1 |
|
new \RecursiveArrayIterator( |
351
|
1 |
|
$value |
352
|
|
|
) |
353
|
|
|
), |
354
|
1 |
|
false |
355
|
|
|
); |
356
|
|
|
|
357
|
1 |
|
$return = []; |
358
|
1 |
|
foreach ($flat_array as $item) { |
359
|
1 |
|
$return = array_merge( |
360
|
1 |
|
$return, |
361
|
1 |
|
$this->normalizeValue($item) |
362
|
|
|
); |
363
|
|
|
} |
364
|
1 |
|
break; |
365
|
|
|
|
366
|
1 |
|
case 'double': |
367
|
1 |
|
case 'integer': |
368
|
|
|
$return = array($value); |
369
|
|
|
break; |
370
|
1 |
|
case 'object': |
371
|
1 |
|
case 'boolean': |
372
|
1 |
|
case 'resource': |
373
|
1 |
|
case 'NULL': |
374
|
|
|
default: |
375
|
1 |
|
$return = array(); |
376
|
1 |
|
break; |
377
|
|
|
} |
378
|
|
|
|
379
|
4 |
|
return array_filter($return, 'is_string'); |
380
|
|
|
} |
381
|
|
|
} |
382
|
|
|
|
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.