symfony/css-selector
Symfony CssSelector converts CSS selectors into XPath expressions, making it easy to query DOM/XML documents with familiar CSS syntax. Part of the Symfony Components ecosystem, with full docs and issue tracking in the main Symfony repository.
Installation:
composer require symfony/css-selector
No additional configuration is required—this is a standalone component.
First Use Case: Convert a CSS selector to an XPath expression for DOM traversal:
use Symfony\Component\CssSelector\CssSelectorConverter;
use Symfony\Component\CssSelector\CssSelectorConverterInterface;
$converter = new CssSelectorConverter();
$xpath = $converter->toXPath('div.active > p');
// Output: "//div[contains(@class, 'active')]/p"
Where to Look First:
DOMDocument or SimpleXML for DOM manipulation:
$dom = new DOMDocument();
$dom->loadHTML($html);
$xpath = new DOMXPath($dom);
$nodes = $xpath->query($converter->toXPath('div:is(.active, .selected)'));
$converter = new CssSelectorConverter();
$xpath = $converter->toXPath('article.header h1');
// Output: "//article[contains(@class, 'header')]/h1"
use Illuminate\Support\Facades\File;
$html = File::get('path/to/file.html');
$dom = new DOMDocument();
$dom->loadHTML($html);
$xpath = new DOMXPath($dom);
$nodes = $xpath->query($converter->toXPath('div.content > p'));
:is() and :where()$xpath = $converter->toXPath('div:is(.active, .selected)');
// Output: "//div[contains(@class, 'active') or contains(@class, 'selected')]"
$scraper = new Spatie\WebScraper\WebScraper();
$scraper->scrape('https://example.com')->css('div:is(.user--active, .user--premium)')->each(function ($node) {
// Process nodes
});
data-*, aria-*).$xpath = $converter->toXPath('[data-role="button"]');
// Output: "//*[@data-role='button']"
$xpath = $converter->toXPath('[aria-label]:is(:focus, :hover)');
$nodes = $dom->xpath->query($xpath);
:nth-child, :first-of-type, etc.$xpath = $converter->toXPath('li:nth-child(odd)');
// Output: "//li[position() mod 2 = 1]"
$activePage = $converter->toXPath('li.active a');
$nodes = $dom->xpath->query($activePage);
DOMDocument, SimpleXML, or spatie/laravel-web-scraper.use Spatie\WebScraper\WebScraper;
$scraper = new WebScraper();
$scraper->scrape('https://example.com')->css('div:is(.desktop, .mobile)')->get();
if (!class_exists('App\Helpers\XPathHelper')) {
class XPathHelper {
public static function query($html, $selector) {
$converter = new CssSelectorConverter();
$dom = new DOMDocument();
$dom->loadHTML($html);
$xpath = new DOMXPath($dom);
return $xpath->query($converter->toXPath($selector));
}
}
}
$cache = new \Symfony\Component\Cache\Simple\FilesystemCache();
$converter = new CssSelectorConverter();
$xpath = $cache->get('xpath_div_active', function () use ($converter) {
return $converter->toXPath('div.active');
});
try {
$xpath = $converter->toXPath('div:custom-pseudo');
} catch (\Symfony\Component\CssSelector\Exception\CssSelectorException $e) {
// Fallback to regex or alternative logic
$xpath = '//div[contains(@class, "custom")]';
}
use Symfony\Component\CssSelector\CssSelectorConverter;
use PHPUnit\Framework\TestCase;
class SelectorTest extends TestCase {
public function testSelectorConversion() {
$converter = new CssSelectorConverter();
$xpath = $converter->toXPath('div:is(.active, .selected)');
$this->assertEquals(
"//div[contains(@class, 'active') or contains(@class, 'selected')]",
$xpath
);
}
}
// In a Blade component
@php
$converter = new \Symfony\Component\CssSelector\CssSelectorConverter();
$xpath = $converter->toXPath('div:is('.request('theme').', .default)');
@endphp
<div x-data="{ nodes: @js($xpath->query($dom)->toArray()) }"></div>
// Enable LRU cache (built-in since v7.4.6)
$converter = new CssSelectorConverter();
$xpath = $converter->toXPath('div:is(.active, .selected)'); // Cached automatically
div > :is(p, span) may behave unexpectedly if the parent (div) is not explicitly defined in the DOM.$xpath = $converter->toXPath('ul > li:is(.item, .highlight)');
$converter = new CssSelectorConverter();
$converter->setCache(new \Symfony\Component\Cache\Adapter\FilesystemAdapter('', 0, '/tmp/css-selector-cache'));
:my-custom-pseudo) will throw CssSelectorException.try {
$xpath = $converter->toXPath('div:my-custom-pseudo');
} catch (\Symfony\Component\CssSelector\Exception\CssSelectorException $e) {
$xpath = '//div'; // Fallback
}
How can I help you explore Laravel packages today?