Completed
Pull Request — master (#56)
by
unknown
04:05
created
src/PHPHtmlParser/Dom/Tag.php 2 patches
Indentation   +266 added lines, -266 removed lines patch added patch discarded remove patch
@@ -12,270 +12,270 @@
 block discarded – undo
12 12
 class Tag
13 13
 {
14 14
 
15
-    /**
16
-     * The name of the tag.
17
-     *
18
-     * @var string
19
-     */
20
-    protected $name;
21
-
22
-    /**
23
-     * The attributes of the tag.
24
-     *
25
-     * @var array
26
-     */
27
-    protected $attr = [];
28
-
29
-    /**
30
-     * Is this tag self closing.
31
-     *
32
-     * @var bool
33
-     */
34
-    protected $selfClosing = false;
35
-
36
-    /**
37
-     * Tag noise
38
-     */
39
-    protected $noise = '';
40
-
41
-    /**
42
-     * The encoding class to... encode the tags
43
-     *
44
-     * @var mixed
45
-     */
46
-    protected $encode = null;
47
-
48
-    /**
49
-     * Sets up the tag with a name.
50
-     *
51
-     * @param $name
52
-     */
53
-    public function __construct($name)
54
-    {
55
-        $this->name = $name;
56
-    }
57
-
58
-    /**
59
-     * Magic method to get any of the attributes.
60
-     *
61
-     * @param string $key
62
-     * @return mixed
63
-     */
64
-    public function __get($key)
65
-    {
66
-        return $this->getAttribute($key);
67
-    }
68
-
69
-    /**
70
-     * Magic method to set any attribute.
71
-     *
72
-     * @param string $key
73
-     * @param mixed $value
74
-     */
75
-    public function __set($key, $value)
76
-    {
77
-        $this->setAttribute($key, $value);
78
-    }
79
-
80
-    /**
81
-     * Returns the name of this tag.
82
-     *
83
-     * @return string
84
-     */
85
-    public function name()
86
-    {
87
-        return $this->name;
88
-    }
89
-
90
-    /**
91
-     * Sets the tag to be self closing.
92
-     *
93
-     * @return $this
94
-     */
95
-    public function selfClosing()
96
-    {
97
-        $this->selfClosing = true;
98
-
99
-        return $this;
100
-    }
101
-
102
-    /**
103
-     * Checks if the tag is self closing.
104
-     *
105
-     * @return bool
106
-     */
107
-    public function isSelfClosing()
108
-    {
109
-        return $this->selfClosing;
110
-    }
111
-
112
-    /**
113
-     * Sets the encoding type to be used.
114
-     *
115
-     * @param Encode $encode
116
-     */
117
-    public function setEncoding(Encode $encode)
118
-    {
119
-        $this->encode = $encode;
120
-    }
121
-
122
-    /**
123
-     * Sets the noise for this tag (if any)
124
-     *
125
-     * @param $noise
126
-     * @return $this
127
-     */
128
-    public function noise($noise)
129
-    {
130
-        $this->noise = $noise;
131
-
132
-        return $this;
133
-    }
134
-
135
-    /**
136
-     * Set an attribute for this tag.
137
-     *
138
-     * @param string $key
139
-     * @param mixed $value
140
-     * @return $this
141
-     */
142
-    public function setAttribute($key, $value)
143
-    {
144
-        $key = strtolower($key);
145
-        if ( ! is_array($value)) {
146
-            $value = [
147
-                'value'       => $value,
148
-                'doubleQuote' => true,
149
-            ];
150
-        }
151
-        $this->attr[$key] = $value;
152
-
153
-        return $this;
154
-    }
155
-
156
-    /**
157
-     * Sets the attributes for this tag
158
-     *
159
-     * @param array $attr
160
-     * @return $this
161
-     */
162
-    public function setAttributes(array $attr)
163
-    {
164
-        foreach ($attr as $key => $value) {
165
-            $this->setAttribute($key, $value);
166
-        }
167
-
168
-        return $this;
169
-    }
170
-
171
-    /**
172
-     * Returns all attributes of this tag.
173
-     *
174
-     * @return array
175
-     */
176
-    public function getAttributes()
177
-    {
178
-        $return = [];
179
-        foreach ($this->attr as $attr => $info) {
180
-            $return[$attr] = $this->getAttribute($attr);
181
-        }
182
-
183
-        return $return;
184
-    }
185
-
186
-    /**
187
-     * Returns an attribute by the key
188
-     *
189
-     * @param string $key
190
-     * @return mixed
191
-     */
192
-    public function getAttribute($key)
193
-    {
194
-        if ( ! isset($this->attr[$key])) {
195
-            return null;
196
-        }
197
-        $value = $this->attr[$key]['value'];
198
-        if (is_string($value) AND ! is_null($this->encode)) {
199
-            // convert charset
200
-            $this->attr[$key]['value'] = $this->encode->convert($value);
201
-        }
202
-
203
-        return $this->attr[$key];
204
-    }
205
-
206
-    /**
207
-     * Removes an attribute for this tag
208
-     *
209
-     * @param string $key
210
-     * @return mixed
211
-     */
212
-    public function removeAttribute($key)
213
-    {
214
-        $key = strtolower($key);
215
-        unset($this->attr[$key]);
216
-
217
-        return $this;
218
-    }
219
-
220
-    /**
221
-     * Removes all attributes for this tag except the ones in the $keys array
222
-     *
223
-     * @param array $keys
224
-     * @return mixed
225
-     */
226
-    public function removeAttributes($keys)
227
-    {
228
-        $attributes = $this->getAttributes();
229
-        foreach($attributes as $key => $attribute) {
230
-            $key = strtolower($key);
231
-            if(!in_array($key, $keys)) {
232
-                unset($this->attr[$key]);
233
-            }
234
-        }
235
-
236
-        return $this;
237
-    }
238
-
239
-    /**
240
-     * Generates the opening tag for this object.
241
-     *
242
-     * @return string
243
-     */
244
-    public function makeOpeningTag()
245
-    {
246
-        $return = '<'.$this->name;
247
-
248
-        // add the attributes
249
-        foreach ($this->attr as $key => $info) {
250
-            $info = $this->getAttribute($key);
251
-            $val  = $info['value'];
252
-            if (is_null($val)) {
253
-                $return .= ' '.$key;
254
-            } elseif ($info['doubleQuote']) {
255
-                $return .= ' '.$key.'="'.$val.'"';
256
-            } else {
257
-                $return .= ' '.$key.'=\''.$val.'\'';
258
-            }
259
-        }
260
-
261
-        if ($this->selfClosing) {
262
-            return $return.' />';
263
-        } else {
264
-            return $return.'>';
265
-        }
266
-    }
267
-
268
-    /**
269
-     * Generates the closing tag for this object.
270
-     *
271
-     * @return string
272
-     */
273
-    public function makeClosingTag()
274
-    {
275
-        if ($this->selfClosing) {
276
-            return '';
277
-        }
278
-
279
-        return '</'.$this->name.'>';
280
-    }
15
+	/**
16
+	 * The name of the tag.
17
+	 *
18
+	 * @var string
19
+	 */
20
+	protected $name;
21
+
22
+	/**
23
+	 * The attributes of the tag.
24
+	 *
25
+	 * @var array
26
+	 */
27
+	protected $attr = [];
28
+
29
+	/**
30
+	 * Is this tag self closing.
31
+	 *
32
+	 * @var bool
33
+	 */
34
+	protected $selfClosing = false;
35
+
36
+	/**
37
+	 * Tag noise
38
+	 */
39
+	protected $noise = '';
40
+
41
+	/**
42
+	 * The encoding class to... encode the tags
43
+	 *
44
+	 * @var mixed
45
+	 */
46
+	protected $encode = null;
47
+
48
+	/**
49
+	 * Sets up the tag with a name.
50
+	 *
51
+	 * @param $name
52
+	 */
53
+	public function __construct($name)
54
+	{
55
+		$this->name = $name;
56
+	}
57
+
58
+	/**
59
+	 * Magic method to get any of the attributes.
60
+	 *
61
+	 * @param string $key
62
+	 * @return mixed
63
+	 */
64
+	public function __get($key)
65
+	{
66
+		return $this->getAttribute($key);
67
+	}
68
+
69
+	/**
70
+	 * Magic method to set any attribute.
71
+	 *
72
+	 * @param string $key
73
+	 * @param mixed $value
74
+	 */
75
+	public function __set($key, $value)
76
+	{
77
+		$this->setAttribute($key, $value);
78
+	}
79
+
80
+	/**
81
+	 * Returns the name of this tag.
82
+	 *
83
+	 * @return string
84
+	 */
85
+	public function name()
86
+	{
87
+		return $this->name;
88
+	}
89
+
90
+	/**
91
+	 * Sets the tag to be self closing.
92
+	 *
93
+	 * @return $this
94
+	 */
95
+	public function selfClosing()
96
+	{
97
+		$this->selfClosing = true;
98
+
99
+		return $this;
100
+	}
101
+
102
+	/**
103
+	 * Checks if the tag is self closing.
104
+	 *
105
+	 * @return bool
106
+	 */
107
+	public function isSelfClosing()
108
+	{
109
+		return $this->selfClosing;
110
+	}
111
+
112
+	/**
113
+	 * Sets the encoding type to be used.
114
+	 *
115
+	 * @param Encode $encode
116
+	 */
117
+	public function setEncoding(Encode $encode)
118
+	{
119
+		$this->encode = $encode;
120
+	}
121
+
122
+	/**
123
+	 * Sets the noise for this tag (if any)
124
+	 *
125
+	 * @param $noise
126
+	 * @return $this
127
+	 */
128
+	public function noise($noise)
129
+	{
130
+		$this->noise = $noise;
131
+
132
+		return $this;
133
+	}
134
+
135
+	/**
136
+	 * Set an attribute for this tag.
137
+	 *
138
+	 * @param string $key
139
+	 * @param mixed $value
140
+	 * @return $this
141
+	 */
142
+	public function setAttribute($key, $value)
143
+	{
144
+		$key = strtolower($key);
145
+		if ( ! is_array($value)) {
146
+			$value = [
147
+				'value'       => $value,
148
+				'doubleQuote' => true,
149
+			];
150
+		}
151
+		$this->attr[$key] = $value;
152
+
153
+		return $this;
154
+	}
155
+
156
+	/**
157
+	 * Sets the attributes for this tag
158
+	 *
159
+	 * @param array $attr
160
+	 * @return $this
161
+	 */
162
+	public function setAttributes(array $attr)
163
+	{
164
+		foreach ($attr as $key => $value) {
165
+			$this->setAttribute($key, $value);
166
+		}
167
+
168
+		return $this;
169
+	}
170
+
171
+	/**
172
+	 * Returns all attributes of this tag.
173
+	 *
174
+	 * @return array
175
+	 */
176
+	public function getAttributes()
177
+	{
178
+		$return = [];
179
+		foreach ($this->attr as $attr => $info) {
180
+			$return[$attr] = $this->getAttribute($attr);
181
+		}
182
+
183
+		return $return;
184
+	}
185
+
186
+	/**
187
+	 * Returns an attribute by the key
188
+	 *
189
+	 * @param string $key
190
+	 * @return mixed
191
+	 */
192
+	public function getAttribute($key)
193
+	{
194
+		if ( ! isset($this->attr[$key])) {
195
+			return null;
196
+		}
197
+		$value = $this->attr[$key]['value'];
198
+		if (is_string($value) AND ! is_null($this->encode)) {
199
+			// convert charset
200
+			$this->attr[$key]['value'] = $this->encode->convert($value);
201
+		}
202
+
203
+		return $this->attr[$key];
204
+	}
205
+
206
+	/**
207
+	 * Removes an attribute for this tag
208
+	 *
209
+	 * @param string $key
210
+	 * @return mixed
211
+	 */
212
+	public function removeAttribute($key)
213
+	{
214
+		$key = strtolower($key);
215
+		unset($this->attr[$key]);
216
+
217
+		return $this;
218
+	}
219
+
220
+	/**
221
+	 * Removes all attributes for this tag except the ones in the $keys array
222
+	 *
223
+	 * @param array $keys
224
+	 * @return mixed
225
+	 */
226
+	public function removeAttributes($keys)
227
+	{
228
+		$attributes = $this->getAttributes();
229
+		foreach($attributes as $key => $attribute) {
230
+			$key = strtolower($key);
231
+			if(!in_array($key, $keys)) {
232
+				unset($this->attr[$key]);
233
+			}
234
+		}
235
+
236
+		return $this;
237
+	}
238
+
239
+	/**
240
+	 * Generates the opening tag for this object.
241
+	 *
242
+	 * @return string
243
+	 */
244
+	public function makeOpeningTag()
245
+	{
246
+		$return = '<'.$this->name;
247
+
248
+		// add the attributes
249
+		foreach ($this->attr as $key => $info) {
250
+			$info = $this->getAttribute($key);
251
+			$val  = $info['value'];
252
+			if (is_null($val)) {
253
+				$return .= ' '.$key;
254
+			} elseif ($info['doubleQuote']) {
255
+				$return .= ' '.$key.'="'.$val.'"';
256
+			} else {
257
+				$return .= ' '.$key.'=\''.$val.'\'';
258
+			}
259
+		}
260
+
261
+		if ($this->selfClosing) {
262
+			return $return.' />';
263
+		} else {
264
+			return $return.'>';
265
+		}
266
+	}
267
+
268
+	/**
269
+	 * Generates the closing tag for this object.
270
+	 *
271
+	 * @return string
272
+	 */
273
+	public function makeClosingTag()
274
+	{
275
+		if ($this->selfClosing) {
276
+			return '';
277
+		}
278
+
279
+		return '</'.$this->name.'>';
280
+	}
281 281
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -226,9 +226,9 @@
 block discarded – undo
226 226
     public function removeAttributes($keys)
227 227
     {
228 228
         $attributes = $this->getAttributes();
229
-        foreach($attributes as $key => $attribute) {
229
+        foreach ($attributes as $key => $attribute) {
230 230
             $key = strtolower($key);
231
-            if(!in_array($key, $keys)) {
231
+            if ( ! in_array($key, $keys)) {
232 232
                 unset($this->attr[$key]);
233 233
             }
234 234
         }
Please login to merge, or discard this patch.
src/PHPHtmlParser/Dom/AbstractNode.php 1 patch
Indentation   +713 added lines, -713 removed lines patch added patch discarded remove patch
@@ -17,717 +17,717 @@
 block discarded – undo
17 17
 abstract class AbstractNode
18 18
 {
19 19
 
20
-    /**
21
-     * Contains the tag name/type
22
-     *
23
-     * @var \PHPHtmlParser\Dom\Tag
24
-     */
25
-    protected $tag;
26
-
27
-    /**
28
-     * Contains a list of attributes on this tag.
29
-     *
30
-     * @var array
31
-     */
32
-    protected $attr = [];
33
-
34
-    /**
35
-     * An array of all the children.
36
-     *
37
-     * @var array
38
-     */
39
-    protected $children = [];
40
-
41
-    /**
42
-     * Contains the parent Node.
43
-     *
44
-     * @var AbstractNode
45
-     */
46
-    protected $parent = null;
47
-
48
-    /**
49
-     * The unique id of the class. Given by PHP.
50
-     *
51
-     * @var string
52
-     */
53
-    protected $id;
54
-
55
-    /**
56
-     * The encoding class used to encode strings.
57
-     *
58
-     * @var mixed
59
-     */
60
-    protected $encode;
61
-
62
-    /**
63
-     * Creates a unique spl hash for this node.
64
-     */
65
-    public function __construct()
66
-    {
67
-        $this->id = spl_object_hash($this);
68
-    }
69
-
70
-    /**
71
-     * Magic get method for attributes and certain methods.
72
-     *
73
-     * @param string $key
74
-     * @return mixed
75
-     */
76
-    public function __get($key)
77
-    {
78
-        // check attribute first
79
-        if ( ! is_null($this->getAttribute($key))) {
80
-            return $this->getAttribute($key);
81
-        }
82
-        switch (strtolower($key)) {
83
-            case 'outerhtml':
84
-                return $this->outerHtml();
85
-            case 'innerhtml':
86
-                return $this->innerHtml();
87
-            case 'text':
88
-                return $this->text();
89
-        }
90
-
91
-        return null;
92
-    }
93
-
94
-    /**
95
-     * Attempts to clear out any object references.
96
-     */
97
-    public function __destruct()
98
-    {
99
-        $this->tag      = null;
100
-        $this->attr     = [];
101
-        $this->parent   = null;
102
-        $this->children = [];
103
-    }
104
-
105
-    /**
106
-     * Simply calls the outer text method.
107
-     *
108
-     * @return string
109
-     */
110
-    public function __toString()
111
-    {
112
-        return $this->outerHtml();
113
-    }
114
-
115
-    /**
116
-     * Returns the id of this object.
117
-     */
118
-    public function id()
119
-    {
120
-        return $this->id;
121
-    }
122
-
123
-    /**
124
-     * Returns the parent of node.
125
-     *
126
-     * @return AbstractNode
127
-     */
128
-    public function getParent()
129
-    {
130
-        return $this->parent;
131
-    }
132
-
133
-    /**
134
-     * Sets the parent node.
135
-     *
136
-     * @param AbstractNode $parent
137
-     * @return $this
138
-     * @throws CircularException
139
-     */
140
-    public function setParent(AbstractNode $parent)
141
-    {
142
-        // check integrity
143
-        if ($this->isDescendant($parent->id())) {
144
-            throw new CircularException('Can not add descendant "'.$parent->id().'" as my parent.');
145
-        }
146
-
147
-        // remove from old parent
148
-        if ( ! is_null($this->parent)) {
149
-            if ($this->parent->id() == $parent->id()) {
150
-                // already the parent
151
-                return $this;
152
-            }
153
-
154
-            $this->parent->removeChild($this->id);
155
-        }
156
-
157
-        $this->parent = $parent;
158
-
159
-        // assign child to parent
160
-        $this->parent->addChild($this);
161
-
162
-        //clear any cache
163
-        $this->clear();
164
-
165
-        return $this;
166
-    }
167
-
168
-    /**
169
-     * Sets the encoding class to this node and propagates it
170
-     * to all its children.
171
-     *
172
-     * @param Encode $encode
173
-     */
174
-    public function propagateEncoding(Encode $encode)
175
-    {
176
-        $this->encode = $encode;
177
-        $this->tag->setEncoding($encode);
178
-        // check children
179
-        foreach ($this->children as $id => $child) {
180
-            /** @var AbstractNode $node */
181
-            $node = $child['node'];
182
-            $node->propagateEncoding($encode);
183
-        }
184
-    }
185
-
186
-    /**
187
-     * Checks if this node has children.
188
-     *
189
-     * @return bool
190
-     */
191
-    public function hasChildren()
192
-    {
193
-        return ! empty($this->children);
194
-    }
195
-
196
-    /**
197
-     * Returns the child by id.
198
-     *
199
-     * @param int $id
200
-     * @return AbstractNode
201
-     * @throws ChildNotFoundException
202
-     */
203
-    public function getChild($id)
204
-    {
205
-        if ( ! isset($this->children[$id])) {
206
-            throw new ChildNotFoundException("Child '$id' not found in this node.");
207
-        }
208
-
209
-        return $this->children[$id]['node'];
210
-    }
211
-
212
-    /**
213
-     * Returns a new array of child nodes
214
-     *
215
-     * @return array
216
-     */
217
-    public function getChildren()
218
-    {
219
-        $nodes = [];
220
-        try {
221
-            $child = $this->firstChild();
222
-            do {
223
-                $nodes[] = $child;
224
-                $child   = $this->nextChild($child->id());
225
-            } while ( ! is_null($child));
226
-        } catch (ChildNotFoundException $e) {
227
-            // we are done looking for children
228
-        }
229
-
230
-        return $nodes;
231
-    }
232
-
233
-    /**
234
-     * Counts children
235
-     *
236
-     * @return int
237
-     */
238
-    public function countChildren()
239
-    {
240
-        return count($this->children);
241
-    }
242
-
243
-    /**
244
-     * Adds a child node to this node and returns the id of the child for this
245
-     * parent.
246
-     *
247
-     * @param AbstractNode $child
248
-     * @return bool
249
-     * @throws CircularException
250
-     */
251
-    public function addChild(AbstractNode $child)
252
-    {
253
-        $key = null;
254
-
255
-        // check integrity
256
-        if ($this->isAncestor($child->id())) {
257
-            throw new CircularException('Can not add child. It is my ancestor.');
258
-        }
259
-
260
-        // check if child is itself
261
-        if ($child->id() == $this->id) {
262
-            throw new CircularException('Can not set itself as a child.');
263
-        }
264
-
265
-        if ($this->hasChildren()) {
266
-            if (isset($this->children[$child->id()])) {
267
-                // we already have this child
268
-                return false;
269
-            }
270
-            $sibling                      = $this->lastChild();
271
-            $key                          = $sibling->id();
272
-            $this->children[$key]['next'] = $child->id();
273
-        }
274
-
275
-        // add the child
276
-        $this->children[$child->id()] = [
277
-            'node' => $child,
278
-            'next' => null,
279
-            'prev' => $key,
280
-        ];
281
-
282
-        // tell child I am the new parent
283
-        $child->setParent($this);
284
-
285
-        //clear any cache
286
-        $this->clear();
287
-
288
-        return true;
289
-    }
290
-
291
-    /**
292
-     * Removes the child by id.
293
-     *
294
-     * @param int $id
295
-     * @return $this
296
-     */
297
-    public function removeChild($id)
298
-    {
299
-        if ( ! isset($this->children[$id])) {
300
-            return $this;
301
-        }
302
-
303
-        // handle moving next and previous assignments.
304
-        $next = $this->children[$id]['next'];
305
-        $prev = $this->children[$id]['prev'];
306
-        if ( ! is_null($next)) {
307
-            $this->children[$next]['prev'] = $prev;
308
-        }
309
-        if ( ! is_null($prev)) {
310
-            $this->children[$prev]['next'] = $next;
311
-        }
312
-
313
-        // remove the child
314
-        unset($this->children[$id]);
315
-
316
-        //clear any cache
317
-        $this->clear();
318
-
319
-        return $this;
320
-    }
321
-
322
-    /**
323
-     * Attempts to get the next child.
324
-     *
325
-     * @param int $id
326
-     * @return AbstractNode
327
-     * @uses $this->getChild()
328
-     */
329
-    public function nextChild($id)
330
-    {
331
-        $child = $this->getChild($id);
332
-        $next  = $this->children[$child->id()]['next'];
333
-
334
-        return $this->getChild($next);
335
-    }
336
-
337
-    /**
338
-     * Attempts to get the previous child.
339
-     *
340
-     * @param int $id
341
-     * @return AbstractNode
342
-     * @uses $this->getChild()
343
-     */
344
-    public function previousChild($id)
345
-    {
346
-        $child = $this->getchild($id);
347
-        $next  = $this->children[$child->id()]['prev'];
348
-
349
-        return $this->getChild($next);
350
-    }
351
-
352
-    /**
353
-     * Checks if the given node id is a child of the
354
-     * current node.
355
-     *
356
-     * @param int $id
357
-     * @return bool
358
-     */
359
-    public function isChild($id)
360
-    {
361
-        foreach ($this->children as $childId => $child) {
362
-            if ($id == $childId) {
363
-                return true;
364
-            }
365
-        }
366
-
367
-        return false;
368
-    }
369
-
370
-    /**
371
-     * Checks if the given node id is a descendant of the
372
-     * current node.
373
-     *
374
-     * @param int $id
375
-     * @return bool
376
-     */
377
-    public function isDescendant($id)
378
-    {
379
-        if ($this->isChild($id)) {
380
-            return true;
381
-        }
382
-
383
-        foreach ($this->children as $childId => $child) {
384
-            /** @var AbstractNode $node */
385
-            $node = $child['node'];
386
-            if ($node->hasChildren() &&
387
-                $node->isDescendant($id)
388
-            ) {
389
-                return true;
390
-            }
391
-        }
392
-
393
-        return false;
394
-    }
395
-
396
-    /**
397
-     * Checks if the given node id is an ancestor of
398
-     * the current node.
399
-     *
400
-     * @param int $id
401
-     * @return bool
402
-     */
403
-    public function isAncestor($id)
404
-    {
405
-        if ( ! is_null($this->parent)) {
406
-            if ($this->parent->id() == $id) {
407
-                return true;
408
-            }
409
-
410
-            return $this->parent->isAncestor($id);
411
-        }
412
-
413
-        return false;
414
-    }
415
-
416
-    /**
417
-     * Attempts to get an ancestor node by the given id.
418
-     *
419
-     * @param int $id
420
-     * @return null|AbstractNode
421
-     */
422
-    public function getAncestor($id)
423
-    {
424
-        if ( ! is_null($this->parent)) {
425
-            if ($this->parent->id() == $id) {
426
-                return $this->parent;
427
-            }
428
-
429
-            return $this->parent->getAncestor($id);
430
-        }
431
-
432
-        return null;
433
-    }
434
-
435
-    /**
436
-     * Shortcut to return the first child.
437
-     *
438
-     * @return AbstractNode
439
-     * @uses $this->getChild()
440
-     */
441
-    public function firstChild()
442
-    {
443
-        reset($this->children);
444
-        $key = key($this->children);
445
-
446
-        return $this->getChild($key);
447
-    }
448
-
449
-    /**
450
-     * Attempts to get the last child.
451
-     *
452
-     * @return AbstractNode
453
-     */
454
-    public function lastChild()
455
-    {
456
-        end($this->children);
457
-        $key = key($this->children);
458
-
459
-        return $this->getChild($key);
460
-    }
461
-
462
-    /**
463
-     * Attempts to get the next sibling.
464
-     *
465
-     * @return AbstractNode
466
-     * @throws ParentNotFoundException
467
-     */
468
-    public function nextSibling()
469
-    {
470
-        if (is_null($this->parent)) {
471
-            throw new ParentNotFoundException('Parent is not set for this node.');
472
-        }
473
-
474
-        return $this->parent->nextChild($this->id);
475
-    }
476
-
477
-    /**
478
-     * Attempts to get the previous sibling
479
-     *
480
-     * @return AbstractNode
481
-     * @throws ParentNotFoundException
482
-     */
483
-    public function previousSibling()
484
-    {
485
-        if (is_null($this->parent)) {
486
-            throw new ParentNotFoundException('Parent is not set for this node.');
487
-        }
488
-
489
-        return $this->parent->previousChild($this->id);
490
-    }
491
-
492
-    /**
493
-     * Gets the tag object of this node.
494
-     *
495
-     * @return Tag
496
-     */
497
-    public function getTag()
498
-    {
499
-        return $this->tag;
500
-    }
501
-
502
-    /**
503
-     * A wrapper method that simply calls the getAttribute method
504
-     * on the tag of this node.
505
-     *
506
-     * @return array
507
-     */
508
-    public function getAttributes()
509
-    {
510
-        $attributes = $this->tag->getAttributes();
511
-        foreach ($attributes as $name => $info) {
512
-            $attributes[$name] = $info['value'];
513
-        }
514
-
515
-        return $attributes;
516
-    }
517
-
518
-    /**
519
-     * A wrapper method that simply calls the getAttribute method
520
-     * on the tag of this node.
521
-     *
522
-     * @param string $key
523
-     * @return mixed
524
-     */
525
-    public function getAttribute($key)
526
-    {
527
-        $attribute = $this->tag->getAttribute($key);
528
-        if ( ! is_null($attribute)) {
529
-            $attribute = $attribute['value'];
530
-        }
531
-
532
-        return $attribute;
533
-    }
534
-
535
-    /**
536
-     * A wrapper method that simply calls the setAttribute method
537
-     * on the tag of this node.
538
-     *
539
-     * @param string $key
540
-     * @param string $value
541
-     * @return $this
542
-     */
543
-    public function setAttribute($key, $value)
544
-    {
545
-        $this->tag->setAttribute($key, $value);
546
-
547
-        return $this;
548
-    }
549
-
550
-    /**
551
-     * A wrapper method that simply calls the removeAttribute method
552
-     * on the tag of this node.
553
-     *
554
-     * @param string $key
555
-     * @return $this
556
-     */
557
-    public function removeAttribute($key)
558
-    {
559
-        $this->tag->removeAttribute($key);
560
-
561
-        return $this;
562
-    }
563
-
564
-    /**
565
-     * A wrapper method that simply calls the removeAttributes method
566
-     * on the tag of this node.
567
-     *
568
-     * @param array $keys
569
-     * @return $this
570
-     */
571
-    public function removeAttributes($keys)
572
-    {
573
-        $this->tag->removeAttributes($keys);
574
-
575
-        return $this;
576
-    }
577
-
578
-    /**
579
-     * Function to locate a specific ancestor tag in the path to the root.
580
-     *
581
-     * @param  string $tag
582
-     * @return AbstractNode
583
-     * @throws ParentNotFoundException
584
-     */
585
-    public function ancestorByTag($tag)
586
-    {
587
-        // Start by including ourselves in the comparison.
588
-        $node = $this;
589
-
590
-        while ( ! is_null($node)) {
591
-            if ($node->tag->name() == $tag) {
592
-                return $node;
593
-            }
594
-
595
-            $node = $node->getParent();
596
-        }
597
-
598
-        throw new ParentNotFoundException('Could not find an ancestor with "'.$tag.'" tag');
599
-    }
600
-
601
-    /**
602
-     * Find elements by css selector
603
-     *
604
-     * @param string $selector
605
-     * @param int $nth
606
-     * @return array|AbstractNode
607
-     */
608
-    public function find($selector, $nth = null)
609
-    {
610
-        $selector = new Selector($selector);
611
-        $nodes    = $selector->find($this);
612
-
613
-        if ( ! is_null($nth)) {
614
-            // return nth-element or array
615
-            if (isset($nodes[$nth])) {
616
-                return $nodes[$nth];
617
-            }
618
-
619
-            return null;
620
-        }
621
-
622
-        return $nodes;
623
-    }
624
-
625
-    /**
626
-     * Function to try a few tricks to determine the displayed size of an img on the page.
627
-     * NOTE: This will ONLY work on an IMG tag. Returns FALSE on all other tag types.
628
-     *
629
-     * Future enhancement:
630
-     * Look in the tag to see if there is a class or id specified that has a height or width attribute to it.
631
-     *
632
-     * Far future enhancement
633
-     * Look at all the parent tags of this image to see if they specify a class or id that has an img selector that specifies a height or width
634
-     * Note that in this case, the class or id will have the img sub-selector for it to apply to the image.
635
-     *
636
-     * ridiculously far future development
637
-     * If the class or id is specified in a SEPARATE css file that's not on the page, go get it and do what we were just doing for the ones on the page.
638
-     *
639
-     * @author John Schlick
640
-     * @return array an array containing the 'height' and 'width' of the image on the page or -1 if we can't figure it out.
641
-     */
642
-    public function get_display_size()
643
-    {
644
-        $width  = -1;
645
-        $height = -1;
646
-
647
-        if ($this->tag->name() != 'img') {
648
-            return false;
649
-        }
650
-
651
-        // See if there is a height or width attribute in the tag itself.
652
-        if ( ! is_null($this->tag->getAttribute('width'))) {
653
-            $width = $this->tag->getAttribute('width');
654
-        }
655
-
656
-        if ( ! is_null($this->tag->getAttribute('height'))) {
657
-            $height = $this->tag->getAttribute('height');
658
-        }
659
-
660
-        // Now look for an inline style.
661
-        if ( ! is_null($this->tag->getAttribute('style'))) {
662
-            // Thanks to user 'gnarf' from stackoverflow for this regular expression.
663
-            $attributes = [];
664
-            preg_match_all("/([\w-]+)\s*:\s*([^;]+)\s*;?/", $this->tag->getAttribute('style'), $matches,
665
-                PREG_SET_ORDER);
666
-            foreach ($matches as $match) {
667
-                $attributes[$match[1]] = $match[2];
668
-            }
669
-
670
-            // If there is a width in the style attributes:
671
-            if (isset($attributes['width']) and $width == -1) {
672
-                // check that the last two characters are px (pixels)
673
-                if (strtolower(substr($attributes['width'], -2)) == 'px') {
674
-                    $proposed_width = substr($attributes['width'], 0, -2);
675
-                    // Now make sure that it's an integer and not something stupid.
676
-                    if (filter_var($proposed_width, FILTER_VALIDATE_INT)) {
677
-                        $width = $proposed_width;
678
-                    }
679
-                }
680
-            }
681
-
682
-            // If there is a width in the style attributes:
683
-            if (isset($attributes['height']) and $height == -1) {
684
-                // check that the last two characters are px (pixels)
685
-                if (strtolower(substr($attributes['height'], -2)) == 'px') {
686
-                    $proposed_height = substr($attributes['height'], 0, -2);
687
-                    // Now make sure that it's an integer and not something stupid.
688
-                    if (filter_var($proposed_height, FILTER_VALIDATE_INT)) {
689
-                        $height = $proposed_height;
690
-                    }
691
-                }
692
-            }
693
-
694
-        }
695
-
696
-        $result = [
697
-            'height' => $height,
698
-            'width'  => $width,
699
-        ];
700
-
701
-        return $result;
702
-    }
703
-
704
-    /**
705
-     * Gets the inner html of this node.
706
-     *
707
-     * @return string
708
-     */
709
-    abstract public function innerHtml();
710
-
711
-    /**
712
-     * Gets the html of this node, including it's own
713
-     * tag.
714
-     *
715
-     * @return string
716
-     */
717
-    abstract public function outerHtml();
718
-
719
-    /**
720
-     * Gets the text of this node (if there is any text).
721
-     *
722
-     * @return string
723
-     */
724
-    abstract public function text();
725
-
726
-    /**
727
-     * Call this when something in the node tree has changed. Like a child has been added
728
-     * or a parent has been changed.
729
-     *
730
-     * @return void
731
-     */
732
-    abstract protected function clear();
20
+	/**
21
+	 * Contains the tag name/type
22
+	 *
23
+	 * @var \PHPHtmlParser\Dom\Tag
24
+	 */
25
+	protected $tag;
26
+
27
+	/**
28
+	 * Contains a list of attributes on this tag.
29
+	 *
30
+	 * @var array
31
+	 */
32
+	protected $attr = [];
33
+
34
+	/**
35
+	 * An array of all the children.
36
+	 *
37
+	 * @var array
38
+	 */
39
+	protected $children = [];
40
+
41
+	/**
42
+	 * Contains the parent Node.
43
+	 *
44
+	 * @var AbstractNode
45
+	 */
46
+	protected $parent = null;
47
+
48
+	/**
49
+	 * The unique id of the class. Given by PHP.
50
+	 *
51
+	 * @var string
52
+	 */
53
+	protected $id;
54
+
55
+	/**
56
+	 * The encoding class used to encode strings.
57
+	 *
58
+	 * @var mixed
59
+	 */
60
+	protected $encode;
61
+
62
+	/**
63
+	 * Creates a unique spl hash for this node.
64
+	 */
65
+	public function __construct()
66
+	{
67
+		$this->id = spl_object_hash($this);
68
+	}
69
+
70
+	/**
71
+	 * Magic get method for attributes and certain methods.
72
+	 *
73
+	 * @param string $key
74
+	 * @return mixed
75
+	 */
76
+	public function __get($key)
77
+	{
78
+		// check attribute first
79
+		if ( ! is_null($this->getAttribute($key))) {
80
+			return $this->getAttribute($key);
81
+		}
82
+		switch (strtolower($key)) {
83
+			case 'outerhtml':
84
+				return $this->outerHtml();
85
+			case 'innerhtml':
86
+				return $this->innerHtml();
87
+			case 'text':
88
+				return $this->text();
89
+		}
90
+
91
+		return null;
92
+	}
93
+
94
+	/**
95
+	 * Attempts to clear out any object references.
96
+	 */
97
+	public function __destruct()
98
+	{
99
+		$this->tag      = null;
100
+		$this->attr     = [];
101
+		$this->parent   = null;
102
+		$this->children = [];
103
+	}
104
+
105
+	/**
106
+	 * Simply calls the outer text method.
107
+	 *
108
+	 * @return string
109
+	 */
110
+	public function __toString()
111
+	{
112
+		return $this->outerHtml();
113
+	}
114
+
115
+	/**
116
+	 * Returns the id of this object.
117
+	 */
118
+	public function id()
119
+	{
120
+		return $this->id;
121
+	}
122
+
123
+	/**
124
+	 * Returns the parent of node.
125
+	 *
126
+	 * @return AbstractNode
127
+	 */
128
+	public function getParent()
129
+	{
130
+		return $this->parent;
131
+	}
132
+
133
+	/**
134
+	 * Sets the parent node.
135
+	 *
136
+	 * @param AbstractNode $parent
137
+	 * @return $this
138
+	 * @throws CircularException
139
+	 */
140
+	public function setParent(AbstractNode $parent)
141
+	{
142
+		// check integrity
143
+		if ($this->isDescendant($parent->id())) {
144
+			throw new CircularException('Can not add descendant "'.$parent->id().'" as my parent.');
145
+		}
146
+
147
+		// remove from old parent
148
+		if ( ! is_null($this->parent)) {
149
+			if ($this->parent->id() == $parent->id()) {
150
+				// already the parent
151
+				return $this;
152
+			}
153
+
154
+			$this->parent->removeChild($this->id);
155
+		}
156
+
157
+		$this->parent = $parent;
158
+
159
+		// assign child to parent
160
+		$this->parent->addChild($this);
161
+
162
+		//clear any cache
163
+		$this->clear();
164
+
165
+		return $this;
166
+	}
167
+
168
+	/**
169
+	 * Sets the encoding class to this node and propagates it
170
+	 * to all its children.
171
+	 *
172
+	 * @param Encode $encode
173
+	 */
174
+	public function propagateEncoding(Encode $encode)
175
+	{
176
+		$this->encode = $encode;
177
+		$this->tag->setEncoding($encode);
178
+		// check children
179
+		foreach ($this->children as $id => $child) {
180
+			/** @var AbstractNode $node */
181
+			$node = $child['node'];
182
+			$node->propagateEncoding($encode);
183
+		}
184
+	}
185
+
186
+	/**
187
+	 * Checks if this node has children.
188
+	 *
189
+	 * @return bool
190
+	 */
191
+	public function hasChildren()
192
+	{
193
+		return ! empty($this->children);
194
+	}
195
+
196
+	/**
197
+	 * Returns the child by id.
198
+	 *
199
+	 * @param int $id
200
+	 * @return AbstractNode
201
+	 * @throws ChildNotFoundException
202
+	 */
203
+	public function getChild($id)
204
+	{
205
+		if ( ! isset($this->children[$id])) {
206
+			throw new ChildNotFoundException("Child '$id' not found in this node.");
207
+		}
208
+
209
+		return $this->children[$id]['node'];
210
+	}
211
+
212
+	/**
213
+	 * Returns a new array of child nodes
214
+	 *
215
+	 * @return array
216
+	 */
217
+	public function getChildren()
218
+	{
219
+		$nodes = [];
220
+		try {
221
+			$child = $this->firstChild();
222
+			do {
223
+				$nodes[] = $child;
224
+				$child   = $this->nextChild($child->id());
225
+			} while ( ! is_null($child));
226
+		} catch (ChildNotFoundException $e) {
227
+			// we are done looking for children
228
+		}
229
+
230
+		return $nodes;
231
+	}
232
+
233
+	/**
234
+	 * Counts children
235
+	 *
236
+	 * @return int
237
+	 */
238
+	public function countChildren()
239
+	{
240
+		return count($this->children);
241
+	}
242
+
243
+	/**
244
+	 * Adds a child node to this node and returns the id of the child for this
245
+	 * parent.
246
+	 *
247
+	 * @param AbstractNode $child
248
+	 * @return bool
249
+	 * @throws CircularException
250
+	 */
251
+	public function addChild(AbstractNode $child)
252
+	{
253
+		$key = null;
254
+
255
+		// check integrity
256
+		if ($this->isAncestor($child->id())) {
257
+			throw new CircularException('Can not add child. It is my ancestor.');
258
+		}
259
+
260
+		// check if child is itself
261
+		if ($child->id() == $this->id) {
262
+			throw new CircularException('Can not set itself as a child.');
263
+		}
264
+
265
+		if ($this->hasChildren()) {
266
+			if (isset($this->children[$child->id()])) {
267
+				// we already have this child
268
+				return false;
269
+			}
270
+			$sibling                      = $this->lastChild();
271
+			$key                          = $sibling->id();
272
+			$this->children[$key]['next'] = $child->id();
273
+		}
274
+
275
+		// add the child
276
+		$this->children[$child->id()] = [
277
+			'node' => $child,
278
+			'next' => null,
279
+			'prev' => $key,
280
+		];
281
+
282
+		// tell child I am the new parent
283
+		$child->setParent($this);
284
+
285
+		//clear any cache
286
+		$this->clear();
287
+
288
+		return true;
289
+	}
290
+
291
+	/**
292
+	 * Removes the child by id.
293
+	 *
294
+	 * @param int $id
295
+	 * @return $this
296
+	 */
297
+	public function removeChild($id)
298
+	{
299
+		if ( ! isset($this->children[$id])) {
300
+			return $this;
301
+		}
302
+
303
+		// handle moving next and previous assignments.
304
+		$next = $this->children[$id]['next'];
305
+		$prev = $this->children[$id]['prev'];
306
+		if ( ! is_null($next)) {
307
+			$this->children[$next]['prev'] = $prev;
308
+		}
309
+		if ( ! is_null($prev)) {
310
+			$this->children[$prev]['next'] = $next;
311
+		}
312
+
313
+		// remove the child
314
+		unset($this->children[$id]);
315
+
316
+		//clear any cache
317
+		$this->clear();
318
+
319
+		return $this;
320
+	}
321
+
322
+	/**
323
+	 * Attempts to get the next child.
324
+	 *
325
+	 * @param int $id
326
+	 * @return AbstractNode
327
+	 * @uses $this->getChild()
328
+	 */
329
+	public function nextChild($id)
330
+	{
331
+		$child = $this->getChild($id);
332
+		$next  = $this->children[$child->id()]['next'];
333
+
334
+		return $this->getChild($next);
335
+	}
336
+
337
+	/**
338
+	 * Attempts to get the previous child.
339
+	 *
340
+	 * @param int $id
341
+	 * @return AbstractNode
342
+	 * @uses $this->getChild()
343
+	 */
344
+	public function previousChild($id)
345
+	{
346
+		$child = $this->getchild($id);
347
+		$next  = $this->children[$child->id()]['prev'];
348
+
349
+		return $this->getChild($next);
350
+	}
351
+
352
+	/**
353
+	 * Checks if the given node id is a child of the
354
+	 * current node.
355
+	 *
356
+	 * @param int $id
357
+	 * @return bool
358
+	 */
359
+	public function isChild($id)
360
+	{
361
+		foreach ($this->children as $childId => $child) {
362
+			if ($id == $childId) {
363
+				return true;
364
+			}
365
+		}
366
+
367
+		return false;
368
+	}
369
+
370
+	/**
371
+	 * Checks if the given node id is a descendant of the
372
+	 * current node.
373
+	 *
374
+	 * @param int $id
375
+	 * @return bool
376
+	 */
377
+	public function isDescendant($id)
378
+	{
379
+		if ($this->isChild($id)) {
380
+			return true;
381
+		}
382
+
383
+		foreach ($this->children as $childId => $child) {
384
+			/** @var AbstractNode $node */
385
+			$node = $child['node'];
386
+			if ($node->hasChildren() &&
387
+				$node->isDescendant($id)
388
+			) {
389
+				return true;
390
+			}
391
+		}
392
+
393
+		return false;
394
+	}
395
+
396
+	/**
397
+	 * Checks if the given node id is an ancestor of
398
+	 * the current node.
399
+	 *
400
+	 * @param int $id
401
+	 * @return bool
402
+	 */
403
+	public function isAncestor($id)
404
+	{
405
+		if ( ! is_null($this->parent)) {
406
+			if ($this->parent->id() == $id) {
407
+				return true;
408
+			}
409
+
410
+			return $this->parent->isAncestor($id);
411
+		}
412
+
413
+		return false;
414
+	}
415
+
416
+	/**
417
+	 * Attempts to get an ancestor node by the given id.
418
+	 *
419
+	 * @param int $id
420
+	 * @return null|AbstractNode
421
+	 */
422
+	public function getAncestor($id)
423
+	{
424
+		if ( ! is_null($this->parent)) {
425
+			if ($this->parent->id() == $id) {
426
+				return $this->parent;
427
+			}
428
+
429
+			return $this->parent->getAncestor($id);
430
+		}
431
+
432
+		return null;
433
+	}
434
+
435
+	/**
436
+	 * Shortcut to return the first child.
437
+	 *
438
+	 * @return AbstractNode
439
+	 * @uses $this->getChild()
440
+	 */
441
+	public function firstChild()
442
+	{
443
+		reset($this->children);
444
+		$key = key($this->children);
445
+
446
+		return $this->getChild($key);
447
+	}
448
+
449
+	/**
450
+	 * Attempts to get the last child.
451
+	 *
452
+	 * @return AbstractNode
453
+	 */
454
+	public function lastChild()
455
+	{
456
+		end($this->children);
457
+		$key = key($this->children);
458
+
459
+		return $this->getChild($key);
460
+	}
461
+
462
+	/**
463
+	 * Attempts to get the next sibling.
464
+	 *
465
+	 * @return AbstractNode
466
+	 * @throws ParentNotFoundException
467
+	 */
468
+	public function nextSibling()
469
+	{
470
+		if (is_null($this->parent)) {
471
+			throw new ParentNotFoundException('Parent is not set for this node.');
472
+		}
473
+
474
+		return $this->parent->nextChild($this->id);
475
+	}
476
+
477
+	/**
478
+	 * Attempts to get the previous sibling
479
+	 *
480
+	 * @return AbstractNode
481
+	 * @throws ParentNotFoundException
482
+	 */
483
+	public function previousSibling()
484
+	{
485
+		if (is_null($this->parent)) {
486
+			throw new ParentNotFoundException('Parent is not set for this node.');
487
+		}
488
+
489
+		return $this->parent->previousChild($this->id);
490
+	}
491
+
492
+	/**
493
+	 * Gets the tag object of this node.
494
+	 *
495
+	 * @return Tag
496
+	 */
497
+	public function getTag()
498
+	{
499
+		return $this->tag;
500
+	}
501
+
502
+	/**
503
+	 * A wrapper method that simply calls the getAttribute method
504
+	 * on the tag of this node.
505
+	 *
506
+	 * @return array
507
+	 */
508
+	public function getAttributes()
509
+	{
510
+		$attributes = $this->tag->getAttributes();
511
+		foreach ($attributes as $name => $info) {
512
+			$attributes[$name] = $info['value'];
513
+		}
514
+
515
+		return $attributes;
516
+	}
517
+
518
+	/**
519
+	 * A wrapper method that simply calls the getAttribute method
520
+	 * on the tag of this node.
521
+	 *
522
+	 * @param string $key
523
+	 * @return mixed
524
+	 */
525
+	public function getAttribute($key)
526
+	{
527
+		$attribute = $this->tag->getAttribute($key);
528
+		if ( ! is_null($attribute)) {
529
+			$attribute = $attribute['value'];
530
+		}
531
+
532
+		return $attribute;
533
+	}
534
+
535
+	/**
536
+	 * A wrapper method that simply calls the setAttribute method
537
+	 * on the tag of this node.
538
+	 *
539
+	 * @param string $key
540
+	 * @param string $value
541
+	 * @return $this
542
+	 */
543
+	public function setAttribute($key, $value)
544
+	{
545
+		$this->tag->setAttribute($key, $value);
546
+
547
+		return $this;
548
+	}
549
+
550
+	/**
551
+	 * A wrapper method that simply calls the removeAttribute method
552
+	 * on the tag of this node.
553
+	 *
554
+	 * @param string $key
555
+	 * @return $this
556
+	 */
557
+	public function removeAttribute($key)
558
+	{
559
+		$this->tag->removeAttribute($key);
560
+
561
+		return $this;
562
+	}
563
+
564
+	/**
565
+	 * A wrapper method that simply calls the removeAttributes method
566
+	 * on the tag of this node.
567
+	 *
568
+	 * @param array $keys
569
+	 * @return $this
570
+	 */
571
+	public function removeAttributes($keys)
572
+	{
573
+		$this->tag->removeAttributes($keys);
574
+
575
+		return $this;
576
+	}
577
+
578
+	/**
579
+	 * Function to locate a specific ancestor tag in the path to the root.
580
+	 *
581
+	 * @param  string $tag
582
+	 * @return AbstractNode
583
+	 * @throws ParentNotFoundException
584
+	 */
585
+	public function ancestorByTag($tag)
586
+	{
587
+		// Start by including ourselves in the comparison.
588
+		$node = $this;
589
+
590
+		while ( ! is_null($node)) {
591
+			if ($node->tag->name() == $tag) {
592
+				return $node;
593
+			}
594
+
595
+			$node = $node->getParent();
596
+		}
597
+
598
+		throw new ParentNotFoundException('Could not find an ancestor with "'.$tag.'" tag');
599
+	}
600
+
601
+	/**
602
+	 * Find elements by css selector
603
+	 *
604
+	 * @param string $selector
605
+	 * @param int $nth
606
+	 * @return array|AbstractNode
607
+	 */
608
+	public function find($selector, $nth = null)
609
+	{
610
+		$selector = new Selector($selector);
611
+		$nodes    = $selector->find($this);
612
+
613
+		if ( ! is_null($nth)) {
614
+			// return nth-element or array
615
+			if (isset($nodes[$nth])) {
616
+				return $nodes[$nth];
617
+			}
618
+
619
+			return null;
620
+		}
621
+
622
+		return $nodes;
623
+	}
624
+
625
+	/**
626
+	 * Function to try a few tricks to determine the displayed size of an img on the page.
627
+	 * NOTE: This will ONLY work on an IMG tag. Returns FALSE on all other tag types.
628
+	 *
629
+	 * Future enhancement:
630
+	 * Look in the tag to see if there is a class or id specified that has a height or width attribute to it.
631
+	 *
632
+	 * Far future enhancement
633
+	 * Look at all the parent tags of this image to see if they specify a class or id that has an img selector that specifies a height or width
634
+	 * Note that in this case, the class or id will have the img sub-selector for it to apply to the image.
635
+	 *
636
+	 * ridiculously far future development
637
+	 * If the class or id is specified in a SEPARATE css file that's not on the page, go get it and do what we were just doing for the ones on the page.
638
+	 *
639
+	 * @author John Schlick
640
+	 * @return array an array containing the 'height' and 'width' of the image on the page or -1 if we can't figure it out.
641
+	 */
642
+	public function get_display_size()
643
+	{
644
+		$width  = -1;
645
+		$height = -1;
646
+
647
+		if ($this->tag->name() != 'img') {
648
+			return false;
649
+		}
650
+
651
+		// See if there is a height or width attribute in the tag itself.
652
+		if ( ! is_null($this->tag->getAttribute('width'))) {
653
+			$width = $this->tag->getAttribute('width');
654
+		}
655
+
656
+		if ( ! is_null($this->tag->getAttribute('height'))) {
657
+			$height = $this->tag->getAttribute('height');
658
+		}
659
+
660
+		// Now look for an inline style.
661
+		if ( ! is_null($this->tag->getAttribute('style'))) {
662
+			// Thanks to user 'gnarf' from stackoverflow for this regular expression.
663
+			$attributes = [];
664
+			preg_match_all("/([\w-]+)\s*:\s*([^;]+)\s*;?/", $this->tag->getAttribute('style'), $matches,
665
+				PREG_SET_ORDER);
666
+			foreach ($matches as $match) {
667
+				$attributes[$match[1]] = $match[2];
668
+			}
669
+
670
+			// If there is a width in the style attributes:
671
+			if (isset($attributes['width']) and $width == -1) {
672
+				// check that the last two characters are px (pixels)
673
+				if (strtolower(substr($attributes['width'], -2)) == 'px') {
674
+					$proposed_width = substr($attributes['width'], 0, -2);
675
+					// Now make sure that it's an integer and not something stupid.
676
+					if (filter_var($proposed_width, FILTER_VALIDATE_INT)) {
677
+						$width = $proposed_width;
678
+					}
679
+				}
680
+			}
681
+
682
+			// If there is a width in the style attributes:
683
+			if (isset($attributes['height']) and $height == -1) {
684
+				// check that the last two characters are px (pixels)
685
+				if (strtolower(substr($attributes['height'], -2)) == 'px') {
686
+					$proposed_height = substr($attributes['height'], 0, -2);
687
+					// Now make sure that it's an integer and not something stupid.
688
+					if (filter_var($proposed_height, FILTER_VALIDATE_INT)) {
689
+						$height = $proposed_height;
690
+					}
691
+				}
692
+			}
693
+
694
+		}
695
+
696
+		$result = [
697
+			'height' => $height,
698
+			'width'  => $width,
699
+		];
700
+
701
+		return $result;
702
+	}
703
+
704
+	/**
705
+	 * Gets the inner html of this node.
706
+	 *
707
+	 * @return string
708
+	 */
709
+	abstract public function innerHtml();
710
+
711
+	/**
712
+	 * Gets the html of this node, including it's own
713
+	 * tag.
714
+	 *
715
+	 * @return string
716
+	 */
717
+	abstract public function outerHtml();
718
+
719
+	/**
720
+	 * Gets the text of this node (if there is any text).
721
+	 *
722
+	 * @return string
723
+	 */
724
+	abstract public function text();
725
+
726
+	/**
727
+	 * Call this when something in the node tree has changed. Like a child has been added
728
+	 * or a parent has been changed.
729
+	 *
730
+	 * @return void
731
+	 */
732
+	abstract protected function clear();
733 733
 }
Please login to merge, or discard this patch.