Weave 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

Pquery Laravel Package

tburry/pquery

pQuery is a jQuery-inspired PHP library for parsing and manipulating HTML. It offers CSS selector queries, chainable DOM traversal and modification, and a familiar API for scraping or transforming markup without fragile regex.

Deep Wiki
Context7

Getting Started

Install with Composer — the package has no dependencies and ships a global pq() helper via its autoload files:

composer require tburry/pquery
require 'vendor/autoload.php';

$doc = pq(file_get_contents('page.html'));          // helper form
$doc = pq\Query\Query::create('<div><a href="/x">X</a></div>'); // explicit form

foreach ($doc->find('a') as $a) {
    echo $a->attr('href'), ' => ', $a->text(), PHP_EOL;
}

Query::create() returns a pq\Dom\Document, which is itself an Element, so every selection method works on the root. find() returns a pq\Query\Query collection that is iterable and chainable.

Where to look first:

  • The README on the repo — it is effectively the API reference and lists every supported selector and method.
  • src/pq/Query.php — the jQuery-style surface (find, filter, each, attr, html, text, css, mutations).
  • src/pq/Dom/ — Document, Element, Text, Comment, DocumentFragment, Attribute; this is where node-level behavior lives.

First useful case: pull structured data out of a page without pulling in libxml/DOMDocument.

$rows = [];
foreach ($doc->find('table.results tbody tr') as $tr) {
    $cells = $tr->find('td');
    $rows[] = [
        'name'  => $cells->eq(0)->text(),
        'price' => $cells->eq(1)->text(),
        'link'  => $cells->eq(0)->find('a')->attr('href'),
    ];
}

Implementation Patterns

Load once, query many times. Parsing builds a lightweight PHP object tree. Parse at the boundary (HTTP response, file, fixture string) and pass the Document around rather than re-parsing.

$doc = pq($html);
$title = $doc->find('h1')->first()->text();
$nav   = $doc->find('nav a')->items(); // raw array of Elements

Iterate with each when you also need the index, and foreach/items() when you don't:

$doc->find('li')->each(function ($i, $li) {
    // $i is the position in the collection, $li is a pq\Dom\Element
    echo $i, ': ', trim($li->text()), PHP_EOL;
});

Extraction helpers. text() collapses descendant text; html() returns inner markup; attr('href') returns the value or null; attr() with no arguments returns the attribute map; css('color') reads inline style; val() reads form values; hasClass()/is('.selector') for predicates.

Mutation and re-serialization. Strings passed to html(), append(), prepend(), before(), after() are parsed as fragments, so you build markup with markup:

$doc->find('.article')->append('<p class="note">Generated</p>');
$doc->find('.ad, .tracking')->remove();       // detach matching nodes
$doc->find('a[href^="http"]')->addClass('external');

echo (string) $doc;                            // Document::__toString renders full HTML

Filtering instead of re-selecting. Narrow an existing collection rather than walking the tree again: ->filter('.active'), ->not('.hidden'), ->has('img'), ->closest('.card'), ->parent(), ->children(), ->siblings(), ->next(), ->prev(). Traversal inside a loop is usually cheaper than a fresh find().

Composition for scrapers. Because the selector engine is hand-rolled and permissive, write selectors as small, testable steps rather than one giant CSS expression:

$cards = $doc->find('div.card');
foreach ($cards as $card) {
    $title = $card->find('.title')->first()->text();
    $tags  = array_map(fn($t) => $t->text(), $card->find('.tag')->items());
}

Sanitizing/rewriting HTML in a pipeline. A common day-to-day use is post-processing stored markup: strip attributes, rewrite URLs, remove scripts, normalize classes — then persist (string) $doc. Keep a fixture in tests and assert on the serialized output so parser upgrades don't silently change behavior.

Gotchas and Tips

  • Unmaintained since 2016. Pin an exact version, run it under your target PHP, and expect deprecation noise on PHP 8+ (dynamic properties, implicit nullable params). If you need fixes, fork under vendor/ or a VCS repository entry — upstream is dormant.
  • Custom selector engine, not full CSS3. Simple selectors, attribute selectors, and many pseudos (:first, :last, :eq(), :gt(), :lt(), :contains(), :has(), :not(), :nth-child()) work, but complex combinators, namespaces, and esoteric pseudos may not. When a selector returns nothing, test the pieces separately before assuming your HTML is wrong.
  • :contains() and attribute matching are case-sensitive. Normalize case yourself when scraping heterogeneous markup.
  • Lenient parsing cuts both ways. Malformed HTML is tolerated instead of erroring, which is great for scraped pages but means bad input fails silently as an empty selection. Assert on ->length in tests.
  • Whitespace text nodes are real nodes. children() skips them; raw iteration may not. trim() extracted text and don't be surprised by indentation inside html().
  • Encoding is byte-oriented. The parser does not do multibyte normalization. Convert input to UTF-8 first (mb_convert_encoding) and ensure a <meta charset> is present, or you'll get mojibake and mis-split characters.
  • text($value) replaces children. Setting text clears descendant elements; use html() when you mean to replace inner markup. For HTML output, run html_entity_decode() if you need the entities resolved for downstream consumers.
  • Collections are live-ish containers of element references, not copies. Mutating a matched node changes the document; ->remove() detaches but the Element object you hold is still valid. Clone explicitly if you need an independent copy for templating.
  • LGPL-2.1. Consuming it as a Composer dependency from application code is normal; if you modify the library itself and distribute, the modified library source must remain available under the same terms. Note this in your dependency policy.
  • Performance shape. Great for tens to hundreds of KB of HTML; every find() walks the subtree, so hoist repeated selections out of loops and prefer filter()/traversal over re-querying. For megabyte documents, benchmark against DOMDocument + DOMXPath before committing.
  • Extension points. Subclass pq\Dom\Document / pq\Dom\Element / pq\Query\Query if you need custom node behavior or helper methods; instantiate your document class at the parse boundary and let find() produce your query subclass. Debug by dumping ->items() and (string) $el rather than var_dump-ing the whole tree.
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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle