Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Css Selector Laravel Package

symfony/css-selector

Symfony CssSelector converts CSS selectors into XPath expressions for querying HTML/XML documents. Useful with DOM tools and crawlers, it provides a fast, reliable bridge between familiar CSS syntax and XPath in PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require symfony/css-selector
    

    Add to composer.json under require or require-dev depending on your use case (e.g., testing vs. scraping).

  2. First Use Case: Convert a simple CSS selector to XPath:

    use Symfony\Component\CssSelector\CssSelectorConverter;
    use Symfony\Component\CssSelector\XPathNodeFinder;
    
    $converter = new CssSelectorConverter();
    $xpath = $converter->toXPath('div.content > p');
    echo $xpath; // Outputs: "//div[contains(@class, 'content')]/p"
    
  3. Where to Look First:

    • Official Documentation
    • src/CssSelectorConverter.php for core logic.
    • src/XPathNodeFinder.php for querying DOM nodes with the converted XPath.

Implementation Patterns

Core Workflows

1. DOM Querying with CSS Selectors

use Symfony\Component\DomCrawler\Crawler;
use Symfony\Component\CssSelector\XPathNodeFinder;

$html = '<div class="container"><p>Hello</p></div>';
$crawler = new Crawler($html);
$finder = new XPathNodeFinder($crawler->getNode(0));

// Convert and query
$converter = new CssSelectorConverter();
$xpath = $converter->toXPath('div.container > p');
$nodes = $finder->find($xpath);

foreach ($nodes as $node) {
    echo $node->nodeValue; // Outputs: "Hello"
}

2. Laravel Integration with Blade or Scraping

Scraping Example (e.g., spatie/laravel-web-scraper):

use Spatie\WebScraper\Facades\WebScraper;
use Symfony\Component\CssSelector\CssSelectorConverter;

$converter = new CssSelectorConverter();
$xpath = $converter->toXPath('article.post:is(.featured, .promoted) h2');

$scraper = WebScraper::scrape('https://example.com/blog');
$titles = $scraper->xpath($xpath)->text();

Blade Template Example:

// In a Blade view or service
$converter = resolve(CssSelectorConverter::class);
$xpath = $converter->toXPath('nav ul li.active');
$activeItems = $finder->find($xpath);

3. Testing with Laravel Dusk or Pest

use Laravel\Dusk\Browser;
use Symfony\Component\CssSelector\CssSelectorConverter;

public function testDynamicSelector(Browser $browser)
{
    $browser->visit('/dashboard');
    $converter = new CssSelectorConverter();
    $xpath = $converter->toXPath(':is(.button, .cta)');
    $browser->assertSeeIn('@' . $xpath, 'Submit');
}

4. Caching XPath for Performance

Leverage the built-in LRU cache to avoid redundant conversions:

$converter = new CssSelectorConverter();
$xpath1 = $converter->toXPath('div.container'); // Cached
$xpath2 = $converter->toXPath('div.container'); // Retrieved from cache

Integration Tips

  1. Service Provider Binding: Bind the converter as a singleton in AppServiceProvider for global access:

    public function register()
    {
        $this->app->singleton(CssSelectorConverter::class, function () {
            return new CssSelectorConverter();
        });
    }
    
  2. Custom XPathNodeFinder Wrapper: Extend XPathNodeFinder for Laravel-specific DOM handling (e.g., DOMDocument or SimpleXML):

    use Symfony\Component\CssSelector\XPathNodeFinder;
    use DOMDocument;
    
    class LaravelXPathNodeFinder extends XPathNodeFinder
    {
        public function __construct(DOMDocument $dom)
        {
            parent::__construct($dom->documentElement);
        }
    }
    
  3. Selector Validation: Use the isValid() method to validate selectors before conversion:

    if ($converter->isValid('div:is(.active, .inactive)')) {
        $xpath = $converter->toXPath('div:is(.active, .inactive)');
    }
    
  4. Combining with Laravel Collections: Convert selectors to XPath and use Laravel’s collect() for post-processing:

    $nodes = collect($finder->find($xpath))->map->textContent;
    

Gotchas and Tips

Pitfalls

  1. Memory Exhaustion in Large-Scale Scraping:

    • Issue: Without caching, repeated conversions of the same selector can bloat memory.
    • Fix: Use the built-in LRU cache or implement a custom cache (e.g., Redis) for high-volume scraping:
      $converter = new CssSelectorConverter();
      $converter->setCache(new \Symfony\Component\Cache\Adapter\FilesystemAdapter());
      
  2. Malformed HTML/XML:

    • Issue: The converter may fail on invalid markup (e.g., unquoted attributes, nested :is()).
    • Fix: Pre-process HTML with DOMDocument::loadHTML() or use spatie/html for cleaning:
      $dom = new DOMDocument();
      @$dom->loadHTML($dirtyHtml, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
      
  3. :is() and :where() Quirks:

    • Issue: Complex combinators like :is(.a, .b):where(.active) may not work as expected in older versions.
    • Fix: Upgrade to v8.1.0-BETA3+ for fixes (e.g., bug #64250). Test with:
      $xpath = $converter->toXPath('div:is(.header, .footer) > p:where(.highlight)');
      
  4. Namespace Handling:

    • Issue: XPath namespaces (e.g., xmlns) may not map cleanly from CSS selectors.
    • Fix: Register namespaces explicitly in DOMDocument or use local-name() in XPath:
      $dom->registerNamespace('ns', 'http://example.com/ns');
      $xpath = '//ns:div[@class="content"]';
      
  5. PHP Version Requirements:

    • Issue: Requires PHP 8.4+ (as of v8.0.0-BETA1).
    • Fix: Use v7.4.x for PHP 8.1–8.3 or upgrade your environment.

Debugging Tips

  1. Enable Debugging: Set the DEBUG constant to log conversion steps:

    define('DEBUG', true);
    $converter = new CssSelectorConverter();
    $xpath = $converter->toXPath('div > p'); // Logs parsing details
    
  2. Validate XPath: Use DOMXPath::evaluate() to test XPath manually:

    $dom = new DOMDocument();
    $dom->loadHTML('<div><p>Test</p></div>');
    $xpath = new DOMXPath($dom);
    $result = $xpath->evaluate('//div/p');
    
  3. Common Selector Edge Cases:

    • Attribute Selectors: Ensure quotes in attributes (e.g., [href="url"] vs. [href=url]).
    • Pseudo-Classes: Test :nth-child(), :not(), and :has() for compatibility.
    • Combinators: Verify >, +, and ~ work as expected in nested structures.

Extension Points

  1. Custom Selector Syntax: Extend CssSelectorConverter to support domain-specific selectors:

    class CustomCssSelectorConverter extends CssSelectorConverter
    {
        protected function parseCustomSelector(string $selector): array
        {
            // Add logic for custom syntax (e.g., `.my-custom[role=admin]`)
            return parent::parseSelector($selector);
        }
    }
    
  2. Post-Processing XPath: Modify XPath after conversion for Laravel-specific needs:

    $xpath = $converter->toXPath('div');
    $xpath = str_replace('//div', '//div[contains(@class, "module")]', $xpath);
    
  3. Integration with Laravel Events: Cache converted selectors globally during boot:

    public function boot()
    {
        $this->app->booted(function () {
            $converter = app(CssSelectorConverter::class);
            $converter->setCache(app('cache')->store('array'));
        });
    }
    
  4. **Testing Selector

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
codraw/framework-extra-bundle
codraw/messenger
codraw/security
codraw/mailer
codraw/contracts
codraw/profiling
codraw/dependency-injection
codraw/tester
codraw/core
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony