Product Decisions This Supports
- PHP upgrade sequencing (biggest lever). This package decouples "write modern URI code" from "upgrade the runtime." It lets teams adopt the incoming native
Uri\Rfc3986\Uri / Uri\WhatWg\Url API today on older PHP, then delete the dependency with a one-line Composer removal once the platform upgrade lands. That converts a hard-blocked migration into two independent, reversible decisions.
- Build vs. buy on URL handling. The default today is bespoke regex parsing,
parse_url() plus edge-case glue, or a heavier URI library. This is a ~zero-cost, MIT, core-aligned option that replaces hand-rolled parsing with spec-defined behavior — a clear "buy/consume" over "build," with an unusually clean exit path (it is designed to become obsolete).
- Standardization of URL semantics across services. Choosing the WHATWG parser for user-facing URLs and the RFC 3986 parser for machine-to-machine identifiers gives the org one vocabulary for "what is a valid URL," instead of each team's regex disagreeing. This is a platform/architecture decision, not just a library pick.
- Security-relevant surface: SSRF, open redirect, allowlist bypass, IDN homograph. URL canonicalization and host extraction feed allowlists and egress rules. Adopting a spec-driven parser (with WHATWG IDNA/percent-encoding handling) materially reduces the class of bypass bugs that come from naive string splitting.
- Feature areas it unblocks or hardens: webhook/callback URL validation and normalization, user-supplied link ingestion and canonical link rewriting, referrer/UTM parsing, multi-tenant subdomain routing, sitemap/canonical URL normalization, and PSR-7/PSR-15 URI interop behind an adapter.
- Roadmap hygiene. It belongs in the tech-debt register as a deliberate, time-boxed dependency with an explicit removal trigger (target PHP version), not an open-ended commitment. That framing is what makes it defensible to a change-approval board.
When to Consider This Package
Adopt when most of these hold:
- You target an older PHP runtime than the one shipping native URI classes, and you have a credible upgrade date on the roadmap. Without a removal plan, you are just adding a dependency to avoid writing an adapter.
- You want to write against the future core API, not a vendor API — the point is that your code survives the dependency's deletion unchanged.
- Your needs fit the native surface: parse, validate, normalize, resolve relative references, extract components, compare/canonicalize. If you need more than that, this is the wrong layer.
- You need browser-consistent behavior for user-facing URLs (WHATWG semantics, IDNA, percent-encoding normalization) rather than strict RFC 3986 only.
- You can afford to verify parity yourself. Treat the polyfill as untrusted-for-correctness until you have run your own hostile-URL corpus against it. Low stars and zero dependents are not disqualifying here (it is a read-only split of The League's
uri monorepo), but they do mean you are the QA function.
- Your dependency policy tolerates a transitional shim — small surface, permissive license, single-maintainer-org risk, no SLA.
Look elsewhere when:
- You need a full URI toolkit now: component-level mutation, query-string builders, RFC 6570 template expansion, separate IDNA/UTS#46 conversion, PSR-7
UriInterface implementations, or Laravel-native wrappers. Use the parent league/uri package (or Laravel's Uri/Url helpers) instead — the polyfill is deliberately minimal and will not grow into that.
- The package's PHP constraint doesn't reach your floor. Verify the Composer
require before designing around it; on very old runtimes this may not even install.
- You need vendor support, security backports with SLAs, or long-term guarantees. A 1-star transitional polyfill is not that, regardless of who publishes it.
- You cannot tolerate behavior drift. If the polyfill and the eventual native implementation diverge on an edge case, your code's semantics shift under you at upgrade time. Mitigate with a golden-file test corpus, or skip the polyfill and write a thin internal adapter instead.
- Your team would rather own ~200 lines of parsing wrapper than take a maintenance dependency for a stopgap. For a narrow use case (host extraction only), that is a defensible alternative.
Due diligence checklist before greenlight: confirm parity test coverage against native in CI across your PHP versions, confirm the classes no-op cleanly when PHP provides them natively (no redeclaration fatal), pin the version, and add the hostile-URL corpus to your own test suite rather than relying on upstream.
How to Pitch It (Stakeholders)
For executives (30 seconds): "We handle URLs — user-submitted links, webhooks, redirects — in a dozen places with hand-written parsing, which is where a class of security bugs (SSRF, open-redirect bypass) comes from. PHP is adding a standard, browser-grade URL parser to the language itself. This free, MIT-licensed package lets us write against that standard API now, on our current PHP version, so our code is correct today and needs no rewrite when we upgrade. It's a temporary bridge, not a permanent dependency — it gets deleted at the PHP upgrade, and its cost is a Composer line plus a test corpus."
For engineering: "You write new Uri\WhatWg\Url($input) and Uri\Rfc3986\Uri::parse() exactly as you would on the target PHP. When the runtime ships the native classes, the polyfill stops declaring them and you drop the requirement — zero source changes. Trade-off: it's a minimal shim, so it won't replace league/uri's component manipulation or query builders; keep those where you need them. Requirement before merge: run our hostile-URL corpus (backslash authority tricks, 0x7f.1-style hosts, //evil.com protocol-relative, unicode dots, %2e%2e traversal, userinfo @ confusion) through it and pin the version."
Ask: approve as a scoped, time-boxed dependency with a named owner, a pinned version, a parity test gate in CI, and a documented removal trigger tied to the PHP upgrade milestone.