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 Manipulations Laravel Package

league/uri-manipulations

League URI Manipulations provides immutable, PSR-7-friendly tools for modifying URIs in PHP. Add, remove, update, or conditionally alter query parameters, paths, and other components through a clean, composable API.

View on GitHub
Deep Wiki
Context7

Getting Started

league/uri-manipulations is a small, function-based companion to the League URI stack. It takes any PSR-7 UriInterface and returns a new immutable instance with one targeted change applied (query param added/removed, path segment appended, trailing slash toggled, etc.). It is not a router, a builder DSL, or a URL generator — it is a set of safe, encoding-aware string surgery helpers.

Minimal setup:

composer require league/uri-manipulations:^2.0

It pulls in league/uri-components and psr/http-message. No service provider, no config file, no facade — the functions are autoloaded via Composer files, so once installed they are callable immediately.

Where to look first: the README's function table (it lists every function and its one-line contract) and the tests/ directory, which doubles as the most accurate documentation of edge-case behaviour (empty query strings, root paths, encoded delimiters).

First use case — the one you will hit on day one:

use League\Uri\Uri;
use function League\Uri\Manipulations\append_query;

$uri = Uri::createFromString('https://example.com/search?q=shoes');
echo append_query($uri, 'page=2');
// https://example.com/search?q=shoes&page=2

The value proposition: you never concatenate '?', '&', or '/' yourself, and you never accidentally double-encode a value that the query parser already owns.

Implementation Patterns

Tracking / UTM decoration at the edge. Wrap the decoration in one helper so you can swap the implementation later:

function withCampaign(UriInterface $uri, string $source): UriInterface
{
    return merge_query($uri, ['utm_source' => $source]);
}

Prefer merge_query over append_query for anything keyed — merge replaces an existing key, append will happily produce ?page=1&page=2.

Pagination links. Take the current request URI straight off the PSR-7 request and derive page links without re-parsing anything:

$next = merge_query($request->getUri(), ['page' => $current + 1]);
$prev = $current > 1
    ? merge_query($request->getUri(), ['page' => $current - 1])
    : remove_query($request->getUri(), 'page');

Canonical URLs and cache keys. sort_query gives you a deterministic query ordering, and remove_query strips churn-inducing params (utm_*, fbclid, session ids). The pair is a compact canonicaliser:

$canonical = (string) sort_query(remove_query($uri, ['fbclid', 'gclid']));
$cacheKey  = sha1($canonical);

API base URLs and link assembly. append_path / append_segment are the safe way to join a base endpoint with a resource id, because they respect whether the base already ends in / and re-encode the segment:

$endpoint = append_segment(Uri::createFromString('https://api.example.com/v1'), 'users');
// https://api.example.com/v1/users

Framework glue. Put the functions behind one class (or a Twig/Blade helper) so application code depends on your interface, not on a set of global functions from an archived package. This is also the seam you will later reimplement against league/uri v6+.

Composition order matters and is usually: normalise → mutate → serialise. remove_trailing_slash before appending a segment avoids // in the path; sort_query last before hashing.

Gotchas and Tips

  • Archived since 2018. No fixes, no PHP 8.1+ hardening, and it drags in league/uri-components 5.x. It generally still runs, but treat it as vendored code: pin the exact version, and plan the migration to League\Uri\Uri + League\Uri\Components\Query/Path (which absorbed these operations as methods: Query::merge(), Uri::withPath(), etc.).
  • Immutability bites. Every function returns a new object. append_query($uri, 'a=1'); followed by echo $uri; silently does nothing. Always assign.
  • append_query duplicates keys. If you might touch an existing parameter, use merge_query. Duplicate-key query strings are legal but are a classic source of "why is page 1 and 2?" bugs.
  • Encoding asymmetry. Strings passed to query functions are treated as already-encoded query fragments; arrays are encoded for you. Mixing the two (append_query($uri, 'q=shoes & boots')) produces an invalid URI rather than an exception — validate with Uri::createFromString on the result in tests.
  • Trailing-slash helpers are path-only and idempotent-to-a-fault. They no-op on the root path / and never touch the host or fragment. If you expect https://example.com → https://example.com/, that is withPath('/') territory, not this function.
  • Empty query handling. remove_query on the last remaining parameter may leave a dangling ? depending on version; normalise by round-tripping through Uri::createFromString((string) $uri) if the exact serialisation matters (e.g. signed URLs — where you should not be using this package at all).
  • Fragment ordering. Query manipulation does not reorder fragments; but some proxies treat #frag before ?query as invalid. Sanity-check anything you generate for redirects.
  • No relative resolution. append_path does not implement RFC 3986 merge semantics. append_path($base, '../x') will not do what you want; resolve against a base URI yourself first.
  • Debugging tip: the fastest triage is var_dump((string) $before, (string) $after) on the two URIs and diff the components individually (getScheme, getHost, getPath, getQuery, getFragment). Almost every reported "bug" is the wrong component being mutated.
  • Extension point: the only real one is your own wrapper class. There are no events, no middleware, and no config — so build the seam yourself before the migration deadline forces it.
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.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core