[php-src] Issue #24260: querySelectorAll() returns duplicate elements for selector lists

From: Date: Sun, 11 Oct 2026 11:42:42 +0000
Subject: [php-src] Issue #24260: querySelectorAll() returns duplicate elements for selector lists
Groups: php.bugs 
Request: Send a blank email to php-bugs+get-252973@lists.php.net to get a copy of this message
Issue: https://github.com/php/php-src/issues/24260 Author: fivefilters ### Description I got some strange results while working on some PHP code recently, caused by how querySelectorAll handles comma separated selectors. The following analysis is mostly via AI (Claude Opus 5.5), but I have checked it over and it looks like it has captured the issue well. I'm less sure of the PHP src change as I'm not a C developer and have never contributed to internals. The following code: ```php <?php $doc = Dom\HTMLDocument::createFromString( '<!DOCTYPE html><p class="a b">one</p><p class="b">two</p>' ); foreach (['.a, .b', '.a, .b, .a.b', ':is(.a, .b)'] as $selector) { $texts = []; foreach ($doc->querySelectorAll($selector) as $element) { $texts[] = $element->textContent; } echo $selector, ' => ', implode(' ', $texts), "\n"; } ``` Resulted in this output (3v4l shows [same output](https://3v4l.org/11SH2) on 8.4.1 through 8.4.26 and 8.5.0 through 8.5.11) ``` .a, .b => one one two .a, .b, .a.b => one one one two :is(.a, .b) => one two ``` But I expected this output instead: ``` .a, .b => one two .a, .b, .a.b => one two :is(.a, .b) => one two ``` Each element should appear once: "A comma-separated list of selectors represents the union of all elements selected by each of the individual selectors in the selector list." ([Selectors 4, §4.1](https://drafts.csswg.org/selectors-4/#grouping)) The [same code in JavaScript](https://www.typescriptlang.org/play/?filetype=js#code/MYewdgzgLgBAJiYMC8MwFMDuMAiB5AWQAUBDAJwnTIAoBKAOgAdzKAxMkAWwGUoyBLMAHNqAKBgwA5AB4AhPgDCAFQCaRAKIwAFlE4AbAHzTGMYHpIQIyAEQkYAI2sHw6aQHpGRk2YtXrjgyhMEHdPSQAaKSh0AA8oNx19SVFaAG5RUQAzEDIYalBIWEo9dGAoHJgQTJgAbUl6Ekj6ewipBqb7JpJm1skALn4IanaYZtpJAF1aGABvcVNwaBhouIgUGABBMjISAE96TI5OagRgegBHAFcqXe50ErKcjb09amLS8rJaSPQUAxh0PQVlAFOBomAoGl5gUICASvQ9CARO9HmRIpJkAZWsCIPQAFYgQTUSRSWhQgC+oiAA) prints the expected output (click Run). > AI (Claude Opus 5.5) cause and tested fix: > > querySelectorAll() passes > LXB_SELECTORS_OPT_DEFAULT to Lexbor in > ext/dom/parentnode/css_selectors.c, so a node is reported once per > matching selector. querySelector(), > matches() and closest() pass > LXB_SELECTORS_OPT_MATCH_FIRST, which Lexbor documents as removing > these duplicates. Using it in querySelectorAll() fixes this on an > 8.5.8 build, and the ext/dom tests still pass. > > ```diff > --- a/ext/dom/parentnode/css_selectors.c > +++ b/ext/dom/parentnode/css_selectors.c > @@ -241,7 +241,7 @@ > selectors_str, > dom_query_selector_find_array_callback, > &ctx, > - LXB_SELECTORS_OPT_DEFAULT > + LXB_SELECTORS_OPT_MATCH_FIRST > ) != LXB_STATUS_OK) { > zend_array_destroy(list); > RETURN_THROWS(); > ``` ### PHP Version ```plain PHP 8.5.8 (cli) (built: Jul 7 2026 05:21:52) (NTS gcc 14.2.0) Copyright (c) The PHP Group Built by static-php-cli 2.8.6 Zend Engine v4.5.8, Copyright (c) Zend Technologies with Zend OPcache v8.5.8, Copyright (c), by Zend Technologies ``` ### Operating System Ubuntu 24.04 (x86_64)

« previous php.bugs (#252973) next »