phpgt/cssxpath
Lightweight, dependency-free PHP library that translates CSS selectors into XPath queries, bringing querySelectorAll-style DOM selection to PHP. Works with HTML and, via htmlMode: false, case-sensitive XML attributes.
phpgt/cssxpath) bridges CSS selectors (common in frontend scraping, testing, or DOM manipulation) to XPath (useful for backend processing, XML/HTML parsing, or legacy systems). This is valuable for:
DOM and SimpleHTMLDom packages could leverage this for:
php-cs-fixer, phpunit, or guzzlehttp if used in scraping pipelines.Gt\CssXPath (legacy) and GT\CssXPath (canonical going forward). PHP resolves namespaces case-insensitively at runtime, but Composer/PSR-4 autoloading is filesystem-dependent, so consuming code should prefer the new GT\CssXPath casing to future-proof against a potential removal of the alias in a future major version.SelectorTranslator facade for cleaner syntax:
use App\Facades\SelectorTranslator;
$xpath = SelectorTranslator::toXPath('div.content > p');
Query builder for dynamic XPath generation in Eloquent models (e.g., for XML-based databases like doctrine/dbal).button#submit → //button[@id='submit']).:nth-child), attribute selectors ([data-testid]), and namespace-aware XPath.:has() or :is() may not map cleanly to XPath 1.0 (default in DOMDocument). Requires XPath 2.0+ or manual fallbacks.strictMode flag to fail on unsupported queries.Illuminate\Support\Facades\Cache) or batch-process queries.Gt\CssXPath alias is dropped in a future major release, code still referencing the old casing may break after upgrade.GT\CssXPath in new code and schedule a low-priority task to update legacy references before any future major version bump.div[class^="prefix-"])?Facebook/WebDriver’s built-in conversion)?GT\CssXPath vs Gt\CssXPath) will we standardize on internally, and do we have a policy to track future namespace deprecations?Guzzle + Symfony\Component\DomCrawler.Laravel Dusk or Pest for cross-selector compatibility.spatie/array-to-xml or ext-dom for dynamic responses.doctrine/dbal with XPath queries).symfony/css-selector (similar functionality but Symfony-focused).php-cs-fixer for CSS selector linting.phpunit to assert XPath output matches expectations.$css = 'a[href^="https://"]';
$xpath = (new \GT\CssXPath())->toXPath($css);
$this->assertEquals('//a[starts-with(@href, "https://")]', $xpath);
app/Services/SelectorTranslator.php) to abstract the package.// config/cssxpath.php
'supported_selectors' => ['class', 'id', 'attribute', 'pseudo-class'],
where clauses:
$posts = Post::whereCss('div.post > h2')->get(); // Translates to XPath
Gt\CssXPath references and swap to GT\CssXPath (new canonical casing from v1.6.0). Low-risk, mechanical refactor.div, #id, .class), combinators (>, +), and attributes ([href]).:nth-child), pseudo-elements (::before), or :has() (may require XPath 2.0).DOMDocument).ext-xpath or sabberworm/PHP-XPath.GT\CssXPath + Gt\CssXPath), Composer config cleanup, coverage tooling fixes. Non-breaking, no selector-behavior changes; safe minor upgrade.AppServiceProvider:
public function register()
{
$this->app->singleton(\GT\CssXPath::class, function () {
return new \GT\CssXPath();
});
}
GT\CssXPath class resolves via Composer autoload in CI (guards against future namespace deprecation).Laravel Debugbar).Gt\CssXPath → canonical GT\CssXPath).composer.json replace.Gt\CssXPath alias, treat it as a semver-major and gate the upgrade behind the standard dependency-review checklist.var_dump($css, $xpath)).GT\CssXPath vs Gt\CssXPath) and run composer dump-autoload — PSR-4 maps files by case, so mismatched casing can produce "class not found" errors on case-sensitive filesystems (Linux CI/deploy) even though PHP itself is case-insensitive.class_exists(\GT\CssXPath::class) to catch regressions in CI or post-deploy.Cache facade for cross-request) to eliminate repeated translation of identical selectors in bulk crawl jobs.strictMode protects correctness; ensure a graceful fallback path in production scraping services.class_exists health check described above.composer update may surface conflicts with symfony/css-selector or scraping libraries; resolve via version constraints rather than fork unless necessary.How can I help you explore Laravel packages today?