guzzlehttp/uri-template
Expands RFC 6570 URI templates into concrete URI strings in PHP, turning templates like /users/{id}{?tab} into real paths with query strings from an array of values.
Architecture fit. Single-purpose, stateless, dependency-free utility. UriTemplate::expand() is a pure static function: template string + variable array in, URI string out. There is no service provider, no config surface, no I/O, no state to manage. In a Laravel codebase it belongs in the integration/adapter layer, not the domain layer — the natural consumers are third-party API clients, HATEOAS/hypermedia consumers (HAL, JSON:API, Siren), pagination-link followers, and OAuth/OIDC discovery metadata resolvers, all of which publish links as RFC 6570 templates (/users/{id}{?tab}, {/id}{?page,per_page}, {+base}assets{/path}). If we are not consuming templated links from an external contract, this package adds nothing — naive string interpolation is sufficient and cheaper.
Integration feasibility. composer require guzzlehttp/uri-template and done; no bootstrapping, no migration, no configuration. The real integration question is not "can we install it" but "do we already have it": Guzzle 7 depends on guzzlehttp/uri-template transitively, so most Laravel apps already ship it. The work is therefore governance — pinning a constraint deliberately and potentially upgrading v1 → v2 — rather than greenfield adoption.
Technical risk. Low, with three specific caveats:
Dependents: 0 and 198-star figures understate reality; this is a carve-out of a class that lived inside Guzzle itself and is pulled in by Guzzle 7. The composite score of ~22.7 is an artifact of the scoring model, not a signal about the package. Recommend we do not let the score drive the decision.InvalidArgumentException instead of expanding partially; invalid UTF-8, non-finite floats, and malformed literals now throw; unsupported shapes no longer degrade to warnings or Array output. Any code currently passing loosely-typed data (nulls, floats, mixed arrays, Stringable objects) may start throwing in production where it previously produced a slightly-wrong URL.>=7.4,<8.6, while the v2.0.1 release notes claim PHP 8.6 support. That inconsistency needs resolving before we commit, because an upper bound below our platform's next PHP upgrade becomes a forced-upgrade trigger mid-cycle.Key questions.
{+var}) or fragment ({#var}) expansion — or only the simple/query/path operators?<8.6 bound?Stack fit. Laravel 10/11/12 on PHP 8.2–8.5 all satisfy the constraint. No container bindings are strictly required, but I would still wrap it: a thin UriTemplateExpander service (or a HypermediaLink value object factory) that owns the expand() call, normalizes/validates the variable array, and converts InvalidArgumentException into a domain-meaningful failure. That wrapper is where we add the security validation described below and where we can swap implementations if the package ever stalls.
Migration path.
composer why guzzlehttp/uri-template to establish whether we already have it transitively and at which major.^1.0 (stay lenient, low effort) or move to ^2.0 (strict, better correctness, requires auditing call sites). Prefer ^2.0 on new code; treat existing call sites as a separate, tested upgrade.INF/NAN, deeply nested nulls, sparse arrays, and objects with side-effecting __toString().UriTemplate or serializes it — v2 makes it non-instantiable and non-serializable, which breaks any queued-job or container-singleton usage.Compatibility. Composer platform config and CI matrix must be checked against the <8.6 ceiling; if 2.0.1 does grant 8.6, pin ^2.0.1 rather than ^2.0 so the constraint resolves. No Laravel-version-specific concerns; nothing touches the framework. Note that v2 changed map/list discrimination (dense zero-indexed arrays → lists, sparse/mixed → maps) — payloads built with array_filter or keyed by IDs will silently change expansion, so add fixture-based tests around our real query-building code.
Sequencing. (1) Discovery/audit of template-producing integrations. (2) Constraint decision + lockfile change. (3) Wrapper service + validation + unit tests, merged before any consumer uses it. (4) Pilot on a single low-blast-radius integration (e.g. pagination-link following in a background sync job). (5) Roll out consumer by consumer. (6) Document the input contract in our internal conventions doc so future teams don't hand-roll expansion.
Maintenance. Effectively zero ongoing cost: no config, no scheduled tasks, no storage, no framework coupling. The release history shows a healthy cadence (v1.0.6 → v1.0.10 plus v2.0.0 and v2.0.1, spanning 2025–2026), but the content is almost entirely edge-case correctness fixes — Unicode prefix-modifier counting, pct-encoding preservation, float locale separators, PCRE failure reporting. That pattern tells us the surface is small and being hardened, not feature-churning. Budget a changelog review per Dependabot PR; patch bumps are low-risk, minor bumps on 1.x have historically carried bug fixes only, and any 2.x minor should be read carefully for input-contract tightening.
Support. MIT, maintained by the Guzzle organization, security reports routed to [email protected]. The Tidelift Subscription is available if we need a commercial support agreement or a security-response SLA to clear vendor risk review — that is the pragmatic lever here, and it is one of the few OSS utilities where the option exists at all.
Scaling. Non-issue. Pure in-process string manipulation, microsecond-scale, no allocations that matter, no external calls, safe in Octane and under high concurrency because there is no shared state. If we expand the same template with the same values repeatedly (e.g. per-request link generation), memoize at the wrapper level — but measure first; this will never be the bottleneck.
Failure modes.
InvalidArgumentException at runtime. Every call site needs a try/catch and a defined fallback (skip the link, log at warning, alert if it's a critical path). This is the most likely production incident from the upgrade.1/0 at every nesting level; sparse arrays become maps. URLs will be valid but wrong. Only fixture tests catch this.{+next} → //evil.example). Treat the expanded result as untrusted: re-parse it, allow-list scheme and host, and drop anything that escapes the expected base origin before handing it to an HTTP client. This is an SSRF/open-redirect control, not a library concern, and it is the single most important thing to get right if templates come from outside our codebase.%2F, spaces, and non-ASCII.Ramp-up. Hours, not days. One static method, a one-page quick start, a usage doc, and an input-contract doc. The only genuine learning investment is RFC 6570 operators ({?}, {&}, {#}, {+}, {/}, {.}, {;}), explode (*), and prefix (:n) semantics — roughly half a day for an engineer to become confident, and worth capturing as a short internal note plus a shared test fixture set so the knowledge does not sit with one person.
How can I help you explore Laravel packages today?