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

Uri Query Parser Laravel Package

league/uri-query-parser

A standalone PHP library for parsing and building RFC 3986 query strings. It preserves key order and duplicate parameters, handles configurable separators and encoding, and provides the query-string parsing foundation used by league/uri.

View on GitHub
Deep Wiki
Context7

Getting Started

Install with Composer:

composer require league/uri-query-parser

Two classes are all you need. Look at these first:

  • League\Uri\QueryParser\Parser — turns a raw query string into an ordered list of [name, value] pairs.
  • League\Uri\QueryParser\Builder — turns that pair list back into a query string.

The core idea is that a query string is not an associative array. It is an ordered sequence of pairs where keys may repeat. This package models that faithfully, unlike parse_str(), which flattens duplicates, reorders nothing but loses information, and mangles keys with dots/spaces.

Your first use case is the one that breaks parse_str():

use League\Uri\QueryParser\Parser;
use League\Uri\QueryParser\Builder;

$parser  = new Parser();
$builder = new Builder();

$pairs = $parser->parse('filter=active&filter=archived&sort=name');
// [ ['filter', 'active'], ['filter', 'archived'], ['sort', 'name'] ]

echo $builder->build($pairs);
// filter=active&filter=archived&sort=name

That round-trip is the whole value proposition: order and duplicate keys survive.

Implementation Patterns

Parse → mutate → rebuild. Treat the pair list as an ordered log. To add, filter, or reorder parameters, manipulate the array of pairs directly, then hand it back to the builder. This is the most common pattern in middleware, signed-URL handlers, and API clients.

$pairs = $parser->parse($request->getUri()->getQuery());

// Remove every occurrence of a key
$pairs = array_values(array_filter($pairs, fn ($p) => $p[0] !== 'signature'));

// Append a new parameter
$pairs[] = ['page', '2'];

$query = $builder->build($pairs);

Reading single values. Build a small helper that returns the first value for a key, or a list of all values when duplicates matter. Do not convert the pair list into an associative array at the boundary — that reintroduces the bug you chose this package to avoid. Provide first($pairs, 'key') and all($pairs, 'key') and call them explicitly.

Signing and canonicalization. Because the package preserves order, you can canonicalize deterministically: sort pairs by name (stable, preserving relative order of duplicates), drop known-volatile keys, rebuild, then hash. Keep the canonicalization in one place so verification and generation stay in sync.

Query merging. When combining queries from two URIs, concatenate the pair lists and let duplicate keys remain, rather than array_merge-ing associative arrays. Downstream consumers that read "all values for key x" will see both.

Integration with PSR-7. The pair list works well as an intermediate representation: parse($uri->getQuery()) on the way in, set the rebuilt string via $uri->withQuery(...) on the way out. Because the builder handles encoding, you avoid the double-encoding that happens when you manually urlencode() then pass through a URI library.

Validation layer. The parser performs syntax work, not semantic work. Wrap it in a value object (e.g. QueryParameters) that applies your app's rules — allow-lists of keys, integer coercion, rejection of nested bracket notation — and expose typed getters. This keeps the third-party call surface to two methods and makes a future migration mechanical.

Gotchas and Tips

It is archived (last release 2019). No bug fixes, no PHP 8.2+ deprecation patches, no security response. Pin it exactly, or better, plan the exit. The functionality lives on in league/uri v6+:

use League\Uri\QueryString;

QueryString::parse('a=1&a=2');        // pairs
QueryString::build([['a', '1']]);     // string
QueryString::extract('a=1&a=2');      // assoc, if you want the old parse_str behaviour

The API is close enough that a migration is mostly a use statement change plus swapping the parser/builder instances for static calls. Do it before you accumulate call sites.

Do not pipe the pairs through http_build_query(). It will collapse duplicates and re-encode with application/x-www-form-urlencoded rules (+ for space) instead of RFC 3986 (%20). Always rebuild with the package's Builder.

Space encoding differs between query strings and form bodies. In a URI query, a space is %20; in application/x-www-form-urlencoded bodies it is +. If you parse an HTML form POST body with this package you may mis-handle +. Restrict it to URIs, and let the framework handle request bodies.

Encoding round-trips can surprise you. Percent-encoding normalization (%7e vs ~), uppercase vs lowercase hex, and already-encoded input all affect the output string byte-for-byte. If you sign or cache-key on the rebuilt query, normalize consistently (e.g. through League\Uri itself) before comparing, or your signature check will fail intermittently.

No max_input_vars truncation. Because it does not go through parse_str(), it is not subject to the max_input_vars / max_input_nesting_level ini limits — a genuine advantage for large machine-generated queries. But it also does no size limiting, so apply your own cap on query length if the input is untrusted.

Bracket notation is not a data structure. a[]=1&a[]=2 is, to this parser, just two pairs whose names contain brackets. It will not produce nested arrays. Decide explicitly whether brackets are significant in your API; if they are, do the interpretation yourself after parsing.

Empty key vs empty value is distinguishable. foo, foo=, and =bar are three different things here and all three are preserved. Good for protocol correctness, but it means you must validate; downstream code that assumes $value !== '' will break on foo.

No extension points worth using. There is no pluggable encoder or custom pair class. If you need non-standard behaviour (semicolon separators, case-insensitive key folding), wrap the package rather than subclass it. Keep the wrapper thin so the eventual move to league/uri touches one file.

Composer hygiene. With zero dependents and no activity, expect no upstream support. Add it to your own composer.json with an explicit constraint, and record the migration target in a TODO — the cheapest time to remove it is now, while it is only wrapped in one class.

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.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums