Free tools Windows power users keep installed
One-click scans. No signup required.
PHP 8.4 adds browser-style CSS selectors to its new Dom namespace. Create a standards-aware document with DomHTMLDocument::createFromString(), then call querySelector() for one element or querySelectorAll() for every match. The same API family also provides closest() and matches(). Existing DOMDocument/DOMXPath code still works, but it uses different classes and requires a deliberate migration.
The short answer: PHP 8.4 does support querySelector
The methods are not added to the legacy DOMDocument class. They belong to the new classes in the Dom namespace, including DomHTMLDocument and DomXMLDocument. For HTML, the smallest useful example is:
<?php
$html = '<main>
<article>First</article>
<article class="featured">Second</article>
</main>';
$dom = DomHTMLDocument::createFromString($html);
$first = $dom->querySelector('main > article:last-child');
$all = $dom->querySelectorAll('article.featured');
echo $first?->textContent, PHP_EOL;
foreach ($all as $article) {
echo $article->textContent, PHP_EOL;
}
querySelector() returns the first matching descendant element, or null if no element matches. querySelectorAll() returns all matching descendants in tree order as a static collection. An invalid selector is not treated as “no results”: PHP throws a DOMException with the DomSYNTAX_ERR code.
Requirements and document creation
Use PHP 8.4 and the new DOM classes
The selector API is part of PHP 8.4’s redesigned DOM hierarchy. Your runtime must therefore be PHP 8.4 or newer, and the DOM extension must be available in that build. Check the runtime before deploying:
#1 Best Overall
php -v
php -m | grep -i '^dom$'
Use DomHTMLDocument for HTML5 input and DomXMLDocument when the source is XML. The new API is designed for standards-compliant parsing and addresses long-standing compliance issues in the older implementation.
Parse a string safely
<?php
declare(strict_types=1);
$html = file_get_contents(__DIR__ . '/page.html');
if ($html === false) {
throw new RuntimeException('Could not read page.html');
}
$document = DomHTMLDocument::createFromString($html);
Parsing is separate from selection. Keep the document object while you perform several queries; there is no need to parse the same HTML repeatedly.
querySelector(): retrieve one element
First match and missing elements
The method follows browser DOM conventions: selector matching is evaluated among descendants of the document (or element on which you call it), and only the first match is returned.
$title = $document->querySelector('main h1');
if ($title === null) {
echo "The page has no main heading", PHP_EOL;
} else {
echo trim($title->textContent), PHP_EOL;
}
Do not dereference the result without checking for null. A changed template, an optional component, or a blocked response can legitimately produce no match.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Useful CSS selector forms
articleselects every article element..featuredselects an element carrying thefeaturedclass.article.featuredrequires both the element name and class.[data-id="42"]selects an exact attribute value.main > articleselects direct article children ofmain.nav aselects links at any depth insidenav.article:last-childselects an article that is the last child of its parent.h1, h2combines alternatives in one selector.
CSS selectors are often shorter and easier to read than equivalent XPath expressions, particularly for classes, attributes and combinators.
querySelectorAll(): process every match
Use the plural method when the page can contain zero, one or many results:
Rank #2
$cards = $document->querySelectorAll('section.products article.card');
foreach ($cards as $card) {
$name = $card->querySelector('.name');
$price = $card->querySelector('[data-price]');
echo trim($name?->textContent ?? 'Unnamed'), ': ';
echo trim($price?->getAttribute('data-price') ?? 'unknown'), PHP_EOL;
}
The returned collection is static: it represents the matches at the time of the call, in document order. If you later modify the tree, run the query again when you need an updated collection.
Calling selectors on an element
After selecting a container, query within it to avoid accidentally reading similarly named elements elsewhere:
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallCrashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minute$main = $document->querySelector('main');
if ($main !== null) {
foreach ($main->querySelectorAll('article') as $article) {
echo trim($article->textContent), PHP_EOL;
}
}
closest() and matches()
closest(): walk toward an ancestor
closest() answers “which nearest ancestor, including this element, matches?” It is useful when a small child node identifies the component you need:
$button = $document->querySelector('button[data-action="buy"]');
$card = $button?->closest('article.card');
if ($card !== null) {
echo trim($card->textContent), PHP_EOL;
}
If no ancestor satisfies the selector, the result is null. As with other selector methods, malformed CSS must be handled as an exception rather than interpreted as an empty result.
matches(): test an element without selecting siblings
Use matches() when you already have an element and need a boolean classification:
$node = $document->querySelector('[data-state]');
if ($node !== null && $node->matches('[data-state="published"]')) {
echo "Published", PHP_EOL;
}
Validate selectors and handle failures
Invalid CSS syntax
Wrap user-supplied or configuration-supplied selectors in a narrow exception handler. PHP reports invalid syntax with DOMException and DomSYNTAX_ERR:
try {
$result = $document->querySelector($selector);
} catch (DOMException $e) {
if ($e->code === DomSYNTAX_ERR) {
throw new InvalidArgumentException(
"Invalid CSS selector: {$selector}",
0,
$e
);
}
throw $e;
}
Prefer a fixed, application-owned selector whenever possible. If selectors come from an API request, validate and constrain them before parsing untrusted documents.
Rendering-only pseudo-classes do not apply
Server-side PHP has no pointer, focus state or layout engine. Pseudo-classes that describe rendering state, such as :hover, are nonsensical here and match nothing. Select structural and attribute state instead, such as [aria-expanded="true"] or :last-child.
Malformed or unexpected HTML
HTML5 parsing can repair common markup errors, so inspect the resulting tree rather than assuming the source string’s indentation or implied elements. Treat absent elements as normal input variation, and log the source URL or document identifier when a required element is missing.
CSS selectors versus XPath
| Concern | New CSS-selector API | XPath with legacy DOM |
|---|---|---|
| Readability | Familiar to browser developers; concise classes, attributes and combinators. | Powerful, but expressions can be more verbose for common CSS-style relationships. |
| Primary methods | querySelector(), querySelectorAll(), closest(), matches(). |
DOMXPath::query() and related XPath expressions. |
| Result behavior | One element or null; plural queries return a static collection in tree order. |
Typically an XPath node list whose handling follows the older API. |
| Invalid expressions | Invalid CSS raises DOMException with DomSYNTAX_ERR. |
Uses XPath parsing and its own error behavior. |
| Ancestors and predicates | closest() handles the common nearest-ancestor case; matches() tests an existing element. |
XPath remains better suited to XPath-specific axes, predicates and expression logic. |
| Namespaces | Review namespace-sensitive documents carefully; HTML and XML have different rules. | Existing namespace-aware XPath code may be the safer choice for established XML workflows. |
| Compatibility | Requires PHP 8.4’s new Dom classes. |
Existing DOMDocument/DOMXPath code remains available. |
There is no cited numeric benchmark establishing that one approach is faster. Choose CSS selectors for browser-like readability and the new convenience methods; retain XPath where your code already depends on XPath-specific expressions, namespace handling or older PHP support.
Migrating from DOMDocument and DOMXPath
Audit object types first
A migration is not a search-and-replace of method names. The new classes have different namespaces and object types. Review constructors, return-type assumptions, namespace registration, serialization, and any helper that accepts DOMNode or DOMElement.
Convert a simple lookup
// Legacy style
$legacy = new DOMDocument();
$legacy->loadHTML($html);
$xpath = new DOMXPath($legacy);
$nodes = $xpath->query('//main/article[contains(@class, "featured")]');
// PHP 8.4 style
$modern = DomHTMLDocument::createFromString($html);
$nodes = $modern->querySelectorAll('main > article.featured');
Keep the legacy path when a library or supported runtime requires it. The opt-in DOM compliance work anticipates gradual migration while projects continue to support older PHP releases.
Rank #4
Use a compatibility boundary
For applications supporting PHP versions before 8.4, isolate selection behind a service or adapter. One implementation can use DOMXPath; the PHP 8.4 implementation can use DomHTMLDocument. This prevents version checks from spreading through business logic and makes a later cutover reviewable.
Practical extraction pattern
The following complete example extracts links from featured articles while handling missing nodes and invalid configuration:
<?php
declare(strict_types=1);
function featuredLinks(string $html, string $selector = 'article.featured'): array
{
$document = DomHTMLDocument::createFromString($html);
$links = [];
try {
$articles = $document->querySelectorAll($selector);
} catch (DOMException $e) {
if ($e->code === DomSYNTAX_ERR) {
throw new InvalidArgumentException('Bad article selector', 0, $e);
}
throw $e;
}
foreach ($articles as $article) {
$link = $article->querySelector('a[href]');
if ($link === null) {
continue;
}
$href = trim($link->getAttribute('href'));
if ($href !== '') {
$links[] = $href;
}
}
return $links;
}
This pattern keeps parsing, selection, validation and extraction distinct. It also avoids assuming that every matching article contains a link.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
If your goal is to obtain a clean image or PDF of the page you are parsing, ScreenshotNeo provides a website screenshot API and MCP server. It accepts cookie and consent banners as a visitor, then removes more than 60 known consent platforms, newsletter popups and chat widgets before capture; each step can be disabled. Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits cost nothing, and response headers identify the page verdict and billing status.
One GET request is enough:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo documentation for all parameters. The equivalent PHP-friendly Python and Node.js calls are:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo also offers an MCP server with take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients. Its Free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Troubleshooting checklist
“Class DomHTMLDocument not found”
Confirm the process is running PHP 8.4 or newer, not a different CLI or web-server binary. Check php -v from the same environment that executes the application and ensure the DOM extension is installed.
The selector returns null or an empty collection
Print or inspect the parsed tree, then verify the selector against the repaired HTML structure. Check spelling, class names, direct-child versus descendant combinators, and whether the expected content is actually present in the response.
A DOMException is thrown
Validate punctuation, brackets, quotes and pseudo-class syntax. Catch the exception only where you can provide a useful error; do not silently convert malformed selectors into empty data.
Results differ from browser JavaScript
PHP parses the supplied HTML; it does not execute page JavaScript or provide layout and interaction state. Content inserted after load, and rendering-only states such as :hover, will not appear as browser-rendered results. Obtain the final HTML from the appropriate source before selecting.
Recommended Free Tools
Old helpers reject the new nodes
Update type declarations and adapters that explicitly require legacy DOMElement or DOMNode. Do not mix old and new object assumptions without a conversion strategy.
What to remember
- PHP 8.4’s CSS selector API is in the new
Domnamespace. - Use
querySelector()for one element and check fornull. - Use
querySelectorAll()for a static, tree-ordered collection of matches. closest()finds the nearest matching ancestor;matches()tests an existing element.- Invalid CSS raises
DOMException; rendering-only pseudo-classes do not describe server-side HTML. - Legacy DOM classes remain available, so migration should begin with an API and type audit.
Frequently Asked Questions
Can I call querySelector() directly on DOMDocument?
No. The new selector methods belong to PHP 8.4’s Dom classes, such as DomHTMLDocument. DOMDocument remains the legacy API.
Does querySelectorAll() return a live collection?
No. It returns a static collection representing the matches at query time and ordered as they occur in the tree.
Should every XPath query be rewritten as CSS?
No. CSS is a concise choice for common HTML selection, while XPath remains appropriate for existing code, namespace-heavy XML, or XPath-specific axes and predicates.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemsCan PHP selectors find content created by JavaScript?
Not from the original HTML alone. The DOM API parses the HTML supplied to PHP; it does not run a browser’s JavaScript or provide rendering state.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

