@@ -409,9 +409,9 @@ protected function containsExactly($node, $value): bool
409409 */
410410 protected function has ($ node , $ selector ): bool
411411 {
412- foreach ($ this ->relativeSelector ($ selector ) as [$ group , $ combinator , $ nextOnly ]) {
412+ foreach ($ this ->relativeSelector ($ selector ) as [$ group , $ combinator , $ nextOnly, $ descends ]) {
413413 if (! ($ combinator & (SimpleSelector::ADJACENT | SimpleSelector::SIBLING ))) {
414- if ($ this ->findsAny ($ node , $ node , $ group )) {
414+ if ($ this ->findsAny ($ node , $ node , $ group, false )) {
415415 return true ;
416416 }
417417 continue ;
@@ -423,7 +423,10 @@ protected function has($node, $selector): bool
423423 continue ;
424424 }
425425
426- if ($ this ->findsAny ($ next , $ node , $ group )) {
426+ // The sibling can match itself, and its descendants when the group goes on to them (`+ ul li`).
427+ if ($ this ->findsAny ($ next , $ node , $ group , true )
428+ || ($ descends && $ this ->findsAny ($ next , $ node , $ group , false ))
429+ ) {
427430 return true ;
428431 }
429432
@@ -443,7 +446,8 @@ protected function has($node, $selector): bool
443446 * which becomes :scope. Any other group (`li`) is a descendant of :scope.
444447 *
445448 * @return array
446- * One [group, anchor combinator, whether only the next sibling can match] per group.
449+ * One [group, anchor combinator, whether only the next sibling can match, whether the group
450+ * reaches below the sibling it starts from] per group.
447451 * @throws ParseException
448452 */
449453 protected function relativeSelector ($ selector ): array
@@ -470,28 +474,32 @@ protected function relativeSelector($selector): array
470474
471475 // `+ li` can only be the next sibling, unless the group goes on to another one (`+ li ~ li`).
472476 $ nextOnly = $ anchor ->combinator === SimpleSelector::ADJACENT ;
477+ $ descends = false ;
473478 foreach (array_slice ($ group , 0 , -1 ) as $ simpleSelector ) {
474479 if ($ simpleSelector ->combinator & $ sibling ) {
475480 $ nextOnly = false ;
481+ } elseif ($ simpleSelector ->combinator ) {
482+ $ descends = true ;
476483 }
477484 }
478485
479- $ groups [] = [$ group , $ anchor ->combinator , $ nextOnly ];
486+ $ groups [] = [$ group , $ anchor ->combinator , $ nextOnly, $ descends ];
480487 }
481488
482489 return $ this ->relativeSelectors [$ selector ] = $ groups ;
483490 }
484491
485492 /**
486- * Whether $group matches $root or any of its descendants.
493+ * Whether $group matches $root itself or, when $self is FALSE, any of its descendants.
487494 *
488495 * @param DOMElement $root
489496 * @param DOMElement $scope
490497 * @param SimpleSelector[] $group
498+ * @param bool $self
491499 *
492500 * @throws ParseException
493501 */
494- protected function findsAny (DOMElement $ root , DOMElement $ scope , array $ group ): bool
502+ protected function findsAny (DOMElement $ root , DOMElement $ scope , array $ group, bool $ self ): bool
495503 {
496504 $ splos = new SPLObjectStorage ();
497505 $ splos ->offsetSet ($ root );
@@ -501,7 +509,7 @@ protected function findsAny(DOMElement $root, DOMElement $scope, array $group):
501509 foreach ($ group as $ simpleSelector ) {
502510 $ copy [] = clone $ simpleSelector ;
503511 }
504- $ traverser = new DOMTraverser ($ splos , false , $ scope );
512+ $ traverser = new DOMTraverser ($ splos , $ self , $ scope );
505513
506514 return count ($ traverser ->findGroups ([$ copy ])->matches ()) > 0 ;
507515 }
0 commit comments