Passed
Pull Request — master (#12)
by Jake
02:34
created
src/Extension/QPXML.php 2 patches
Indentation   +210 added lines, -210 removed lines patch added patch discarded remove patch
@@ -25,214 +25,214 @@
 block discarded – undo
25 25
 class QPXML implements Extension
26 26
 {
27 27
 
28
-	protected $qp;
29
-
30
-	public function __construct(Query $qp)
31
-	{
32
-		$this->qp = $qp;
33
-	}
34
-
35
-	public function schema($file)
36
-	{
37
-		$doc = $this->qp->branch()->top()->get(0)->ownerDocument;
38
-
39
-		if (! $doc->schemaValidate($file)) {
40
-			throw new Exception('Document did not validate against the schema.');
41
-		}
42
-	}
43
-
44
-	/**
45
-	 * Get or set a CDATA section.
46
-	 *
47
-	 * If this is given text, it will create a CDATA section in each matched element,
48
-	 * setting that item's value to $text.
49
-	 *
50
-	 * If no parameter is passed in, this will return the first CDATA section that it
51
-	 * finds in the matched elements.
52
-	 *
53
-	 * @param string $text
54
-	 *  The text data to insert into the current matches. If this is NULL, then the first
55
-	 *  CDATA will be returned.
56
-	 *
57
-	 * @return mixed
58
-	 *  If $text is not NULL, this will return a {@link QueryPath}. Otherwise, it will
59
-	 *  return a string. If no CDATA is found, this will return NULL.
60
-	 * @see comment()
61
-	 * @see QueryPath::text()
62
-	 * @see QueryPath::html()
63
-	 */
64
-	public function cdata($text = null)
65
-	{
66
-		if (isset($text)) {
67
-			// Add this text as CDATA in the current elements.
68
-			foreach ($this->qp->get() as $element) {
69
-				$cdata = $element->ownerDocument->createCDATASection($text);
70
-				$element->appendChild($cdata);
71
-			}
72
-
73
-			return $this->qp;
74
-		}
75
-
76
-		// Look for CDATA sections.
77
-		foreach ($this->qp->get() as $ele) {
78
-			foreach ($ele->childNodes as $node) {
79
-				if ($node->nodeType === XML_CDATA_SECTION_NODE) {
80
-					// Return first match.
81
-					return $node->textContent;
82
-				}
83
-			}
84
-		}
85
-
86
-		return null;
87
-		// Nothing found
88
-	}
89
-
90
-	/**
91
-	 * Get or set a comment.
92
-	 *
93
-	 * This function is used to get or set comments in an XML or HTML document.
94
-	 * If a $text value is passed in (and is not NULL), then this will add a comment
95
-	 * (with the value $text) to every match in the set.
96
-	 *
97
-	 * If no text is passed in, this will return the first comment in the set of matches.
98
-	 * If no comments are found, NULL will be returned.
99
-	 *
100
-	 * @param string $text
101
-	 *  The text of the comment. If set, a new comment will be created in every item
102
-	 *  wrapped by the current {@link QueryPath}.
103
-	 *
104
-	 * @return mixed
105
-	 *  If $text is set, this will return a {@link QueryPath}. If no text is set, this
106
-	 *  will search for a comment and attempt to return the string value of the first
107
-	 *  comment it finds. If no comment is found, NULL will be returned.
108
-	 * @see cdata()
109
-	 */
110
-	public function comment($text = null)
111
-	{
112
-		if (isset($text)) {
113
-			foreach ($this->qp->get() as $element) {
114
-				$comment = $element->ownerDocument->createComment($text);
115
-				$element->appendChild($comment);
116
-			}
117
-
118
-			return $this->qp;
119
-		}
120
-		foreach ($this->qp->get() as $ele) {
121
-			foreach ($ele->childNodes as $node) {
122
-				if ($node->nodeType == XML_COMMENT_NODE) {
123
-					// Return first match.
124
-					return $node->textContent;
125
-				}
126
-			}
127
-		}
128
-	}
129
-
130
-	/**
131
-	 * Get or set a processor instruction.
132
-	 */
133
-	public function pi($prefix = null, $text = null)
134
-	{
135
-		if (isset($text)) {
136
-			foreach ($this->qp->get() as $element) {
137
-				$comment = $element->ownerDocument->createProcessingInstruction($prefix, $text);
138
-				$element->appendChild($comment);
139
-			}
140
-
141
-			return $this->qp;
142
-		}
143
-		foreach ($this->qp->get() as $ele) {
144
-			foreach ($ele->childNodes as $node) {
145
-				if ($node->nodeType == XML_PI_NODE) {
146
-					if (isset($prefix)) {
147
-						if ($node->tagName == $prefix) {
148
-							return $node->textContent;
149
-						}
150
-					} else {
151
-						// Return first match.
152
-						return $node->textContent;
153
-					}
154
-				}
155
-			} // foreach
156
-		} // foreach
157
-	}
158
-
159
-	public function toXml()
160
-	{
161
-		return $this->qp->document()->saveXml();
162
-	}
163
-
164
-	/**
165
-	 * Create a NIL element.
166
-	 *
167
-	 * @param string $text
168
-	 * @param string $value
169
-	 *
170
-	 * @reval object $element
171
-	 */
172
-	public function createNilElement($text, $value)
173
-	{
174
-		$value   = ($value) ? 'true' : 'false';
175
-		$element = $this->qp->createElement($text);
176
-		$element->attr('xsi:nil', $value);
177
-
178
-		return $element;
179
-	}
180
-
181
-	/**
182
-	 * Create an element with the given namespace.
183
-	 *
184
-	 * @param string $text
185
-	 * @param string $nsUri
186
-	 *   The namespace URI for the given element.
187
-	 *
188
-	 * @return DOMQuery
189
-	 */
190
-	public function createElement($text, $nsUri = null)
191
-	{
192
-		if (isset($text)) {
193
-			foreach ($this->qp->get() as $element) {
194
-				if ($nsUri === null && strpos($text, ':') !== false) {
195
-					$ns    = array_shift(explode(':', $text));
196
-					$nsUri = $element->ownerDocument->lookupNamespaceURI($ns);
197
-
198
-					if ($nsUri === null) {
199
-						throw new Exception("Undefined namespace for: " . $text);
200
-					}
201
-				}
202
-
203
-				$node = null;
204
-				if ($nsUri !== null) {
205
-					$node = $element->ownerDocument->createElementNS(
206
-						$nsUri,
207
-						$text
208
-					);
209
-				} else {
210
-					$node = $element->ownerDocument->createElement($text);
211
-				}
212
-
213
-				return QueryPath::with($node);
214
-			}
215
-		}
216
-
217
-		return new DOMQuery();
218
-	}
219
-
220
-	/**
221
-	 * Append an element.
222
-	 *
223
-	 * @param string $text
224
-	 *
225
-	 * @return DOMQuery
226
-	 */
227
-	public function appendElement($text)
228
-	{
229
-		if (isset($text)) {
230
-			foreach ($this->qp->get() as $element) {
231
-				$node = $this->qp->createElement($text);
232
-				QueryPath::with($element)->append($node);
233
-			}
234
-		}
235
-
236
-		return $this->qp;
237
-	}
28
+    protected $qp;
29
+
30
+    public function __construct(Query $qp)
31
+    {
32
+        $this->qp = $qp;
33
+    }
34
+
35
+    public function schema($file)
36
+    {
37
+        $doc = $this->qp->branch()->top()->get(0)->ownerDocument;
38
+
39
+        if (! $doc->schemaValidate($file)) {
40
+            throw new Exception('Document did not validate against the schema.');
41
+        }
42
+    }
43
+
44
+    /**
45
+     * Get or set a CDATA section.
46
+     *
47
+     * If this is given text, it will create a CDATA section in each matched element,
48
+     * setting that item's value to $text.
49
+     *
50
+     * If no parameter is passed in, this will return the first CDATA section that it
51
+     * finds in the matched elements.
52
+     *
53
+     * @param string $text
54
+     *  The text data to insert into the current matches. If this is NULL, then the first
55
+     *  CDATA will be returned.
56
+     *
57
+     * @return mixed
58
+     *  If $text is not NULL, this will return a {@link QueryPath}. Otherwise, it will
59
+     *  return a string. If no CDATA is found, this will return NULL.
60
+     * @see comment()
61
+     * @see QueryPath::text()
62
+     * @see QueryPath::html()
63
+     */
64
+    public function cdata($text = null)
65
+    {
66
+        if (isset($text)) {
67
+            // Add this text as CDATA in the current elements.
68
+            foreach ($this->qp->get() as $element) {
69
+                $cdata = $element->ownerDocument->createCDATASection($text);
70
+                $element->appendChild($cdata);
71
+            }
72
+
73
+            return $this->qp;
74
+        }
75
+
76
+        // Look for CDATA sections.
77
+        foreach ($this->qp->get() as $ele) {
78
+            foreach ($ele->childNodes as $node) {
79
+                if ($node->nodeType === XML_CDATA_SECTION_NODE) {
80
+                    // Return first match.
81
+                    return $node->textContent;
82
+                }
83
+            }
84
+        }
85
+
86
+        return null;
87
+        // Nothing found
88
+    }
89
+
90
+    /**
91
+     * Get or set a comment.
92
+     *
93
+     * This function is used to get or set comments in an XML or HTML document.
94
+     * If a $text value is passed in (and is not NULL), then this will add a comment
95
+     * (with the value $text) to every match in the set.
96
+     *
97
+     * If no text is passed in, this will return the first comment in the set of matches.
98
+     * If no comments are found, NULL will be returned.
99
+     *
100
+     * @param string $text
101
+     *  The text of the comment. If set, a new comment will be created in every item
102
+     *  wrapped by the current {@link QueryPath}.
103
+     *
104
+     * @return mixed
105
+     *  If $text is set, this will return a {@link QueryPath}. If no text is set, this
106
+     *  will search for a comment and attempt to return the string value of the first
107
+     *  comment it finds. If no comment is found, NULL will be returned.
108
+     * @see cdata()
109
+     */
110
+    public function comment($text = null)
111
+    {
112
+        if (isset($text)) {
113
+            foreach ($this->qp->get() as $element) {
114
+                $comment = $element->ownerDocument->createComment($text);
115
+                $element->appendChild($comment);
116
+            }
117
+
118
+            return $this->qp;
119
+        }
120
+        foreach ($this->qp->get() as $ele) {
121
+            foreach ($ele->childNodes as $node) {
122
+                if ($node->nodeType == XML_COMMENT_NODE) {
123
+                    // Return first match.
124
+                    return $node->textContent;
125
+                }
126
+            }
127
+        }
128
+    }
129
+
130
+    /**
131
+     * Get or set a processor instruction.
132
+     */
133
+    public function pi($prefix = null, $text = null)
134
+    {
135
+        if (isset($text)) {
136
+            foreach ($this->qp->get() as $element) {
137
+                $comment = $element->ownerDocument->createProcessingInstruction($prefix, $text);
138
+                $element->appendChild($comment);
139
+            }
140
+
141
+            return $this->qp;
142
+        }
143
+        foreach ($this->qp->get() as $ele) {
144
+            foreach ($ele->childNodes as $node) {
145
+                if ($node->nodeType == XML_PI_NODE) {
146
+                    if (isset($prefix)) {
147
+                        if ($node->tagName == $prefix) {
148
+                            return $node->textContent;
149
+                        }
150
+                    } else {
151
+                        // Return first match.
152
+                        return $node->textContent;
153
+                    }
154
+                }
155
+            } // foreach
156
+        } // foreach
157
+    }
158
+
159
+    public function toXml()
160
+    {
161
+        return $this->qp->document()->saveXml();
162
+    }
163
+
164
+    /**
165
+     * Create a NIL element.
166
+     *
167
+     * @param string $text
168
+     * @param string $value
169
+     *
170
+     * @reval object $element
171
+     */
172
+    public function createNilElement($text, $value)
173
+    {
174
+        $value   = ($value) ? 'true' : 'false';
175
+        $element = $this->qp->createElement($text);
176
+        $element->attr('xsi:nil', $value);
177
+
178
+        return $element;
179
+    }
180
+
181
+    /**
182
+     * Create an element with the given namespace.
183
+     *
184
+     * @param string $text
185
+     * @param string $nsUri
186
+     *   The namespace URI for the given element.
187
+     *
188
+     * @return DOMQuery
189
+     */
190
+    public function createElement($text, $nsUri = null)
191
+    {
192
+        if (isset($text)) {
193
+            foreach ($this->qp->get() as $element) {
194
+                if ($nsUri === null && strpos($text, ':') !== false) {
195
+                    $ns    = array_shift(explode(':', $text));
196
+                    $nsUri = $element->ownerDocument->lookupNamespaceURI($ns);
197
+
198
+                    if ($nsUri === null) {
199
+                        throw new Exception("Undefined namespace for: " . $text);
200
+                    }
201
+                }
202
+
203
+                $node = null;
204
+                if ($nsUri !== null) {
205
+                    $node = $element->ownerDocument->createElementNS(
206
+                        $nsUri,
207
+                        $text
208
+                    );
209
+                } else {
210
+                    $node = $element->ownerDocument->createElement($text);
211
+                }
212
+
213
+                return QueryPath::with($node);
214
+            }
215
+        }
216
+
217
+        return new DOMQuery();
218
+    }
219
+
220
+    /**
221
+     * Append an element.
222
+     *
223
+     * @param string $text
224
+     *
225
+     * @return DOMQuery
226
+     */
227
+    public function appendElement($text)
228
+    {
229
+        if (isset($text)) {
230
+            foreach ($this->qp->get() as $element) {
231
+                $node = $this->qp->createElement($text);
232
+                QueryPath::with($element)->append($node);
233
+            }
234
+        }
235
+
236
+        return $this->qp;
237
+    }
238 238
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -36,7 +36,7 @@
 block discarded – undo
36 36
 	{
37 37
 		$doc = $this->qp->branch()->top()->get(0)->ownerDocument;
38 38
 
39
-		if (! $doc->schemaValidate($file)) {
39
+		if (!$doc->schemaValidate($file)) {
40 40
 			throw new Exception('Document did not validate against the schema.');
41 41
 		}
42 42
 	}
Please login to merge, or discard this patch.
src/Extension/QPXSL.php 2 patches
Indentation   +32 added lines, -32 removed lines patch added patch discarded remove patch
@@ -48,39 +48,39 @@
 block discarded – undo
48 48
 class QPXSL implements Extension
49 49
 {
50 50
 
51
-	protected $src;
51
+    protected $src;
52 52
 
53
-	public function __construct(Query $qp)
54
-	{
55
-		$this->src = $qp;
56
-	}
53
+    public function __construct(Query $qp)
54
+    {
55
+        $this->src = $qp;
56
+    }
57 57
 
58
-	/**
59
-	 * Given an XSLT stylesheet, run a transformation.
60
-	 *
61
-	 * This will attempt to read the provided stylesheet and then
62
-	 * execute it on the current source document.
63
-	 *
64
-	 * @param mixed $style
65
-	 *  This takes a QueryPath object or <em>any</em> of the types that the
66
-	 *  {@link qp()} function can take.
67
-	 *
68
-	 * @return QueryPath
69
-	 *  A QueryPath object wrapping the transformed document. Note that this is a
70
-	 *  <i>different</em> document than the original. As such, it has no history.
71
-	 *  You cannot call {@link QueryPath::end()} to undo a transformation. (However,
72
-	 *  the original source document will remain unchanged.)
73
-	 */
74
-	public function xslt($style)
75
-	{
76
-		if (! ($style instanceof QueryPath)) {
77
-			$style = QueryPath::with($style);
78
-		}
79
-		$sourceDoc = $this->src->top()->get(0)->ownerDocument;
80
-		$styleDoc  = $style->get(0)->ownerDocument;
81
-		$processor = new XSLTProcessor();
82
-		$processor->importStylesheet($styleDoc);
58
+    /**
59
+     * Given an XSLT stylesheet, run a transformation.
60
+     *
61
+     * This will attempt to read the provided stylesheet and then
62
+     * execute it on the current source document.
63
+     *
64
+     * @param mixed $style
65
+     *  This takes a QueryPath object or <em>any</em> of the types that the
66
+     *  {@link qp()} function can take.
67
+     *
68
+     * @return QueryPath
69
+     *  A QueryPath object wrapping the transformed document. Note that this is a
70
+     *  <i>different</em> document than the original. As such, it has no history.
71
+     *  You cannot call {@link QueryPath::end()} to undo a transformation. (However,
72
+     *  the original source document will remain unchanged.)
73
+     */
74
+    public function xslt($style)
75
+    {
76
+        if (! ($style instanceof QueryPath)) {
77
+            $style = QueryPath::with($style);
78
+        }
79
+        $sourceDoc = $this->src->top()->get(0)->ownerDocument;
80
+        $styleDoc  = $style->get(0)->ownerDocument;
81
+        $processor = new XSLTProcessor();
82
+        $processor->importStylesheet($styleDoc);
83 83
 
84
-		return QueryPath::with($processor->transformToDoc($sourceDoc));
85
-	}
84
+        return QueryPath::with($processor->transformToDoc($sourceDoc));
85
+    }
86 86
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -73,7 +73,7 @@
 block discarded – undo
73 73
 	 */
74 74
 	public function xslt($style)
75 75
 	{
76
-		if (! ($style instanceof QueryPath)) {
76
+		if (!($style instanceof QueryPath)) {
77 77
 			$style = QueryPath::with($style);
78 78
 		}
79 79
 		$sourceDoc = $this->src->top()->get(0)->ownerDocument;
Please login to merge, or discard this patch.
src/Extension/Format.php 2 patches
Indentation   +180 added lines, -180 removed lines patch added patch discarded remove patch
@@ -45,184 +45,184 @@
 block discarded – undo
45 45
  */
46 46
 class Format implements Extension
47 47
 {
48
-	protected $qp;
49
-
50
-	public function __construct(Query $qp)
51
-	{
52
-		$this->qp = $qp;
53
-	}
54
-
55
-	/**
56
-	 * Formats the text content of each selected element in the current DOMQuery object.
57
-	 *
58
-	 * Usage:
59
-	 * <code>
60
-	 * <?php
61
-	 * QueryPath::enable('Noi\QueryPath\FormatExtension');
62
-	 * $qp = qp('<?xml version="1.0"?><root><div>Apple</div><div>Orange</div></root>');
63
-	 *
64
-	 * $qp->find('div')->format('strtoupper');
65
-	 * $qp->find('div')->format(function ($text) {
66
-	 *     return '*' . $text . '*';
67
-	 * });
68
-	 *
69
-	 * $qp->writeXML();
70
-	 * </code>
71
-	 *
72
-	 * OUTPUT:
73
-	 * <code>
74
-	 * <?xml version="1.0"?>
75
-	 * <root>
76
-	 *   <div>*APPLE*</div>
77
-	 *   <div>*ORANGE*</div>
78
-	 * </root>
79
-	 * </code>
80
-	 *
81
-	 * @param callable $callback The callable to be called on every element.
82
-	 * @param mixed    $args     [optional] Zero or more parameters to be passed to the callback.
83
-	 * @param null     $additional
84
-	 *
85
-	 * @return DOMQuery The DOMQuery object with the same element(s) selected.
86
-	 * @throws Exception
87
-	 */
88
-	public function format($callback, $args = null, $additional = null): Query
89
-	{
90
-		if (isset($additional)) {
91
-			$args = func_get_args();
92
-			array_shift($args);
93
-		}
94
-
95
-		$getter = function ($qp) {
96
-			return $qp->text();
97
-		};
98
-
99
-		$setter = function ($qp, $value) {
100
-			$qp->text($value);
101
-		};
102
-
103
-		return $this->forAll($callback, $args, $getter, $setter);
104
-	}
105
-
106
-	/**
107
-	 * Formats the given attribute of each selected element in the current DOMQuery object.
108
-	 *
109
-	 * Usage:
110
-	 * <code>
111
-	 * QueryPath::enable('Noi\QueryPath\FormatExtension');
112
-	 * $qp = qp('<?xml version="1.0"?><root><item label="_apple_" total="12,345,678" /><item label="_orange_" total="987,654,321" /></root>');
113
-	 *
114
-	 * $qp->find('item')
115
-	 *     ->formatAttr('label', 'trim', '_')
116
-	 *     ->formatAttr('total', 'str_replace[2]', ',', '');
117
-	 *
118
-	 * $qp->find('item')->formatAttr('label', function ($value) {
119
-	 *     return ucfirst(strtolower($value));
120
-	 * });
121
-	 *
122
-	 * $qp->writeXML();
123
-	 * </code>
124
-	 *
125
-	 * OUTPUT:
126
-	 * <code>
127
-	 * <?xml version="1.0"?>
128
-	 * <root>
129
-	 *   <item label="Apple" total="12345678"/>
130
-	 *   <item label="Orange" total="987654321"/>
131
-	 * </root>
132
-	 * </code>
133
-	 *
134
-	 * @param string   $attrName The attribute name.
135
-	 * @param callable $callback The callable to be called on every element.
136
-	 * @param mixed    $args     [optional] Zero or more parameters to be passed to the callback.
137
-	 * @param null     $additional
138
-	 *
139
-	 * @return DOMQuery The DOMQuery object with the same element(s) selected.
140
-	 * @throws Exception
141
-	 */
142
-	public function formatAttr($attrName, $callback, $args = null, $additional = null): Query
143
-	{
144
-		if (isset($additional)) {
145
-			$args = array_slice(func_get_args(), 2);
146
-		}
147
-
148
-		$getter = function ($qp) use ($attrName) {
149
-			return $qp->attr($attrName);
150
-		};
151
-
152
-		$setter = function ($qp, $value) use ($attrName) {
153
-			return $qp->attr($attrName, $value);
154
-		};
155
-
156
-		return $this->forAll($callback, $args, $getter, $setter);
157
-	}
158
-
159
-	/**
160
-	 * @param $callback
161
-	 * @param $args
162
-	 * @param $getter
163
-	 * @param $setter
164
-	 *
165
-	 * @return Query
166
-	 * @throws Exception
167
-	 */
168
-	protected function forAll($callback, $args, $getter, $setter): Query
169
-	{
170
-		[$callback, $pos] = $this->prepareCallback($callback);
171
-		if (! is_callable($callback)) {
172
-			throw new Exception('Callback is not callable.');
173
-		}
174
-
175
-		$padded = $this->prepareArgs($args, $pos);
176
-		foreach ($this->qp as $qp) {
177
-			$padded[$pos] = $getter($qp);
178
-			$setter($qp, call_user_func_array($callback, $padded));
179
-		}
180
-
181
-		return $this->qp;
182
-	}
183
-
184
-	/**
185
-	 * @param $callback
186
-	 *
187
-	 * @return array
188
-	 */
189
-	protected function prepareCallback($callback)
190
-	{
191
-		if (is_string($callback)) {
192
-			[$callback, $trail] = $this->splitFunctionName($callback);
193
-			$pos = (int) $trail;
194
-		} elseif (is_array($callback) && isset($callback[2])) {
195
-			$pos      = $callback[2];
196
-			$callback = [$callback[0], $callback[1]];
197
-		} else {
198
-			$pos = 0;
199
-		}
200
-
201
-		return [$callback, $pos];
202
-	}
203
-
204
-	/**
205
-	 * @param string $string
206
-	 *
207
-	 * @return array[]|false|string[]
208
-	 */
209
-	protected function splitFunctionName(string $string)
210
-	{
211
-		// 'func_name:2', 'func_name@3', 'func_name[1]', ...
212
-		return preg_split('/[^a-zA-Z0-9_\x7f-\xff][^\d]*|$/', $string, 2);
213
-	}
214
-
215
-	/**
216
-	 * @param $args
217
-	 * @param $pos
218
-	 *
219
-	 * @return array
220
-	 */
221
-	protected function prepareArgs($args, $pos): array
222
-	{
223
-		$padded = array_pad((array) $args, (0 < $pos) ? $pos - 1 : 0, null);
224
-		array_splice($padded, $pos, 0, [null]); // insert null as a place holder
225
-
226
-		return $padded;
227
-	}
48
+    protected $qp;
49
+
50
+    public function __construct(Query $qp)
51
+    {
52
+        $this->qp = $qp;
53
+    }
54
+
55
+    /**
56
+     * Formats the text content of each selected element in the current DOMQuery object.
57
+     *
58
+     * Usage:
59
+     * <code>
60
+     * <?php
61
+     * QueryPath::enable('Noi\QueryPath\FormatExtension');
62
+     * $qp = qp('<?xml version="1.0"?><root><div>Apple</div><div>Orange</div></root>');
63
+     *
64
+     * $qp->find('div')->format('strtoupper');
65
+     * $qp->find('div')->format(function ($text) {
66
+     *     return '*' . $text . '*';
67
+     * });
68
+     *
69
+     * $qp->writeXML();
70
+     * </code>
71
+     *
72
+     * OUTPUT:
73
+     * <code>
74
+     * <?xml version="1.0"?>
75
+     * <root>
76
+     *   <div>*APPLE*</div>
77
+     *   <div>*ORANGE*</div>
78
+     * </root>
79
+     * </code>
80
+     *
81
+     * @param callable $callback The callable to be called on every element.
82
+     * @param mixed    $args     [optional] Zero or more parameters to be passed to the callback.
83
+     * @param null     $additional
84
+     *
85
+     * @return DOMQuery The DOMQuery object with the same element(s) selected.
86
+     * @throws Exception
87
+     */
88
+    public function format($callback, $args = null, $additional = null): Query
89
+    {
90
+        if (isset($additional)) {
91
+            $args = func_get_args();
92
+            array_shift($args);
93
+        }
94
+
95
+        $getter = function ($qp) {
96
+            return $qp->text();
97
+        };
98
+
99
+        $setter = function ($qp, $value) {
100
+            $qp->text($value);
101
+        };
102
+
103
+        return $this->forAll($callback, $args, $getter, $setter);
104
+    }
105
+
106
+    /**
107
+     * Formats the given attribute of each selected element in the current DOMQuery object.
108
+     *
109
+     * Usage:
110
+     * <code>
111
+     * QueryPath::enable('Noi\QueryPath\FormatExtension');
112
+     * $qp = qp('<?xml version="1.0"?><root><item label="_apple_" total="12,345,678" /><item label="_orange_" total="987,654,321" /></root>');
113
+     *
114
+     * $qp->find('item')
115
+     *     ->formatAttr('label', 'trim', '_')
116
+     *     ->formatAttr('total', 'str_replace[2]', ',', '');
117
+     *
118
+     * $qp->find('item')->formatAttr('label', function ($value) {
119
+     *     return ucfirst(strtolower($value));
120
+     * });
121
+     *
122
+     * $qp->writeXML();
123
+     * </code>
124
+     *
125
+     * OUTPUT:
126
+     * <code>
127
+     * <?xml version="1.0"?>
128
+     * <root>
129
+     *   <item label="Apple" total="12345678"/>
130
+     *   <item label="Orange" total="987654321"/>
131
+     * </root>
132
+     * </code>
133
+     *
134
+     * @param string   $attrName The attribute name.
135
+     * @param callable $callback The callable to be called on every element.
136
+     * @param mixed    $args     [optional] Zero or more parameters to be passed to the callback.
137
+     * @param null     $additional
138
+     *
139
+     * @return DOMQuery The DOMQuery object with the same element(s) selected.
140
+     * @throws Exception
141
+     */
142
+    public function formatAttr($attrName, $callback, $args = null, $additional = null): Query
143
+    {
144
+        if (isset($additional)) {
145
+            $args = array_slice(func_get_args(), 2);
146
+        }
147
+
148
+        $getter = function ($qp) use ($attrName) {
149
+            return $qp->attr($attrName);
150
+        };
151
+
152
+        $setter = function ($qp, $value) use ($attrName) {
153
+            return $qp->attr($attrName, $value);
154
+        };
155
+
156
+        return $this->forAll($callback, $args, $getter, $setter);
157
+    }
158
+
159
+    /**
160
+     * @param $callback
161
+     * @param $args
162
+     * @param $getter
163
+     * @param $setter
164
+     *
165
+     * @return Query
166
+     * @throws Exception
167
+     */
168
+    protected function forAll($callback, $args, $getter, $setter): Query
169
+    {
170
+        [$callback, $pos] = $this->prepareCallback($callback);
171
+        if (! is_callable($callback)) {
172
+            throw new Exception('Callback is not callable.');
173
+        }
174
+
175
+        $padded = $this->prepareArgs($args, $pos);
176
+        foreach ($this->qp as $qp) {
177
+            $padded[$pos] = $getter($qp);
178
+            $setter($qp, call_user_func_array($callback, $padded));
179
+        }
180
+
181
+        return $this->qp;
182
+    }
183
+
184
+    /**
185
+     * @param $callback
186
+     *
187
+     * @return array
188
+     */
189
+    protected function prepareCallback($callback)
190
+    {
191
+        if (is_string($callback)) {
192
+            [$callback, $trail] = $this->splitFunctionName($callback);
193
+            $pos = (int) $trail;
194
+        } elseif (is_array($callback) && isset($callback[2])) {
195
+            $pos      = $callback[2];
196
+            $callback = [$callback[0], $callback[1]];
197
+        } else {
198
+            $pos = 0;
199
+        }
200
+
201
+        return [$callback, $pos];
202
+    }
203
+
204
+    /**
205
+     * @param string $string
206
+     *
207
+     * @return array[]|false|string[]
208
+     */
209
+    protected function splitFunctionName(string $string)
210
+    {
211
+        // 'func_name:2', 'func_name@3', 'func_name[1]', ...
212
+        return preg_split('/[^a-zA-Z0-9_\x7f-\xff][^\d]*|$/', $string, 2);
213
+    }
214
+
215
+    /**
216
+     * @param $args
217
+     * @param $pos
218
+     *
219
+     * @return array
220
+     */
221
+    protected function prepareArgs($args, $pos): array
222
+    {
223
+        $padded = array_pad((array) $args, (0 < $pos) ? $pos - 1 : 0, null);
224
+        array_splice($padded, $pos, 0, [null]); // insert null as a place holder
225
+
226
+        return $padded;
227
+    }
228 228
 }
Please login to merge, or discard this patch.
Spacing   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -92,11 +92,11 @@  discard block
 block discarded – undo
92 92
 			array_shift($args);
93 93
 		}
94 94
 
95
-		$getter = function ($qp) {
95
+		$getter = function($qp) {
96 96
 			return $qp->text();
97 97
 		};
98 98
 
99
-		$setter = function ($qp, $value) {
99
+		$setter = function($qp, $value) {
100 100
 			$qp->text($value);
101 101
 		};
102 102
 
@@ -145,11 +145,11 @@  discard block
 block discarded – undo
145 145
 			$args = array_slice(func_get_args(), 2);
146 146
 		}
147 147
 
148
-		$getter = function ($qp) use ($attrName) {
148
+		$getter = function($qp) use ($attrName) {
149 149
 			return $qp->attr($attrName);
150 150
 		};
151 151
 
152
-		$setter = function ($qp, $value) use ($attrName) {
152
+		$setter = function($qp, $value) use ($attrName) {
153 153
 			return $qp->attr($attrName, $value);
154 154
 		};
155 155
 
@@ -168,7 +168,7 @@  discard block
 block discarded – undo
168 168
 	protected function forAll($callback, $args, $getter, $setter): Query
169 169
 	{
170 170
 		[$callback, $pos] = $this->prepareCallback($callback);
171
-		if (! is_callable($callback)) {
171
+		if (!is_callable($callback)) {
172 172
 			throw new Exception('Callback is not callable.');
173 173
 		}
174 174
 
@@ -190,7 +190,7 @@  discard block
 block discarded – undo
190 190
 	{
191 191
 		if (is_string($callback)) {
192 192
 			[$callback, $trail] = $this->splitFunctionName($callback);
193
-			$pos = (int) $trail;
193
+			$pos = (int)$trail;
194 194
 		} elseif (is_array($callback) && isset($callback[2])) {
195 195
 			$pos      = $callback[2];
196 196
 			$callback = [$callback[0], $callback[1]];
@@ -220,7 +220,7 @@  discard block
 block discarded – undo
220 220
 	 */
221 221
 	protected function prepareArgs($args, $pos): array
222 222
 	{
223
-		$padded = array_pad((array) $args, (0 < $pos) ? $pos - 1 : 0, null);
223
+		$padded = array_pad((array)$args, (0 < $pos) ? $pos - 1 : 0, null);
224 224
 		array_splice($padded, $pos, 0, [null]); // insert null as a place holder
225 225
 
226 226
 		return $padded;
Please login to merge, or discard this patch.
src/Entities.php 1 patch
Indentation   +72 added lines, -72 removed lines patch added patch discarded remove patch
@@ -14,81 +14,81 @@
 block discarded – undo
14 14
 class Entities implements EntitiesContract
15 15
 {
16 16
 
17
-	/**
18
-	 * This is three regexes wrapped into 1. The | divides them.
19
-	 * 1: Match any char-based entity. This will go in $matches[1]
20
-	 * 2: Match any num-based entity. This will go in $matches[2]
21
-	 * 3: Match any hex-based entry. This will go in $matches[3]
22
-	 * 4: Match any ampersand that is not an entity. This goes in $matches[4]
23
-	 *    This last rule will only match if one of the previous two has not already
24
-	 *    matched.
25
-	 * XXX: Are octal encodings for entities acceptable?
26
-	 */
27
-	//protected static $regex = '/&([\w]+);|&#([\d]+);|&([\w]*[\s$]+)/m';
28
-	protected static $regex = '/&([\w]+);|&#([\d]+);|&#(x[0-9a-fA-F]+);|(&)/m';
17
+    /**
18
+     * This is three regexes wrapped into 1. The | divides them.
19
+     * 1: Match any char-based entity. This will go in $matches[1]
20
+     * 2: Match any num-based entity. This will go in $matches[2]
21
+     * 3: Match any hex-based entry. This will go in $matches[3]
22
+     * 4: Match any ampersand that is not an entity. This goes in $matches[4]
23
+     *    This last rule will only match if one of the previous two has not already
24
+     *    matched.
25
+     * XXX: Are octal encodings for entities acceptable?
26
+     */
27
+    //protected static $regex = '/&([\w]+);|&#([\d]+);|&([\w]*[\s$]+)/m';
28
+    protected static $regex = '/&([\w]+);|&#([\d]+);|&#(x[0-9a-fA-F]+);|(&)/m';
29 29
 
30
-	/**
31
-	 * Replace all entities.
32
-	 * This will scan a string and will attempt to replace all
33
-	 * entities with their numeric equivalent. This will not work
34
-	 * with specialized entities.
35
-	 *
36
-	 * @param string $string
37
-	 *  The string to perform replacements on.
38
-	 *
39
-	 * @return string
40
-	 *  Returns a string that is similar to the original one, but with
41
-	 *  all entity replacements made.
42
-	 */
43
-	public static function replaceAllEntities(string $string): string
44
-	{
45
-		return preg_replace_callback(self::$regex, '\QueryPath\Entities::doReplacement', $string);
46
-	}
30
+    /**
31
+     * Replace all entities.
32
+     * This will scan a string and will attempt to replace all
33
+     * entities with their numeric equivalent. This will not work
34
+     * with specialized entities.
35
+     *
36
+     * @param string $string
37
+     *  The string to perform replacements on.
38
+     *
39
+     * @return string
40
+     *  Returns a string that is similar to the original one, but with
41
+     *  all entity replacements made.
42
+     */
43
+    public static function replaceAllEntities(string $string): string
44
+    {
45
+        return preg_replace_callback(self::$regex, '\QueryPath\Entities::doReplacement', $string);
46
+    }
47 47
 
48
-	/**
49
-	 * Callback for processing replacements.
50
-	 *
51
-	 * @param array $matches
52
-	 *  The regular expression replacement array.
53
-	 *
54
-	 * @return string
55
-	 */
56
-	protected static function doReplacement($matches): string
57
-	{
58
-		// See how the regex above works out.
48
+    /**
49
+     * Callback for processing replacements.
50
+     *
51
+     * @param array $matches
52
+     *  The regular expression replacement array.
53
+     *
54
+     * @return string
55
+     */
56
+    protected static function doReplacement($matches): string
57
+    {
58
+        // See how the regex above works out.
59 59
 
60
-		// From count, we can tell whether we got a
61
-		// char, num, or bare ampersand.
62
-		$count = count($matches);
63
-		switch ($count) {
64
-			case 2:
65
-				// We have a character entity
66
-				return '&#' . self::replaceEntity($matches[1]) . ';';
67
-			case 3:
68
-			case 4:
69
-				// we have a numeric entity
70
-				return '&#' . $matches[$count - 1] . ';';
71
-			case 5:
72
-				// We have an unescaped ampersand.
73
-				return '&#38;';
74
-		}
60
+        // From count, we can tell whether we got a
61
+        // char, num, or bare ampersand.
62
+        $count = count($matches);
63
+        switch ($count) {
64
+            case 2:
65
+                // We have a character entity
66
+                return '&#' . self::replaceEntity($matches[1]) . ';';
67
+            case 3:
68
+            case 4:
69
+                // we have a numeric entity
70
+                return '&#' . $matches[$count - 1] . ';';
71
+            case 5:
72
+                // We have an unescaped ampersand.
73
+                return '&#38;';
74
+        }
75 75
 
76
-		return '';
77
-	}
76
+        return '';
77
+    }
78 78
 
79
-	/**
80
-	 * Lookup an entity string's numeric equivalent.
81
-	 *
82
-	 * @param string $entity
83
-	 *  The entity whose numeric value is needed.
84
-	 *
85
-	 * @return int
86
-	 *  The integer value corresponding to the entity.
87
-	 * @author Matt Butcher
88
-	 * @author Ryan Mahoney
89
-	 */
90
-	public static function replaceEntity(string $entity): int
91
-	{
92
-		return self::ENTITIES[$entity];
93
-	}
79
+    /**
80
+     * Lookup an entity string's numeric equivalent.
81
+     *
82
+     * @param string $entity
83
+     *  The entity whose numeric value is needed.
84
+     *
85
+     * @return int
86
+     *  The integer value corresponding to the entity.
87
+     * @author Matt Butcher
88
+     * @author Ryan Mahoney
89
+     */
90
+    public static function replaceEntity(string $entity): int
91
+    {
92
+        return self::ENTITIES[$entity];
93
+    }
94 94
 }
Please login to merge, or discard this patch.
src/CSS/EventHandler.php 1 patch
Indentation   +149 added lines, -149 removed lines patch added patch discarded remove patch
@@ -43,153 +43,153 @@
 block discarded – undo
43 43
  */
44 44
 interface EventHandler
45 45
 {
46
-	/** The is-exactly (=) operator. */
47
-	public const IS_EXACTLY = 0; // =
48
-	/** The contains-with-space operator (~=). */
49
-	public const CONTAINS_WITH_SPACE = 1; // ~=
50
-	/** The contains-with-hyphen operator (!=). */
51
-	public const CONTAINS_WITH_HYPHEN = 2; // |=
52
-	/** The contains-in-string operator (*=). */
53
-	public const CONTAINS_IN_STRING = 3; // *=
54
-	/** The begins-with operator (^=). */
55
-	public const BEGINS_WITH = 4; // ^=
56
-	/** The ends-with operator ($=). */
57
-	public const ENDS_WITH = 5; // $=
58
-	/** The any-element operator (*). */
59
-	public const ANY_ELEMENT = '*';
60
-
61
-	/**
62
-	 * This event is fired when a CSS ID is encountered.
63
-	 * An ID begins with an octothorp: #name.
64
-	 *
65
-	 * @param string $id
66
-	 *  The ID passed in.
67
-	 */
68
-	public function elementID($id); // #name
69
-
70
-	/**
71
-	 * Handle an element name.
72
-	 * Example: name
73
-	 *
74
-	 * @param string $name
75
-	 *  The name of the element.
76
-	 */
77
-	public function element($name); // name
78
-
79
-	/**
80
-	 * Handle a namespaced element name.
81
-	 * example: namespace|name
82
-	 *
83
-	 * @param string $name
84
-	 *  The tag name.
85
-	 * @param string $namespace
86
-	 *  The namespace identifier (Not the URI)
87
-	 */
88
-	public function elementNS($name, $namespace = null);
89
-
90
-	/**
91
-	 * Handle an any-element (*) operator.
92
-	 * Example: *
93
-	 */
94
-	public function anyElement(); // *
95
-
96
-	/**
97
-	 * Handle an any-element operator that is constrained to a namespace.
98
-	 * Example: ns|*
99
-	 *
100
-	 * @param string $ns
101
-	 *  The namespace identifier (not the URI).
102
-	 */
103
-	public function anyElementInNS($ns); // ns|*
104
-
105
-	/**
106
-	 * Handle a CSS class selector.
107
-	 * Example: .name
108
-	 *
109
-	 * @param string $name
110
-	 *  The name of the class.
111
-	 */
112
-	public function elementClass($name); // .name
113
-
114
-	/**
115
-	 * Handle an attribute selector.
116
-	 * Example: [name=attr]
117
-	 * Example: [name~=attr]
118
-	 *
119
-	 * @param string $name
120
-	 *  The attribute name.
121
-	 * @param string $value
122
-	 *  The value of the attribute, if given.
123
-	 * @param int    $operation
124
-	 *  The operation to be used for matching. See {@link EventHandler}
125
-	 *  constants for a list of supported operations.
126
-	 */
127
-	public function attribute($name, $value = null, $operation = EventHandler::IS_EXACTLY); // [name=attr]
128
-
129
-	/**
130
-	 * Handle an attribute selector bound to a specific namespace.
131
-	 * Example: [ns|name=attr]
132
-	 * Example: [ns|name~=attr]
133
-	 *
134
-	 * @param string $name
135
-	 *  The attribute name.
136
-	 * @param string $ns
137
-	 *  The namespace identifier (not the URI).
138
-	 * @param string $value
139
-	 *  The value of the attribute, if given.
140
-	 * @param int    $operation
141
-	 *  The operation to be used for matching. See {@link EventHandler}
142
-	 *  constants for a list of supported operations.
143
-	 */
144
-	public function attributeNS($name, $ns, $value = null, $operation = EventHandler::IS_EXACTLY);
145
-
146
-	/**
147
-	 * Handle a pseudo-class.
148
-	 * Example: :name(value)
149
-	 *
150
-	 * @param string $name
151
-	 *  The pseudo-class name.
152
-	 * @param string $value
153
-	 *  The value, if one is found.
154
-	 */
155
-	public function pseudoClass($name, $value = null); //:name(value)
156
-
157
-	/**
158
-	 * Handle a pseudo-element.
159
-	 * Example: ::name
160
-	 *
161
-	 * @param string $name
162
-	 *  The pseudo-element name.
163
-	 */
164
-	public function pseudoElement($name); // ::name
165
-
166
-	/**
167
-	 * Handle a direct descendant combinator.
168
-	 * Example: >
169
-	 */
170
-	public function directDescendant(); // >
171
-
172
-	/**
173
-	 * Handle a adjacent combinator.
174
-	 * Example: +
175
-	 */
176
-	public function adjacent(); // +
177
-
178
-	/**
179
-	 * Handle an another-selector combinator.
180
-	 * Example: ,
181
-	 */
182
-	public function anotherSelector(); // ,
183
-
184
-	/**
185
-	 * Handle a sibling combinator.
186
-	 * Example: ~
187
-	 */
188
-	public function sibling(); // ~ combinator
189
-
190
-	/**
191
-	 * Handle an any-descendant combinator.
192
-	 * Example: ' '
193
-	 */
194
-	public function anyDescendant(); // ' ' (space) operator.
46
+    /** The is-exactly (=) operator. */
47
+    public const IS_EXACTLY = 0; // =
48
+    /** The contains-with-space operator (~=). */
49
+    public const CONTAINS_WITH_SPACE = 1; // ~=
50
+    /** The contains-with-hyphen operator (!=). */
51
+    public const CONTAINS_WITH_HYPHEN = 2; // |=
52
+    /** The contains-in-string operator (*=). */
53
+    public const CONTAINS_IN_STRING = 3; // *=
54
+    /** The begins-with operator (^=). */
55
+    public const BEGINS_WITH = 4; // ^=
56
+    /** The ends-with operator ($=). */
57
+    public const ENDS_WITH = 5; // $=
58
+    /** The any-element operator (*). */
59
+    public const ANY_ELEMENT = '*';
60
+
61
+    /**
62
+     * This event is fired when a CSS ID is encountered.
63
+     * An ID begins with an octothorp: #name.
64
+     *
65
+     * @param string $id
66
+     *  The ID passed in.
67
+     */
68
+    public function elementID($id); // #name
69
+
70
+    /**
71
+     * Handle an element name.
72
+     * Example: name
73
+     *
74
+     * @param string $name
75
+     *  The name of the element.
76
+     */
77
+    public function element($name); // name
78
+
79
+    /**
80
+     * Handle a namespaced element name.
81
+     * example: namespace|name
82
+     *
83
+     * @param string $name
84
+     *  The tag name.
85
+     * @param string $namespace
86
+     *  The namespace identifier (Not the URI)
87
+     */
88
+    public function elementNS($name, $namespace = null);
89
+
90
+    /**
91
+     * Handle an any-element (*) operator.
92
+     * Example: *
93
+     */
94
+    public function anyElement(); // *
95
+
96
+    /**
97
+     * Handle an any-element operator that is constrained to a namespace.
98
+     * Example: ns|*
99
+     *
100
+     * @param string $ns
101
+     *  The namespace identifier (not the URI).
102
+     */
103
+    public function anyElementInNS($ns); // ns|*
104
+
105
+    /**
106
+     * Handle a CSS class selector.
107
+     * Example: .name
108
+     *
109
+     * @param string $name
110
+     *  The name of the class.
111
+     */
112
+    public function elementClass($name); // .name
113
+
114
+    /**
115
+     * Handle an attribute selector.
116
+     * Example: [name=attr]
117
+     * Example: [name~=attr]
118
+     *
119
+     * @param string $name
120
+     *  The attribute name.
121
+     * @param string $value
122
+     *  The value of the attribute, if given.
123
+     * @param int    $operation
124
+     *  The operation to be used for matching. See {@link EventHandler}
125
+     *  constants for a list of supported operations.
126
+     */
127
+    public function attribute($name, $value = null, $operation = EventHandler::IS_EXACTLY); // [name=attr]
128
+
129
+    /**
130
+     * Handle an attribute selector bound to a specific namespace.
131
+     * Example: [ns|name=attr]
132
+     * Example: [ns|name~=attr]
133
+     *
134
+     * @param string $name
135
+     *  The attribute name.
136
+     * @param string $ns
137
+     *  The namespace identifier (not the URI).
138
+     * @param string $value
139
+     *  The value of the attribute, if given.
140
+     * @param int    $operation
141
+     *  The operation to be used for matching. See {@link EventHandler}
142
+     *  constants for a list of supported operations.
143
+     */
144
+    public function attributeNS($name, $ns, $value = null, $operation = EventHandler::IS_EXACTLY);
145
+
146
+    /**
147
+     * Handle a pseudo-class.
148
+     * Example: :name(value)
149
+     *
150
+     * @param string $name
151
+     *  The pseudo-class name.
152
+     * @param string $value
153
+     *  The value, if one is found.
154
+     */
155
+    public function pseudoClass($name, $value = null); //:name(value)
156
+
157
+    /**
158
+     * Handle a pseudo-element.
159
+     * Example: ::name
160
+     *
161
+     * @param string $name
162
+     *  The pseudo-element name.
163
+     */
164
+    public function pseudoElement($name); // ::name
165
+
166
+    /**
167
+     * Handle a direct descendant combinator.
168
+     * Example: >
169
+     */
170
+    public function directDescendant(); // >
171
+
172
+    /**
173
+     * Handle a adjacent combinator.
174
+     * Example: +
175
+     */
176
+    public function adjacent(); // +
177
+
178
+    /**
179
+     * Handle an another-selector combinator.
180
+     * Example: ,
181
+     */
182
+    public function anotherSelector(); // ,
183
+
184
+    /**
185
+     * Handle a sibling combinator.
186
+     * Example: ~
187
+     */
188
+    public function sibling(); // ~ combinator
189
+
190
+    /**
191
+     * Handle an any-descendant combinator.
192
+     * Example: ' '
193
+     */
194
+    public function anyDescendant(); // ' ' (space) operator.
195 195
 }
Please login to merge, or discard this patch.
src/CSS/DOMTraverser/PseudoClass.php 2 patches
Indentation   +453 added lines, -453 removed lines patch added patch discarded remove patch
@@ -25,457 +25,457 @@
 block discarded – undo
25 25
 class PseudoClass
26 26
 {
27 27
 
28
-	/**
29
-	 * Tests whether the given element matches the given pseudoclass.
30
-	 *
31
-	 * @param string   $pseudoclass
32
-	 *   The string name of the pseudoclass
33
-	 * @param resource $node
34
-	 *   The DOMNode to be tested.
35
-	 * @param resource $scope
36
-	 *   The DOMElement that is the active root for this node.
37
-	 * @param mixed    $value
38
-	 *   The optional value string provided with this class. This is
39
-	 *   used, for example, in an+b psuedoclasses.
40
-	 *
41
-	 * @return bool
42
-	 * @throws NotImplementedException
43
-	 * @throws ParseException
44
-	 * @retval boolean
45
-	 *   TRUE if the node matches, FALSE otherwise.
46
-	 */
47
-	public function elementMatches($pseudoclass, $node, $scope, $value = null)
48
-	{
49
-		$name = strtolower($pseudoclass);
50
-		// Need to handle known pseudoclasses.
51
-		switch ($name) {
52
-			case 'current':
53
-			case 'past':
54
-			case 'future':
55
-			case 'visited':
56
-			case 'hover':
57
-			case 'active':
58
-			case 'focus':
59
-			case 'animated': //  Last 3 are from jQuery
60
-			case 'visible':
61
-			case 'hidden':
62
-				// These require a UA, which we don't have.
63
-			case 'valid':
64
-			case 'invalid':
65
-			case 'required':
66
-			case 'optional':
67
-			case 'read-only':
68
-			case 'read-write':
69
-				// Since we don't know how to validate elements,
70
-				// we can't supply these.
71
-			case 'dir':
72
-				// FIXME: I don't know how to get directionality info.
73
-			case 'nth-column':
74
-			case 'nth-last-column':
75
-				// We don't know what a column is in most documents.
76
-				// FIXME: Can we do this for HTML?
77
-			case 'target':
78
-				// This requires a location URL, which we don't have.
79
-				return false;
80
-			case 'indeterminate':
81
-				// Because sometimes screwing with people is fun.
82
-				return (boolean) mt_rand(0, 1);
83
-			case 'lang':
84
-				// No value = exception.
85
-				if (! isset($value)) {
86
-					throw new NotImplementedException(':lang() requires a value.');
87
-				}
88
-
89
-				return $this->lang($node, $value);
90
-			case 'any-link':
91
-				return Util::matchesAttribute($node, 'href')
92
-					   || Util::matchesAttribute($node, 'src')
93
-					   || Util::matchesAttribute($node, 'link');
94
-			case 'link':
95
-				return Util::matchesAttribute($node, 'href');
96
-			case 'local-link':
97
-				return $this->isLocalLink($node);
98
-			case 'root':
99
-				return $node->isSameNode($node->ownerDocument->documentElement);
100
-
101
-			// CSS 4 declares the :scope pseudo-class, which describes what was
102
-			// the :x-root QueryPath extension.
103
-			case 'x-root':
104
-			case 'x-reset':
105
-			case 'scope':
106
-				return $node->isSameNode($scope);
107
-			// NON-STANDARD extensions for simple support of even and odd. These
108
-			// are supported by jQuery, FF, and other user agents.
109
-			case 'even':
110
-				return $this->isNthChild($node, 'even');
111
-			case 'odd':
112
-				return $this->isNthChild($node, 'odd');
113
-			case 'nth-child':
114
-				return $this->isNthChild($node, $value);
115
-			case 'nth-last-child':
116
-				return $this->isNthChild($node, $value, true);
117
-			case 'nth-of-type':
118
-				return $this->isNthChild($node, $value, false, true);
119
-			case 'nth-last-of-type':
120
-				return $this->isNthChild($node, $value, true, true);
121
-			case 'first-of-type':
122
-				return $this->isFirstOfType($node);
123
-			case 'last-of-type':
124
-				return $this->isLastOfType($node);
125
-			case 'only-of-type':
126
-				return $this->isFirstOfType($node) && $this->isLastOfType($node);
127
-
128
-			// Additional pseudo-classes defined in jQuery:
129
-			case 'lt':
130
-				// I'm treating this as "less than or equal to".
131
-				$rule = sprintf('-n + %d', (int) $value);
132
-
133
-				// $rule = '-n+15';
134
-				return $this->isNthChild($node, $rule);
135
-			case 'gt':
136
-				// I'm treating this as "greater than"
137
-				// return $this->nodePositionFromEnd($node) > (int) $value;
138
-				return $this->nodePositionFromStart($node) > (int) $value;
139
-			case 'nth':
140
-			case 'eq':
141
-				$rule = (int) $value;
142
-
143
-				return $this->isNthChild($node, $rule);
144
-			case 'first':
145
-				return $this->isNthChild($node, 1);
146
-			case 'first-child':
147
-				return $this->isFirst($node);
148
-			case 'last':
149
-			case 'last-child':
150
-				return $this->isLast($node);
151
-			case 'only-child':
152
-				return $this->isFirst($node) && $this->isLast($node);
153
-			case 'empty':
154
-				return $this->isEmpty($node);
155
-			case 'parent':
156
-				return ! $this->isEmpty($node);
157
-
158
-			case 'enabled':
159
-			case 'disabled':
160
-			case 'checked':
161
-				return Util::matchesAttribute($node, $name);
162
-			case 'text':
163
-			case 'radio':
164
-			case 'checkbox':
165
-			case 'file':
166
-			case 'password':
167
-			case 'submit':
168
-			case 'image':
169
-			case 'reset':
170
-			case 'button':
171
-				return Util::matchesAttribute($node, 'type', $name);
172
-
173
-			case 'header':
174
-				return $this->header($node);
175
-			case 'has':
176
-			case 'matches':
177
-				return $this->has($node, $value);
178
-			break;
179
-			case 'not':
180
-				if (empty($value)) {
181
-					throw new ParseException(':not() requires a value.');
182
-				}
183
-
184
-				return $this->isNot($node, $value);
185
-			// Contains == text matches.
186
-			// In QP 2.1, this was changed.
187
-			case 'contains':
188
-				return $this->contains($node, $value);
189
-			// Since QP 2.1
190
-			case 'contains-exactly':
191
-				return $this->containsExactly($node, $value);
192
-			default:
193
-				throw new ParseException('Unknown Pseudo-Class: ' . $name);
194
-		}
195
-	}
196
-
197
-	/**
198
-	 * Pseudo-class handler for :lang
199
-	 *
200
-	 * Note that this does not implement the spec in its entirety because we do
201
-	 * not presume to "know the language" of the document. If anyone is interested
202
-	 * in making this more intelligent, please do so.
203
-	 */
204
-	protected function lang($node, $value)
205
-	{
206
-		// TODO: This checks for cases where an explicit language is
207
-		// set. The spec seems to indicate that an element should inherit
208
-		// language from the parent... but this is unclear.
209
-		$operator = (strpos($value, '-') !== false) ? EventHandler::IS_EXACTLY : EventHandler::CONTAINS_WITH_HYPHEN;
210
-
211
-		$match = true;
212
-		foreach ($node->attributes as $attrNode) {
213
-			if ($attrNode->localName === 'lang') {
214
-				if ($attrNode->nodeName === $attrNode->localName) {
215
-					// fprintf(STDOUT, "%s in NS %s\n", $attrNode->name, $attrNode->nodeName);
216
-					return Util::matchesAttribute($node, 'lang', $value, $operator);
217
-				}
218
-
219
-				$nsuri = $attrNode->namespaceURI;
220
-
221
-				// fprintf(STDOUT, "%s in NS %s\n", $attrNode->name, $nsuri);
222
-				return Util::matchesAttributeNS($node, 'lang', $nsuri, $value, $operator);
223
-			}
224
-		}
225
-
226
-		return false;
227
-	}
228
-
229
-	/**
230
-	 * Provides jQuery pseudoclass ':header'.
231
-	 *
232
-	 * @param $node
233
-	 *
234
-	 * @return bool
235
-	 */
236
-	protected function header($node): bool
237
-	{
238
-		return preg_match('/^h[1-9]$/i', $node->tagName) === 1;
239
-	}
240
-
241
-	/**
242
-	 * Provides pseudoclass :empty.
243
-	 */
244
-	protected function isEmpty($node): bool
245
-	{
246
-		foreach ($node->childNodes as $kid) {
247
-			// We don't want to count PIs and comments. From the spec, it
248
-			// appears that CDATA is also not counted.
249
-			if ($kid->nodeType === XML_ELEMENT_NODE || $kid->nodeType === XML_TEXT_NODE) {
250
-				// As soon as we hit a FALSE, return.
251
-				return false;
252
-			}
253
-		}
254
-
255
-		return true;
256
-	}
257
-
258
-	/**
259
-	 * Provides jQuery pseudoclass :first.
260
-	 *
261
-	 * @todo
262
-	 *   This can be replaced by isNthChild().
263
-	 */
264
-	protected function isFirst($node): bool
265
-	{
266
-		while (isset($node->previousSibling)) {
267
-			$node = $node->previousSibling;
268
-			if ($node->nodeType === XML_ELEMENT_NODE) {
269
-				return false;
270
-			}
271
-		}
272
-
273
-		return true;
274
-	}
275
-
276
-	/**
277
-	 * Fast version of first-of-type.
278
-	 */
279
-	protected function isFirstOfType($node)
280
-	{
281
-		$type = $node->tagName;
282
-		while (isset($node->previousSibling)) {
283
-			$node = $node->previousSibling;
284
-			if ($node->nodeType === XML_ELEMENT_NODE && $node->tagName === $type) {
285
-				return false;
286
-			}
287
-		}
288
-
289
-		return true;
290
-	}
291
-
292
-	/**
293
-	 * Fast version of jQuery :last.
294
-	 */
295
-	protected function isLast($node)
296
-	{
297
-		while (isset($node->nextSibling)) {
298
-			$node = $node->nextSibling;
299
-			if ($node->nodeType === XML_ELEMENT_NODE) {
300
-				return false;
301
-			}
302
-		}
303
-
304
-		return true;
305
-	}
306
-
307
-	/**
308
-	 * Provides last-of-type.
309
-	 */
310
-	protected function isLastOfType($node)
311
-	{
312
-		$type = $node->tagName;
313
-		while (isset($node->nextSibling)) {
314
-			$node = $node->nextSibling;
315
-			if ($node->nodeType === XML_ELEMENT_NODE && $node->tagName === $type) {
316
-				return false;
317
-			}
318
-		}
319
-
320
-		return true;
321
-	}
322
-
323
-	/**
324
-	 * Provides :contains() as the original spec called for.
325
-	 *
326
-	 * This is an INEXACT match.
327
-	 */
328
-	protected function contains($node, $value): bool
329
-	{
330
-		$text  = $node->textContent;
331
-		$value = Util::removeQuotes($value);
332
-
333
-		return isset($text) && (stripos($text, $value) !== false);
334
-	}
335
-
336
-	/**
337
-	 * Provides :contains-exactly QueryPath pseudoclass.
338
-	 *
339
-	 * This is an EXACT match.
340
-	 */
341
-	protected function containsExactly($node, $value): bool
342
-	{
343
-		$text  = $node->textContent;
344
-		$value = Util::removeQuotes($value);
345
-
346
-		return isset($text) && $text == $value;
347
-	}
348
-
349
-	/**
350
-	 * Provides :has pseudoclass.
351
-	 *
352
-	 * @throws ParseException
353
-	 */
354
-	protected function has($node, $selector): bool
355
-	{
356
-		$splos = new SPLObjectStorage();
357
-		$splos->attach($node);
358
-		$traverser = new DOMTraverser($splos, true);
359
-		$results   = $traverser->find($selector)->matches();
360
-
361
-		return count($results) > 0;
362
-	}
363
-
364
-	/**
365
-	 * Provides :not pseudoclass.
366
-	 *
367
-	 * @throws ParseException
368
-	 */
369
-	protected function isNot($node, $selector): bool
370
-	{
371
-		return ! $this->has($node, $selector);
372
-	}
373
-
374
-	/**
375
-	 * Get the relative position of a node in its sibling set.
376
-	 */
377
-	protected function nodePositionFromStart($node, $byType = false): int
378
-	{
379
-		$i   = 1;
380
-		$tag = $node->tagName;
381
-		while (isset($node->previousSibling)) {
382
-			$node = $node->previousSibling;
383
-			if ($node->nodeType === XML_ELEMENT_NODE && ( ! $byType || $node->tagName === $tag)) {
384
-				++$i;
385
-			}
386
-		}
387
-
388
-		return $i;
389
-	}
390
-
391
-	/**
392
-	 * Get the relative position of a node in its sibling set.
393
-	 *
394
-	 * @param      $node
395
-	 * @param bool $byType
396
-	 *
397
-	 * @return int
398
-	 */
399
-	protected function nodePositionFromEnd($node, $byType = false): int
400
-	{
401
-		$i   = 1;
402
-		$tag = $node->tagName;
403
-		while (isset($node->nextSibling)) {
404
-			$node = $node->nextSibling;
405
-			if ($node->nodeType === XML_ELEMENT_NODE && ( ! $byType || $node->tagName === $tag)) {
406
-				++$i;
407
-			}
408
-		}
409
-
410
-		return $i;
411
-	}
412
-
413
-	/**
414
-	 * Provides functionality for all "An+B" rules.
415
-	 * Provides nth-child and also the functionality required for:
416
-	 *
417
-	 *- nth-last-child
418
-	 *- even
419
-	 *- odd
420
-	 *- first
421
-	 *- last
422
-	 *- eq
423
-	 *- nth
424
-	 *- nth-of-type
425
-	 *- first-of-type
426
-	 *- last-of-type
427
-	 *- nth-last-of-type
428
-	 *
429
-	 * See also QueryPath::CSS::DOMTraverser::Util::parseAnB().
430
-	 *
431
-	 * @param      $node
432
-	 * @param      $value
433
-	 * @param bool $reverse
434
-	 * @param bool $byType
435
-	 *
436
-	 * @return bool
437
-	 */
438
-	protected function isNthChild($node, $value, $reverse = false, $byType = false): bool
439
-	{
440
-		[$groupSize, $elementInGroup] = Util::parseAnB($value);
441
-		$parent = $node->parentNode;
442
-		if (empty($parent)
443
-			|| ($groupSize === 0 && $elementInGroup === 0)
444
-			|| ($groupSize > 0 && $elementInGroup > $groupSize)
445
-		) {
446
-			return false;
447
-		}
448
-
449
-		// First we need to find the position of $node in other elements.
450
-		if ($reverse) {
451
-			$pos = $this->nodePositionFromEnd($node, $byType);
452
-		} else {
453
-			$pos = $this->nodePositionFromStart($node, $byType);
454
-		}
455
-
456
-		// If group size is 0, we just check to see if this
457
-		// is the nth element:
458
-		if ($groupSize === 0) {
459
-			return $pos === $elementInGroup;
460
-		}
461
-
462
-		// Next, we normalize $elementInGroup
463
-		if ($elementInGroup < 0) {
464
-			$elementInGroup = $groupSize + $elementInGroup;
465
-		}
466
-		$prod = ($pos - $elementInGroup) / $groupSize;
467
-
468
-		return is_int($prod) && $prod >= 0;
469
-	}
470
-
471
-	protected function isLocalLink($node): bool
472
-	{
473
-		if (! $node->hasAttribute('href')) {
474
-			return false;
475
-		}
476
-		$url    = $node->getAttribute('href');
477
-		$scheme = parse_url($url, PHP_URL_SCHEME);
478
-
479
-		return empty($scheme) || $scheme === 'file';
480
-	}
28
+    /**
29
+     * Tests whether the given element matches the given pseudoclass.
30
+     *
31
+     * @param string   $pseudoclass
32
+     *   The string name of the pseudoclass
33
+     * @param resource $node
34
+     *   The DOMNode to be tested.
35
+     * @param resource $scope
36
+     *   The DOMElement that is the active root for this node.
37
+     * @param mixed    $value
38
+     *   The optional value string provided with this class. This is
39
+     *   used, for example, in an+b psuedoclasses.
40
+     *
41
+     * @return bool
42
+     * @throws NotImplementedException
43
+     * @throws ParseException
44
+     * @retval boolean
45
+     *   TRUE if the node matches, FALSE otherwise.
46
+     */
47
+    public function elementMatches($pseudoclass, $node, $scope, $value = null)
48
+    {
49
+        $name = strtolower($pseudoclass);
50
+        // Need to handle known pseudoclasses.
51
+        switch ($name) {
52
+            case 'current':
53
+            case 'past':
54
+            case 'future':
55
+            case 'visited':
56
+            case 'hover':
57
+            case 'active':
58
+            case 'focus':
59
+            case 'animated': //  Last 3 are from jQuery
60
+            case 'visible':
61
+            case 'hidden':
62
+                // These require a UA, which we don't have.
63
+            case 'valid':
64
+            case 'invalid':
65
+            case 'required':
66
+            case 'optional':
67
+            case 'read-only':
68
+            case 'read-write':
69
+                // Since we don't know how to validate elements,
70
+                // we can't supply these.
71
+            case 'dir':
72
+                // FIXME: I don't know how to get directionality info.
73
+            case 'nth-column':
74
+            case 'nth-last-column':
75
+                // We don't know what a column is in most documents.
76
+                // FIXME: Can we do this for HTML?
77
+            case 'target':
78
+                // This requires a location URL, which we don't have.
79
+                return false;
80
+            case 'indeterminate':
81
+                // Because sometimes screwing with people is fun.
82
+                return (boolean) mt_rand(0, 1);
83
+            case 'lang':
84
+                // No value = exception.
85
+                if (! isset($value)) {
86
+                    throw new NotImplementedException(':lang() requires a value.');
87
+                }
88
+
89
+                return $this->lang($node, $value);
90
+            case 'any-link':
91
+                return Util::matchesAttribute($node, 'href')
92
+                       || Util::matchesAttribute($node, 'src')
93
+                       || Util::matchesAttribute($node, 'link');
94
+            case 'link':
95
+                return Util::matchesAttribute($node, 'href');
96
+            case 'local-link':
97
+                return $this->isLocalLink($node);
98
+            case 'root':
99
+                return $node->isSameNode($node->ownerDocument->documentElement);
100
+
101
+            // CSS 4 declares the :scope pseudo-class, which describes what was
102
+            // the :x-root QueryPath extension.
103
+            case 'x-root':
104
+            case 'x-reset':
105
+            case 'scope':
106
+                return $node->isSameNode($scope);
107
+            // NON-STANDARD extensions for simple support of even and odd. These
108
+            // are supported by jQuery, FF, and other user agents.
109
+            case 'even':
110
+                return $this->isNthChild($node, 'even');
111
+            case 'odd':
112
+                return $this->isNthChild($node, 'odd');
113
+            case 'nth-child':
114
+                return $this->isNthChild($node, $value);
115
+            case 'nth-last-child':
116
+                return $this->isNthChild($node, $value, true);
117
+            case 'nth-of-type':
118
+                return $this->isNthChild($node, $value, false, true);
119
+            case 'nth-last-of-type':
120
+                return $this->isNthChild($node, $value, true, true);
121
+            case 'first-of-type':
122
+                return $this->isFirstOfType($node);
123
+            case 'last-of-type':
124
+                return $this->isLastOfType($node);
125
+            case 'only-of-type':
126
+                return $this->isFirstOfType($node) && $this->isLastOfType($node);
127
+
128
+            // Additional pseudo-classes defined in jQuery:
129
+            case 'lt':
130
+                // I'm treating this as "less than or equal to".
131
+                $rule = sprintf('-n + %d', (int) $value);
132
+
133
+                // $rule = '-n+15';
134
+                return $this->isNthChild($node, $rule);
135
+            case 'gt':
136
+                // I'm treating this as "greater than"
137
+                // return $this->nodePositionFromEnd($node) > (int) $value;
138
+                return $this->nodePositionFromStart($node) > (int) $value;
139
+            case 'nth':
140
+            case 'eq':
141
+                $rule = (int) $value;
142
+
143
+                return $this->isNthChild($node, $rule);
144
+            case 'first':
145
+                return $this->isNthChild($node, 1);
146
+            case 'first-child':
147
+                return $this->isFirst($node);
148
+            case 'last':
149
+            case 'last-child':
150
+                return $this->isLast($node);
151
+            case 'only-child':
152
+                return $this->isFirst($node) && $this->isLast($node);
153
+            case 'empty':
154
+                return $this->isEmpty($node);
155
+            case 'parent':
156
+                return ! $this->isEmpty($node);
157
+
158
+            case 'enabled':
159
+            case 'disabled':
160
+            case 'checked':
161
+                return Util::matchesAttribute($node, $name);
162
+            case 'text':
163
+            case 'radio':
164
+            case 'checkbox':
165
+            case 'file':
166
+            case 'password':
167
+            case 'submit':
168
+            case 'image':
169
+            case 'reset':
170
+            case 'button':
171
+                return Util::matchesAttribute($node, 'type', $name);
172
+
173
+            case 'header':
174
+                return $this->header($node);
175
+            case 'has':
176
+            case 'matches':
177
+                return $this->has($node, $value);
178
+            break;
179
+            case 'not':
180
+                if (empty($value)) {
181
+                    throw new ParseException(':not() requires a value.');
182
+                }
183
+
184
+                return $this->isNot($node, $value);
185
+            // Contains == text matches.
186
+            // In QP 2.1, this was changed.
187
+            case 'contains':
188
+                return $this->contains($node, $value);
189
+            // Since QP 2.1
190
+            case 'contains-exactly':
191
+                return $this->containsExactly($node, $value);
192
+            default:
193
+                throw new ParseException('Unknown Pseudo-Class: ' . $name);
194
+        }
195
+    }
196
+
197
+    /**
198
+     * Pseudo-class handler for :lang
199
+     *
200
+     * Note that this does not implement the spec in its entirety because we do
201
+     * not presume to "know the language" of the document. If anyone is interested
202
+     * in making this more intelligent, please do so.
203
+     */
204
+    protected function lang($node, $value)
205
+    {
206
+        // TODO: This checks for cases where an explicit language is
207
+        // set. The spec seems to indicate that an element should inherit
208
+        // language from the parent... but this is unclear.
209
+        $operator = (strpos($value, '-') !== false) ? EventHandler::IS_EXACTLY : EventHandler::CONTAINS_WITH_HYPHEN;
210
+
211
+        $match = true;
212
+        foreach ($node->attributes as $attrNode) {
213
+            if ($attrNode->localName === 'lang') {
214
+                if ($attrNode->nodeName === $attrNode->localName) {
215
+                    // fprintf(STDOUT, "%s in NS %s\n", $attrNode->name, $attrNode->nodeName);
216
+                    return Util::matchesAttribute($node, 'lang', $value, $operator);
217
+                }
218
+
219
+                $nsuri = $attrNode->namespaceURI;
220
+
221
+                // fprintf(STDOUT, "%s in NS %s\n", $attrNode->name, $nsuri);
222
+                return Util::matchesAttributeNS($node, 'lang', $nsuri, $value, $operator);
223
+            }
224
+        }
225
+
226
+        return false;
227
+    }
228
+
229
+    /**
230
+     * Provides jQuery pseudoclass ':header'.
231
+     *
232
+     * @param $node
233
+     *
234
+     * @return bool
235
+     */
236
+    protected function header($node): bool
237
+    {
238
+        return preg_match('/^h[1-9]$/i', $node->tagName) === 1;
239
+    }
240
+
241
+    /**
242
+     * Provides pseudoclass :empty.
243
+     */
244
+    protected function isEmpty($node): bool
245
+    {
246
+        foreach ($node->childNodes as $kid) {
247
+            // We don't want to count PIs and comments. From the spec, it
248
+            // appears that CDATA is also not counted.
249
+            if ($kid->nodeType === XML_ELEMENT_NODE || $kid->nodeType === XML_TEXT_NODE) {
250
+                // As soon as we hit a FALSE, return.
251
+                return false;
252
+            }
253
+        }
254
+
255
+        return true;
256
+    }
257
+
258
+    /**
259
+     * Provides jQuery pseudoclass :first.
260
+     *
261
+     * @todo
262
+     *   This can be replaced by isNthChild().
263
+     */
264
+    protected function isFirst($node): bool
265
+    {
266
+        while (isset($node->previousSibling)) {
267
+            $node = $node->previousSibling;
268
+            if ($node->nodeType === XML_ELEMENT_NODE) {
269
+                return false;
270
+            }
271
+        }
272
+
273
+        return true;
274
+    }
275
+
276
+    /**
277
+     * Fast version of first-of-type.
278
+     */
279
+    protected function isFirstOfType($node)
280
+    {
281
+        $type = $node->tagName;
282
+        while (isset($node->previousSibling)) {
283
+            $node = $node->previousSibling;
284
+            if ($node->nodeType === XML_ELEMENT_NODE && $node->tagName === $type) {
285
+                return false;
286
+            }
287
+        }
288
+
289
+        return true;
290
+    }
291
+
292
+    /**
293
+     * Fast version of jQuery :last.
294
+     */
295
+    protected function isLast($node)
296
+    {
297
+        while (isset($node->nextSibling)) {
298
+            $node = $node->nextSibling;
299
+            if ($node->nodeType === XML_ELEMENT_NODE) {
300
+                return false;
301
+            }
302
+        }
303
+
304
+        return true;
305
+    }
306
+
307
+    /**
308
+     * Provides last-of-type.
309
+     */
310
+    protected function isLastOfType($node)
311
+    {
312
+        $type = $node->tagName;
313
+        while (isset($node->nextSibling)) {
314
+            $node = $node->nextSibling;
315
+            if ($node->nodeType === XML_ELEMENT_NODE && $node->tagName === $type) {
316
+                return false;
317
+            }
318
+        }
319
+
320
+        return true;
321
+    }
322
+
323
+    /**
324
+     * Provides :contains() as the original spec called for.
325
+     *
326
+     * This is an INEXACT match.
327
+     */
328
+    protected function contains($node, $value): bool
329
+    {
330
+        $text  = $node->textContent;
331
+        $value = Util::removeQuotes($value);
332
+
333
+        return isset($text) && (stripos($text, $value) !== false);
334
+    }
335
+
336
+    /**
337
+     * Provides :contains-exactly QueryPath pseudoclass.
338
+     *
339
+     * This is an EXACT match.
340
+     */
341
+    protected function containsExactly($node, $value): bool
342
+    {
343
+        $text  = $node->textContent;
344
+        $value = Util::removeQuotes($value);
345
+
346
+        return isset($text) && $text == $value;
347
+    }
348
+
349
+    /**
350
+     * Provides :has pseudoclass.
351
+     *
352
+     * @throws ParseException
353
+     */
354
+    protected function has($node, $selector): bool
355
+    {
356
+        $splos = new SPLObjectStorage();
357
+        $splos->attach($node);
358
+        $traverser = new DOMTraverser($splos, true);
359
+        $results   = $traverser->find($selector)->matches();
360
+
361
+        return count($results) > 0;
362
+    }
363
+
364
+    /**
365
+     * Provides :not pseudoclass.
366
+     *
367
+     * @throws ParseException
368
+     */
369
+    protected function isNot($node, $selector): bool
370
+    {
371
+        return ! $this->has($node, $selector);
372
+    }
373
+
374
+    /**
375
+     * Get the relative position of a node in its sibling set.
376
+     */
377
+    protected function nodePositionFromStart($node, $byType = false): int
378
+    {
379
+        $i   = 1;
380
+        $tag = $node->tagName;
381
+        while (isset($node->previousSibling)) {
382
+            $node = $node->previousSibling;
383
+            if ($node->nodeType === XML_ELEMENT_NODE && ( ! $byType || $node->tagName === $tag)) {
384
+                ++$i;
385
+            }
386
+        }
387
+
388
+        return $i;
389
+    }
390
+
391
+    /**
392
+     * Get the relative position of a node in its sibling set.
393
+     *
394
+     * @param      $node
395
+     * @param bool $byType
396
+     *
397
+     * @return int
398
+     */
399
+    protected function nodePositionFromEnd($node, $byType = false): int
400
+    {
401
+        $i   = 1;
402
+        $tag = $node->tagName;
403
+        while (isset($node->nextSibling)) {
404
+            $node = $node->nextSibling;
405
+            if ($node->nodeType === XML_ELEMENT_NODE && ( ! $byType || $node->tagName === $tag)) {
406
+                ++$i;
407
+            }
408
+        }
409
+
410
+        return $i;
411
+    }
412
+
413
+    /**
414
+     * Provides functionality for all "An+B" rules.
415
+     * Provides nth-child and also the functionality required for:
416
+     *
417
+     *- nth-last-child
418
+     *- even
419
+     *- odd
420
+     *- first
421
+     *- last
422
+     *- eq
423
+     *- nth
424
+     *- nth-of-type
425
+     *- first-of-type
426
+     *- last-of-type
427
+     *- nth-last-of-type
428
+     *
429
+     * See also QueryPath::CSS::DOMTraverser::Util::parseAnB().
430
+     *
431
+     * @param      $node
432
+     * @param      $value
433
+     * @param bool $reverse
434
+     * @param bool $byType
435
+     *
436
+     * @return bool
437
+     */
438
+    protected function isNthChild($node, $value, $reverse = false, $byType = false): bool
439
+    {
440
+        [$groupSize, $elementInGroup] = Util::parseAnB($value);
441
+        $parent = $node->parentNode;
442
+        if (empty($parent)
443
+            || ($groupSize === 0 && $elementInGroup === 0)
444
+            || ($groupSize > 0 && $elementInGroup > $groupSize)
445
+        ) {
446
+            return false;
447
+        }
448
+
449
+        // First we need to find the position of $node in other elements.
450
+        if ($reverse) {
451
+            $pos = $this->nodePositionFromEnd($node, $byType);
452
+        } else {
453
+            $pos = $this->nodePositionFromStart($node, $byType);
454
+        }
455
+
456
+        // If group size is 0, we just check to see if this
457
+        // is the nth element:
458
+        if ($groupSize === 0) {
459
+            return $pos === $elementInGroup;
460
+        }
461
+
462
+        // Next, we normalize $elementInGroup
463
+        if ($elementInGroup < 0) {
464
+            $elementInGroup = $groupSize + $elementInGroup;
465
+        }
466
+        $prod = ($pos - $elementInGroup) / $groupSize;
467
+
468
+        return is_int($prod) && $prod >= 0;
469
+    }
470
+
471
+    protected function isLocalLink($node): bool
472
+    {
473
+        if (! $node->hasAttribute('href')) {
474
+            return false;
475
+        }
476
+        $url    = $node->getAttribute('href');
477
+        $scheme = parse_url($url, PHP_URL_SCHEME);
478
+
479
+        return empty($scheme) || $scheme === 'file';
480
+    }
481 481
 }
Please login to merge, or discard this patch.
Spacing   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -79,10 +79,10 @@  discard block
 block discarded – undo
79 79
 				return false;
80 80
 			case 'indeterminate':
81 81
 				// Because sometimes screwing with people is fun.
82
-				return (boolean) mt_rand(0, 1);
82
+				return (boolean)mt_rand(0, 1);
83 83
 			case 'lang':
84 84
 				// No value = exception.
85
-				if (! isset($value)) {
85
+				if (!isset($value)) {
86 86
 					throw new NotImplementedException(':lang() requires a value.');
87 87
 				}
88 88
 
@@ -128,17 +128,17 @@  discard block
 block discarded – undo
128 128
 			// Additional pseudo-classes defined in jQuery:
129 129
 			case 'lt':
130 130
 				// I'm treating this as "less than or equal to".
131
-				$rule = sprintf('-n + %d', (int) $value);
131
+				$rule = sprintf('-n + %d', (int)$value);
132 132
 
133 133
 				// $rule = '-n+15';
134 134
 				return $this->isNthChild($node, $rule);
135 135
 			case 'gt':
136 136
 				// I'm treating this as "greater than"
137 137
 				// return $this->nodePositionFromEnd($node) > (int) $value;
138
-				return $this->nodePositionFromStart($node) > (int) $value;
138
+				return $this->nodePositionFromStart($node) > (int)$value;
139 139
 			case 'nth':
140 140
 			case 'eq':
141
-				$rule = (int) $value;
141
+				$rule = (int)$value;
142 142
 
143 143
 				return $this->isNthChild($node, $rule);
144 144
 			case 'first':
@@ -153,7 +153,7 @@  discard block
 block discarded – undo
153 153
 			case 'empty':
154 154
 				return $this->isEmpty($node);
155 155
 			case 'parent':
156
-				return ! $this->isEmpty($node);
156
+				return !$this->isEmpty($node);
157 157
 
158 158
 			case 'enabled':
159 159
 			case 'disabled':
@@ -368,7 +368,7 @@  discard block
 block discarded – undo
368 368
 	 */
369 369
 	protected function isNot($node, $selector): bool
370 370
 	{
371
-		return ! $this->has($node, $selector);
371
+		return !$this->has($node, $selector);
372 372
 	}
373 373
 
374 374
 	/**
@@ -380,7 +380,7 @@  discard block
 block discarded – undo
380 380
 		$tag = $node->tagName;
381 381
 		while (isset($node->previousSibling)) {
382 382
 			$node = $node->previousSibling;
383
-			if ($node->nodeType === XML_ELEMENT_NODE && ( ! $byType || $node->tagName === $tag)) {
383
+			if ($node->nodeType === XML_ELEMENT_NODE && (!$byType || $node->tagName === $tag)) {
384 384
 				++$i;
385 385
 			}
386 386
 		}
@@ -402,7 +402,7 @@  discard block
 block discarded – undo
402 402
 		$tag = $node->tagName;
403 403
 		while (isset($node->nextSibling)) {
404 404
 			$node = $node->nextSibling;
405
-			if ($node->nodeType === XML_ELEMENT_NODE && ( ! $byType || $node->tagName === $tag)) {
405
+			if ($node->nodeType === XML_ELEMENT_NODE && (!$byType || $node->tagName === $tag)) {
406 406
 				++$i;
407 407
 			}
408 408
 		}
@@ -470,7 +470,7 @@  discard block
 block discarded – undo
470 470
 
471 471
 	protected function isLocalLink($node): bool
472 472
 	{
473
-		if (! $node->hasAttribute('href')) {
473
+		if (!$node->hasAttribute('href')) {
474 474
 			return false;
475 475
 		}
476 476
 		$url    = $node->getAttribute('href');
Please login to merge, or discard this patch.
src/CSS/DOMTraverser/Util.php 2 patches
Indentation   +149 added lines, -149 removed lines patch added patch discarded remove patch
@@ -14,153 +14,153 @@
 block discarded – undo
14 14
  */
15 15
 class Util
16 16
 {
17
-	/**
18
-	 * Check whether the given DOMElement has the given attribute.
19
-	 *
20
-	 * @param      $node
21
-	 * @param      $name
22
-	 * @param null $value
23
-	 * @param int  $operation
24
-	 *
25
-	 * @return bool
26
-	 */
27
-	public static function matchesAttribute($node, $name, $value = null, $operation = EventHandler::IS_EXACTLY): bool
28
-	{
29
-		if (! $node->hasAttribute($name)) {
30
-			return false;
31
-		}
32
-
33
-		if (null === $value) {
34
-			return true;
35
-		}
36
-
37
-		return self::matchesAttributeValue($value, $node->getAttribute($name), $operation);
38
-	}
39
-
40
-	/**
41
-	 * Check whether the given DOMElement has the given namespaced attribute.
42
-	 */
43
-	public static function matchesAttributeNS(
44
-		$node,
45
-		$name,
46
-		$nsuri,
47
-		$value = null,
48
-		$operation = EventHandler::IS_EXACTLY
49
-	) {
50
-		if (! $node->hasAttributeNS($nsuri, $name)) {
51
-			return false;
52
-		}
53
-
54
-		if (is_null($value)) {
55
-			return true;
56
-		}
57
-
58
-		return self::matchesAttributeValue($value, $node->getAttributeNS($nsuri, $name), $operation);
59
-	}
60
-
61
-	/**
62
-	 * Check for attr value matches based on an operation.
63
-	 */
64
-	public static function matchesAttributeValue($needle, $haystack, $operation): bool
65
-	{
66
-
67
-		if (strlen($haystack) < strlen($needle)) {
68
-			return false;
69
-		}
70
-
71
-		// According to the spec:
72
-		// "The case-sensitivity of attribute names in selectors depends on the document language."
73
-		// (6.3.2)
74
-		// To which I say, "huh?". We assume case sensitivity.
75
-		switch ($operation) {
76
-			case EventHandler::IS_EXACTLY:
77
-				return $needle == $haystack;
78
-			case EventHandler::CONTAINS_WITH_SPACE:
79
-				// XXX: This needs testing!
80
-				return preg_match('/\b/', $haystack) == 1;
81
-			//return in_array($needle, explode(' ', $haystack));
82
-			case EventHandler::CONTAINS_WITH_HYPHEN:
83
-				return in_array($needle, explode('-', $haystack));
84
-			case EventHandler::CONTAINS_IN_STRING:
85
-				return strpos($haystack, $needle) !== false;
86
-			case EventHandler::BEGINS_WITH:
87
-				return strpos($haystack, $needle) === 0;
88
-			case EventHandler::ENDS_WITH:
89
-				//return strrpos($haystack, $needle) === strlen($needle) - 1;
90
-				return preg_match('/' . $needle . '$/', $haystack) == 1;
91
-		}
92
-
93
-		return false; // Shouldn't be able to get here.
94
-	}
95
-
96
-	/**
97
-	 * Remove leading and trailing quotes.
98
-	 */
99
-	public static function removeQuotes(string $str)
100
-	{
101
-		$f = mb_substr($str, 0, 1);
102
-		$l = mb_substr($str, -1);
103
-		if ($f === $l && ($f === '"' || $f === "'")) {
104
-			$str = mb_substr($str, 1, -1);
105
-		}
106
-
107
-		return $str;
108
-	}
109
-
110
-	/**
111
-	 * Parse an an+b rule for CSS pseudo-classes.
112
-	 *
113
-	 * Invalid rules return `array(0, 0)`. This is per the spec.
114
-	 *
115
-	 * @param $rule
116
-	 *  Some rule in the an+b format.
117
-	 *
118
-	 * @retval array
119
-	 *  `array($aVal, $bVal)` of the two values.
120
-	 * @return array
121
-	 */
122
-	public static function parseAnB($rule): array
123
-	{
124
-		if ($rule === 'even') {
125
-			return [2, 0];
126
-		}
127
-
128
-		if ($rule === 'odd') {
129
-			return [2, 1];
130
-		}
131
-
132
-		if ($rule === 'n') {
133
-			return [1, 0];
134
-		}
135
-
136
-		if (is_numeric($rule)) {
137
-			return [0, (int) $rule];
138
-		}
139
-
140
-		$regex   = '/^\s*([+\-]?[0-9]*)n\s*([+\-]?)\s*([0-9]*)\s*$/';
141
-		$matches = [];
142
-		$res     = preg_match($regex, $rule, $matches);
143
-
144
-		// If it doesn't parse, return 0, 0.
145
-		if (! $res) {
146
-			return [0, 0];
147
-		}
148
-
149
-		$aVal = $matches[1] ?? 1;
150
-		if ($aVal === '-') {
151
-			$aVal = -1;
152
-		} else {
153
-			$aVal = (int) $aVal;
154
-		}
155
-
156
-		$bVal = 0;
157
-		if (isset($matches[3])) {
158
-			$bVal = (int) $matches[3];
159
-			if (isset($matches[2]) && $matches[2] === '-') {
160
-				$bVal *= -1;
161
-			}
162
-		}
163
-
164
-		return [$aVal, $bVal];
165
-	}
17
+    /**
18
+     * Check whether the given DOMElement has the given attribute.
19
+     *
20
+     * @param      $node
21
+     * @param      $name
22
+     * @param null $value
23
+     * @param int  $operation
24
+     *
25
+     * @return bool
26
+     */
27
+    public static function matchesAttribute($node, $name, $value = null, $operation = EventHandler::IS_EXACTLY): bool
28
+    {
29
+        if (! $node->hasAttribute($name)) {
30
+            return false;
31
+        }
32
+
33
+        if (null === $value) {
34
+            return true;
35
+        }
36
+
37
+        return self::matchesAttributeValue($value, $node->getAttribute($name), $operation);
38
+    }
39
+
40
+    /**
41
+     * Check whether the given DOMElement has the given namespaced attribute.
42
+     */
43
+    public static function matchesAttributeNS(
44
+        $node,
45
+        $name,
46
+        $nsuri,
47
+        $value = null,
48
+        $operation = EventHandler::IS_EXACTLY
49
+    ) {
50
+        if (! $node->hasAttributeNS($nsuri, $name)) {
51
+            return false;
52
+        }
53
+
54
+        if (is_null($value)) {
55
+            return true;
56
+        }
57
+
58
+        return self::matchesAttributeValue($value, $node->getAttributeNS($nsuri, $name), $operation);
59
+    }
60
+
61
+    /**
62
+     * Check for attr value matches based on an operation.
63
+     */
64
+    public static function matchesAttributeValue($needle, $haystack, $operation): bool
65
+    {
66
+
67
+        if (strlen($haystack) < strlen($needle)) {
68
+            return false;
69
+        }
70
+
71
+        // According to the spec:
72
+        // "The case-sensitivity of attribute names in selectors depends on the document language."
73
+        // (6.3.2)
74
+        // To which I say, "huh?". We assume case sensitivity.
75
+        switch ($operation) {
76
+            case EventHandler::IS_EXACTLY:
77
+                return $needle == $haystack;
78
+            case EventHandler::CONTAINS_WITH_SPACE:
79
+                // XXX: This needs testing!
80
+                return preg_match('/\b/', $haystack) == 1;
81
+            //return in_array($needle, explode(' ', $haystack));
82
+            case EventHandler::CONTAINS_WITH_HYPHEN:
83
+                return in_array($needle, explode('-', $haystack));
84
+            case EventHandler::CONTAINS_IN_STRING:
85
+                return strpos($haystack, $needle) !== false;
86
+            case EventHandler::BEGINS_WITH:
87
+                return strpos($haystack, $needle) === 0;
88
+            case EventHandler::ENDS_WITH:
89
+                //return strrpos($haystack, $needle) === strlen($needle) - 1;
90
+                return preg_match('/' . $needle . '$/', $haystack) == 1;
91
+        }
92
+
93
+        return false; // Shouldn't be able to get here.
94
+    }
95
+
96
+    /**
97
+     * Remove leading and trailing quotes.
98
+     */
99
+    public static function removeQuotes(string $str)
100
+    {
101
+        $f = mb_substr($str, 0, 1);
102
+        $l = mb_substr($str, -1);
103
+        if ($f === $l && ($f === '"' || $f === "'")) {
104
+            $str = mb_substr($str, 1, -1);
105
+        }
106
+
107
+        return $str;
108
+    }
109
+
110
+    /**
111
+     * Parse an an+b rule for CSS pseudo-classes.
112
+     *
113
+     * Invalid rules return `array(0, 0)`. This is per the spec.
114
+     *
115
+     * @param $rule
116
+     *  Some rule in the an+b format.
117
+     *
118
+     * @retval array
119
+     *  `array($aVal, $bVal)` of the two values.
120
+     * @return array
121
+     */
122
+    public static function parseAnB($rule): array
123
+    {
124
+        if ($rule === 'even') {
125
+            return [2, 0];
126
+        }
127
+
128
+        if ($rule === 'odd') {
129
+            return [2, 1];
130
+        }
131
+
132
+        if ($rule === 'n') {
133
+            return [1, 0];
134
+        }
135
+
136
+        if (is_numeric($rule)) {
137
+            return [0, (int) $rule];
138
+        }
139
+
140
+        $regex   = '/^\s*([+\-]?[0-9]*)n\s*([+\-]?)\s*([0-9]*)\s*$/';
141
+        $matches = [];
142
+        $res     = preg_match($regex, $rule, $matches);
143
+
144
+        // If it doesn't parse, return 0, 0.
145
+        if (! $res) {
146
+            return [0, 0];
147
+        }
148
+
149
+        $aVal = $matches[1] ?? 1;
150
+        if ($aVal === '-') {
151
+            $aVal = -1;
152
+        } else {
153
+            $aVal = (int) $aVal;
154
+        }
155
+
156
+        $bVal = 0;
157
+        if (isset($matches[3])) {
158
+            $bVal = (int) $matches[3];
159
+            if (isset($matches[2]) && $matches[2] === '-') {
160
+                $bVal *= -1;
161
+            }
162
+        }
163
+
164
+        return [$aVal, $bVal];
165
+    }
166 166
 }
Please login to merge, or discard this patch.
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -26,7 +26,7 @@  discard block
 block discarded – undo
26 26
 	 */
27 27
 	public static function matchesAttribute($node, $name, $value = null, $operation = EventHandler::IS_EXACTLY): bool
28 28
 	{
29
-		if (! $node->hasAttribute($name)) {
29
+		if (!$node->hasAttribute($name)) {
30 30
 			return false;
31 31
 		}
32 32
 
@@ -47,7 +47,7 @@  discard block
 block discarded – undo
47 47
 		$value = null,
48 48
 		$operation = EventHandler::IS_EXACTLY
49 49
 	) {
50
-		if (! $node->hasAttributeNS($nsuri, $name)) {
50
+		if (!$node->hasAttributeNS($nsuri, $name)) {
51 51
 			return false;
52 52
 		}
53 53
 
@@ -134,7 +134,7 @@  discard block
 block discarded – undo
134 134
 		}
135 135
 
136 136
 		if (is_numeric($rule)) {
137
-			return [0, (int) $rule];
137
+			return [0, (int)$rule];
138 138
 		}
139 139
 
140 140
 		$regex   = '/^\s*([+\-]?[0-9]*)n\s*([+\-]?)\s*([0-9]*)\s*$/';
@@ -142,7 +142,7 @@  discard block
 block discarded – undo
142 142
 		$res     = preg_match($regex, $rule, $matches);
143 143
 
144 144
 		// If it doesn't parse, return 0, 0.
145
-		if (! $res) {
145
+		if (!$res) {
146 146
 			return [0, 0];
147 147
 		}
148 148
 
@@ -150,12 +150,12 @@  discard block
 block discarded – undo
150 150
 		if ($aVal === '-') {
151 151
 			$aVal = -1;
152 152
 		} else {
153
-			$aVal = (int) $aVal;
153
+			$aVal = (int)$aVal;
154 154
 		}
155 155
 
156 156
 		$bVal = 0;
157 157
 		if (isset($matches[3])) {
158
-			$bVal = (int) $matches[3];
158
+			$bVal = (int)$matches[3];
159 159
 			if (isset($matches[2]) && $matches[2] === '-') {
160 160
 				$bVal *= -1;
161 161
 			}
Please login to merge, or discard this patch.
src/CSS/Scanner.php 2 patches
Indentation   +263 added lines, -263 removed lines patch added patch discarded remove patch
@@ -17,296 +17,296 @@
 block discarded – undo
17 17
 final class Scanner
18 18
 {
19 19
 
20
-	public $is;
21
-	public $value;
22
-	public $token;
20
+    public $is;
21
+    public $value;
22
+    public $token;
23 23
 
24
-	public $recurse = false;
25
-	public $it = 0;
24
+    public $recurse = false;
25
+    public $it = 0;
26 26
 
27
-	/**
28
-	 * Given a new input stream, tokenize the CSS selector string.
29
-	 *
30
-	 * @param InputStream $in
31
-	 *  An input stream to be scanned.
32
-	 *
33
-	 * @see InputStream
34
-	 */
35
-	public function __construct(InputStream $in)
36
-	{
37
-		$this->is = $in;
38
-	}
27
+    /**
28
+     * Given a new input stream, tokenize the CSS selector string.
29
+     *
30
+     * @param InputStream $in
31
+     *  An input stream to be scanned.
32
+     *
33
+     * @see InputStream
34
+     */
35
+    public function __construct(InputStream $in)
36
+    {
37
+        $this->is = $in;
38
+    }
39 39
 
40
-	/**
41
-	 * Return the position of the reader in the string.
42
-	 */
43
-	public function position(): int
44
-	{
45
-		return $this->is->position;
46
-	}
40
+    /**
41
+     * Return the position of the reader in the string.
42
+     */
43
+    public function position(): int
44
+    {
45
+        return $this->is->position;
46
+    }
47 47
 
48
-	/**
49
-	 * See the next char without removing it from the stack.
50
-	 *
51
-	 * @return string
52
-	 * Returns the next character on the stack.
53
-	 */
54
-	public function peek(): string
55
-	{
56
-		return $this->is->peek();
57
-	}
48
+    /**
49
+     * See the next char without removing it from the stack.
50
+     *
51
+     * @return string
52
+     * Returns the next character on the stack.
53
+     */
54
+    public function peek(): string
55
+    {
56
+        return $this->is->peek();
57
+    }
58 58
 
59
-	/**
60
-	 * Get the next token in the input stream.
61
-	 *
62
-	 * This sets the current token to the value of the next token in
63
-	 * the stream.
64
-	 *
65
-	 * @return int
66
-	 *  Returns an int value corresponding to one of the Token constants,
67
-	 *  or FALSE if the end of the string is reached. (Remember to use
68
-	 *  strong equality checking on FALSE, since 0 is a valid token id.)
69
-	 * @throws ParseException
70
-	 * @throws Exception
71
-	 */
72
-	public function nextToken(): int
73
-	{
74
-		$tok = -1;
75
-		++$this->it;
76
-		if ($this->is->isEmpty()) {
77
-			if ($this->recurse) {
78
-				throw new Exception('Recursion error detected at iteration ' . $this->it . '.');
79
-			}
80
-			//print "{$this->it}: All done\n";
81
-			$this->recurse = true;
82
-			$this->token   = false;
59
+    /**
60
+     * Get the next token in the input stream.
61
+     *
62
+     * This sets the current token to the value of the next token in
63
+     * the stream.
64
+     *
65
+     * @return int
66
+     *  Returns an int value corresponding to one of the Token constants,
67
+     *  or FALSE if the end of the string is reached. (Remember to use
68
+     *  strong equality checking on FALSE, since 0 is a valid token id.)
69
+     * @throws ParseException
70
+     * @throws Exception
71
+     */
72
+    public function nextToken(): int
73
+    {
74
+        $tok = -1;
75
+        ++$this->it;
76
+        if ($this->is->isEmpty()) {
77
+            if ($this->recurse) {
78
+                throw new Exception('Recursion error detected at iteration ' . $this->it . '.');
79
+            }
80
+            //print "{$this->it}: All done\n";
81
+            $this->recurse = true;
82
+            $this->token   = false;
83 83
 
84
-			return false;
85
-		}
86
-		$ch = $this->is->consume();
87
-		//print __FUNCTION__ . " Testing $ch.\n";
88
-		if (ctype_space($ch)) {
89
-			$this->value = ' '; // Collapse all WS to a space.
90
-			$this->token = $tok = Token::WHITE;
84
+            return false;
85
+        }
86
+        $ch = $this->is->consume();
87
+        //print __FUNCTION__ . " Testing $ch.\n";
88
+        if (ctype_space($ch)) {
89
+            $this->value = ' '; // Collapse all WS to a space.
90
+            $this->token = $tok = Token::WHITE;
91 91
 
92
-			//$ch = $this->is->consume();
93
-			return $tok;
94
-		}
92
+            //$ch = $this->is->consume();
93
+            return $tok;
94
+        }
95 95
 
96
-		if ($ch === '-' || $ch === '_' || ctype_alnum($ch)) {
97
-			// It's a character
98
-			$this->value = $ch; //strtolower($ch);
99
-			$this->token = $tok = Token::CHAR;
96
+        if ($ch === '-' || $ch === '_' || ctype_alnum($ch)) {
97
+            // It's a character
98
+            $this->value = $ch; //strtolower($ch);
99
+            $this->token = $tok = Token::CHAR;
100 100
 
101
-			return $tok;
102
-		}
101
+            return $tok;
102
+        }
103 103
 
104
-		$this->value = $ch;
104
+        $this->value = $ch;
105 105
 
106
-		switch ($ch) {
107
-			case '*':
108
-				$tok = Token::STAR;
109
-				break;
110
-			case chr(ord('>')):
111
-				$tok = Token::RANGLE;
112
-				break;
113
-			case '.':
114
-				$tok = Token::DOT;
115
-				break;
116
-			case '#':
117
-				$tok = Token::OCTO;
118
-				break;
119
-			case '[':
120
-				$tok = Token::LSQUARE;
121
-				break;
122
-			case ']':
123
-				$tok = Token::RSQUARE;
124
-				break;
125
-			case ':':
126
-				$tok = Token::COLON;
127
-				break;
128
-			case '(':
129
-				$tok = Token::LPAREN;
130
-				break;
131
-			case ')':
132
-				$tok = Token::RPAREN;
133
-				break;
134
-			case '+':
135
-				$tok = Token::PLUS;
136
-				break;
137
-			case '~':
138
-				$tok = Token::TILDE;
139
-				break;
140
-			case '=':
141
-				$tok = Token::EQ;
142
-				break;
143
-			case '|':
144
-				$tok = Token::PIPE;
145
-				break;
146
-			case ',':
147
-				$tok = Token::COMMA;
148
-				break;
149
-			case chr(34):
150
-				$tok = Token::QUOTE;
151
-				break;
152
-			case "'":
153
-				$tok = Token::SQUOTE;
154
-				break;
155
-			case '\\':
156
-				$tok = Token::BSLASH;
157
-				break;
158
-			case '^':
159
-				$tok = Token::CARAT;
160
-				break;
161
-			case '$':
162
-				$tok = Token::DOLLAR;
163
-				break;
164
-			case '@':
165
-				$tok = Token::AT;
166
-				break;
167
-		}
106
+        switch ($ch) {
107
+            case '*':
108
+                $tok = Token::STAR;
109
+                break;
110
+            case chr(ord('>')):
111
+                $tok = Token::RANGLE;
112
+                break;
113
+            case '.':
114
+                $tok = Token::DOT;
115
+                break;
116
+            case '#':
117
+                $tok = Token::OCTO;
118
+                break;
119
+            case '[':
120
+                $tok = Token::LSQUARE;
121
+                break;
122
+            case ']':
123
+                $tok = Token::RSQUARE;
124
+                break;
125
+            case ':':
126
+                $tok = Token::COLON;
127
+                break;
128
+            case '(':
129
+                $tok = Token::LPAREN;
130
+                break;
131
+            case ')':
132
+                $tok = Token::RPAREN;
133
+                break;
134
+            case '+':
135
+                $tok = Token::PLUS;
136
+                break;
137
+            case '~':
138
+                $tok = Token::TILDE;
139
+                break;
140
+            case '=':
141
+                $tok = Token::EQ;
142
+                break;
143
+            case '|':
144
+                $tok = Token::PIPE;
145
+                break;
146
+            case ',':
147
+                $tok = Token::COMMA;
148
+                break;
149
+            case chr(34):
150
+                $tok = Token::QUOTE;
151
+                break;
152
+            case "'":
153
+                $tok = Token::SQUOTE;
154
+                break;
155
+            case '\\':
156
+                $tok = Token::BSLASH;
157
+                break;
158
+            case '^':
159
+                $tok = Token::CARAT;
160
+                break;
161
+            case '$':
162
+                $tok = Token::DOLLAR;
163
+                break;
164
+            case '@':
165
+                $tok = Token::AT;
166
+                break;
167
+        }
168 168
 
169 169
 
170
-		// Catch all characters that are legal within strings.
171
-		if ($tok === -1) {
172
-			// TODO: This should be UTF-8 compatible, but PHP doesn't
173
-			// have a native UTF-8 string. Should we use external
174
-			// mbstring library?
170
+        // Catch all characters that are legal within strings.
171
+        if ($tok === -1) {
172
+            // TODO: This should be UTF-8 compatible, but PHP doesn't
173
+            // have a native UTF-8 string. Should we use external
174
+            // mbstring library?
175 175
 
176
-			$ord = ord($ch);
177
-			// Characters in this pool are legal for use inside of
178
-			// certain strings. Extended ASCII is used here, though I
179
-			// Don't know if these are really legal.
180
-			if (($ord >= 32 && $ord <= 126) || ($ord >= 128 && $ord <= 255)) {
181
-				$tok = Token::STRING_LEGAL;
182
-			} else {
183
-				throw new ParseException('Illegal character found in stream: ' . $ord);
184
-			}
185
-		}
176
+            $ord = ord($ch);
177
+            // Characters in this pool are legal for use inside of
178
+            // certain strings. Extended ASCII is used here, though I
179
+            // Don't know if these are really legal.
180
+            if (($ord >= 32 && $ord <= 126) || ($ord >= 128 && $ord <= 255)) {
181
+                $tok = Token::STRING_LEGAL;
182
+            } else {
183
+                throw new ParseException('Illegal character found in stream: ' . $ord);
184
+            }
185
+        }
186 186
 
187
-		$this->token = $tok;
187
+        $this->token = $tok;
188 188
 
189
-		return $tok;
190
-	}
189
+        return $tok;
190
+    }
191 191
 
192
-	/**
193
-	 * Get a name string from the input stream.
194
-	 * A name string must be composed of
195
-	 * only characters defined in Token:char: -_a-zA-Z0-9
196
-	 */
197
-	public function getNameString()
198
-	{
199
-		$buf = '';
200
-		while ($this->token === Token::CHAR) {
201
-			$buf .= $this->value;
202
-			$this->nextToken();
203
-		}
192
+    /**
193
+     * Get a name string from the input stream.
194
+     * A name string must be composed of
195
+     * only characters defined in Token:char: -_a-zA-Z0-9
196
+     */
197
+    public function getNameString()
198
+    {
199
+        $buf = '';
200
+        while ($this->token === Token::CHAR) {
201
+            $buf .= $this->value;
202
+            $this->nextToken();
203
+        }
204 204
 
205
-		return $buf;
206
-	}
205
+        return $buf;
206
+    }
207 207
 
208
-	/**
209
-	 * This gets a string with any legal 'string' characters.
210
-	 * See CSS Selectors specification, section 11, for the
211
-	 * definition of string.
212
-	 *
213
-	 * This will check for string1, string2, and the case where a
214
-	 * string is unquoted (Oddly absent from the "official" grammar,
215
-	 * though such strings are present as examples in the spec.)
216
-	 *
217
-	 * Note:
218
-	 * Though the grammar supplied by CSS 3 Selectors section 11 does not
219
-	 * address the contents of a pseudo-class value, the spec itself indicates
220
-	 * that a pseudo-class value is a "value between parenthesis" [6.6]. The
221
-	 * examples given use URLs among other things, making them closer to the
222
-	 * definition of 'string' than to 'name'. So we handle them here as strings.
223
-	 */
224
-	public function getQuotedString()
225
-	{
226
-		if ($this->token === Token::QUOTE || $this->token === Token::SQUOTE || $this->token === Token::LPAREN) {
227
-			$end    = ($this->token === Token::LPAREN) ? Token::RPAREN : $this->token;
228
-			$buf    = '';
229
-			$escape = false;
208
+    /**
209
+     * This gets a string with any legal 'string' characters.
210
+     * See CSS Selectors specification, section 11, for the
211
+     * definition of string.
212
+     *
213
+     * This will check for string1, string2, and the case where a
214
+     * string is unquoted (Oddly absent from the "official" grammar,
215
+     * though such strings are present as examples in the spec.)
216
+     *
217
+     * Note:
218
+     * Though the grammar supplied by CSS 3 Selectors section 11 does not
219
+     * address the contents of a pseudo-class value, the spec itself indicates
220
+     * that a pseudo-class value is a "value between parenthesis" [6.6]. The
221
+     * examples given use URLs among other things, making them closer to the
222
+     * definition of 'string' than to 'name'. So we handle them here as strings.
223
+     */
224
+    public function getQuotedString()
225
+    {
226
+        if ($this->token === Token::QUOTE || $this->token === Token::SQUOTE || $this->token === Token::LPAREN) {
227
+            $end    = ($this->token === Token::LPAREN) ? Token::RPAREN : $this->token;
228
+            $buf    = '';
229
+            $escape = false;
230 230
 
231
-			$this->nextToken(); // Skip the opening quote/paren
231
+            $this->nextToken(); // Skip the opening quote/paren
232 232
 
233
-			// The second conjunct is probably not necessary.
234
-			while ($this->token !== false && $this->token > -1) {
235
-				//print "Char: $this->value \n";
236
-				if ($this->token == Token::BSLASH && ! $escape) {
237
-					// XXX: The backslash (\) is removed here.
238
-					// Turn on escaping.
239
-					//$buf .= $this->value;
240
-					$escape = true;
241
-				} elseif ($escape) {
242
-					// Turn off escaping
243
-					$buf    .= $this->value;
244
-					$escape = false;
245
-				} elseif ($this->token === $end) {
246
-					// At end of string; skip token and break.
247
-					$this->nextToken();
248
-					break;
249
-				} else {
250
-					// Append char.
251
-					$buf .= $this->value;
252
-				}
253
-				$this->nextToken();
254
-			}
233
+            // The second conjunct is probably not necessary.
234
+            while ($this->token !== false && $this->token > -1) {
235
+                //print "Char: $this->value \n";
236
+                if ($this->token == Token::BSLASH && ! $escape) {
237
+                    // XXX: The backslash (\) is removed here.
238
+                    // Turn on escaping.
239
+                    //$buf .= $this->value;
240
+                    $escape = true;
241
+                } elseif ($escape) {
242
+                    // Turn off escaping
243
+                    $buf    .= $this->value;
244
+                    $escape = false;
245
+                } elseif ($this->token === $end) {
246
+                    // At end of string; skip token and break.
247
+                    $this->nextToken();
248
+                    break;
249
+                } else {
250
+                    // Append char.
251
+                    $buf .= $this->value;
252
+                }
253
+                $this->nextToken();
254
+            }
255 255
 
256
-			return $buf;
257
-		}
258
-	}
256
+            return $buf;
257
+        }
258
+    }
259 259
 
260
-	// Get the contents inside of a pseudoClass().
261
-	public function getPseudoClassString()
262
-	{
263
-		if ($this->token === Token::QUOTE || $this->token === Token::SQUOTE || $this->token === Token::LPAREN) {
264
-			$end    = ($this->token === Token::LPAREN) ? Token::RPAREN : $this->token;
265
-			$buf    = '';
266
-			$escape = false;
260
+    // Get the contents inside of a pseudoClass().
261
+    public function getPseudoClassString()
262
+    {
263
+        if ($this->token === Token::QUOTE || $this->token === Token::SQUOTE || $this->token === Token::LPAREN) {
264
+            $end    = ($this->token === Token::LPAREN) ? Token::RPAREN : $this->token;
265
+            $buf    = '';
266
+            $escape = false;
267 267
 
268
-			$this->nextToken(); // Skip the opening quote/paren
268
+            $this->nextToken(); // Skip the opening quote/paren
269 269
 
270
-			// The second conjunct is probably not necessary.
271
-			while ($this->token !== false && $this->token > -1) {
272
-				//print "Char: $this->value \n";
273
-				if ($this->token === Token::BSLASH && ! $escape) {
274
-					// XXX: The backslash (\) is removed here.
275
-					// Turn on escaping.
276
-					//$buf .= $this->value;
277
-					$escape = true;
278
-				} elseif ($escape) {
279
-					// Turn off escaping
280
-					$buf    .= $this->value;
281
-					$escape = false;
282
-				} // Allow nested pseudoclasses.
283
-				elseif ($this->token === Token::LPAREN) {
284
-					$buf .= '(';
285
-					$buf .= $this->getPseudoClassString();
286
-					$buf .= ')';
287
-				} elseif ($this->token === $end) {
288
-					// At end of string; skip token and break.
289
-					$this->nextToken();
290
-					break;
291
-				} else {
292
-					// Append char.
293
-					$buf .= $this->value;
294
-				}
295
-				$this->nextToken();
296
-			}
270
+            // The second conjunct is probably not necessary.
271
+            while ($this->token !== false && $this->token > -1) {
272
+                //print "Char: $this->value \n";
273
+                if ($this->token === Token::BSLASH && ! $escape) {
274
+                    // XXX: The backslash (\) is removed here.
275
+                    // Turn on escaping.
276
+                    //$buf .= $this->value;
277
+                    $escape = true;
278
+                } elseif ($escape) {
279
+                    // Turn off escaping
280
+                    $buf    .= $this->value;
281
+                    $escape = false;
282
+                } // Allow nested pseudoclasses.
283
+                elseif ($this->token === Token::LPAREN) {
284
+                    $buf .= '(';
285
+                    $buf .= $this->getPseudoClassString();
286
+                    $buf .= ')';
287
+                } elseif ($this->token === $end) {
288
+                    // At end of string; skip token and break.
289
+                    $this->nextToken();
290
+                    break;
291
+                } else {
292
+                    // Append char.
293
+                    $buf .= $this->value;
294
+                }
295
+                $this->nextToken();
296
+            }
297 297
 
298
-			return $buf;
299
-		}
300
-	}
298
+            return $buf;
299
+        }
300
+    }
301 301
 
302
-	/**
303
-	 * Get a string from the input stream.
304
-	 * This is a convenience function for getting a string of
305
-	 * characters that are either alphanumber or whitespace. See
306
-	 * the Token::white and Token::char definitions.
307
-	 *
308
-	 * @deprecated This is not used anywhere in QueryPath.
309
-	 *//*
302
+    /**
303
+     * Get a string from the input stream.
304
+     * This is a convenience function for getting a string of
305
+     * characters that are either alphanumber or whitespace. See
306
+     * the Token::white and Token::char definitions.
307
+     *
308
+     * @deprecated This is not used anywhere in QueryPath.
309
+     *//*
310 310
 	public function getStringPlusWhitespace() {
311 311
 	$buf = '';
312 312
 	if($this->token === FALSE) {return '';}
Please login to merge, or discard this patch.
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -233,14 +233,14 @@  discard block
 block discarded – undo
233 233
 			// The second conjunct is probably not necessary.
234 234
 			while ($this->token !== false && $this->token > -1) {
235 235
 				//print "Char: $this->value \n";
236
-				if ($this->token == Token::BSLASH && ! $escape) {
236
+				if ($this->token == Token::BSLASH && !$escape) {
237 237
 					// XXX: The backslash (\) is removed here.
238 238
 					// Turn on escaping.
239 239
 					//$buf .= $this->value;
240 240
 					$escape = true;
241 241
 				} elseif ($escape) {
242 242
 					// Turn off escaping
243
-					$buf    .= $this->value;
243
+					$buf .= $this->value;
244 244
 					$escape = false;
245 245
 				} elseif ($this->token === $end) {
246 246
 					// At end of string; skip token and break.
@@ -270,14 +270,14 @@  discard block
 block discarded – undo
270 270
 			// The second conjunct is probably not necessary.
271 271
 			while ($this->token !== false && $this->token > -1) {
272 272
 				//print "Char: $this->value \n";
273
-				if ($this->token === Token::BSLASH && ! $escape) {
273
+				if ($this->token === Token::BSLASH && !$escape) {
274 274
 					// XXX: The backslash (\) is removed here.
275 275
 					// Turn on escaping.
276 276
 					//$buf .= $this->value;
277 277
 					$escape = true;
278 278
 				} elseif ($escape) {
279 279
 					// Turn off escaping
280
-					$buf    .= $this->value;
280
+					$buf .= $this->value;
281 281
 					$escape = false;
282 282
 				} // Allow nested pseudoclasses.
283 283
 				elseif ($this->token === Token::LPAREN) {
Please login to merge, or discard this patch.
src/CSS/SimpleSelector.php 2 patches
Indentation   +121 added lines, -121 removed lines patch added patch discarded remove patch
@@ -32,133 +32,133 @@
 block discarded – undo
32 32
 class SimpleSelector
33 33
 {
34 34
 
35
-	public const ADJACENT = 0x1;
36
-	public const DIRECT_DESCENDANT = 0x2;
37
-	public const ANOTHER_SELECTOR = 0x4;
38
-	public const SIBLING = 0x8;
39
-	public const ANY_DESCENDANT = 0x10;
35
+    public const ADJACENT = 0x1;
36
+    public const DIRECT_DESCENDANT = 0x2;
37
+    public const ANOTHER_SELECTOR = 0x4;
38
+    public const SIBLING = 0x8;
39
+    public const ANY_DESCENDANT = 0x10;
40 40
 
41
-	public $element;
42
-	public $ns;
43
-	public $id;
44
-	public $classes = [];
45
-	public $attributes = [];
46
-	public $pseudoClasses = [];
47
-	public $pseudoElements = [];
48
-	public $combinator;
41
+    public $element;
42
+    public $ns;
43
+    public $id;
44
+    public $classes = [];
45
+    public $attributes = [];
46
+    public $pseudoClasses = [];
47
+    public $pseudoElements = [];
48
+    public $combinator;
49 49
 
50
-	/**
51
-	 * @param $code
52
-	 *
53
-	 * @return string
54
-	 */
55
-	public static function attributeOperator($code): string
56
-	{
57
-		switch ($code) {
58
-			case EventHandler::CONTAINS_WITH_SPACE:
59
-				return '~=';
60
-			case EventHandler::CONTAINS_WITH_HYPHEN:
61
-				return '|=';
62
-			case EventHandler::CONTAINS_IN_STRING:
63
-				return '*=';
64
-			case EventHandler::BEGINS_WITH:
65
-				return '^=';
66
-			case EventHandler::ENDS_WITH:
67
-				return '$=';
68
-			default:
69
-				return '=';
70
-		}
71
-	}
50
+    /**
51
+     * @param $code
52
+     *
53
+     * @return string
54
+     */
55
+    public static function attributeOperator($code): string
56
+    {
57
+        switch ($code) {
58
+            case EventHandler::CONTAINS_WITH_SPACE:
59
+                return '~=';
60
+            case EventHandler::CONTAINS_WITH_HYPHEN:
61
+                return '|=';
62
+            case EventHandler::CONTAINS_IN_STRING:
63
+                return '*=';
64
+            case EventHandler::BEGINS_WITH:
65
+                return '^=';
66
+            case EventHandler::ENDS_WITH:
67
+                return '$=';
68
+            default:
69
+                return '=';
70
+        }
71
+    }
72 72
 
73
-	/**
74
-	 * @param $code
75
-	 *
76
-	 * @return string
77
-	 */
78
-	public static function combinatorOperator($code): string
79
-	{
80
-		switch ($code) {
81
-			case self::ADJACENT:
82
-				return '+';
83
-			case self::DIRECT_DESCENDANT:
84
-				return '>';
85
-			case self::SIBLING:
86
-				return '~';
87
-			case self::ANOTHER_SELECTOR:
88
-				return ', ';
89
-			case self::ANY_DESCENDANT:
90
-				return '   ';
91
-			default:
92
-				return '';
93
-			break;
94
-		}
95
-	}
73
+    /**
74
+     * @param $code
75
+     *
76
+     * @return string
77
+     */
78
+    public static function combinatorOperator($code): string
79
+    {
80
+        switch ($code) {
81
+            case self::ADJACENT:
82
+                return '+';
83
+            case self::DIRECT_DESCENDANT:
84
+                return '>';
85
+            case self::SIBLING:
86
+                return '~';
87
+            case self::ANOTHER_SELECTOR:
88
+                return ', ';
89
+            case self::ANY_DESCENDANT:
90
+                return '   ';
91
+            default:
92
+                return '';
93
+            break;
94
+        }
95
+    }
96 96
 
97
-	public function __construct()
98
-	{
99
-	}
97
+    public function __construct()
98
+    {
99
+    }
100 100
 
101
-	/**
102
-	 * @return bool
103
-	 */
104
-	public function notEmpty(): bool
105
-	{
106
-		return ! empty($this->element)
107
-			   && ! empty($this->id)
108
-			   && ! empty($this->classes)
109
-			   && ! empty($this->combinator)
110
-			   && ! empty($this->attributes)
111
-			   && ! empty($this->pseudoClasses)
112
-			   && ! empty($this->pseudoElements);
113
-	}
101
+    /**
102
+     * @return bool
103
+     */
104
+    public function notEmpty(): bool
105
+    {
106
+        return ! empty($this->element)
107
+               && ! empty($this->id)
108
+               && ! empty($this->classes)
109
+               && ! empty($this->combinator)
110
+               && ! empty($this->attributes)
111
+               && ! empty($this->pseudoClasses)
112
+               && ! empty($this->pseudoElements);
113
+    }
114 114
 
115
-	public function __toString()
116
-	{
117
-		$buffer = [];
118
-		try {
119
-			if (! empty($this->ns)) {
120
-				$buffer[] = $this->ns;
121
-				$buffer[] = '|';
122
-			}
123
-			if (! empty($this->element)) {
124
-				$buffer[] = $this->element;
125
-			}
126
-			if (! empty($this->id)) {
127
-				$buffer[] = '#' . $this->id;
128
-			}
129
-			if (! empty($this->attributes)) {
130
-				foreach ($this->attributes as $attr) {
131
-					$buffer[] = '[';
132
-					if (! empty($attr['ns'])) {
133
-						$buffer[] = $attr['ns'] . '|';
134
-					}
135
-					$buffer[] = $attr['name'];
136
-					if (! empty($attr['value'])) {
137
-						$buffer[] = self::attributeOperator($attr['op']);
138
-						$buffer[] = $attr['value'];
139
-					}
140
-					$buffer[] = ']';
141
-				}
142
-			}
143
-			if (! empty($this->pseudoClasses)) {
144
-				foreach ($this->pseudoClasses as $ps) {
145
-					$buffer[] = ':' . $ps['name'];
146
-					if (isset($ps['value'])) {
147
-						$buffer[] = '(' . $ps['value'] . ')';
148
-					}
149
-				}
150
-			}
151
-			foreach ($this->pseudoElements as $pe) {
152
-				$buffer[] = '::' . $pe;
153
-			}
115
+    public function __toString()
116
+    {
117
+        $buffer = [];
118
+        try {
119
+            if (! empty($this->ns)) {
120
+                $buffer[] = $this->ns;
121
+                $buffer[] = '|';
122
+            }
123
+            if (! empty($this->element)) {
124
+                $buffer[] = $this->element;
125
+            }
126
+            if (! empty($this->id)) {
127
+                $buffer[] = '#' . $this->id;
128
+            }
129
+            if (! empty($this->attributes)) {
130
+                foreach ($this->attributes as $attr) {
131
+                    $buffer[] = '[';
132
+                    if (! empty($attr['ns'])) {
133
+                        $buffer[] = $attr['ns'] . '|';
134
+                    }
135
+                    $buffer[] = $attr['name'];
136
+                    if (! empty($attr['value'])) {
137
+                        $buffer[] = self::attributeOperator($attr['op']);
138
+                        $buffer[] = $attr['value'];
139
+                    }
140
+                    $buffer[] = ']';
141
+                }
142
+            }
143
+            if (! empty($this->pseudoClasses)) {
144
+                foreach ($this->pseudoClasses as $ps) {
145
+                    $buffer[] = ':' . $ps['name'];
146
+                    if (isset($ps['value'])) {
147
+                        $buffer[] = '(' . $ps['value'] . ')';
148
+                    }
149
+                }
150
+            }
151
+            foreach ($this->pseudoElements as $pe) {
152
+                $buffer[] = '::' . $pe;
153
+            }
154 154
 
155
-			if (! empty($this->combinator)) {
156
-				$buffer[] = self::combinatorOperator($this->combinator);
157
-			}
158
-		} catch (Exception $e) {
159
-			return $e->getMessage();
160
-		}
155
+            if (! empty($this->combinator)) {
156
+                $buffer[] = self::combinatorOperator($this->combinator);
157
+            }
158
+        } catch (Exception $e) {
159
+            return $e->getMessage();
160
+        }
161 161
 
162
-		return implode('', $buffer);
163
-	}
162
+        return implode('', $buffer);
163
+    }
164 164
 }
Please login to merge, or discard this patch.
Spacing   +15 added lines, -15 removed lines patch added patch discarded remove patch
@@ -103,44 +103,44 @@  discard block
 block discarded – undo
103 103
 	 */
104 104
 	public function notEmpty(): bool
105 105
 	{
106
-		return ! empty($this->element)
107
-			   && ! empty($this->id)
108
-			   && ! empty($this->classes)
109
-			   && ! empty($this->combinator)
110
-			   && ! empty($this->attributes)
111
-			   && ! empty($this->pseudoClasses)
112
-			   && ! empty($this->pseudoElements);
106
+		return !empty($this->element)
107
+			   && !empty($this->id)
108
+			   && !empty($this->classes)
109
+			   && !empty($this->combinator)
110
+			   && !empty($this->attributes)
111
+			   && !empty($this->pseudoClasses)
112
+			   && !empty($this->pseudoElements);
113 113
 	}
114 114
 
115 115
 	public function __toString()
116 116
 	{
117 117
 		$buffer = [];
118 118
 		try {
119
-			if (! empty($this->ns)) {
119
+			if (!empty($this->ns)) {
120 120
 				$buffer[] = $this->ns;
121 121
 				$buffer[] = '|';
122 122
 			}
123
-			if (! empty($this->element)) {
123
+			if (!empty($this->element)) {
124 124
 				$buffer[] = $this->element;
125 125
 			}
126
-			if (! empty($this->id)) {
126
+			if (!empty($this->id)) {
127 127
 				$buffer[] = '#' . $this->id;
128 128
 			}
129
-			if (! empty($this->attributes)) {
129
+			if (!empty($this->attributes)) {
130 130
 				foreach ($this->attributes as $attr) {
131 131
 					$buffer[] = '[';
132
-					if (! empty($attr['ns'])) {
132
+					if (!empty($attr['ns'])) {
133 133
 						$buffer[] = $attr['ns'] . '|';
134 134
 					}
135 135
 					$buffer[] = $attr['name'];
136
-					if (! empty($attr['value'])) {
136
+					if (!empty($attr['value'])) {
137 137
 						$buffer[] = self::attributeOperator($attr['op']);
138 138
 						$buffer[] = $attr['value'];
139 139
 					}
140 140
 					$buffer[] = ']';
141 141
 				}
142 142
 			}
143
-			if (! empty($this->pseudoClasses)) {
143
+			if (!empty($this->pseudoClasses)) {
144 144
 				foreach ($this->pseudoClasses as $ps) {
145 145
 					$buffer[] = ':' . $ps['name'];
146 146
 					if (isset($ps['value'])) {
@@ -152,7 +152,7 @@  discard block
 block discarded – undo
152 152
 				$buffer[] = '::' . $pe;
153 153
 			}
154 154
 
155
-			if (! empty($this->combinator)) {
155
+			if (!empty($this->combinator)) {
156 156
 				$buffer[] = self::combinatorOperator($this->combinator);
157 157
 			}
158 158
 		} catch (Exception $e) {
Please login to merge, or discard this patch.