From 60a2fcd9a8a9dfabdb614048b3c06e98d5c55c09 Mon Sep 17 00:00:00 2001 From: Tony Bogdanov Date: Sun, 1 Apr 2018 22:13:21 +0300 Subject: [PATCH] Added wrapInner() and some more tests --- classes/Dom.php | 27 +- .../SelectorMatcher/AttributeNodeTrait.php | 9 + classes/SelectorMatcher/ClassNodeTrait.php | 9 + .../CombinedSelectorNodeTrait.php | 9 + classes/SelectorMatcher/ElementNodeTrait.php | 9 + classes/SelectorMatcher/HashNodeTrait.php | 9 + docs/coverage/Dom.php.html | 660 ++++++++++-------- docs/coverage/Node/CData.php.html | 2 +- docs/coverage/Node/Comment.php.html | 2 +- docs/coverage/Node/DocType.php.html | 2 +- docs/coverage/Node/Element.php.html | 2 +- docs/coverage/Node/NodeInterface.php.html | 2 +- docs/coverage/Node/Text.php.html | 2 +- docs/coverage/Node/dashboard.html | 2 +- docs/coverage/Node/index.html | 2 +- docs/coverage/SelectorMatcher.php.html | 2 +- .../AttributeNodeTrait.php.html | 250 ++++--- .../SelectorMatcher/ClassNodeTrait.php.html | 94 ++- .../CombinedSelectorNodeTrait.php.html | 452 ++++++------ .../SelectorMatcher/ElementNodeTrait.php.html | 90 ++- .../SelectorMatcher/HashNodeTrait.php.html | 92 ++- docs/coverage/SelectorMatcher/dashboard.html | 8 +- docs/coverage/SelectorMatcher/index.html | 2 +- docs/coverage/dashboard.html | 36 +- docs/coverage/index.html | 36 +- docs/docs/class-SDom.Dom.html | 52 +- docs/docs/class-SDom.SelectorMatcher.html | 15 + docs/docs/source-class-SDom.Dom.html | 523 +++++++------- ...om.SelectorMatcher.AttributeNodeTrait.html | 235 ++++--- ...t-SDom.SelectorMatcher.ClassNodeTrait.html | 79 ++- ...ctorMatcher.CombinedSelectorNodeTrait.html | 429 ++++++------ ...SDom.SelectorMatcher.ElementNodeTrait.html | 75 +- ...it-SDom.SelectorMatcher.HashNodeTrait.html | 77 +- ...om.SelectorMatcher.AttributeNodeTrait.html | 33 +- ...t-SDom.SelectorMatcher.ClassNodeTrait.html | 33 +- ...ctorMatcher.CombinedSelectorNodeTrait.html | 41 +- ...SDom.SelectorMatcher.ElementNodeTrait.html | 33 +- ...it-SDom.SelectorMatcher.HashNodeTrait.html | 33 +- tests/classes/DomTest.php | 268 +++++++ tests/classes/Node/ElementTest.php | 3 + 40 files changed, 2242 insertions(+), 1497 deletions(-) diff --git a/classes/Dom.php b/classes/Dom.php index a7a2ec7..86fc9c6 100644 --- a/classes/Dom.php +++ b/classes/Dom.php @@ -624,6 +624,24 @@ public function wrap($content): Dom return $this; } + /** + * Wrap a clone of the supplied content around each child node (not only element nodes) of nodes in the collection. + * + * This function works exactly like wrap() except it wraps the children of the nodes in the collection instead. + * + * Return the original collection for chaining. + * + * @param $content + * @return Dom + */ + public function wrapInner($content): Dom + { + foreach ($this->children() as $child) { + $child->wrap($content); + } + return $this; + } + /** * Return a new Dom collection of all the descendants of each Element node in the current collection, * filtered by the specified CSS selector. @@ -662,13 +680,15 @@ public function find(string $selector): Dom */ public function addClass(string $className): Dom { - $addClasses = preg_split('/\s+/', $className); + $className = trim($className); // bail if no classes to add - if (0 === count($addClasses)) { + if ('' === $className) { return $this; } + $addClasses = preg_split('/\s+/', $className); + /** @var NodeInterface $node */ foreach ($this->nodes as $node) { if (!$node instanceof Element) { @@ -677,7 +697,8 @@ public function addClass(string $className): Dom // if the node already has a "class" attribute, merge all classes & make sure the result is unique if ($node->hasAttribute('class')) { - $currentClasses = preg_split('/\s+/', $node->getAttribute('class')); + $currentClassName = trim($node->getAttribute('class')); + $currentClasses = '' === $currentClassName ? [] : preg_split('/\s+/', $currentClassName); $node->setAttribute('class', implode(' ', array_unique(array_merge($currentClasses, $addClasses)))); } diff --git a/classes/SelectorMatcher/AttributeNodeTrait.php b/classes/SelectorMatcher/AttributeNodeTrait.php index 895465b..b73ed9a 100644 --- a/classes/SelectorMatcher/AttributeNodeTrait.php +++ b/classes/SelectorMatcher/AttributeNodeTrait.php @@ -3,6 +3,7 @@ namespace SDom\SelectorMatcher; use SDom\Node\Element; +use SDom\Node\NodeInterface; use SDom\SelectorMatcher; use Symfony\Component\CssSelector\Node\AttributeNode; @@ -114,4 +115,12 @@ protected function matchAttributeNode(AttributeNode $token, Element $node, Eleme return $this->match($token->getSelector(), $node, $effectiveRoot); } + + /** + * @param NodeInterface $token + * @param Element $node + * @param Element|null $effectiveRoot + * @return bool + */ + abstract public function match(NodeInterface $token, Element $node, Element $effectiveRoot = null): bool; } \ No newline at end of file diff --git a/classes/SelectorMatcher/ClassNodeTrait.php b/classes/SelectorMatcher/ClassNodeTrait.php index 536ea89..cfe7e6b 100644 --- a/classes/SelectorMatcher/ClassNodeTrait.php +++ b/classes/SelectorMatcher/ClassNodeTrait.php @@ -3,6 +3,7 @@ namespace SDom\SelectorMatcher; use SDom\Node\Element; +use SDom\Node\NodeInterface; use SDom\SelectorMatcher; use Symfony\Component\CssSelector\Node\ClassNode; @@ -36,4 +37,12 @@ protected function matchClassNode(ClassNode $token, Element $node, Element $effe return $this->match($token->getSelector(), $node, $effectiveRoot); } + + /** + * @param NodeInterface $token + * @param Element $node + * @param Element|null $effectiveRoot + * @return bool + */ + abstract public function match(NodeInterface $token, Element $node, Element $effectiveRoot = null): bool; } \ No newline at end of file diff --git a/classes/SelectorMatcher/CombinedSelectorNodeTrait.php b/classes/SelectorMatcher/CombinedSelectorNodeTrait.php index acd847a..cb02edb 100644 --- a/classes/SelectorMatcher/CombinedSelectorNodeTrait.php +++ b/classes/SelectorMatcher/CombinedSelectorNodeTrait.php @@ -3,6 +3,7 @@ namespace SDom\SelectorMatcher; use SDom\Node\Element; +use SDom\Node\NodeInterface; use Symfony\Component\CssSelector\Node\CombinedSelectorNode; /** @@ -211,4 +212,12 @@ protected function matchCombinedSelectorNode( )); } } + + /** + * @param NodeInterface $token + * @param Element $node + * @param Element|null $effectiveRoot + * @return bool + */ + abstract public function match(NodeInterface $token, Element $node, Element $effectiveRoot = null): bool; } \ No newline at end of file diff --git a/classes/SelectorMatcher/ElementNodeTrait.php b/classes/SelectorMatcher/ElementNodeTrait.php index af0b615..d0c500d 100644 --- a/classes/SelectorMatcher/ElementNodeTrait.php +++ b/classes/SelectorMatcher/ElementNodeTrait.php @@ -3,6 +3,7 @@ namespace SDom\SelectorMatcher; use SDom\Node\Element; +use SDom\Node\NodeInterface; use Symfony\Component\CssSelector\Node\ElementNode; /** @@ -34,4 +35,12 @@ protected function matchElementNode(ElementNode $token, Element $node): bool // node tag name must match return $node->getTag() === $token->getElement(); } + + /** + * @param NodeInterface $token + * @param Element $node + * @param Element|null $effectiveRoot + * @return bool + */ + abstract public function match(NodeInterface $token, Element $node, Element $effectiveRoot = null): bool; } \ No newline at end of file diff --git a/classes/SelectorMatcher/HashNodeTrait.php b/classes/SelectorMatcher/HashNodeTrait.php index 46198b2..0a60233 100644 --- a/classes/SelectorMatcher/HashNodeTrait.php +++ b/classes/SelectorMatcher/HashNodeTrait.php @@ -3,6 +3,7 @@ namespace SDom\SelectorMatcher; use SDom\Node\Element; +use SDom\Node\NodeInterface; use Symfony\Component\CssSelector\Node\HashNode; /** @@ -35,4 +36,12 @@ protected function matchHashNode(HashNode $token, Element $node, Element $effect return $this->match($token->getSelector(), $node, $effectiveRoot); } + + /** + * @param NodeInterface $token + * @param Element $node + * @param Element|null $effectiveRoot + * @return bool + */ + abstract public function match(NodeInterface $token, Element $node, Element $effectiveRoot = null): bool; } \ No newline at end of file diff --git a/docs/coverage/Dom.php.html b/docs/coverage/Dom.php.html index 76c8c34..85ae0aa 100644 --- a/docs/coverage/Dom.php.html +++ b/docs/coverage/Dom.php.html @@ -51,22 +51,22 @@
0.00%
0 / 1
-
- 57.14% covered (warning) +
+ 68.97% covered (warning)
-
57.14%
-
16 / 28
+
68.97%
+
20 / 29
CRAP
-
- 60.49% covered (warning) +
+ 73.39% covered (warning)
-
60.49%
-
147 / 243
+
73.39%
+
182 / 248
@@ -80,22 +80,22 @@
0.00%
0 / 1
-
- 57.14% covered (warning) +
+ 68.97% covered (warning)
-
57.14%
-
16 / 28
- 1276.44 +
68.97%
+
20 / 29
+ 503.17
-
- 60.49% covered (warning) +
+ 73.39% covered (warning)
-
60.49%
-
147 / 243
+
73.39%
+
182 / 248
@@ -535,7 +535,28 @@ -  find +  wrapInner +
+
+ 100.00% covered (success) +
+
+ +
100.00%
+
1 / 1
+ 2 +
+
+ 100.00% covered (success) +
+
+ +
100.00%
+
3 / 3
+ + + +  find
100.00% covered (success) @@ -556,70 +577,70 @@ -  addClass -
-
- 0.00% covered (danger) +  addClass +
+
+ 100.00% covered (success)
-
0.00%
-
0 / 1
- 30 -
-
- 0.00% covered (danger) +
100.00%
+
1 / 1
+ 6 +
+
+ 100.00% covered (success)
-
0.00%
-
0 / 11
+
100.00%
+
13 / 13
-  removeClass -
-
- 0.00% covered (danger) +  removeClass +
+
+ 100.00% covered (success)
-
0.00%
-
0 / 1
- 72 -
-
- 0.00% covered (danger) +
100.00%
+
1 / 1
+ 8 +
+
+ 100.00% covered (success)
-
0.00%
-
0 / 13
+
100.00%
+
13 / 13
-  hasClass -
-
- 0.00% covered (danger) +  hasClass +
+
+ 100.00% covered (success)
-
0.00%
-
0 / 1
- 30 -
-
- 0.00% covered (danger) +
100.00%
+
1 / 1
+ 5 +
+
+ 100.00% covered (success)
-
0.00%
-
0 / 6
+
100.00%
+
6 / 6
-  attr +  attr
0.00% covered (danger) @@ -640,7 +661,7 @@ -  removeAttr +  removeAttr
0.00% covered (danger) @@ -661,7 +682,7 @@ -  text +  text
0.00% covered (danger) @@ -682,7 +703,7 @@ -  html +  html
0.00% covered (danger) @@ -1334,266 +1355,287 @@     }     /** -      * Return a new Dom collection of all the descendants of each Element node in the current collection, -      * filtered by the specified CSS selector. -      * -      * @param string $selector -      * @return Dom -      */ -     public function find(string $selector): Dom -     { -         if (!isset(self::$selectorParser)) { -             self::$selectorParser = new Parser(); -         } - -         if (!isset(self::$selectorMatcher)) { -             self::$selectorMatcher = new SelectorMatcher(); -         } - -         $dom = new static(); -         $selectorTokens = self::$selectorParser->parse($selector); - -         foreach ($this->get() as $rootNode) { -             /** @var NodeInterface $childNode */ -             foreach ($rootNode as $childNode) { -                 self::traverseMatch($dom, $selectorTokens, $childNode, $rootNode); -             } -         } - -         return $dom; -     } - -     /** -      * Adds the specified class(es) to each Element node in the collection. -      * -      * @param string $className -      * @return Dom -      */ -     public function addClass(string $className): Dom -     { -         $addClasses = preg_split('/\s+/', $className); - -         // bail if no classes to add -         if (0 === count($addClasses)) { -             return $this; +      * Wrap a clone of the supplied content around each child node (not only element nodes) of nodes in the collection. +      * +      * This function works exactly like wrap() except it wraps the children of the nodes in the collection instead. +      * +      * Return the original collection for chaining. +      * +      * @param $content +      * @return Dom +      */ +     public function wrapInner($content): Dom +     { +         foreach ($this->children() as $child) { +             $child->wrap($content); +         } +         return $this; +     } + +     /** +      * Return a new Dom collection of all the descendants of each Element node in the current collection, +      * filtered by the specified CSS selector. +      * +      * @param string $selector +      * @return Dom +      */ +     public function find(string $selector): Dom +     { +         if (!isset(self::$selectorParser)) { +             self::$selectorParser = new Parser(); +         } + +         if (!isset(self::$selectorMatcher)) { +             self::$selectorMatcher = new SelectorMatcher(); +         } + +         $dom = new static(); +         $selectorTokens = self::$selectorParser->parse($selector); + +         foreach ($this->get() as $rootNode) { +             /** @var NodeInterface $childNode */ +             foreach ($rootNode as $childNode) { +                 self::traverseMatch($dom, $selectorTokens, $childNode, $rootNode); +             }         } -         /** @var NodeInterface $node */ -         foreach ($this->nodes as $node) { -             if (!$node instanceof Element) { -                 continue; -             } - -             // if the node already has a "class" attribute, merge all classes & make sure the result is unique -             if ($node->hasAttribute('class')) { -                 $currentClasses = preg_split('/\s+/', $node->getAttribute('class')); -                 $node->setAttribute('class', implode(' ', array_unique(array_merge($currentClasses, $addClasses)))); -             } - -             // if the node does not have a "class" attribute, directly set the new ones -             else { -                 $node->setAttribute('class', implode(' ', $addClasses)); -             } +         return $dom; +     } + +     /** +      * Adds the specified class(es) to each Element node in the collection. +      * +      * @param string $className +      * @return Dom +      */ +     public function addClass(string $className): Dom +     { +         $className = trim($className); + +         // bail if no classes to add +         if ('' === $className) { +             return $this;         } -         return $this; -     } - -     /** -      * Remove a single class, multiple classes, or all classes from each Element node in the collection. -      * -      * @param string|null $className -      * @return Dom -      */ -     public function removeClass(string $className = null): Dom -     { -         // if class to remove isn't set, remove all classes, but keep the "class" attribute present -         if (!isset($className)) { -             /** @var NodeInterface $node */ -             foreach ($this->nodes as $node) { -                 if (!$node instanceof Element || !$node->hasAttribute('class')) { -                     continue; -                 } - -                 $node->setAttribute('class', ''); -             } - -             return $this; -         } - -         $removeClasses = preg_split('/\s+/', $className); - -         /** @var NodeInterface $node */ -         foreach ($this->nodes as $node) { -             if (!$node instanceof Element || !$node->hasAttribute('class')) { -                 continue; -             } - -             // set to the difference between the current classes and the remove ones -             $currentClasses = preg_split('/\s+/', $node->getAttribute('class')); -             $node->setAttribute('class', implode(' ', array_diff($currentClasses, $removeClasses))); -         } - -         return $this; -     } - -     /** -      * Return TRUE if at least one of the Element nodes in the collection has the specified class assigned. -      * -      * @param string $className -      * @return bool -      */ -     public function hasClass(string $className): bool -     { -         /** @var NodeInterface $node */ -         foreach ($this->nodes as $node) { -             if (!$node instanceof Element || !$node->hasAttribute('class')) { -                 continue; -             } - -             if (SelectorMatcher::containsWord($className, $node->getAttribute('class'))) { -                 return true; -             } -         } - -         return false; -     } - -     /** -      * Get the value of an attribute for the first Element node in the collection. -      * Set the value of an attribute for each Element node in the collection. -      * -      * @param string $name -      * @param string|null $value -      * @return string|null|$this -      */ -     public function attr(string $name, string $value = null) -     { -         if (isset($value)) { -             /** @var NodeInterface $node */ -             foreach ($this->nodes as $node) { -                 if (!$node instanceof Element) { -                     continue; -                 } - -                 $node->setAttribute($name, $value); -             } - -             return $this; -         } - -         /** @var NodeInterface $node */ -         foreach ($this->nodes as $node) { -             if (!$node instanceof Element) { -                 continue; -             } - -             return $node->getAttribute($name); -         } - -         return null; -     } - -     /** -      * Remove an attribute from each Element node in the collection. -      * -      * @param string $name -      * @return Dom -      */ -     public function removeAttr(string $name): Dom -     { -         /** @var NodeInterface $node */ -         foreach ($this->nodes as $node) { -             if (!$node instanceof Element) { -                 continue; -             } - -             $node->removeAttribute($name); -         } - -         return $this; -     } - -     /** -      * Get the combined text contents of each Element node in the collection, including their descendants. -      * Set the content of each Element node in the collection to the specified text. +         $addClasses = preg_split('/\s+/', $className); + +         /** @var NodeInterface $node */ +         foreach ($this->nodes as $node) { +             if (!$node instanceof Element) { +                 continue; +             } + +             // if the node already has a "class" attribute, merge all classes & make sure the result is unique +             if ($node->hasAttribute('class')) { +                 $currentClassName = trim($node->getAttribute('class')); +                 $currentClasses = '' === $currentClassName ? [] : preg_split('/\s+/', $currentClassName); +                 $node->setAttribute('class', implode(' ', array_unique(array_merge($currentClasses, $addClasses)))); +             } + +             // if the node does not have a "class" attribute, directly set the new ones +             else { +                 $node->setAttribute('class', implode(' ', $addClasses)); +             } +         } + +         return $this; +     } + +     /** +      * Remove a single class, multiple classes, or all classes from each Element node in the collection. +      * +      * @param string|null $className +      * @return Dom +      */ +     public function removeClass(string $className = null): Dom +     { +         // if class to remove isn't set, remove all classes, but keep the "class" attribute present +         if (!isset($className)) { +             /** @var NodeInterface $node */ +             foreach ($this->nodes as $node) { +                 if (!$node instanceof Element || !$node->hasAttribute('class')) { +                     continue; +                 } + +                 $node->setAttribute('class', ''); +             } + +             return $this; +         } + +         $removeClasses = preg_split('/\s+/', $className); + +         /** @var NodeInterface $node */ +         foreach ($this->nodes as $node) { +             if (!$node instanceof Element || !$node->hasAttribute('class')) { +                 continue; +             } + +             // set to the difference between the current classes and the remove ones +             $currentClasses = preg_split('/\s+/', $node->getAttribute('class')); +             $node->setAttribute('class', implode(' ', array_diff($currentClasses, $removeClasses))); +         } + +         return $this; +     } + +     /** +      * Return TRUE if at least one of the Element nodes in the collection has the specified class assigned. +      * +      * @param string $className +      * @return bool +      */ +     public function hasClass(string $className): bool +     { +         /** @var NodeInterface $node */ +         foreach ($this->nodes as $node) { +             if (!$node instanceof Element || !$node->hasAttribute('class')) { +                 continue; +             } + +             if (SelectorMatcher::containsWord($className, $node->getAttribute('class'))) { +                 return true; +             } +         } + +         return false; +     } + +     /** +      * Get the value of an attribute for the first Element node in the collection. +      * Set the value of an attribute for each Element node in the collection. +      * +      * @param string $name +      * @param string|null $value +      * @return string|null|$this +      */ +     public function attr(string $name, string $value = null) +     { +         if (isset($value)) { +             /** @var NodeInterface $node */ +             foreach ($this->nodes as $node) { +                 if (!$node instanceof Element) { +                     continue; +                 } + +                 $node->setAttribute($name, $value); +             } + +             return $this; +         } + +         /** @var NodeInterface $node */ +         foreach ($this->nodes as $node) { +             if (!$node instanceof Element) { +                 continue; +             } + +             return $node->getAttribute($name); +         } + +         return null; +     } + +     /** +      * Remove an attribute from each Element node in the collection.      * -      * @param string|null $text -      * @return $this|string +      * @param string $name +      * @return Dom      */ -     public function text(string $text = null) +     public function removeAttr(string $name): Dom     { -         if (isset($text)) { -             foreach ($this->nodes as $node) { -                 if (!$node instanceof Element) { -                     continue; -                 } +         /** @var NodeInterface $node */ +         foreach ($this->nodes as $node) { +             if (!$node instanceof Element) { +                 continue; +             } -                 $node->clear()->insertAfter(new Text($text)); -             } +             $node->removeAttribute($name); +         } -             return $this; -         } +         return $this; +     } -         $text = ''; - -         foreach ($this->nodes as $node) { -             if ($node instanceof Text) { -                 $text .= (string) $node; -             } else if ($node instanceof Element) { -                 /** @var Dom $dom */ -                 foreach ((new static($node))->children() as $dom) { -                     $text .= $dom->text(); -                 } -             } -         } - -         return $text; -     } - -     /** -      * Get the HTML contents of the first Element node in the collection. -      * Set the HTML contents of each Element node in the collection. -      * -      * @param string|null $html -      * @return $this|string -      */ -     public function html(string $html = null) -     { -         if (isset($html)) { -             foreach ($this->nodes as $node) { -                 if (!$node instanceof Element) { -                     continue; -                 } - -                 $node->clear(); - -                 foreach ((new static($html))->nodes as $newNode) { -                     $node->insertAfter($newNode); -                 } -             } - -             return $this; -         } - -         foreach ($this->nodes as $node) { -             if (!$node instanceof Element) { -                 continue; -             } - -             $html = ''; - -             /** @var NodeInterface $childNode */ -             foreach ($node as $childNode) { -                 $html .= (string) $childNode; -             } - -             return $html; -         } - -         return ''; -     } - } +     /** +      * Get the combined text contents of each Element node in the collection, including their descendants. +      * Set the content of each Element node in the collection to the specified text. +      * +      * @param string|null $text +      * @return $this|string +      */ +     public function text(string $text = null) +     { +         if (isset($text)) { +             foreach ($this->nodes as $node) { +                 if (!$node instanceof Element) { +                     continue; +                 } + +                 $node->clear()->insertAfter(new Text($text)); +             } + +             return $this; +         } + +         $text = ''; + +         foreach ($this->nodes as $node) { +             if ($node instanceof Text) { +                 $text .= (string) $node; +             } else if ($node instanceof Element) { +                 /** @var Dom $dom */ +                 foreach ((new static($node))->children() as $dom) { +                     $text .= $dom->text(); +                 } +             } +         } + +         return $text; +     } + +     /** +      * Get the HTML contents of the first Element node in the collection. +      * Set the HTML contents of each Element node in the collection. +      * +      * @param string|null $html +      * @return $this|string +      */ +     public function html(string $html = null) +     { +         if (isset($html)) { +             foreach ($this->nodes as $node) { +                 if (!$node instanceof Element) { +                     continue; +                 } + +                 $node->clear(); + +                 foreach ((new static($html))->nodes as $newNode) { +                     $node->insertAfter($newNode); +                 } +             } + +             return $this; +         } + +         foreach ($this->nodes as $node) { +             if (!$node instanceof Element) { +                 continue; +             } + +             $html = ''; + +             /** @var NodeInterface $childNode */ +             foreach ($node as $childNode) { +                 $html .= (string) $childNode; +             } + +             return $html; +         } + +         return ''; +     } + } @@ -1606,7 +1648,7 @@

Legend

Dead Code

- Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 16:56:52 EEST 2018. + Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 22:08:03 EEST 2018.

diff --git a/docs/coverage/Node/CData.php.html b/docs/coverage/Node/CData.php.html index d8819af..f64f67b 100644 --- a/docs/coverage/Node/CData.php.html +++ b/docs/coverage/Node/CData.php.html @@ -374,7 +374,7 @@

Legend

Dead Code

- Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 16:56:52 EEST 2018. + Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 22:08:03 EEST 2018.

diff --git a/docs/coverage/Node/Comment.php.html b/docs/coverage/Node/Comment.php.html index 820a573..01c1213 100644 --- a/docs/coverage/Node/Comment.php.html +++ b/docs/coverage/Node/Comment.php.html @@ -374,7 +374,7 @@

Legend

Dead Code

- Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 16:56:52 EEST 2018. + Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 22:08:03 EEST 2018.

diff --git a/docs/coverage/Node/DocType.php.html b/docs/coverage/Node/DocType.php.html index f4e8c36..bcd1895 100644 --- a/docs/coverage/Node/DocType.php.html +++ b/docs/coverage/Node/DocType.php.html @@ -346,7 +346,7 @@

Legend

Dead Code

- Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 16:56:52 EEST 2018. + Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 22:08:03 EEST 2018.

diff --git a/docs/coverage/Node/Element.php.html b/docs/coverage/Node/Element.php.html index 83191da..9e8afb7 100644 --- a/docs/coverage/Node/Element.php.html +++ b/docs/coverage/Node/Element.php.html @@ -989,7 +989,7 @@

Legend

Dead Code

- Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 16:56:52 EEST 2018. + Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 22:08:03 EEST 2018.

diff --git a/docs/coverage/Node/NodeInterface.php.html b/docs/coverage/Node/NodeInterface.php.html index f7bb601..ed5ea48 100644 --- a/docs/coverage/Node/NodeInterface.php.html +++ b/docs/coverage/Node/NodeInterface.php.html @@ -137,7 +137,7 @@

Legend

Dead Code

- Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 16:56:52 EEST 2018. + Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 22:08:03 EEST 2018.

diff --git a/docs/coverage/Node/Text.php.html b/docs/coverage/Node/Text.php.html index 6204e61..1fa9c58 100644 --- a/docs/coverage/Node/Text.php.html +++ b/docs/coverage/Node/Text.php.html @@ -377,7 +377,7 @@

Legend

Dead Code

- Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 16:56:52 EEST 2018. + Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 22:08:03 EEST 2018.

diff --git a/docs/coverage/Node/dashboard.html b/docs/coverage/Node/dashboard.html index 5dc5e73..c6ac53e 100644 --- a/docs/coverage/Node/dashboard.html +++ b/docs/coverage/Node/dashboard.html @@ -137,7 +137,7 @@

Project Risks

diff --git a/docs/coverage/Node/index.html b/docs/coverage/Node/index.html index 7ce8755..d9b6dd4 100644 --- a/docs/coverage/Node/index.html +++ b/docs/coverage/Node/index.html @@ -234,7 +234,7 @@

Legend

High: 90% to 100%

- Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 16:56:52 EEST 2018. + Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 22:08:03 EEST 2018.

diff --git a/docs/coverage/SelectorMatcher.php.html b/docs/coverage/SelectorMatcher.php.html index f0dbd0e..3a36134 100644 --- a/docs/coverage/SelectorMatcher.php.html +++ b/docs/coverage/SelectorMatcher.php.html @@ -240,7 +240,7 @@

Legend

Dead Code

- Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 16:56:52 EEST 2018. + Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 22:08:03 EEST 2018.

diff --git a/docs/coverage/SelectorMatcher/AttributeNodeTrait.php.html b/docs/coverage/SelectorMatcher/AttributeNodeTrait.php.html index abbd4ba..d4f1537 100644 --- a/docs/coverage/SelectorMatcher/AttributeNodeTrait.php.html +++ b/docs/coverage/SelectorMatcher/AttributeNodeTrait.php.html @@ -88,7 +88,7 @@
100.00%
1 / 1
- 16 + 17
100.00% covered (success) @@ -100,7 +100,7 @@ -  matchAttributeNode +  matchAttributeNode
100.00% covered (success) @@ -120,6 +120,17 @@
38 / 38
+ +  match + +
n/a
+
0 / 0
+ 1 + +
n/a
+
0 / 0
+ + @@ -130,118 +141,127 @@ namespace SDom\SelectorMatcher; use SDom\Node\Element; - use SDom\SelectorMatcher; - use Symfony\Component\CssSelector\Node\AttributeNode; - - /** -  * @pattern E[foo] -  * @meaning an E element with a "foo" attribute -  * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors -  * -  * @pattern E[foo="bar"] -  * @meaning an E element whose "foo" attribute value is exactly equal to "bar" -  * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors -  * -  * @pattern E[foo~="bar"] -  * @meaning an E element whose "foo" attribute value is a list of whitespace-separated values, one of which is -  * exactly equal to "bar" -  * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors -  * -  * @pattern E[foo^="bar"] -  * @meaning an E element whose "foo" attribute value begins exactly with the string "bar" -  * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors -  * -  * @pattern E[foo$="bar"] -  * @meaning an E element whose "foo" attribute value ends exactly with the string "bar" -  * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors -  * -  * @pattern E[foo*="bar"] -  * @meaning an E element whose "foo" attribute value contains the substring "bar" -  * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors -  * -  * @pattern E[foo|="en"] -  * @meaning an E element whose "foo" attribute has a hyphen-separated list of values beginning (from the left) with "en" -  * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors -  * -  * Trait AttributeNodeTrait -  * @package SDom\SelectorMatcher -  */ - trait AttributeNodeTrait - { -     /** -      * @param AttributeNode $token -      * @param Element $node -      * @param null|Element $effectiveRoot -      * @return bool -      */ -     protected function matchAttributeNode(AttributeNode $token, Element $node, Element $effectiveRoot = null): bool -     { -         $attribute = $token->getAttribute(); - -         // node attribute must exist, regardless of operator -         if (!$node->hasAttribute($attribute)) { -             return false; -         } - -         $neededValue = $token->getValue(); -         $actualValue = $node->getAttribute($attribute); -         $operator = $token->getOperator(); - -         // if attribute operator is "exists", no further checks are needed -         if ('exists' === $operator) { -             return $this->match($token->getSelector(), $node, $effectiveRoot); -         } - -         switch ($token->getOperator()) { -             case '=': -                 if ($neededValue !== $actualValue) { -                     return false; -                 } -                 break; - -             case '~=': -                 if (!SelectorMatcher::containsWord($neededValue, $actualValue)) { -                     return false; -                 } -                 break; - -             case '^=': -                 if ($neededValue !== substr($actualValue, 0, strlen($neededValue))) { -                     return false; -                 } -                 break; - -             case '$=': -                 if ($neededValue !== substr($actualValue, -strlen($neededValue))) { -                     return false; -                 } -                 break; - -             case '*=': -                 if (false === strpos($actualValue, $neededValue)) { -                     return false; -                 } -                 break; - -             case '|=': -                 if ( -                     $neededValue !== $actualValue && -                     $neededValue . '-' !== substr($actualValue, 0, strlen($neededValue . '-')) -                 ) { -                     return false; -                 } -                 break; - -             default: -                 throw new \RuntimeException(sprintf( -                     'Invalid node attribute operator "%s".', -                     $token->getOperator() -                 )); -         } - -         return $this->match($token->getSelector(), $node, $effectiveRoot); -     } - } + use SDom\Node\NodeInterface; + use SDom\SelectorMatcher; + use Symfony\Component\CssSelector\Node\AttributeNode; + + /** +  * @pattern E[foo] +  * @meaning an E element with a "foo" attribute +  * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors +  * +  * @pattern E[foo="bar"] +  * @meaning an E element whose "foo" attribute value is exactly equal to "bar" +  * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors +  * +  * @pattern E[foo~="bar"] +  * @meaning an E element whose "foo" attribute value is a list of whitespace-separated values, one of which is +  * exactly equal to "bar" +  * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors +  * +  * @pattern E[foo^="bar"] +  * @meaning an E element whose "foo" attribute value begins exactly with the string "bar" +  * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors +  * +  * @pattern E[foo$="bar"] +  * @meaning an E element whose "foo" attribute value ends exactly with the string "bar" +  * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors +  * +  * @pattern E[foo*="bar"] +  * @meaning an E element whose "foo" attribute value contains the substring "bar" +  * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors +  * +  * @pattern E[foo|="en"] +  * @meaning an E element whose "foo" attribute has a hyphen-separated list of values beginning (from the left) with "en" +  * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors +  * +  * Trait AttributeNodeTrait +  * @package SDom\SelectorMatcher +  */ + trait AttributeNodeTrait + { +     /** +      * @param AttributeNode $token +      * @param Element $node +      * @param null|Element $effectiveRoot +      * @return bool +      */ +     protected function matchAttributeNode(AttributeNode $token, Element $node, Element $effectiveRoot = null): bool +     { +         $attribute = $token->getAttribute(); + +         // node attribute must exist, regardless of operator +         if (!$node->hasAttribute($attribute)) { +             return false; +         } + +         $neededValue = $token->getValue(); +         $actualValue = $node->getAttribute($attribute); +         $operator = $token->getOperator(); + +         // if attribute operator is "exists", no further checks are needed +         if ('exists' === $operator) { +             return $this->match($token->getSelector(), $node, $effectiveRoot); +         } + +         switch ($token->getOperator()) { +             case '=': +                 if ($neededValue !== $actualValue) { +                     return false; +                 } +                 break; + +             case '~=': +                 if (!SelectorMatcher::containsWord($neededValue, $actualValue)) { +                     return false; +                 } +                 break; + +             case '^=': +                 if ($neededValue !== substr($actualValue, 0, strlen($neededValue))) { +                     return false; +                 } +                 break; + +             case '$=': +                 if ($neededValue !== substr($actualValue, -strlen($neededValue))) { +                     return false; +                 } +                 break; + +             case '*=': +                 if (false === strpos($actualValue, $neededValue)) { +                     return false; +                 } +                 break; + +             case '|=': +                 if ( +                     $neededValue !== $actualValue && +                     $neededValue . '-' !== substr($actualValue, 0, strlen($neededValue . '-')) +                 ) { +                     return false; +                 } +                 break; + +             default: +                 throw new \RuntimeException(sprintf( +                     'Invalid node attribute operator "%s".', +                     $token->getOperator() +                 )); +         } + +         return $this->match($token->getSelector(), $node, $effectiveRoot); +     } + +     /** +      * @param NodeInterface $token +      * @param Element $node +      * @param Element|null $effectiveRoot +      * @return bool +      */ +     abstract public function match(NodeInterface $token, Element $node, Element $effectiveRoot = null): bool; + } @@ -254,7 +274,7 @@

Legend

Dead Code

- Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 16:56:52 EEST 2018. + Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 22:08:03 EEST 2018.

diff --git a/docs/coverage/SelectorMatcher/ClassNodeTrait.php.html b/docs/coverage/SelectorMatcher/ClassNodeTrait.php.html index ad13b19..f8820f4 100644 --- a/docs/coverage/SelectorMatcher/ClassNodeTrait.php.html +++ b/docs/coverage/SelectorMatcher/ClassNodeTrait.php.html @@ -88,7 +88,7 @@
100.00%
1 / 1
- 3 + 4
100.00% covered (success) @@ -100,7 +100,7 @@ -  matchClassNode +  matchClassNode
100.00% covered (success) @@ -120,6 +120,17 @@
5 / 5
+ +  match + +
n/a
+
0 / 0
+ 1 + +
n/a
+
0 / 0
+ + @@ -130,40 +141,49 @@ namespace SDom\SelectorMatcher; use SDom\Node\Element; - use SDom\SelectorMatcher; - use Symfony\Component\CssSelector\Node\ClassNode; - - /** -  * @pattern E.warning -  * @meaning an E element whose class is "warning" (the document language specifies how class is determined). -  * @link https://www.w3.org/TR/css3-selectors/#class-html -  * -  * Trait ClassNodeTrait -  * @package SDom\SelectorMatcher -  */ - trait ClassNodeTrait - { -     /** -      * @param ClassNode $token -      * @param Element $node -      * @param null|Element $effectiveRoot -      * @return bool -      */ -     protected function matchClassNode(ClassNode $token, Element $node, Element $effectiveRoot = null): bool -     { -         // attribute "class" must exist -         if (!$node->hasAttribute('class')) { -             return false; -         } - -         // attribute "class" must contain the requested class name -         if (!SelectorMatcher::containsWord($token->getName(), $node->getAttribute('class'))) { -             return false; -         } - -         return $this->match($token->getSelector(), $node, $effectiveRoot); -     } - } + use SDom\Node\NodeInterface; + use SDom\SelectorMatcher; + use Symfony\Component\CssSelector\Node\ClassNode; + + /** +  * @pattern E.warning +  * @meaning an E element whose class is "warning" (the document language specifies how class is determined). +  * @link https://www.w3.org/TR/css3-selectors/#class-html +  * +  * Trait ClassNodeTrait +  * @package SDom\SelectorMatcher +  */ + trait ClassNodeTrait + { +     /** +      * @param ClassNode $token +      * @param Element $node +      * @param null|Element $effectiveRoot +      * @return bool +      */ +     protected function matchClassNode(ClassNode $token, Element $node, Element $effectiveRoot = null): bool +     { +         // attribute "class" must exist +         if (!$node->hasAttribute('class')) { +             return false; +         } + +         // attribute "class" must contain the requested class name +         if (!SelectorMatcher::containsWord($token->getName(), $node->getAttribute('class'))) { +             return false; +         } + +         return $this->match($token->getSelector(), $node, $effectiveRoot); +     } + +     /** +      * @param NodeInterface $token +      * @param Element $node +      * @param Element|null $effectiveRoot +      * @return bool +      */ +     abstract public function match(NodeInterface $token, Element $node, Element $effectiveRoot = null): bool; + } @@ -176,7 +196,7 @@

Legend

Dead Code

- Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 16:56:52 EEST 2018. + Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 22:08:03 EEST 2018.

diff --git a/docs/coverage/SelectorMatcher/CombinedSelectorNodeTrait.php.html b/docs/coverage/SelectorMatcher/CombinedSelectorNodeTrait.php.html index ebd97ec..763c8f5 100644 --- a/docs/coverage/SelectorMatcher/CombinedSelectorNodeTrait.php.html +++ b/docs/coverage/SelectorMatcher/CombinedSelectorNodeTrait.php.html @@ -88,7 +88,7 @@
100.00%
5 / 5
- 27 + 28
100.00% covered (success) @@ -100,7 +100,7 @@ -           
14 / 14
+ +  
match + +
n/a
+
0 / 0
+ 1 + +
n/a
+
0 / 0
+ + @@ -234,215 +245,224 @@ namespace SDom\SelectorMatcher; use SDom\Node\Element; - use Symfony\Component\CssSelector\Node\CombinedSelectorNode; - - /** -  * @pattern E F -  * @meaning an F element descendant of an E element -  * @link https://www.w3.org/TR/css3-selectors/#descendant-combinators -  * -  * @pattern E > F -  * @meaning an F element child of an E element -  * @link https://www.w3.org/TR/css3-selectors/#child-combinators -  * -  * @pattern E + F -  * @meaning an F element immediately preceded by an E element -  * @link https://www.w3.org/TR/css3-selectors/#adjacent-sibling-combinators -  * -  * @pattern E ~ F -  * @meaning an F element preceded by an E element -  * @link https://www.w3.org/TR/css3-selectors/#general-sibling-combinators -  * -  * Trait ClassNodeTrait -  * @package SDom\SelectorMatcher -  */ - trait CombinedSelectorNodeTrait - { -     /** -      * @param CombinedSelectorNode $token -      * @param Element $node -      * @param null|Element $effectiveRoot -      * @return bool -      */ -     protected function matchDescendantCombinedSelectorNode( -         CombinedSelectorNode $token, -         Element $node, -         Element $effectiveRoot = null -     ): bool { -         // node must have a non-root parent -         if (null === $node->parent() || $effectiveRoot === $node->parent()) { -             return false; -         } - -         // node must match the sub-selector -         if (!$this->match($token->getSubSelector(), $node, $effectiveRoot)) { -             return false; -         } - -         $parent = $node; - -         // node must have a parent that matches the selector, anywhere up the chain -         do { -             /** @var Element $parent */ -             $parent = $parent->parent(); - -             if ($this->match($token->getSelector(), $parent, $effectiveRoot)) { -                 return true; -             } -         } while (null !== $parent->parent() && $effectiveRoot !== $parent->parent()); - -         return false; -     } - -     /** -      * @param CombinedSelectorNode $token -      * @param Element $node -      * @param null|Element $effectiveRoot -      * @return bool -      */ -     protected function matchChildCombinedSelectorNode( -         CombinedSelectorNode $token, -         Element $node, -         Element $effectiveRoot = null -     ): bool { -         // node must have a non-root parent -         if (null === $node->parent() || $effectiveRoot === $node->parent()) { -             return false; -         } - -         /** @var Element $parent */ -         $parent = $node->parent(); - -         // node must match the sub-selector -         if (!$this->match($token->getSubSelector(), $node, $effectiveRoot)) { -             return false; -         } - -         // node's parent must match the selector -         return $this->match($token->getSelector(), $parent, $effectiveRoot); -     } - -     /** -      * @param CombinedSelectorNode $token -      * @param Element $node -      * @param null|Element $effectiveRoot -      * @return bool -      */ -     protected function matchAdjacentCombinedSelectorNode( -         CombinedSelectorNode $token, -         Element $node, -         Element $effectiveRoot = null -     ): bool { -         // node must have a parent in order to determine position (index), parent CAN be the effective root -         if (null === $node->parent()) { -             return false; -         } - -         /** @var Element $parent */ -         $parent = $node->parent(); - -         // node must have an immediately preceding sibling that matches the selector -         // don't bother if the node is the first child (no siblings on the left) -         // ignored \InvalidArgumentException as $node is always a child of $parent -         $index = $parent->index($node); -         if (0 === $index) { -             return false; -         } - -         // don't bother if the sibling is not an Element node -         // ignored \OutOfBoundsException as $index will always be within the list of children -         $sibling = $parent->get($index - 1); -         if (!$sibling instanceof Element) { -             return false; -         } - -         // match the selector -         return $this->match($token->getSelector(), $sibling, $effectiveRoot); -     } - -     /** -      * @param CombinedSelectorNode $token -      * @param Element $node -      * @param null|Element $effectiveRoot -      * @return bool -      */ -     protected function matchGeneralSiblingCombinedSelectorNode( -         CombinedSelectorNode $token, -         Element $node, -         Element $effectiveRoot = null -     ): bool { -         // node must have a parent in order to determine position (index), parent CAN be the effective root -         if (null === $node->parent()) { -             return false; -         } - -         /** @var Element $parent */ -         $parent = $node->parent(); - -         // node must have a preceding sibling (may not be immediate) that matches the selector -         // don't bother if the node is the first child (no siblings on the left) -         // ignored \InvalidArgumentException as $node is always a child of $parent -         $index = $parent->index($node); -         if (0 === $index) { -             return false; -         } - -         // test all preceding siblings & bail after the first successful match -         for ($i = $index - 1; $i >= 0; $i--) { -             // skip the sibling if it's not an Element node -             // ignored \OutOfBoundsException as $index will always be within the list of children -             $sibling = $parent->get($i); -             if (!$sibling instanceof Element) { -                 continue; -             } - -             // match the selector -             if ($this->match($token->getSelector(), $sibling, $effectiveRoot)) { -                 return true; -             } -         } - -         // no sibling matches the selector -         return false; -     } - -     /** -      * @param CombinedSelectorNode $token -      * @param Element $node -      * @param null|Element $effectiveRoot -      * @return bool -      */ -     protected function matchCombinedSelectorNode( -         CombinedSelectorNode $token, -         Element $node, -         Element $effectiveRoot = null -     ): bool { -         // node must match the sub selector -         if (!$this->match($token->getSubSelector(), $node, $effectiveRoot)) { -             return false; -         } - -         switch ($token->getCombinator()) { -             case ' ': -                 return $this->matchDescendantCombinedSelectorNode($token, $node, $effectiveRoot); - -             case '>': -                 return $this->matchChildCombinedSelectorNode($token, $node, $effectiveRoot); - -             case '+': -                 return $this->matchAdjacentCombinedSelectorNode($token, $node, $effectiveRoot); - -             case '~': -                 return $this->matchGeneralSiblingCombinedSelectorNode($token, $node, $effectiveRoot); - -             default: -                 throw new \RuntimeException(sprintf( -                     'Invalid combined selector combinator "%s".', -                     $token->getCombinator() -                 )); -         } -     } - } + use SDom\Node\NodeInterface; + use Symfony\Component\CssSelector\Node\CombinedSelectorNode; + + /** +  * @pattern E F +  * @meaning an F element descendant of an E element +  * @link https://www.w3.org/TR/css3-selectors/#descendant-combinators +  * +  * @pattern E > F +  * @meaning an F element child of an E element +  * @link https://www.w3.org/TR/css3-selectors/#child-combinators +  * +  * @pattern E + F +  * @meaning an F element immediately preceded by an E element +  * @link https://www.w3.org/TR/css3-selectors/#adjacent-sibling-combinators +  * +  * @pattern E ~ F +  * @meaning an F element preceded by an E element +  * @link https://www.w3.org/TR/css3-selectors/#general-sibling-combinators +  * +  * Trait ClassNodeTrait +  * @package SDom\SelectorMatcher +  */ + trait CombinedSelectorNodeTrait + { +     /** +      * @param CombinedSelectorNode $token +      * @param Element $node +      * @param null|Element $effectiveRoot +      * @return bool +      */ +     protected function matchDescendantCombinedSelectorNode( +         CombinedSelectorNode $token, +         Element $node, +         Element $effectiveRoot = null +     ): bool { +         // node must have a non-root parent +         if (null === $node->parent() || $effectiveRoot === $node->parent()) { +             return false; +         } + +         // node must match the sub-selector +         if (!$this->match($token->getSubSelector(), $node, $effectiveRoot)) { +             return false; +         } + +         $parent = $node; + +         // node must have a parent that matches the selector, anywhere up the chain +         do { +             /** @var Element $parent */ +             $parent = $parent->parent(); + +             if ($this->match($token->getSelector(), $parent, $effectiveRoot)) { +                 return true; +             } +         } while (null !== $parent->parent() && $effectiveRoot !== $parent->parent()); + +         return false; +     } + +     /** +      * @param CombinedSelectorNode $token +      * @param Element $node +      * @param null|Element $effectiveRoot +      * @return bool +      */ +     protected function matchChildCombinedSelectorNode( +         CombinedSelectorNode $token, +         Element $node, +         Element $effectiveRoot = null +     ): bool { +         // node must have a non-root parent +         if (null === $node->parent() || $effectiveRoot === $node->parent()) { +             return false; +         } + +         /** @var Element $parent */ +         $parent = $node->parent(); + +         // node must match the sub-selector +         if (!$this->match($token->getSubSelector(), $node, $effectiveRoot)) { +             return false; +         } + +         // node's parent must match the selector +         return $this->match($token->getSelector(), $parent, $effectiveRoot); +     } + +     /** +      * @param CombinedSelectorNode $token +      * @param Element $node +      * @param null|Element $effectiveRoot +      * @return bool +      */ +     protected function matchAdjacentCombinedSelectorNode( +         CombinedSelectorNode $token, +         Element $node, +         Element $effectiveRoot = null +     ): bool { +         // node must have a parent in order to determine position (index), parent CAN be the effective root +         if (null === $node->parent()) { +             return false; +         } + +         /** @var Element $parent */ +         $parent = $node->parent(); + +         // node must have an immediately preceding sibling that matches the selector +         // don't bother if the node is the first child (no siblings on the left) +         // ignored \InvalidArgumentException as $node is always a child of $parent +         $index = $parent->index($node); +         if (0 === $index) { +             return false; +         } + +         // don't bother if the sibling is not an Element node +         // ignored \OutOfBoundsException as $index will always be within the list of children +         $sibling = $parent->get($index - 1); +         if (!$sibling instanceof Element) { +             return false; +         } + +         // match the selector +         return $this->match($token->getSelector(), $sibling, $effectiveRoot); +     } + +     /** +      * @param CombinedSelectorNode $token +      * @param Element $node +      * @param null|Element $effectiveRoot +      * @return bool +      */ +     protected function matchGeneralSiblingCombinedSelectorNode( +         CombinedSelectorNode $token, +         Element $node, +         Element $effectiveRoot = null +     ): bool { +         // node must have a parent in order to determine position (index), parent CAN be the effective root +         if (null === $node->parent()) { +             return false; +         } + +         /** @var Element $parent */ +         $parent = $node->parent(); + +         // node must have a preceding sibling (may not be immediate) that matches the selector +         // don't bother if the node is the first child (no siblings on the left) +         // ignored \InvalidArgumentException as $node is always a child of $parent +         $index = $parent->index($node); +         if (0 === $index) { +             return false; +         } + +         // test all preceding siblings & bail after the first successful match +         for ($i = $index - 1; $i >= 0; $i--) { +             // skip the sibling if it's not an Element node +             // ignored \OutOfBoundsException as $index will always be within the list of children +             $sibling = $parent->get($i); +             if (!$sibling instanceof Element) { +                 continue; +             } + +             // match the selector +             if ($this->match($token->getSelector(), $sibling, $effectiveRoot)) { +                 return true; +             } +         } + +         // no sibling matches the selector +         return false; +     } + +     /** +      * @param CombinedSelectorNode $token +      * @param Element $node +      * @param null|Element $effectiveRoot +      * @return bool +      */ +     protected function matchCombinedSelectorNode( +         CombinedSelectorNode $token, +         Element $node, +         Element $effectiveRoot = null +     ): bool { +         // node must match the sub selector +         if (!$this->match($token->getSubSelector(), $node, $effectiveRoot)) { +             return false; +         } + +         switch ($token->getCombinator()) { +             case ' ': +                 return $this->matchDescendantCombinedSelectorNode($token, $node, $effectiveRoot); + +             case '>': +                 return $this->matchChildCombinedSelectorNode($token, $node, $effectiveRoot); + +             case '+': +                 return $this->matchAdjacentCombinedSelectorNode($token, $node, $effectiveRoot); + +             case '~': +                 return $this->matchGeneralSiblingCombinedSelectorNode($token, $node, $effectiveRoot); + +             default: +                 throw new \RuntimeException(sprintf( +                     'Invalid combined selector combinator "%s".', +                     $token->getCombinator() +                 )); +         } +     } + +     /** +      * @param NodeInterface $token +      * @param Element $node +      * @param Element|null $effectiveRoot +      * @return bool +      */ +     abstract public function match(NodeInterface $token, Element $node, Element $effectiveRoot = null): bool; + } @@ -455,7 +475,7 @@

Legend

Dead Code

- Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 16:56:52 EEST 2018. + Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 22:08:03 EEST 2018.

diff --git a/docs/coverage/SelectorMatcher/ElementNodeTrait.php.html b/docs/coverage/SelectorMatcher/ElementNodeTrait.php.html index 67dda36..66fcefc 100644 --- a/docs/coverage/SelectorMatcher/ElementNodeTrait.php.html +++ b/docs/coverage/SelectorMatcher/ElementNodeTrait.php.html @@ -88,7 +88,7 @@
100.00%
1 / 1
- 2 + 3
100.00% covered (success) @@ -100,7 +100,7 @@ -  matchElementNode +  matchElementNode
100.00% covered (success) @@ -120,6 +120,17 @@
3 / 3
+ +  match + +
n/a
+
0 / 0
+ 1 + +
n/a
+
0 / 0
+ + @@ -130,38 +141,47 @@ namespace SDom\SelectorMatcher; use SDom\Node\Element; - use Symfony\Component\CssSelector\Node\ElementNode; - - /** -  * @pattern * -  * @meaning any element -  * @link https://www.w3.org/TR/css3-selectors/#universal-selector -  * -  * @pattern E -  * @meaning an element of type E -  * @link https://www.w3.org/TR/css3-selectors/#type-selectors -  * -  * Trait ElementNodeTrait -  * @package SDom\SelectorMatcher -  */ - trait ElementNodeTrait - { -     /** -      * @param ElementNode $token -      * @param Element $node -      * @return bool -      */ -     protected function matchElementNode(ElementNode $token, Element $node): bool -     { -         // target element tag name may be null, directly return true as ElementNode tokens have no sub-selectors -         if (null === $token->getElement()) { -             return true; -         } - -         // node tag name must match -         return $node->getTag() === $token->getElement(); -     } - } + use SDom\Node\NodeInterface; + use Symfony\Component\CssSelector\Node\ElementNode; + + /** +  * @pattern * +  * @meaning any element +  * @link https://www.w3.org/TR/css3-selectors/#universal-selector +  * +  * @pattern E +  * @meaning an element of type E +  * @link https://www.w3.org/TR/css3-selectors/#type-selectors +  * +  * Trait ElementNodeTrait +  * @package SDom\SelectorMatcher +  */ + trait ElementNodeTrait + { +     /** +      * @param ElementNode $token +      * @param Element $node +      * @return bool +      */ +     protected function matchElementNode(ElementNode $token, Element $node): bool +     { +         // target element tag name may be null, directly return true as ElementNode tokens have no sub-selectors +         if (null === $token->getElement()) { +             return true; +         } + +         // node tag name must match +         return $node->getTag() === $token->getElement(); +     } + +     /** +      * @param NodeInterface $token +      * @param Element $node +      * @param Element|null $effectiveRoot +      * @return bool +      */ +     abstract public function match(NodeInterface $token, Element $node, Element $effectiveRoot = null): bool; + } @@ -174,7 +194,7 @@

Legend

Dead Code

- Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 16:56:52 EEST 2018. + Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 22:08:03 EEST 2018.

diff --git a/docs/coverage/SelectorMatcher/HashNodeTrait.php.html b/docs/coverage/SelectorMatcher/HashNodeTrait.php.html index b747f20..734ddf9 100644 --- a/docs/coverage/SelectorMatcher/HashNodeTrait.php.html +++ b/docs/coverage/SelectorMatcher/HashNodeTrait.php.html @@ -88,7 +88,7 @@
100.00%
1 / 1
- 3 + 4
100.00% covered (success) @@ -100,7 +100,7 @@ -  matchHashNode +  matchHashNode
100.00% covered (success) @@ -120,6 +120,17 @@
5 / 5
+ +  match + +
n/a
+
0 / 0
+ 1 + +
n/a
+
0 / 0
+ + @@ -130,39 +141,48 @@ namespace SDom\SelectorMatcher; use SDom\Node\Element; - use Symfony\Component\CssSelector\Node\HashNode; - - /** -  * @pattern E#myid -  * @meaning an E element with ID equal to "myid". -  * @link https://www.w3.org/TR/css3-selectors/#id-selectors -  * -  * Trait HashNodeTrait -  * @package SDom\SelectorMatcher -  */ - trait HashNodeTrait - { -     /** -      * @param HashNode $token -      * @param Element $node -      * @param null|Element $effectiveRoot -      * @return bool -      */ -     protected function matchHashNode(HashNode $token, Element $node, Element $effectiveRoot = null): bool -     { -         // attribute "id" must exist -         if (!$node->hasAttribute('id')) { -             return false; -         } - -         // attribute "id" must be identical to the requested ID -         if ($token->getId() !== $node->getAttribute('id')) { -             return false; -         } - -         return $this->match($token->getSelector(), $node, $effectiveRoot); -     } - } + use SDom\Node\NodeInterface; + use Symfony\Component\CssSelector\Node\HashNode; + + /** +  * @pattern E#myid +  * @meaning an E element with ID equal to "myid". +  * @link https://www.w3.org/TR/css3-selectors/#id-selectors +  * +  * Trait HashNodeTrait +  * @package SDom\SelectorMatcher +  */ + trait HashNodeTrait + { +     /** +      * @param HashNode $token +      * @param Element $node +      * @param null|Element $effectiveRoot +      * @return bool +      */ +     protected function matchHashNode(HashNode $token, Element $node, Element $effectiveRoot = null): bool +     { +         // attribute "id" must exist +         if (!$node->hasAttribute('id')) { +             return false; +         } + +         // attribute "id" must be identical to the requested ID +         if ($token->getId() !== $node->getAttribute('id')) { +             return false; +         } + +         return $this->match($token->getSelector(), $node, $effectiveRoot); +     } + +     /** +      * @param NodeInterface $token +      * @param Element $node +      * @param Element|null $effectiveRoot +      * @return bool +      */ +     abstract public function match(NodeInterface $token, Element $node, Element $effectiveRoot = null): bool; + } @@ -175,7 +195,7 @@

Legend

Dead Code

- Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 16:56:52 EEST 2018. + Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 22:08:03 EEST 2018.

diff --git a/docs/coverage/SelectorMatcher/dashboard.html b/docs/coverage/SelectorMatcher/dashboard.html index 0c3fc6f..8a3481a 100644 --- a/docs/coverage/SelectorMatcher/dashboard.html +++ b/docs/coverage/SelectorMatcher/dashboard.html @@ -137,7 +137,7 @@

Project Risks

@@ -176,7 +176,7 @@

Project Risks

.yAxis.tickFormat(d3.format('d')); d3.select('#methodCoverageDistribution svg') - .datum(getCoverageDistributionData([0,0,0,0,0,0,0,0,0,0,0,9], "Method Coverage")) + .datum(getCoverageDistributionData([0,0,0,0,0,0,0,0,0,0,0,14], "Method Coverage")) .transition().duration(500).call(chart); nv.utils.windowResize(chart.update); @@ -226,7 +226,7 @@

Project Risks

chart.yAxis.axisLabel('Cyclomatic Complexity'); d3.select('#classComplexity svg') - .datum(getComplexityData([[100,16,"AttributeNodeTrait<\/a>"],[100,3,"ClassNodeTrait<\/a>"],[100,27,"CombinedSelectorNodeTrait<\/a>"],[100,2,"ElementNodeTrait<\/a>"],[100,3,"HashNodeTrait<\/a>"]], 'Class Complexity')) + .datum(getComplexityData([[100,17,"AttributeNodeTrait<\/a>"],[100,4,"ClassNodeTrait<\/a>"],[100,28,"CombinedSelectorNodeTrait<\/a>"],[100,3,"ElementNodeTrait<\/a>"],[100,4,"HashNodeTrait<\/a>"]], 'Class Complexity')) .transition() .duration(500) .call(chart); @@ -250,7 +250,7 @@

Project Risks

chart.yAxis.axisLabel('Method Complexity'); d3.select('#methodComplexity svg') - .datum(getComplexityData([[100,16,"
AttributeNodeTrait::matchAttributeNode<\/a>"],[100,3,"ClassNodeTrait::matchClassNode<\/a>"],[100,7,"CombinedSelectorNodeTrait::matchDescendantCombinedSelectorNode<\/a>"],[100,4,"CombinedSelectorNodeTrait::matchChildCombinedSelectorNode<\/a>"],[100,4,"CombinedSelectorNodeTrait::matchAdjacentCombinedSelectorNode<\/a>"],[100,6,"CombinedSelectorNodeTrait::matchGeneralSiblingCombinedSelectorNode<\/a>"],[100,6,"CombinedSelectorNodeTrait::matchCombinedSelectorNode<\/a>"],[100,2,"ElementNodeTrait::matchElementNode<\/a>"],[100,3,"HashNodeTrait::matchHashNode<\/a>"]], 'Method Complexity')) + .datum(getComplexityData([[100,16,"AttributeNodeTrait::matchAttributeNode<\/a>"],[100,1,"AttributeNodeTrait::match<\/a>"],[100,3,"ClassNodeTrait::matchClassNode<\/a>"],[100,1,"ClassNodeTrait::match<\/a>"],[100,7,"CombinedSelectorNodeTrait::matchDescendantCombinedSelectorNode<\/a>"],[100,4,"CombinedSelectorNodeTrait::matchChildCombinedSelectorNode<\/a>"],[100,4,"CombinedSelectorNodeTrait::matchAdjacentCombinedSelectorNode<\/a>"],[100,6,"CombinedSelectorNodeTrait::matchGeneralSiblingCombinedSelectorNode<\/a>"],[100,6,"CombinedSelectorNodeTrait::matchCombinedSelectorNode<\/a>"],[100,1,"CombinedSelectorNodeTrait::match<\/a>"],[100,2,"ElementNodeTrait::matchElementNode<\/a>"],[100,1,"ElementNodeTrait::match<\/a>"],[100,3,"HashNodeTrait::matchHashNode<\/a>"],[100,1,"HashNodeTrait::match<\/a>"]], 'Method Complexity')) .transition() .duration(500) .call(chart); diff --git a/docs/coverage/SelectorMatcher/index.html b/docs/coverage/SelectorMatcher/index.html index d2976d0..fc84744 100644 --- a/docs/coverage/SelectorMatcher/index.html +++ b/docs/coverage/SelectorMatcher/index.html @@ -221,7 +221,7 @@

Legend

High: 90% to 100%

- Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 16:56:52 EEST 2018. + Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 22:08:03 EEST 2018.

diff --git a/docs/coverage/dashboard.html b/docs/coverage/dashboard.html index e888568..b3558a0 100644 --- a/docs/coverage/dashboard.html +++ b/docs/coverage/dashboard.html @@ -58,7 +58,7 @@

Insufficient Coverage

- SDom\Dom60% + SDom\Dom73% @@ -75,7 +75,7 @@

Project Risks

- SDom\Dom1276 + SDom\Dom503 @@ -116,13 +116,10 @@

Insufficient Coverage

__toString0% before0% after0% - addClass0% - removeClass0% - hasClass0% - attr0% - removeAttr0% - text0% - html0% + attr0% + removeAttr0% + text0% + html0% append85% prepend85% @@ -141,15 +138,12 @@

Project Risks

- text72 - removeClass72 - html72 + html72 + text72 before42 after42 - attr42 - addClass30 - hasClass30 - removeAttr12 + attr42 + removeAttr12 __toString6 append5 prepend5 @@ -162,7 +156,7 @@

Project Risks

@@ -183,7 +177,7 @@

Project Risks

.yAxis.tickFormat(d3.format('d')); d3.select('#classCoverageDistribution svg') - .datum(getCoverageDistributionData([0,0,0,0,0,0,0,1,0,0,0,11], "Class Coverage")) + .datum(getCoverageDistributionData([0,0,0,0,0,0,0,0,1,0,0,11], "Class Coverage")) .transition().duration(500).call(chart); nv.utils.windowResize(chart.update); @@ -201,7 +195,7 @@

Project Risks

.yAxis.tickFormat(d3.format('d')); d3.select('#methodCoverageDistribution svg') - .datum(getCoverageDistributionData([10,0,0,0,0,0,0,0,0,2,0,78], "Method Coverage")) + .datum(getCoverageDistributionData([7,0,0,0,0,0,0,0,0,2,0,87], "Method Coverage")) .transition().duration(500).call(chart); nv.utils.windowResize(chart.update); @@ -251,7 +245,7 @@

Project Risks

chart.yAxis.axisLabel('Cyclomatic Complexity'); d3.select('#classComplexity svg') - .datum(getComplexityData([[60.49382716049383,136,"SDom\\Dom<\/a>"],[100,13,"SDom\\Node\\CData<\/a>"],[100,13,"SDom\\Node\\Comment<\/a>"],[100,8,"SDom\\Node\\DocType<\/a>"],[100,47,"SDom\\Node\\Element<\/a>"],[100,13,"SDom\\Node\\Text<\/a>"],[100,8,"SDom\\SelectorMatcher<\/a>"],[100,16,"AttributeNodeTrait<\/a>"],[100,3,"ClassNodeTrait<\/a>"],[100,27,"CombinedSelectorNodeTrait<\/a>"],[100,2,"ElementNodeTrait<\/a>"],[100,3,"HashNodeTrait<\/a>"]], 'Class Complexity')) + .datum(getComplexityData([[73.38709677419355,139,"SDom\\Dom<\/a>"],[100,13,"SDom\\Node\\CData<\/a>"],[100,13,"SDom\\Node\\Comment<\/a>"],[100,8,"SDom\\Node\\DocType<\/a>"],[100,47,"SDom\\Node\\Element<\/a>"],[100,13,"SDom\\Node\\Text<\/a>"],[100,8,"SDom\\SelectorMatcher<\/a>"],[100,17,"AttributeNodeTrait<\/a>"],[100,4,"ClassNodeTrait<\/a>"],[100,28,"CombinedSelectorNodeTrait<\/a>"],[100,3,"ElementNodeTrait<\/a>"],[100,4,"HashNodeTrait<\/a>"]], 'Class Complexity')) .transition() .duration(500) .call(chart); @@ -275,7 +269,7 @@

Project Risks

chart.yAxis.axisLabel('Method Complexity'); d3.select('#methodComplexity svg') - .datum(getComplexityData([[100,5,"
SDom\\Dom::createInvalidContentException<\/a>"],[100,5,"SDom\\Dom::traverseMatch<\/a>"],[100,4,"SDom\\Dom::findFirstElement<\/a>"],[100,5,"SDom\\Dom::findFirstInnermostElement<\/a>"],[100,18,"SDom\\Dom::__construct<\/a>"],[0,2,"SDom\\Dom::__toString<\/a>"],[100,1,"SDom\\Dom::getIterator<\/a>"],[100,1,"SDom\\Dom::anonymousFunction:292#1280<\/a>"],[100,1,"SDom\\Dom::count<\/a>"],[100,5,"SDom\\Dom::get<\/a>"],[100,3,"SDom\\Dom::add<\/a>"],[100,1,"SDom\\Dom::clear<\/a>"],[100,2,"SDom\\Dom::eq<\/a>"],[100,1,"SDom\\Dom::first<\/a>"],[100,1,"SDom\\Dom::last<\/a>"],[100,4,"SDom\\Dom::children<\/a>"],[85.71428571428571,5,"SDom\\Dom::append<\/a>"],[85.71428571428571,5,"SDom\\Dom::prepend<\/a>"],[0,6,"SDom\\Dom::before<\/a>"],[0,6,"SDom\\Dom::after<\/a>"],[100,7,"SDom\\Dom::wrap<\/a>"],[100,5,"SDom\\Dom::find<\/a>"],[0,5,"SDom\\Dom::addClass<\/a>"],[0,8,"SDom\\Dom::removeClass<\/a>"],[0,5,"SDom\\Dom::hasClass<\/a>"],[0,6,"SDom\\Dom::attr<\/a>"],[0,3,"SDom\\Dom::removeAttr<\/a>"],[0,8,"SDom\\Dom::text<\/a>"],[0,8,"SDom\\Dom::html<\/a>"],[100,1,"SDom\\Node\\CData::__construct<\/a>"],[100,1,"SDom\\Node\\CData::__toString<\/a>"],[100,1,"SDom\\Node\\CData::__clone<\/a>"],[100,2,"SDom\\Node\\CData::parent<\/a>"],[100,4,"SDom\\Node\\CData::attach<\/a>"],[100,3,"SDom\\Node\\CData::detach<\/a>"],[100,1,"SDom\\Node\\CData::clone<\/a>"],[100,1,"SDom\\Node\\Comment::__construct<\/a>"],[100,1,"SDom\\Node\\Comment::__toString<\/a>"],[100,1,"SDom\\Node\\Comment::__clone<\/a>"],[100,2,"SDom\\Node\\Comment::parent<\/a>"],[100,4,"SDom\\Node\\Comment::attach<\/a>"],[100,3,"SDom\\Node\\Comment::detach<\/a>"],[100,1,"SDom\\Node\\Comment::clone<\/a>"],[100,1,"SDom\\Node\\DocType::__construct<\/a>"],[100,1,"SDom\\Node\\DocType::__toString<\/a>"],[100,1,"SDom\\Node\\DocType::__clone<\/a>"],[100,2,"SDom\\Node\\DocType::parent<\/a>"],[100,1,"SDom\\Node\\DocType::attach<\/a>"],[100,1,"SDom\\Node\\DocType::detach<\/a>"],[100,1,"SDom\\Node\\DocType::clone<\/a>"],[100,1,"SDom\\Node\\Element::__construct<\/a>"],[100,5,"SDom\\Node\\Element::__toString<\/a>"],[100,1,"SDom\\Node\\Element::__clone<\/a>"],[100,1,"SDom\\Node\\Element::getTag<\/a>"],[100,1,"SDom\\Node\\Element::hasAttribute<\/a>"],[100,1,"SDom\\Node\\Element::setAttribute<\/a>"],[100,3,"SDom\\Node\\Element::getAttribute<\/a>"],[100,2,"SDom\\Node\\Element::removeAttribute<\/a>"],[100,2,"SDom\\Node\\Element::parent<\/a>"],[100,4,"SDom\\Node\\Element::attach<\/a>"],[100,3,"SDom\\Node\\Element::detach<\/a>"],[100,3,"SDom\\Node\\Element::clone<\/a>"],[100,1,"SDom\\Node\\Element::getIterator<\/a>"],[100,1,"SDom\\Node\\Element::count<\/a>"],[100,3,"SDom\\Node\\Element::insertAfter<\/a>"],[100,3,"SDom\\Node\\Element::insertBefore<\/a>"],[100,1,"SDom\\Node\\Element::isChild<\/a>"],[100,4,"SDom\\Node\\Element::get<\/a>"],[100,2,"SDom\\Node\\Element::index<\/a>"],[100,2,"SDom\\Node\\Element::removeChild<\/a>"],[100,2,"SDom\\Node\\Element::clear<\/a>"],[100,1,"SDom\\Node\\Element::isVoid<\/a>"],[100,1,"SDom\\Node\\Text::__construct<\/a>"],[100,1,"SDom\\Node\\Text::__toString<\/a>"],[100,1,"SDom\\Node\\Text::__clone<\/a>"],[100,2,"SDom\\Node\\Text::parent<\/a>"],[100,4,"SDom\\Node\\Text::attach<\/a>"],[100,3,"SDom\\Node\\Text::detach<\/a>"],[100,1,"SDom\\Node\\Text::clone<\/a>"],[100,1,"SDom\\SelectorMatcher::containsWord<\/a>"],[100,7,"SDom\\SelectorMatcher::match<\/a>"],[100,16,"AttributeNodeTrait::matchAttributeNode<\/a>"],[100,3,"ClassNodeTrait::matchClassNode<\/a>"],[100,7,"CombinedSelectorNodeTrait::matchDescendantCombinedSelectorNode<\/a>"],[100,4,"CombinedSelectorNodeTrait::matchChildCombinedSelectorNode<\/a>"],[100,4,"CombinedSelectorNodeTrait::matchAdjacentCombinedSelectorNode<\/a>"],[100,6,"CombinedSelectorNodeTrait::matchGeneralSiblingCombinedSelectorNode<\/a>"],[100,6,"CombinedSelectorNodeTrait::matchCombinedSelectorNode<\/a>"],[100,2,"ElementNodeTrait::matchElementNode<\/a>"],[100,3,"HashNodeTrait::matchHashNode<\/a>"]], 'Method Complexity')) + .datum(getComplexityData([[100,5,"SDom\\Dom::createInvalidContentException<\/a>"],[100,5,"SDom\\Dom::traverseMatch<\/a>"],[100,4,"SDom\\Dom::findFirstElement<\/a>"],[100,5,"SDom\\Dom::findFirstInnermostElement<\/a>"],[100,18,"SDom\\Dom::__construct<\/a>"],[0,2,"SDom\\Dom::__toString<\/a>"],[100,1,"SDom\\Dom::getIterator<\/a>"],[100,1,"SDom\\Dom::anonymousFunction:292#1280<\/a>"],[100,1,"SDom\\Dom::count<\/a>"],[100,5,"SDom\\Dom::get<\/a>"],[100,3,"SDom\\Dom::add<\/a>"],[100,1,"SDom\\Dom::clear<\/a>"],[100,2,"SDom\\Dom::eq<\/a>"],[100,1,"SDom\\Dom::first<\/a>"],[100,1,"SDom\\Dom::last<\/a>"],[100,4,"SDom\\Dom::children<\/a>"],[85.71428571428571,5,"SDom\\Dom::append<\/a>"],[85.71428571428571,5,"SDom\\Dom::prepend<\/a>"],[0,6,"SDom\\Dom::before<\/a>"],[0,6,"SDom\\Dom::after<\/a>"],[100,7,"SDom\\Dom::wrap<\/a>"],[100,2,"SDom\\Dom::wrapInner<\/a>"],[100,5,"SDom\\Dom::find<\/a>"],[100,6,"SDom\\Dom::addClass<\/a>"],[100,8,"SDom\\Dom::removeClass<\/a>"],[100,5,"SDom\\Dom::hasClass<\/a>"],[0,6,"SDom\\Dom::attr<\/a>"],[0,3,"SDom\\Dom::removeAttr<\/a>"],[0,8,"SDom\\Dom::text<\/a>"],[0,8,"SDom\\Dom::html<\/a>"],[100,1,"SDom\\Node\\CData::__construct<\/a>"],[100,1,"SDom\\Node\\CData::__toString<\/a>"],[100,1,"SDom\\Node\\CData::__clone<\/a>"],[100,2,"SDom\\Node\\CData::parent<\/a>"],[100,4,"SDom\\Node\\CData::attach<\/a>"],[100,3,"SDom\\Node\\CData::detach<\/a>"],[100,1,"SDom\\Node\\CData::clone<\/a>"],[100,1,"SDom\\Node\\Comment::__construct<\/a>"],[100,1,"SDom\\Node\\Comment::__toString<\/a>"],[100,1,"SDom\\Node\\Comment::__clone<\/a>"],[100,2,"SDom\\Node\\Comment::parent<\/a>"],[100,4,"SDom\\Node\\Comment::attach<\/a>"],[100,3,"SDom\\Node\\Comment::detach<\/a>"],[100,1,"SDom\\Node\\Comment::clone<\/a>"],[100,1,"SDom\\Node\\DocType::__construct<\/a>"],[100,1,"SDom\\Node\\DocType::__toString<\/a>"],[100,1,"SDom\\Node\\DocType::__clone<\/a>"],[100,2,"SDom\\Node\\DocType::parent<\/a>"],[100,1,"SDom\\Node\\DocType::attach<\/a>"],[100,1,"SDom\\Node\\DocType::detach<\/a>"],[100,1,"SDom\\Node\\DocType::clone<\/a>"],[100,1,"SDom\\Node\\Element::__construct<\/a>"],[100,5,"SDom\\Node\\Element::__toString<\/a>"],[100,1,"SDom\\Node\\Element::__clone<\/a>"],[100,1,"SDom\\Node\\Element::getTag<\/a>"],[100,1,"SDom\\Node\\Element::hasAttribute<\/a>"],[100,1,"SDom\\Node\\Element::setAttribute<\/a>"],[100,3,"SDom\\Node\\Element::getAttribute<\/a>"],[100,2,"SDom\\Node\\Element::removeAttribute<\/a>"],[100,2,"SDom\\Node\\Element::parent<\/a>"],[100,4,"SDom\\Node\\Element::attach<\/a>"],[100,3,"SDom\\Node\\Element::detach<\/a>"],[100,3,"SDom\\Node\\Element::clone<\/a>"],[100,1,"SDom\\Node\\Element::getIterator<\/a>"],[100,1,"SDom\\Node\\Element::count<\/a>"],[100,3,"SDom\\Node\\Element::insertAfter<\/a>"],[100,3,"SDom\\Node\\Element::insertBefore<\/a>"],[100,1,"SDom\\Node\\Element::isChild<\/a>"],[100,4,"SDom\\Node\\Element::get<\/a>"],[100,2,"SDom\\Node\\Element::index<\/a>"],[100,2,"SDom\\Node\\Element::removeChild<\/a>"],[100,2,"SDom\\Node\\Element::clear<\/a>"],[100,1,"SDom\\Node\\Element::isVoid<\/a>"],[100,1,"SDom\\Node\\Text::__construct<\/a>"],[100,1,"SDom\\Node\\Text::__toString<\/a>"],[100,1,"SDom\\Node\\Text::__clone<\/a>"],[100,2,"SDom\\Node\\Text::parent<\/a>"],[100,4,"SDom\\Node\\Text::attach<\/a>"],[100,3,"SDom\\Node\\Text::detach<\/a>"],[100,1,"SDom\\Node\\Text::clone<\/a>"],[100,1,"SDom\\SelectorMatcher::containsWord<\/a>"],[100,7,"SDom\\SelectorMatcher::match<\/a>"],[100,16,"AttributeNodeTrait::matchAttributeNode<\/a>"],[100,1,"AttributeNodeTrait::match<\/a>"],[100,3,"ClassNodeTrait::matchClassNode<\/a>"],[100,1,"ClassNodeTrait::match<\/a>"],[100,7,"CombinedSelectorNodeTrait::matchDescendantCombinedSelectorNode<\/a>"],[100,4,"CombinedSelectorNodeTrait::matchChildCombinedSelectorNode<\/a>"],[100,4,"CombinedSelectorNodeTrait::matchAdjacentCombinedSelectorNode<\/a>"],[100,6,"CombinedSelectorNodeTrait::matchGeneralSiblingCombinedSelectorNode<\/a>"],[100,6,"CombinedSelectorNodeTrait::matchCombinedSelectorNode<\/a>"],[100,1,"CombinedSelectorNodeTrait::match<\/a>"],[100,2,"ElementNodeTrait::matchElementNode<\/a>"],[100,1,"ElementNodeTrait::match<\/a>"],[100,3,"HashNodeTrait::matchHashNode<\/a>"],[100,1,"HashNodeTrait::match<\/a>"]], 'Method Complexity')) .transition() .duration(500) .call(chart); diff --git a/docs/coverage/index.html b/docs/coverage/index.html index b841913..18d0507 100644 --- a/docs/coverage/index.html +++ b/docs/coverage/index.html @@ -43,21 +43,21 @@ Total
-
- 82.39% covered (warning) +
+ 88.00% covered (warning)
-
82.39%
-
449 / 545
-
-
- 86.52% covered (warning) +
88.00%
+
484 / 550
+
+
+ 90.00% covered (success)
-
86.52%
-
77 / 89
+
90.00%
+
81 / 90
91.67% covered (success) @@ -127,21 +127,21 @@ Dom.php
-
- 60.49% covered (warning) +
+ 73.39% covered (warning)
-
60.49%
-
147 / 243
+
73.39%
+
182 / 248
-
- 57.14% covered (warning) +
+ 68.97% covered (warning)
-
57.14%
-
16 / 28
+
68.97%
+
20 / 29
0.00% covered (danger) @@ -192,7 +192,7 @@

Legend

High: 90% to 100%

- Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 16:56:52 EEST 2018. + Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 22:08:03 EEST 2018.

diff --git a/docs/docs/class-SDom.Dom.html b/docs/docs/class-SDom.Dom.html index 41cc4b0..a8816ec 100644 --- a/docs/docs/class-SDom.Dom.html +++ b/docs/docs/class-SDom.Dom.html @@ -860,6 +860,42 @@

Implementation of

+
+
+ + + + + + + + + + public + + + + + + +
+ # + wrapInner( $content ) + +
+ Wrap a clone of the supplied content around each child node (not only element nodes) of nodes in the collection. + +This function works exactly like wrap() except it wraps the children of the nodes in the collection instead. + +Return the original collection for chaining. + + + + + + + +
@@ -880,7 +916,7 @@

Implementation of

# - find( string $selector ) + find( string $selector )
Return a new Dom collection of all the descendants of each Element node in the current collection, @@ -913,7 +949,7 @@

Implementation of

# - addClass( string $className ) + addClass( string $className )
Adds the specified class(es) to each Element node in the collection. @@ -945,7 +981,7 @@

Implementation of

# - removeClass( string $className = NULL ) + removeClass( string $className = NULL )
Remove a single class, multiple classes, or all classes from each Element node in the collection. @@ -977,7 +1013,7 @@

Implementation of

# - hasClass( string $className ) + hasClass( string $className )
Return TRUE if at least one of the Element nodes in the collection has the specified class assigned. @@ -1009,7 +1045,7 @@

Implementation of

# - attr( string $name , string $value = NULL ) + attr( string $name , string $value = NULL )
Get the value of an attribute for the first Element node in the collection. @@ -1043,7 +1079,7 @@

Implementation of

# - removeAttr( string $name ) + removeAttr( string $name )
Remove an attribute from each Element node in the collection. @@ -1075,7 +1111,7 @@

Implementation of

# - text( string $text = NULL ) + text( string $text = NULL )
Get the combined text contents of each Element node in the collection, including their descendants. @@ -1109,7 +1145,7 @@

Implementation of

# - html( string $html = NULL ) + html( string $html = NULL )
Get the HTML contents of the first Element node in the collection. diff --git a/docs/docs/class-SDom.SelectorMatcher.html b/docs/docs/class-SDom.SelectorMatcher.html index 2c43699..66e613d 100644 --- a/docs/docs/class-SDom.SelectorMatcher.html +++ b/docs/docs/class-SDom.SelectorMatcher.html @@ -207,6 +207,9 @@

SelectorMatcher

matchAttributeNode() + + match() + @@ -219,6 +222,9 @@

SelectorMatcher

matchClassNode() + + match() + @@ -243,6 +249,9 @@

SelectorMatcher

matchCombinedSelectorNode() + + match() + @@ -255,6 +264,9 @@

SelectorMatcher

matchElementNode() + + match() + @@ -267,6 +279,9 @@

SelectorMatcher

matchHashNode() + + match() + diff --git a/docs/docs/source-class-SDom.Dom.html b/docs/docs/source-class-SDom.Dom.html index b94089e..ec6837a 100644 --- a/docs/docs/source-class-SDom.Dom.html +++ b/docs/docs/source-class-SDom.Dom.html @@ -68,7 +68,7 @@
-
  1:   2:   3:   4:   5:   6:   7:   8:   9:  10:  11:  12:  13:  14:  15:  16:  17:  18:  19:  20:  21:  22:  23:  24:  25:  26:  27:  28:  29:  30:  31:  32:  33:  34:  35:  36:  37:  38:  39:  40:  41:  42:  43:  44:  45:  46:  47:  48:  49:  50:  51:  52:  53:  54:  55:  56:  57:  58:  59:  60:  61:  62:  63:  64:  65:  66:  67:  68:  69:  70:  71:  72:  73:  74:  75:  76:  77:  78:  79:  80:  81:  82:  83:  84:  85:  86:  87:  88:  89:  90:  91:  92:  93:  94:  95:  96:  97:  98:  99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357: 358: 359: 360: 361: 362: 363: 364: 365: 366: 367: 368: 369: 370: 371: 372: 373: 374: 375: 376: 377: 378: 379: 380: 381: 382: 383: 384: 385: 386: 387: 388: 389: 390: 391: 392: 393: 394: 395: 396: 397: 398: 399: 400: 401: 402: 403: 404: 405: 406: 407: 408: 409: 410: 411: 412: 413: 414: 415: 416: 417: 418: 419: 420: 421: 422: 423: 424: 425: 426: 427: 428: 429: 430: 431: 432: 433: 434: 435: 436: 437: 438: 439: 440: 441: 442: 443: 444: 445: 446: 447: 448: 449: 450: 451: 452: 453: 454: 455: 456: 457: 458: 459: 460: 461: 462: 463: 464: 465: 466: 467: 468: 469: 470: 471: 472: 473: 474: 475: 476: 477: 478: 479: 480: 481: 482: 483: 484: 485: 486: 487: 488: 489: 490: 491: 492: 493: 494: 495: 496: 497: 498: 499: 500: 501: 502: 503: 504: 505: 506: 507: 508: 509: 510: 511: 512: 513: 514: 515: 516: 517: 518: 519: 520: 521: 522: 523: 524: 525: 526: 527: 528: 529: 530: 531: 532: 533: 534: 535: 536: 537: 538: 539: 540: 541: 542: 543: 544: 545: 546: 547: 548: 549: 550: 551: 552: 553: 554: 555: 556: 557: 558: 559: 560: 561: 562: 563: 564: 565: 566: 567: 568: 569: 570: 571: 572: 573: 574: 575: 576: 577: 578: 579: 580: 581: 582: 583: 584: 585: 586: 587: 588: 589: 590: 591: 592: 593: 594: 595: 596: 597: 598: 599: 600: 601: 602: 603: 604: 605: 606: 607: 608: 609: 610: 611: 612: 613: 614: 615: 616: 617: 618: 619: 620: 621: 622: 623: 624: 625: 626: 627: 628: 629: 630: 631: 632: 633: 634: 635: 636: 637: 638: 639: 640: 641: 642: 643: 644: 645: 646: 647: 648: 649: 650: 651: 652: 653: 654: 655: 656: 657: 658: 659: 660: 661: 662: 663: 664: 665: 666: 667: 668: 669: 670: 671: 672: 673: 674: 675: 676: 677: 678: 679: 680: 681: 682: 683: 684: 685: 686: 687: 688: 689: 690: 691: 692: 693: 694: 695: 696: 697: 698: 699: 700: 701: 702: 703: 704: 705: 706: 707: 708: 709: 710: 711: 712: 713: 714: 715: 716: 717: 718: 719: 720: 721: 722: 723: 724: 725: 726: 727: 728: 729: 730: 731: 732: 733: 734: 735: 736: 737: 738: 739: 740: 741: 742: 743: 744: 745: 746: 747: 748: 749: 750: 751: 752: 753: 754: 755: 756: 757: 758: 759: 760: 761: 762: 763: 764: 765: 766: 767: 768: 769: 770: 771: 772: 773: 774: 775: 776: 777: 778: 779: 780: 781: 782: 783: 784: 785: 786: 787: 788: 789: 790: 791: 792: 793: 794: 795: 796: 797: 798: 799: 800: 801: 802: 803: 804: 805: 806: 807: 808: 809: 810: 811: 812: 813: 814: 815: 816: 817: 818: 819: 820: 821: 822: 823: 824: 825: 826: 827: 828: 829: 830: 831: 832: 833: 834: 835: 836: 837: 838: 839: 840: 841: 842: 843: 844: 845: 846: 847: 848: 849: 850: 851: 852: 853: 854: 855: 856: 857: 858: 859: 860: 861: 862: 863: 864: 865: 866: 867: 868: 869: 870: 871: 872: 873: 874: 875: 876: 877: 878: 879: 880: 881: 882: 883: 884: 885: 886: 887: 
+
  1:   2:   3:   4:   5:   6:   7:   8:   9:  10:  11:  12:  13:  14:  15:  16:  17:  18:  19:  20:  21:  22:  23:  24:  25:  26:  27:  28:  29:  30:  31:  32:  33:  34:  35:  36:  37:  38:  39:  40:  41:  42:  43:  44:  45:  46:  47:  48:  49:  50:  51:  52:  53:  54:  55:  56:  57:  58:  59:  60:  61:  62:  63:  64:  65:  66:  67:  68:  69:  70:  71:  72:  73:  74:  75:  76:  77:  78:  79:  80:  81:  82:  83:  84:  85:  86:  87:  88:  89:  90:  91:  92:  93:  94:  95:  96:  97:  98:  99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357: 358: 359: 360: 361: 362: 363: 364: 365: 366: 367: 368: 369: 370: 371: 372: 373: 374: 375: 376: 377: 378: 379: 380: 381: 382: 383: 384: 385: 386: 387: 388: 389: 390: 391: 392: 393: 394: 395: 396: 397: 398: 399: 400: 401: 402: 403: 404: 405: 406: 407: 408: 409: 410: 411: 412: 413: 414: 415: 416: 417: 418: 419: 420: 421: 422: 423: 424: 425: 426: 427: 428: 429: 430: 431: 432: 433: 434: 435: 436: 437: 438: 439: 440: 441: 442: 443: 444: 445: 446: 447: 448: 449: 450: 451: 452: 453: 454: 455: 456: 457: 458: 459: 460: 461: 462: 463: 464: 465: 466: 467: 468: 469: 470: 471: 472: 473: 474: 475: 476: 477: 478: 479: 480: 481: 482: 483: 484: 485: 486: 487: 488: 489: 490: 491: 492: 493: 494: 495: 496: 497: 498: 499: 500: 501: 502: 503: 504: 505: 506: 507: 508: 509: 510: 511: 512: 513: 514: 515: 516: 517: 518: 519: 520: 521: 522: 523: 524: 525: 526: 527: 528: 529: 530: 531: 532: 533: 534: 535: 536: 537: 538: 539: 540: 541: 542: 543: 544: 545: 546: 547: 548: 549: 550: 551: 552: 553: 554: 555: 556: 557: 558: 559: 560: 561: 562: 563: 564: 565: 566: 567: 568: 569: 570: 571: 572: 573: 574: 575: 576: 577: 578: 579: 580: 581: 582: 583: 584: 585: 586: 587: 588: 589: 590: 591: 592: 593: 594: 595: 596: 597: 598: 599: 600: 601: 602: 603: 604: 605: 606: 607: 608: 609: 610: 611: 612: 613: 614: 615: 616: 617: 618: 619: 620: 621: 622: 623: 624: 625: 626: 627: 628: 629: 630: 631: 632: 633: 634: 635: 636: 637: 638: 639: 640: 641: 642: 643: 644: 645: 646: 647: 648: 649: 650: 651: 652: 653: 654: 655: 656: 657: 658: 659: 660: 661: 662: 663: 664: 665: 666: 667: 668: 669: 670: 671: 672: 673: 674: 675: 676: 677: 678: 679: 680: 681: 682: 683: 684: 685: 686: 687: 688: 689: 690: 691: 692: 693: 694: 695: 696: 697: 698: 699: 700: 701: 702: 703: 704: 705: 706: 707: 708: 709: 710: 711: 712: 713: 714: 715: 716: 717: 718: 719: 720: 721: 722: 723: 724: 725: 726: 727: 728: 729: 730: 731: 732: 733: 734: 735: 736: 737: 738: 739: 740: 741: 742: 743: 744: 745: 746: 747: 748: 749: 750: 751: 752: 753: 754: 755: 756: 757: 758: 759: 760: 761: 762: 763: 764: 765: 766: 767: 768: 769: 770: 771: 772: 773: 774: 775: 776: 777: 778: 779: 780: 781: 782: 783: 784: 785: 786: 787: 788: 789: 790: 791: 792: 793: 794: 795: 796: 797: 798: 799: 800: 801: 802: 803: 804: 805: 806: 807: 808: 809: 810: 811: 812: 813: 814: 815: 816: 817: 818: 819: 820: 821: 822: 823: 824: 825: 826: 827: 828: 829: 830: 831: 832: 833: 834: 835: 836: 837: 838: 839: 840: 841: 842: 843: 844: 845: 846: 847: 848: 849: 850: 851: 852: 853: 854: 855: 856: 857: 858: 859: 860: 861: 862: 863: 864: 865: 866: 867: 868: 869: 870: 871: 872: 873: 874: 875: 876: 877: 878: 879: 880: 881: 882: 883: 884: 885: 886: 887: 888: 889: 890: 891: 892: 893: 894: 895: 896: 897: 898: 899: 900: 901: 902: 903: 904: 905: 906: 907: 908: 
<?php
 
 namespace SDom;
@@ -696,266 +696,287 @@
     }
 
     /**
-     * Return a new Dom collection of all the descendants of each Element node in the current collection,
-     * filtered by the specified CSS selector.
-     *
-     * @param string $selector
-     * @return Dom
-     */
-    public function find(string $selector): Dom
-    {
-        if (!isset(self::$selectorParser)) {
-            self::$selectorParser = new Parser();
-        }
-
-        if (!isset(self::$selectorMatcher)) {
-            self::$selectorMatcher = new SelectorMatcher();
-        }
-
-        $dom = new static();
-        $selectorTokens = self::$selectorParser->parse($selector);
-
-        foreach ($this->get() as $rootNode) {
-            /** @var NodeInterface $childNode */
-            foreach ($rootNode as $childNode) {
-                self::traverseMatch($dom, $selectorTokens, $childNode, $rootNode);
-            }
-        }
-
-        return $dom;
-    }
-
-    /**
-     * Adds the specified class(es) to each Element node in the collection.
-     *
-     * @param string $className
-     * @return Dom
-     */
-    public function addClass(string $className): Dom
-    {
-        $addClasses = preg_split('/\s+/', $className);
-
-        // bail if no classes to add
-        if (0 === count($addClasses)) {
-            return $this;
+     * Wrap a clone of the supplied content around each child node (not only element nodes) of nodes in the collection.
+     *
+     * This function works exactly like wrap() except it wraps the children of the nodes in the collection instead.
+     *
+     * Return the original collection for chaining.
+     *
+     * @param $content
+     * @return Dom
+     */
+    public function wrapInner($content): Dom
+    {
+        foreach ($this->children() as $child) {
+            $child->wrap($content);
+        }
+        return $this;
+    }
+
+    /**
+     * Return a new Dom collection of all the descendants of each Element node in the current collection,
+     * filtered by the specified CSS selector.
+     *
+     * @param string $selector
+     * @return Dom
+     */
+    public function find(string $selector): Dom
+    {
+        if (!isset(self::$selectorParser)) {
+            self::$selectorParser = new Parser();
+        }
+
+        if (!isset(self::$selectorMatcher)) {
+            self::$selectorMatcher = new SelectorMatcher();
+        }
+
+        $dom = new static();
+        $selectorTokens = self::$selectorParser->parse($selector);
+
+        foreach ($this->get() as $rootNode) {
+            /** @var NodeInterface $childNode */
+            foreach ($rootNode as $childNode) {
+                self::traverseMatch($dom, $selectorTokens, $childNode, $rootNode);
+            }
         }
 
-        /** @var NodeInterface $node */
-        foreach ($this->nodes as $node) {
-            if (!$node instanceof Element) {
-                continue;
-            }
-
-            // if the node already has a "class" attribute, merge all classes & make sure the result is unique
-            if ($node->hasAttribute('class')) {
-                $currentClasses = preg_split('/\s+/', $node->getAttribute('class'));
-                $node->setAttribute('class', implode(' ', array_unique(array_merge($currentClasses, $addClasses))));
-            }
-
-            // if the node does not have a "class" attribute, directly set the new ones
-            else {
-                $node->setAttribute('class', implode(' ', $addClasses));
-            }
+        return $dom;
+    }
+
+    /**
+     * Adds the specified class(es) to each Element node in the collection.
+     *
+     * @param string $className
+     * @return Dom
+     */
+    public function addClass(string $className): Dom
+    {
+        $className = trim($className);
+
+        // bail if no classes to add
+        if ('' === $className) {
+            return $this;
         }
 
-        return $this;
-    }
-
-    /**
-     * Remove a single class, multiple classes, or all classes from each Element node in the collection.
-     *
-     * @param string|null $className
-     * @return Dom
-     */
-    public function removeClass(string $className = null): Dom
-    {
-        // if class to remove isn't set, remove all classes, but keep the "class" attribute present
-        if (!isset($className)) {
-            /** @var NodeInterface $node */
-            foreach ($this->nodes as $node) {
-                if (!$node instanceof Element || !$node->hasAttribute('class')) {
-                    continue;
-                }
-
-                $node->setAttribute('class', '');
-            }
-
-            return $this;
-        }
-
-        $removeClasses = preg_split('/\s+/', $className);
-
-        /** @var NodeInterface $node */
-        foreach ($this->nodes as $node) {
-            if (!$node instanceof Element || !$node->hasAttribute('class')) {
-                continue;
-            }
-
-            // set to the difference between the current classes and the remove ones
-            $currentClasses = preg_split('/\s+/', $node->getAttribute('class'));
-            $node->setAttribute('class', implode(' ', array_diff($currentClasses, $removeClasses)));
-        }
-
-        return $this;
-    }
-
-    /**
-     * Return TRUE if at least one of the Element nodes in the collection has the specified class assigned.
-     *
-     * @param string $className
-     * @return bool
-     */
-    public function hasClass(string $className): bool
-    {
-        /** @var NodeInterface $node */
-        foreach ($this->nodes as $node) {
-            if (!$node instanceof Element || !$node->hasAttribute('class')) {
-                continue;
-            }
-
-            if (SelectorMatcher::containsWord($className, $node->getAttribute('class'))) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Get the value of an attribute for the first Element node in the collection.
-     * Set the value of an attribute for each Element node in the collection.
-     *
-     * @param string $name
-     * @param string|null $value
-     * @return string|null|$this
-     */
-    public function attr(string $name, string $value = null)
-    {
-        if (isset($value)) {
-            /** @var NodeInterface $node */
-            foreach ($this->nodes as $node) {
-                if (!$node instanceof Element) {
-                    continue;
-                }
-
-                $node->setAttribute($name, $value);
-            }
-
-            return $this;
-        }
-
-        /** @var NodeInterface $node */
-        foreach ($this->nodes as $node) {
-            if (!$node instanceof Element) {
-                continue;
-            }
-
-            return $node->getAttribute($name);
-        }
-
-        return null;
-    }
-
-    /**
-     * Remove an attribute from each Element node in the collection.
-     *
-     * @param string $name
-     * @return Dom
-     */
-    public function removeAttr(string $name): Dom
-    {
-        /** @var NodeInterface $node */
-        foreach ($this->nodes as $node) {
-            if (!$node instanceof Element) {
-                continue;
-            }
-
-            $node->removeAttribute($name);
-        }
-
-        return $this;
-    }
-
-    /**
-     * Get the combined text contents of each Element node in the collection, including their descendants.
-     * Set the content of each Element node in the collection to the specified text.
+        $addClasses = preg_split('/\s+/', $className);
+
+        /** @var NodeInterface $node */
+        foreach ($this->nodes as $node) {
+            if (!$node instanceof Element) {
+                continue;
+            }
+
+            // if the node already has a "class" attribute, merge all classes & make sure the result is unique
+            if ($node->hasAttribute('class')) {
+                $currentClassName = trim($node->getAttribute('class'));
+                $currentClasses = '' === $currentClassName ? [] : preg_split('/\s+/', $currentClassName);
+                $node->setAttribute('class', implode(' ', array_unique(array_merge($currentClasses, $addClasses))));
+            }
+
+            // if the node does not have a "class" attribute, directly set the new ones
+            else {
+                $node->setAttribute('class', implode(' ', $addClasses));
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Remove a single class, multiple classes, or all classes from each Element node in the collection.
+     *
+     * @param string|null $className
+     * @return Dom
+     */
+    public function removeClass(string $className = null): Dom
+    {
+        // if class to remove isn't set, remove all classes, but keep the "class" attribute present
+        if (!isset($className)) {
+            /** @var NodeInterface $node */
+            foreach ($this->nodes as $node) {
+                if (!$node instanceof Element || !$node->hasAttribute('class')) {
+                    continue;
+                }
+
+                $node->setAttribute('class', '');
+            }
+
+            return $this;
+        }
+
+        $removeClasses = preg_split('/\s+/', $className);
+
+        /** @var NodeInterface $node */
+        foreach ($this->nodes as $node) {
+            if (!$node instanceof Element || !$node->hasAttribute('class')) {
+                continue;
+            }
+
+            // set to the difference between the current classes and the remove ones
+            $currentClasses = preg_split('/\s+/', $node->getAttribute('class'));
+            $node->setAttribute('class', implode(' ', array_diff($currentClasses, $removeClasses)));
+        }
+
+        return $this;
+    }
+
+    /**
+     * Return TRUE if at least one of the Element nodes in the collection has the specified class assigned.
+     *
+     * @param string $className
+     * @return bool
+     */
+    public function hasClass(string $className): bool
+    {
+        /** @var NodeInterface $node */
+        foreach ($this->nodes as $node) {
+            if (!$node instanceof Element || !$node->hasAttribute('class')) {
+                continue;
+            }
+
+            if (SelectorMatcher::containsWord($className, $node->getAttribute('class'))) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Get the value of an attribute for the first Element node in the collection.
+     * Set the value of an attribute for each Element node in the collection.
+     *
+     * @param string $name
+     * @param string|null $value
+     * @return string|null|$this
+     */
+    public function attr(string $name, string $value = null)
+    {
+        if (isset($value)) {
+            /** @var NodeInterface $node */
+            foreach ($this->nodes as $node) {
+                if (!$node instanceof Element) {
+                    continue;
+                }
+
+                $node->setAttribute($name, $value);
+            }
+
+            return $this;
+        }
+
+        /** @var NodeInterface $node */
+        foreach ($this->nodes as $node) {
+            if (!$node instanceof Element) {
+                continue;
+            }
+
+            return $node->getAttribute($name);
+        }
+
+        return null;
+    }
+
+    /**
+     * Remove an attribute from each Element node in the collection.
      *
-     * @param string|null $text
-     * @return $this|string
+     * @param string $name
+     * @return Dom
      */
-    public function text(string $text = null)
+    public function removeAttr(string $name): Dom
     {
-        if (isset($text)) {
-            foreach ($this->nodes as $node) {
-                if (!$node instanceof Element) {
-                    continue;
-                }
+        /** @var NodeInterface $node */
+        foreach ($this->nodes as $node) {
+            if (!$node instanceof Element) {
+                continue;
+            }
 
-                $node->clear()->insertAfter(new Text($text));
-            }
+            $node->removeAttribute($name);
+        }
 
-            return $this;
-        }
+        return $this;
+    }
 
-        $text = '';
-
-        foreach ($this->nodes as $node) {
-            if ($node instanceof Text) {
-                $text .= (string) $node;
-            } else if ($node instanceof Element) {
-                /** @var Dom $dom */
-                foreach ((new static($node))->children() as $dom) {
-                    $text .= $dom->text();
-                }
-            }
-        }
-
-        return $text;
-    }
-
-    /**
-     * Get the HTML contents of the first Element node in the collection.
-     * Set the HTML contents of each Element node in the collection.
-     *
-     * @param string|null $html
-     * @return $this|string
-     */
-    public function html(string $html = null)
-    {
-        if (isset($html)) {
-            foreach ($this->nodes as $node) {
-                if (!$node instanceof Element) {
-                    continue;
-                }
-
-                $node->clear();
-
-                foreach ((new static($html))->nodes as $newNode) {
-                    $node->insertAfter($newNode);
-                }
-            }
-
-            return $this;
-        }
-
-        foreach ($this->nodes as $node) {
-            if (!$node instanceof Element) {
-                continue;
-            }
-
-            $html = '';
-
-            /** @var NodeInterface $childNode */
-            foreach ($node as $childNode) {
-                $html .= (string) $childNode;
-            }
-
-            return $html;
-        }
-
-        return '';
-    }
-}
+ /** + * Get the combined text contents of each Element node in the collection, including their descendants. + * Set the content of each Element node in the collection to the specified text. + * + * @param string|null $text + * @return $this|string + */ + public function text(string $text = null) + { + if (isset($text)) { + foreach ($this->nodes as $node) { + if (!$node instanceof Element) { + continue; + } + + $node->clear()->insertAfter(new Text($text)); + } + + return $this; + } + + $text = ''; + + foreach ($this->nodes as $node) { + if ($node instanceof Text) { + $text .= (string) $node; + } else if ($node instanceof Element) { + /** @var Dom $dom */ + foreach ((new static($node))->children() as $dom) { + $text .= $dom->text(); + } + } + } + + return $text; + } + + /** + * Get the HTML contents of the first Element node in the collection. + * Set the HTML contents of each Element node in the collection. + * + * @param string|null $html + * @return $this|string + */ + public function html(string $html = null) + { + if (isset($html)) { + foreach ($this->nodes as $node) { + if (!$node instanceof Element) { + continue; + } + + $node->clear(); + + foreach ((new static($html))->nodes as $newNode) { + $node->insertAfter($newNode); + } + } + + return $this; + } + + foreach ($this->nodes as $node) { + if (!$node instanceof Element) { + continue; + } + + $html = ''; + + /** @var NodeInterface $childNode */ + foreach ($node as $childNode) { + $html .= (string) $childNode; + } + + return $html; + } + + return ''; + } +}