rize/uri-template
PHP implementation of RFC 6570 URI Templates with full support for all expression types and levels. Expand templates with variables, path segments, and more, plus extract variables from URIs. Supports base URIs and default params for clean API endpoint building.
Architecture fit. rize/uri-template is a stateless, dependency-free leaf utility with a two-method surface (expand(), extract()). It owns no I/O, no persistence, and no lifecycle, so it cannot distort the application architecture — it either gets called or it doesn't. That is its strength for Laravel: there is no service provider, facade, config namespace, migration, or published asset to own. The correct framing for the decision is therefore not "does this fit our architecture" but "do we have a real RFC 6570 requirement." Two capabilities justify adoption, and only two:
{/segments*}, {.extensions*}, {?q*,limit}, {+path}) that route(), url(), and http_build_query() cannot express without hand-rolled string assembly — relevant when integrating third-party APIs, generating templated CDN/callback URLs, or consuming Google-style discovery documents.If neither pattern appears in the backlog, this package is a solution looking for a problem, and the correct TPM action is to close the evaluation and let it arrive transitively (it is reported to be a dependency of Google Cloud Core, so it may already be in the tree unowned).
Integration feasibility. Composer install, one singleton binding, one adapter class, one config file. Roughly a day for a vertical slice including tests. The effort is not integration — it is governance: naming the canonical templates, deciding who owns them, and wiring contract tests. Two documentation traps to record in the ADR: the README's install snippet still advertises ~0.3 while the 0.4.x line is PHP 8.1+ only, and the index reports Dependents: 0 while the README claims Google Cloud consumption. The second is a signal-quality problem, not a technical one — verify via Packagist and composer why rize/uri-template in the actual dependency tree before treating either number as fact.
Technical risk. Four items, in descending order of operational consequence:
~0.3 constraint — the one the README still shows — silently strands a project on an unmaintained branch with PHP 8.4/8.5 deprecation noise. Mitigation is trivial but non-optional: pin ^0.4.2, and require human review on any minor bump via Renovate/Dependabot rules.expand() does not throw on a missing or mistyped parameter; it produces a plausible-but-wrong URL. In an outbound integration this manifests as an upstream 404 or, worse, a 200 against the wrong resource. The adapter must enforce required-variable assertions rather than trusting the library.% array modifier. Useful for list[]=a query styles, but it is an extension beyond RFC 6570. It is safe inside a closed loop; it is an interoperability hazard the moment a template is shared with a partner or another language's implementation. Policy: allowed for internal use only.google/cloud-* pinning ^0.3, creates a Composer resolution conflict that will surface at the worst possible moment. Baseline the estate first.Verdict: adopt as a narrow utility behind an adapter, or defer entirely — but do not adopt it casually as a "nice to have," because the version-governance cost exceeds the integration cost.
Key questions for the team: Which pattern is actually required — expansion, extraction, or both? Are templates trusted configuration or user-supplied input (this determines whether ReDoS and false-positive matching are security concerns)? Do we need % array syntax in any cross-system contract? Can we commit to PHP 8.1+ everywhere the library is used? Who owns the template registry?
Stack fit. Framework-agnostic by design, which is the desired shape. Laravel 10/11/12 on PHP 8.2+ is a clean fit. Do not inject Rize\UriTemplate directly into controllers, jobs, or domain services; wrap it. A UriTemplateAdapter behind a small UriExpander/UriMatcher interface gives three things a direct dependency doesn't: a place to enforce required-parameter checks, a place to redact secrets before logging expanded URLs, and a one-class swap if the package is abandoned or the RFC 6570 need evaporates.
Concrete placements where the abstraction earns its keep: the outbound HTTP client layer (per-tenant base URI plus default params, matching the constructor pattern the README demonstrates); templated callback/webhook URL generation and verification; a middleware path for parsing partner URIs that don't conform to our route conventions; OpenAPI/discovery document consumption where path templates are already in {+var} form. Keep Laravel routing for Laravel routes — extraction should not become a second, shadow router.
Migration path. Inventory every place the codebase does manual URL assembly (sprintf, concatenation, http_build_query chains) and classify each as expandable or not. For each candidate: capture the current output as a golden fixture, express the template, and assert byte-equality against the fixture. Run the adapter in shadow mode alongside the legacy builder for one release cycle, logging divergences rather than acting on them, then flip and delete the legacy path. Extraction has no legacy equivalent, so it is greenfield — introduce it behind a feature flag with strict mode defaulted on.
Compatibility. Pin ^0.4.2. Verify composer why rize/uri-template and composer show --tree before adding a direct constraint, since a transitive ^0.3 from a Google Cloud package will force either an upgrade of that package or a downgrade of ours. Document the PHP 8.1 floor as a hard prerequisite in the ADR. Avoid % modifiers in any template that crosses a system boundary. Note that extraction returns null on strict-mode mismatch versus an array (with null values) in non-strict mode — the adapter must normalize this or callers will write ?? [] and erase the distinction that made strict mode valuable.
Sequencing. (1) Dependency audit, PHP floor check, constraint pin, Dependabot rule. (2) Adapter, interface, config file with named template sets and per-service base URIs, plus unit tests including malformed input. (3) Lowest-risk expansion use case, read-only, shipped behind config. (4) Extraction behind a flag, strict mode on, with explicit logging of unmatched URIs. (5) Template registry and a lint test that every configured template expands without missing variables. (6) Deprecate and remove hand-rolled URL builders.
Maintenance. The code volume is negligible; the maintenance burden is entirely version governance. A pre-1.0 dependency in a production tree needs an owner, a pin, and an upgrade ritual — treat minor bumps as potentially breaking, read the release notes (they are short and specific), and run the golden-fixture tests before merging. Budget a few hours per upgrade event, not per sprint.
Support. No vendor, no SLA. The maintainer appears effectively single-handed with occasional community contributors, backed by CI and an RFC conformance surface — reasonable for a utility of this size, but the realistic escalation path is "fork it." Pre-qualify that: MIT license, one class of substance, so the fork cost is hours, not weeks. This is the strongest argument for the adapter: it makes the fork a one-file change. The reported Google Cloud consumption is a mild positive signal that the package will not be abandoned without someone noticing.
Scaling. Pure CPU string and regex work, no I/O. Per-call cost is negligible against any network hop. Two caveats worth engineering around: template parsing likely occurs on every call, so memoize parsed results or cache adapter output per template+params for hot paths; and {var*} list expansion plus very long inbound URIs for extraction are unbounded inputs — cap array sizes and URI length at the adapter boundary rather than discovering the cost in a noisy-neighbour incident.
Failure modes (ranked by likelihood × blast radius):
% modifiers turning brackets into %5B%5D. Fix with round-trip tests on real payloads, not synthetic ones.0.3/0.4 split.Ramp-up. An engineer new to RFC 6570 needs roughly half a day with the operator table; the README covers most of the practical surface, and the examples are unusually good. The real onboarding cost is the convention: which templates are canonical, where they live, and how they are tested. Ship a one-page cheat sheet plus the golden-fixture suite in-repo, and have the adapter reject unknown template keys so a typo fails loudly at the boundary instead of silently producing a URL that 404s three services downstream. On-call impact is low but opaque — failures surface as third-party errors, not as library errors, so add expanded-URL logging (redacted) to the HTTP client layer before the first production use.
How can I help you explore Laravel packages today?