1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* The DOMNode parser plugin is particularly useful as it is both the only way |
5
|
|
|
* to see inside the DOMNode without print_r, and the only way to see mixed |
6
|
|
|
* text and node inside XML (SimpleXMLElement will strip out the text). |
7
|
|
|
*/ |
8
|
|
|
class Kint_Parser_DOMNode extends Kint_Parser_Plugin |
|
|
|
|
9
|
|
|
{ |
10
|
|
|
/** |
11
|
|
|
* List of properties to skip parsing. |
12
|
|
|
* |
13
|
|
|
* The properties of a DOMNode can do a *lot* of damage to debuggers. The |
14
|
|
|
* DOMNode contains not one, not two, not three, not four, not 5, not 6, |
15
|
|
|
* not 7 but 8 different ways to recurse into itself: |
16
|
|
|
* * firstChild |
17
|
|
|
* * lastChild |
18
|
|
|
* * previousSibling |
19
|
|
|
* * nextSibling |
20
|
|
|
* * ownerDocument |
21
|
|
|
* * parentNode |
22
|
|
|
* * childNodes |
23
|
|
|
* * attributes |
24
|
|
|
* |
25
|
|
|
* All of this combined: the tiny SVGs used as the caret in Kint are already |
26
|
|
|
* enough to make parsing and rendering take over a second, and send memory |
27
|
|
|
* usage over 128 megs. So we blacklist every field we don't strictly need |
28
|
|
|
* and hope that that's good enough. |
29
|
|
|
* |
30
|
|
|
* In retrospect - this is probably why print_r does the same |
31
|
|
|
* |
32
|
|
|
* @var array |
33
|
|
|
*/ |
34
|
|
|
public static $blacklist = array( |
35
|
|
|
'parentNode' => 'DOMNode', |
36
|
|
|
'firstChild' => 'DOMNode', |
37
|
|
|
'lastChild' => 'DOMNode', |
38
|
|
|
'previousSibling' => 'DOMNode', |
39
|
|
|
'nextSibling' => 'DOMNode', |
40
|
|
|
'ownerDocument' => 'DOMDocument', |
41
|
|
|
); |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Show all properties and methods. |
45
|
|
|
* |
46
|
|
|
* @var bool |
47
|
|
|
*/ |
48
|
|
|
public static $verbose = false; |
49
|
|
|
|
50
|
|
|
public function getTypes() |
51
|
|
|
{ |
52
|
|
|
return array('object'); |
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
public function getTriggers() |
|
|
|
|
56
|
|
|
{ |
57
|
|
|
return Kint_Parser::TRIGGER_SUCCESS; |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
public function parse(&$var, Kint_Object &$o, $trigger) |
|
|
|
|
61
|
|
|
{ |
62
|
|
|
if (!$var instanceof DOMNode) { |
63
|
|
|
return; |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
// Fill the properties |
67
|
|
|
// They can't be enumerated through reflection or casting, |
68
|
|
|
// so we have to trust the docs and try them one at a time |
69
|
|
|
$known_properties = array( |
|
|
|
|
70
|
|
|
'nodeValue', |
71
|
|
|
'childNodes', |
72
|
|
|
'attributes', |
73
|
|
|
); |
74
|
|
|
|
75
|
|
|
if (self::$verbose) { |
76
|
|
|
$known_properties = array( |
|
|
|
|
77
|
|
|
'nodeName', |
78
|
|
|
'nodeValue', |
79
|
|
|
'nodeType', |
80
|
|
|
'parentNode', |
81
|
|
|
'childNodes', |
82
|
|
|
'firstChild', |
83
|
|
|
'lastChild', |
84
|
|
|
'previousSibling', |
85
|
|
|
'nextSibling', |
86
|
|
|
'attributes', |
87
|
|
|
'ownerDocument', |
88
|
|
|
'namespaceURI', |
89
|
|
|
'prefix', |
90
|
|
|
'localName', |
91
|
|
|
'baseURI', |
92
|
|
|
'textContent', |
93
|
|
|
); |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
$childNodes = array(); |
97
|
|
|
$attributes = array(); |
98
|
|
|
|
99
|
|
|
$rep = $o->value; |
100
|
|
|
|
101
|
|
|
foreach ($known_properties as $prop) { |
|
|
|
|
102
|
|
|
$prop_obj = $this->parseProperty($o, $prop, $var); |
|
|
|
|
103
|
|
|
$rep->contents[] = $prop_obj; |
|
|
|
|
104
|
|
|
|
105
|
|
|
if ($prop === 'childNodes') { |
106
|
|
|
$childNodes = $prop_obj->getRepresentation('iterator'); |
|
|
|
|
107
|
|
|
} elseif ($prop === 'attributes') { |
108
|
|
|
$attributes = $prop_obj->getRepresentation('iterator'); |
|
|
|
|
109
|
|
|
} |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
if (!self::$verbose) { |
113
|
|
|
$o->removeRepresentation('methods'); |
114
|
|
|
$o->removeRepresentation('properties'); |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
// Attributes and comments and text nodes don't |
118
|
|
|
// need children or attributes of their own |
119
|
|
|
if (in_array($o->classname, array('DOMAttr', 'DOMText', 'DOMComment'))) { |
|
|
|
|
120
|
|
|
return; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
// Set the attributes |
124
|
|
|
if ($attributes) { |
125
|
|
|
$a = new Kint_Object_Representation('Attributes'); |
|
|
|
|
126
|
|
|
foreach ($attributes->contents as $attribute) { |
127
|
|
|
$a->contents[] = self::textualNodeToString($attribute); |
128
|
|
|
} |
129
|
|
|
$o->addRepresentation($a, 0); |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
// Set the children |
133
|
|
|
if ($childNodes) { |
134
|
|
|
$c = new Kint_Object_Representation('Children'); |
|
|
|
|
135
|
|
|
|
136
|
|
|
if (count($childNodes->contents) === 1 && ($node = reset($childNodes->contents)) && in_array('depth_limit', $node->hints)) { |
137
|
|
|
$node = $node->transplant(new Kint_Object_Instance()); |
138
|
|
|
$node->name = 'childNodes'; |
139
|
|
|
$node->classname = 'DOMNodeList'; |
140
|
|
|
$c->contents = array($node); |
141
|
|
|
} else { |
142
|
|
|
foreach ($childNodes->contents as $index => $node) { |
143
|
|
|
// Shortcircuit text nodes to plain strings |
144
|
|
|
if ($node->classname === 'DOMText' || $node->classname === 'DOMComment') { |
145
|
|
|
$node = self::textualNodeToString($node); |
146
|
|
|
|
147
|
|
|
// And remove them if they're empty |
148
|
|
|
if (ctype_space($node->value->contents) || $node->value->contents === '') { |
149
|
|
|
continue; |
150
|
|
|
} |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
$c->contents[] = $node; |
154
|
|
|
} |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
$o->addRepresentation($c, 0); |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
if (isset($c) && count($c->contents)) { |
161
|
|
|
$o->size = count($c->contents); |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
if (!$o->size) { |
|
|
|
|
165
|
|
|
$o->size = null; |
166
|
|
|
} |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
protected function parseProperty(Kint_Object $o, $prop, &$var) |
|
|
|
|
170
|
|
|
{ |
171
|
|
|
// Duplicating (And slightly optimizing) the Kint_Parser::parseObject() code here |
172
|
|
|
$base_obj = new Kint_Object(); |
|
|
|
|
173
|
|
|
$base_obj->depth = $o->depth + 1; |
|
|
|
|
174
|
|
|
$base_obj->owner_class = $o->classname; |
|
|
|
|
175
|
|
|
$base_obj->name = $prop; |
|
|
|
|
176
|
|
|
$base_obj->operator = Kint_Object::OPERATOR_OBJECT; |
|
|
|
|
177
|
|
|
$base_obj->access = Kint_Object::ACCESS_PUBLIC; |
|
|
|
|
178
|
|
|
|
179
|
|
|
if ($o->access_path !== null) { |
180
|
|
|
$base_obj->access_path = $o->access_path; |
|
|
|
|
181
|
|
|
|
182
|
|
|
if (preg_match('/^[A-Za-z0-9_]+$/', $base_obj->name)) { |
|
|
|
|
183
|
|
|
$base_obj->access_path .= '->'.$base_obj->name; |
|
|
|
|
184
|
|
|
} else { |
185
|
|
|
$base_obj->access_path .= '->{'.var_export($base_obj->name, true).'}'; |
|
|
|
|
186
|
|
|
} |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
if (!isset($var->$prop)) { |
190
|
|
|
$base_obj->type = 'null'; |
|
|
|
|
191
|
|
|
} elseif (isset(self::$blacklist[$prop])) { |
192
|
|
|
$base_obj = $base_obj->transplant(new Kint_Object_Instance()); |
|
|
|
|
193
|
|
|
$base_obj->hints[] = 'blacklist'; |
|
|
|
|
194
|
|
|
$base_obj->classname = self::$blacklist[$prop]; |
|
|
|
|
195
|
|
|
} elseif ($prop === 'attributes') { |
196
|
|
|
$depth_stash = $this->parser->max_depth; |
|
|
|
|
197
|
|
|
$this->parser->max_depth = 0; |
198
|
|
|
$base_obj = $this->parser->parse($var->$prop, $base_obj); |
|
|
|
|
199
|
|
|
$this->parser->max_depth = $depth_stash; |
|
|
|
|
200
|
|
|
} else { |
201
|
|
|
$base_obj = $this->parser->parse($var->$prop, $base_obj); |
|
|
|
|
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
return $base_obj; |
|
|
|
|
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
protected static function textualNodeToString(Kint_Object_Instance $o) |
|
|
|
|
208
|
|
|
{ |
209
|
|
|
if (empty($o->value) || empty($o->value->contents) || empty($o->classname)) { |
210
|
|
|
return; |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
if (!in_array($o->classname, array('DOMText', 'DOMAttr', 'DOMComment'))) { |
214
|
|
|
return; |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
foreach ($o->value->contents as $property) { |
218
|
|
|
if ($property->name === 'nodeValue') { |
219
|
|
|
$ret = clone $property; |
220
|
|
|
$ret->name = $o->name; |
221
|
|
|
|
222
|
|
|
return $ret; |
223
|
|
|
} |
224
|
|
|
} |
225
|
|
|
} |
226
|
|
|
} |
227
|
|
|
|
This check examines a number of code elements and verifies that they conform to the given naming conventions.
You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.