1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Groundskeeper\Tokens; |
4
|
|
|
|
5
|
|
|
use Groundskeeper\Configuration; |
6
|
|
|
use Psr\Log\LoggerInterface; |
7
|
|
|
|
8
|
|
|
class Element extends AbstractToken implements Cleanable, ContainsChildren, Removable |
9
|
|
|
{ |
10
|
|
|
const ATTR_BOOL = 'ci_boo'; // boolean |
11
|
|
|
const ATTR_CI_ENUM = 'ci_enu'; // case-insensitive enumeration |
12
|
|
|
const ATTR_CI_SSENUM = 'ci_sse'; // case-insensitive space-separated enumeration |
13
|
|
|
const ATTR_INT = 'ci_int'; // integer |
14
|
|
|
const ATTR_JS = 'cs_jsc'; // javascript |
15
|
|
|
const ATTR_CI_STRING = 'ci_str'; // case-insensitive string |
16
|
|
|
const ATTR_CS_STRING = 'cs_str'; // case-sensitive string |
17
|
|
|
const ATTR_URI = 'cs_uri'; // uri |
18
|
|
|
|
19
|
|
|
/** @var array */ |
20
|
|
|
protected $attributes; |
21
|
|
|
|
22
|
|
|
/** @var array[Token] */ |
23
|
|
|
protected $children; |
24
|
|
|
|
25
|
|
|
/** @var string */ |
26
|
|
|
private $name; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* Constructor |
30
|
|
|
*/ |
31
|
95 |
|
public function __construct(Configuration $configuration, $name, array $attributes = array()) |
32
|
|
|
{ |
33
|
95 |
|
parent::__construct($configuration); |
34
|
|
|
|
35
|
95 |
|
$this->attributes = array(); |
36
|
95 |
|
foreach ($attributes as $key => $value) { |
37
|
51 |
|
$this->addAttribute($key, $value); |
38
|
95 |
|
} |
39
|
|
|
|
40
|
95 |
|
$this->children = array(); |
41
|
|
|
|
42
|
95 |
|
if (!is_string($name)) { |
43
|
3 |
|
throw new \InvalidArgumentException('Element name must be string type.'); |
44
|
1 |
|
} |
45
|
|
|
|
46
|
94 |
|
$this->name = trim(strtolower($name)); |
47
|
94 |
|
} |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* Getter for 'attributes'. |
51
|
|
|
*/ |
52
|
2 |
|
public function getAttributes() |
53
|
2 |
|
{ |
54
|
2 |
|
return $this->attributes; |
55
|
|
|
} |
56
|
|
|
|
57
|
3 |
|
public function getAttribute($key) |
58
|
|
|
{ |
59
|
3 |
|
if (!$this->hasAttribute($key)) { |
60
|
1 |
|
throw new \InvalidArgumentException('Invalid attribute key: ' . $key); |
61
|
|
|
} |
62
|
|
|
|
63
|
3 |
|
return $this->attributes[$key]; |
64
|
1 |
|
} |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* Hasser for 'attributes'. |
68
|
|
|
* |
69
|
|
|
* @param string $key |
70
|
|
|
* |
71
|
|
|
* @return bool True if the attribute is present. |
72
|
|
|
*/ |
73
|
23 |
|
public function hasAttribute($key) |
74
|
|
|
{ |
75
|
23 |
|
return array_key_exists($key, $this->attributes); |
76
|
|
|
} |
77
|
|
|
|
78
|
51 |
|
public function addAttribute($key, $value) |
79
|
1 |
|
{ |
80
|
51 |
|
$key = trim(strtolower($key)); |
81
|
51 |
|
if ($key == '') { |
82
|
1 |
|
throw new \InvalidArgumentException('Invalid empty attribute key.'); |
83
|
1 |
|
} |
84
|
|
|
|
85
|
50 |
|
$this->attributes[$key] = $value; |
86
|
|
|
|
87
|
50 |
|
return $this; |
88
|
|
|
} |
89
|
|
|
|
90
|
2 |
|
public function removeAttribute($key) |
91
|
1 |
|
{ |
92
|
2 |
|
$key = trim(strtolower($key)); |
93
|
2 |
|
if (isset($this->attributes[$key])) { |
94
|
2 |
|
unset($this->attributes[$key]); |
95
|
|
|
|
96
|
2 |
|
return true; |
97
|
|
|
} |
98
|
|
|
|
99
|
2 |
|
return false; |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* Required by ContainsChildren interface. |
104
|
|
|
*/ |
105
|
5 |
|
public function getChildren() |
106
|
|
|
{ |
107
|
5 |
|
return $this->children; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Required by ContainsChildren interface. |
112
|
|
|
*/ |
113
|
1 |
|
public function hasChild(Token $token) |
114
|
|
|
{ |
115
|
1 |
|
return array_search($token, $this->children, true) !== false; |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* Required by ContainsChildren interface. |
120
|
|
|
*/ |
121
|
83 |
|
public function appendChild(Token $token) |
122
|
|
|
{ |
123
|
83 |
|
$token->setParent($this); |
124
|
83 |
|
$this->children[] = $token; |
125
|
|
|
|
126
|
83 |
|
return $this; |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* Required by ContainsChildren interface. |
131
|
|
|
*/ |
132
|
5 |
|
public function prependChild(Token $token) |
133
|
|
|
{ |
134
|
5 |
|
$token->setParent($this); |
135
|
5 |
|
array_unshift($this->children, $token); |
136
|
|
|
|
137
|
5 |
|
return $this; |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* Required by the ContainsChildren interface. |
142
|
|
|
*/ |
143
|
26 |
View Code Duplication |
public function removeChild(Token $token) |
|
|
|
|
144
|
|
|
{ |
145
|
26 |
|
$key = array_search($token, $this->children, true); |
146
|
26 |
|
if ($key !== false) { |
147
|
26 |
|
unset($this->children[$key]); |
148
|
|
|
|
149
|
26 |
|
return true; |
150
|
|
|
} |
151
|
|
|
|
152
|
1 |
|
return false; |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* Getter for 'name'. |
157
|
|
|
*/ |
158
|
79 |
|
public function getName() |
159
|
|
|
{ |
160
|
79 |
|
return $this->name; |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
/** |
164
|
|
|
* Required by the Cleanable interface. |
165
|
|
|
*/ |
166
|
79 |
|
public function clean(LoggerInterface $logger) |
167
|
|
|
{ |
168
|
79 |
|
if ($this->configuration->get('clean-strategy') == Configuration::CLEAN_STRATEGY_NONE) { |
169
|
1 |
|
return true; |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
// Remove non-standard attributes. |
173
|
78 |
|
if ($this->configuration->get('clean-strategy') != Configuration::CLEAN_STRATEGY_LENIENT) { |
174
|
78 |
|
foreach ($this->attributes as $name => $value) { |
175
|
41 |
|
$attributeParameters = $this->getAttributeParameters($name); |
176
|
41 |
|
if (empty($attributeParameters)) { |
177
|
7 |
|
$logger->debug('Removing non-standard attribute "' . $name . '" from ' . $this); |
178
|
7 |
|
unset($this->attributes[$name]); |
179
|
7 |
|
} |
180
|
78 |
|
} |
181
|
78 |
|
} |
182
|
|
|
|
183
|
78 |
|
foreach ($this->attributes as $name => $value) { |
184
|
|
|
// Validate attribute value. |
185
|
42 |
|
$attributeParameters = $this->getAttributeParameters($name); |
186
|
42 |
|
if (empty($attributeParameters)) { |
187
|
|
|
$attributeParameters = array( |
188
|
5 |
|
'name' => $name, |
189
|
5 |
|
'regex' => '/\S*/i', |
190
|
|
|
'valueType' => self::ATTR_CS_STRING |
191
|
5 |
|
); |
192
|
5 |
|
} |
193
|
|
|
|
194
|
|
|
list($caseSensitivity, $attributeType) = |
195
|
42 |
|
explode('_', $attributeParameters['valueType']); |
196
|
|
|
|
197
|
|
|
// Handle case-insensitivity. |
198
|
|
|
// Standard is case-insensitive attribute values should be lower case. |
199
|
42 |
|
if ($caseSensitivity == 'ci') { |
200
|
10 |
|
$newValue = strtolower($value); |
201
|
10 |
|
if ($newValue !== $value) { |
202
|
2 |
|
$logger->debug('Within ' . $this . ' the value for the attribute "' . $name . '" is case-insensitive. The value has been converted to lower case.'); |
203
|
2 |
|
$this->attributes[$name] = $newValue; |
204
|
2 |
|
} |
205
|
10 |
|
} |
206
|
|
|
|
207
|
42 |
|
switch (substr($attributeType, 0, 3)) { |
208
|
42 |
|
case 'enu': // enumeration |
209
|
|
|
/// @todo |
210
|
1 |
|
break; |
211
|
|
|
|
212
|
41 |
|
case 'uri': // URI |
213
|
|
|
/// @todo |
214
|
13 |
|
break; |
215
|
42 |
|
} |
216
|
78 |
|
} |
217
|
|
|
|
218
|
|
|
// Fix self (if possible) |
|
|
|
|
219
|
78 |
|
$this->fixSelf($logger); |
220
|
|
|
|
221
|
|
|
// Remove self or children? |
222
|
78 |
|
if ($this->configuration->get('clean-strategy') !== Configuration::CLEAN_STRATEGY_LENIENT) { |
223
|
|
|
// Remove self? |
224
|
78 |
|
if ($this->removeInvalidSelf($logger)) { |
225
|
18 |
|
return false; |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
// Remove children? |
229
|
78 |
|
$this->removeInvalidChildren($logger); |
230
|
78 |
|
} |
231
|
|
|
|
232
|
|
|
// Clean children. |
233
|
78 |
|
return AbstractToken::cleanChildTokens( |
234
|
78 |
|
$this->configuration, |
235
|
78 |
|
$this->children, |
236
|
|
|
$logger |
237
|
78 |
|
); |
238
|
|
|
} |
239
|
|
|
|
240
|
73 |
|
protected function fixSelf(LoggerInterface $logger) |
241
|
|
|
{ |
242
|
73 |
|
} |
243
|
|
|
|
244
|
70 |
|
protected function removeInvalidChildren(LoggerInterface $logger) |
245
|
|
|
{ |
246
|
70 |
|
} |
247
|
|
|
|
248
|
73 |
|
protected function removeInvalidSelf(LoggerInterface $logger) |
249
|
|
|
{ |
250
|
73 |
|
return false; |
251
|
|
|
} |
252
|
|
|
|
253
|
42 |
|
protected function getAllowedAttributes() |
254
|
|
|
{ |
255
|
|
|
return array( |
256
|
|
|
// Global Attributes |
257
|
42 |
|
'/^accesskey$/i' => self::ATTR_CS_STRING, |
258
|
42 |
|
'/^class$/i' => self::ATTR_CS_STRING, |
259
|
42 |
|
'/^contenteditable$/i' => self::ATTR_CS_STRING, |
260
|
42 |
|
'/^contextmenu$/i' => self::ATTR_CS_STRING, |
261
|
42 |
|
'/^data-\S/i' => self::ATTR_CS_STRING, |
262
|
42 |
|
'/^dir$/i' => self::ATTR_CI_ENUM . '("ltr","rtl"|"ltr")', |
263
|
42 |
|
'/^draggable$/i' => self::ATTR_CS_STRING, |
264
|
42 |
|
'/^dropzone$/i' => self::ATTR_CS_STRING, |
265
|
42 |
|
'/^hidden$/i' => self::ATTR_CS_STRING, |
266
|
42 |
|
'/^id$/i' => self::ATTR_CS_STRING, |
267
|
42 |
|
'/^is$/i' => self::ATTR_CS_STRING, |
268
|
42 |
|
'/^itemid$/i' => self::ATTR_CS_STRING, |
269
|
42 |
|
'/^itemprop$/i' => self::ATTR_CS_STRING, |
270
|
42 |
|
'/^itemref$/i' => self::ATTR_CS_STRING, |
271
|
42 |
|
'/^itemscope$/i' => self::ATTR_CS_STRING, |
272
|
42 |
|
'/^itemtype$/i' => self::ATTR_CS_STRING, |
273
|
42 |
|
'/^lang$/i' => self::ATTR_CI_STRING, |
274
|
42 |
|
'/^slot$/i' => self::ATTR_CS_STRING, |
275
|
42 |
|
'/^spellcheck$/i' => self::ATTR_CS_STRING, |
276
|
42 |
|
'/^style$/i' => self::ATTR_CS_STRING, |
277
|
42 |
|
'/^tabindex$/i' => self::ATTR_CS_STRING, |
278
|
42 |
|
'/^title$/i' => self::ATTR_CS_STRING, |
279
|
42 |
|
'/^translate$/i' => self::ATTR_CI_ENUM . '("yes","no",""|"yes")', |
280
|
|
|
|
281
|
|
|
// Event Handler Content Attributes |
282
|
|
|
// https://html.spec.whatwg.org/multipage/webappapis.html#event-handler-content-attributes |
283
|
42 |
|
'/^onabort$/i' => self::ATTR_JS, |
284
|
42 |
|
'/^onautocomplete$/i' => self::ATTR_JS, |
285
|
42 |
|
'/^onautocompleteerror$/i' => self::ATTR_JS, |
286
|
42 |
|
'/^onblur$/i' => self::ATTR_JS, |
287
|
42 |
|
'/^oncancel$/i' => self::ATTR_JS, |
288
|
42 |
|
'/^oncanplay$/i' => self::ATTR_JS, |
289
|
42 |
|
'/^oncanplaythrough$/i' => self::ATTR_JS, |
290
|
42 |
|
'/^onchange$/i' => self::ATTR_JS, |
291
|
42 |
|
'/^onclick$/i' => self::ATTR_JS, |
292
|
42 |
|
'/^onclose$/i' => self::ATTR_JS, |
293
|
42 |
|
'/^oncontextmenu$/i' => self::ATTR_JS, |
294
|
42 |
|
'/^oncuechange$/i' => self::ATTR_JS, |
295
|
42 |
|
'/^ondblclick$/i' => self::ATTR_JS, |
296
|
42 |
|
'/^ondrag$/i' => self::ATTR_JS, |
297
|
42 |
|
'/^ondragend$/i' => self::ATTR_JS, |
298
|
42 |
|
'/^ondragenter$/i' => self::ATTR_JS, |
299
|
42 |
|
'/^ondragexit$/i' => self::ATTR_JS, |
300
|
42 |
|
'/^ondragleave$/i' => self::ATTR_JS, |
301
|
42 |
|
'/^ondragover$/i' => self::ATTR_JS, |
302
|
42 |
|
'/^ondragstart$/i' => self::ATTR_JS, |
303
|
42 |
|
'/^ondrop$/i' => self::ATTR_JS, |
304
|
42 |
|
'/^ondurationchange$/i' => self::ATTR_JS, |
305
|
42 |
|
'/^onemptied$/i' => self::ATTR_JS, |
306
|
42 |
|
'/^onended$/i' => self::ATTR_JS, |
307
|
42 |
|
'/^onerror$/i' => self::ATTR_JS, |
308
|
42 |
|
'/^onfocus$/i' => self::ATTR_JS, |
309
|
42 |
|
'/^oninput$/i' => self::ATTR_JS, |
310
|
42 |
|
'/^oninvalid$/i' => self::ATTR_JS, |
311
|
42 |
|
'/^onkeydown$/i' => self::ATTR_JS, |
312
|
42 |
|
'/^onkeypress$/i' => self::ATTR_JS, |
313
|
42 |
|
'/^onkeyup$/i' => self::ATTR_JS, |
314
|
42 |
|
'/^onload$/i' => self::ATTR_JS, |
315
|
42 |
|
'/^onloadeddata$/i' => self::ATTR_JS, |
316
|
42 |
|
'/^onloadedmetadata$/i' => self::ATTR_JS, |
317
|
42 |
|
'/^onloadstart$/i' => self::ATTR_JS, |
318
|
42 |
|
'/^onmousedown$/i' => self::ATTR_JS, |
319
|
42 |
|
'/^onmouseenter$/i' => self::ATTR_JS, |
320
|
42 |
|
'/^onmouseleave$/i' => self::ATTR_JS, |
321
|
42 |
|
'/^onmousemove$/i' => self::ATTR_JS, |
322
|
42 |
|
'/^onmouseout$/i' => self::ATTR_JS, |
323
|
42 |
|
'/^onmouseover$/i' => self::ATTR_JS, |
324
|
42 |
|
'/^onmouseup$/i' => self::ATTR_JS, |
325
|
42 |
|
'/^onwheel$/i' => self::ATTR_JS, |
326
|
42 |
|
'/^onpause$/i' => self::ATTR_JS, |
327
|
42 |
|
'/^onplay$/i' => self::ATTR_JS, |
328
|
42 |
|
'/^onplaying$/i' => self::ATTR_JS, |
329
|
42 |
|
'/^onprogress$/i' => self::ATTR_JS, |
330
|
42 |
|
'/^onratechange$/i' => self::ATTR_JS, |
331
|
42 |
|
'/^onreset$/i' => self::ATTR_JS, |
332
|
42 |
|
'/^onresize$/i' => self::ATTR_JS, |
333
|
42 |
|
'/^onscroll$/i' => self::ATTR_JS, |
334
|
42 |
|
'/^onseeked$/i' => self::ATTR_JS, |
335
|
42 |
|
'/^onseeking$/i' => self::ATTR_JS, |
336
|
42 |
|
'/^onselect$/i' => self::ATTR_JS, |
337
|
42 |
|
'/^onshow$/i' => self::ATTR_JS, |
338
|
42 |
|
'/^onstalled$/i' => self::ATTR_JS, |
339
|
42 |
|
'/^onsubmit$/i' => self::ATTR_JS, |
340
|
42 |
|
'/^onsuspend$/i' => self::ATTR_JS, |
341
|
42 |
|
'/^ontimeupdate$/i' => self::ATTR_JS, |
342
|
42 |
|
'/^ontoggle$/i' => self::ATTR_JS, |
343
|
42 |
|
'/^onvolumechange$/i' => self::ATTR_JS, |
344
|
42 |
|
'/^onwaiting$/i' => self::ATTR_JS, |
345
|
|
|
|
346
|
|
|
// WAI-ARIA |
347
|
|
|
// https://w3c.github.io/aria/aria/aria.html |
348
|
42 |
|
'/^role$/i' => self::ATTR_CI_STRING, |
349
|
|
|
|
350
|
|
|
// ARIA global states and properties |
351
|
42 |
|
'/^aria-atomic$/i' => self::ATTR_CS_STRING, |
352
|
42 |
|
'/^aria-busy$/i' => self::ATTR_CS_STRING, |
353
|
42 |
|
'/^aria-controls$/i' => self::ATTR_CS_STRING, |
354
|
42 |
|
'/^aria-current$/i' => self::ATTR_CS_STRING, |
355
|
42 |
|
'/^aria-describedby$/i' => self::ATTR_CS_STRING, |
356
|
42 |
|
'/^aria-details$/i' => self::ATTR_CS_STRING, |
357
|
42 |
|
'/^aria-disabled$/i' => self::ATTR_CS_STRING, |
358
|
42 |
|
'/^aria-dropeffect$/i' => self::ATTR_CS_STRING, |
359
|
42 |
|
'/^aria-errormessage$/i' => self::ATTR_CS_STRING, |
360
|
42 |
|
'/^aria-flowto$/i' => self::ATTR_CS_STRING, |
361
|
42 |
|
'/^aria-grabbed$/i' => self::ATTR_CS_STRING, |
362
|
42 |
|
'/^aria-haspopup$/i' => self::ATTR_CS_STRING, |
363
|
42 |
|
'/^aria-hidden$/i' => self::ATTR_CS_STRING, |
364
|
42 |
|
'/^aria-invalid$/i' => self::ATTR_CS_STRING, |
365
|
42 |
|
'/^aria-label$/i' => self::ATTR_CS_STRING, |
366
|
42 |
|
'/^aria-labelledby$/i' => self::ATTR_CS_STRING, |
367
|
42 |
|
'/^aria-live$/i' => self::ATTR_CS_STRING, |
368
|
42 |
|
'/^aria-owns$/i' => self::ATTR_CS_STRING, |
369
|
42 |
|
'/^aria-relevant$/i' => self::ATTR_CS_STRING, |
370
|
42 |
|
'/^aria-roledescription$/i' => self::ATTR_CS_STRING, |
371
|
|
|
|
372
|
|
|
// ARIA widget attributes |
373
|
42 |
|
'/^aria-autocomplete$/i' => self::ATTR_CS_STRING, |
374
|
42 |
|
'/^aria-checked$/i' => self::ATTR_CS_STRING, |
375
|
42 |
|
'/^aria-expanded$/i' => self::ATTR_CS_STRING, |
376
|
42 |
|
'/^aria-level$/i' => self::ATTR_CS_STRING, |
377
|
42 |
|
'/^aria-modal$/i' => self::ATTR_CS_STRING, |
378
|
42 |
|
'/^aria-multiline$/i' => self::ATTR_CS_STRING, |
379
|
42 |
|
'/^aria-multiselectable$/i' => self::ATTR_CS_STRING, |
380
|
42 |
|
'/^aria-orientation$/i' => self::ATTR_CS_STRING, |
381
|
42 |
|
'/^aria-placeholder$/i' => self::ATTR_CS_STRING, |
382
|
42 |
|
'/^aria-pressed$/i' => self::ATTR_CS_STRING, |
383
|
42 |
|
'/^aria-readonly$/i' => self::ATTR_CS_STRING, |
384
|
42 |
|
'/^aria-required$/i' => self::ATTR_CS_STRING, |
385
|
42 |
|
'/^aria-selected$/i' => self::ATTR_CS_STRING, |
386
|
42 |
|
'/^aria-sort$/i' => self::ATTR_CS_STRING, |
387
|
42 |
|
'/^aria-valuemax$/i' => self::ATTR_CS_STRING, |
388
|
42 |
|
'/^aria-valuemin$/i' => self::ATTR_CS_STRING, |
389
|
42 |
|
'/^aria-valuenow$/i' => self::ATTR_CS_STRING, |
390
|
42 |
|
'/^aria-valuetext$/i' => self::ATTR_CS_STRING, |
391
|
|
|
|
392
|
|
|
// ARIA relationship attributes |
393
|
42 |
|
'/^aria-activedescendant$/i' => self::ATTR_CS_STRING, |
394
|
42 |
|
'/^aria-colcount$/i' => self::ATTR_CS_STRING, |
395
|
42 |
|
'/^aria-colindex$/i' => self::ATTR_CS_STRING, |
396
|
42 |
|
'/^aria-colspan$/i' => self::ATTR_CS_STRING, |
397
|
42 |
|
'/^aria-posinset$/i' => self::ATTR_CS_STRING, |
398
|
42 |
|
'/^aria-rowcount$/i' => self::ATTR_CS_STRING, |
399
|
42 |
|
'/^aria-rowindex$/i' => self::ATTR_CS_STRING, |
400
|
42 |
|
'/^aria-rowspan$/i' => self::ATTR_CS_STRING, |
401
|
|
|
'/^aria-setsize$/i' => self::ATTR_CS_STRING |
402
|
42 |
|
); |
403
|
|
|
} |
404
|
|
|
|
405
|
42 |
|
protected function getAttributeParameters($name) |
406
|
|
|
{ |
407
|
42 |
|
$allowedAttributes = $this->getAllowedAttributes(); |
408
|
42 |
|
foreach ($allowedAttributes as $attrRegex => $valueType) { |
409
|
42 |
|
if (preg_match($attrRegex, $name) === 1) { |
410
|
|
|
return array( |
411
|
42 |
|
'name' => $name, |
412
|
42 |
|
'regex' => $attrRegex, |
413
|
|
|
'valueType' => $valueType |
414
|
42 |
|
); |
415
|
|
|
} |
416
|
33 |
|
} |
417
|
|
|
|
418
|
7 |
|
return array(); |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
/** |
422
|
|
|
* Required by the Removable interface. |
423
|
|
|
*/ |
424
|
76 |
View Code Duplication |
public function remove(LoggerInterface $logger) |
|
|
|
|
425
|
|
|
{ |
426
|
76 |
|
$hasRemovableElements = $this->configuration->get('element-blacklist') != ''; |
427
|
76 |
|
$hasRemovableTypes = $this->configuration->get('type-blacklist') != ''; |
428
|
76 |
|
foreach ($this->children as $child) { |
429
|
|
|
// Check types. |
430
|
74 |
|
if ($hasRemovableTypes && |
431
|
74 |
|
!$this->configuration->isAllowedType($child->getType())) { |
432
|
2 |
|
$logger->debug('Removing ' . $child); |
433
|
2 |
|
$this->removeChild($child); |
434
|
|
|
|
435
|
2 |
|
continue; |
436
|
|
|
} |
437
|
|
|
|
438
|
|
|
// Check elements. |
439
|
72 |
|
if ($hasRemovableElements && |
440
|
72 |
|
$child instanceof self && |
441
|
72 |
|
!$this->configuration->isAllowedElement($child->getName())) { |
442
|
3 |
|
$logger->debug('Removing ' . $child); |
443
|
3 |
|
$this->removeChild($child); |
444
|
|
|
|
445
|
3 |
|
continue; |
446
|
|
|
} |
447
|
|
|
|
448
|
|
|
// Check children. |
449
|
72 |
|
if ($child instanceof Removable) { |
450
|
63 |
|
$child->remove($logger); |
451
|
63 |
|
} |
452
|
76 |
|
} |
453
|
76 |
|
} |
454
|
|
|
|
455
|
|
|
/** |
456
|
|
|
* Required by the Token interface. |
457
|
|
|
*/ |
458
|
7 |
|
public function toHtml($prefix, $suffix) |
459
|
|
|
{ |
460
|
7 |
|
$output = $this->buildStartTag($prefix, $suffix); |
461
|
7 |
|
if (empty($this->children)) { |
462
|
6 |
|
return $output; |
463
|
|
|
} |
464
|
|
|
|
465
|
5 |
|
$output .= $this->buildChildrenHtml($prefix, $suffix); |
466
|
|
|
|
467
|
5 |
|
return $output . $prefix . '</' . $this->name . '>' . $suffix; |
468
|
|
|
} |
469
|
|
|
|
470
|
82 |
|
protected function buildStartTag($prefix, $suffix, $forceOpen = false) |
471
|
|
|
{ |
472
|
82 |
|
$output = $prefix . '<' . $this->name; |
473
|
82 |
|
foreach ($this->attributes as $key => $value) { |
474
|
44 |
|
$output .= ' ' . strtolower($key); |
475
|
44 |
|
if (is_string($value)) { |
476
|
|
|
/// @todo Escape double quotes in value. |
477
|
44 |
|
$output .= '="' . $value . '"'; |
478
|
44 |
|
} |
479
|
82 |
|
} |
480
|
|
|
|
481
|
82 |
|
if (!$forceOpen && empty($this->children)) { |
482
|
27 |
|
return $output . '/>' . $suffix; |
483
|
|
|
} |
484
|
|
|
|
485
|
80 |
|
return $output . '>' . $suffix; |
486
|
|
|
} |
487
|
|
|
|
488
|
80 |
|
protected function buildChildrenHtml($prefix, $suffix) |
489
|
|
|
{ |
490
|
80 |
|
$output = ''; |
491
|
80 |
|
foreach ($this->children as $child) { |
492
|
|
|
$newPrefix = $prefix . |
493
|
78 |
|
str_repeat( |
494
|
78 |
|
' ', |
495
|
78 |
|
$this->configuration->get('indent-spaces') |
496
|
78 |
|
); |
497
|
78 |
|
$output .= $child->toHtml($newPrefix, $suffix); |
498
|
80 |
|
} |
499
|
|
|
|
500
|
80 |
|
return $output; |
501
|
|
|
} |
502
|
|
|
|
503
|
16 |
|
public function getType() |
504
|
|
|
{ |
505
|
16 |
|
return Token::ELEMENT; |
506
|
|
|
} |
507
|
|
|
|
508
|
50 |
|
public function __toString() |
509
|
|
|
{ |
510
|
50 |
|
return '"' . $this->name . '" element'; |
511
|
|
|
} |
512
|
|
|
} |
513
|
|
|
|
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.