guzzlehttp/uri-template
RFC 6570 URI Template expansion for PHP. Build URLs by substituting variables into templates, handling reserved characters, query strings, and fragments. Lightweight Guzzle component installable via Composer, with tests and changelog included.
Installation:
composer require guzzlehttp/uri-template
Add to composer.json under require if not using Composer globally.
First Use Case: Expand a URI template with variables:
use GuzzleHttp\UriTemplate;
$template = '/users/{userId}/posts/{postId}';
$uri = UriTemplate::expand($template, [
'userId' => 123,
'postId' => 456
]);
// Output: "/users/123/posts/456"
Where to Look First:
UriTemplate::expand() and UriTemplate::expandQuery() methods in the source.Dynamic API Endpoints:
$template = 'https://api.example.com/v1/{resource}/{id}?filter={query}';
$uri = UriTemplate::expand($template, [
'resource' => 'users',
'id' => $userId,
'query' => 'active=true'
]);
Query String Expansion:
Use expandQuery() for query parameters with operators:
$template = '?{+sort},{+page}';
$query = UriTemplate::expandQuery($template, [
'sort' => 'name',
'page' => 2
]);
// Output: "?+sort=name&+page=2"
Laravel HTTP Client Integration:
Combine with Guzzle’s Client for API calls:
use Illuminate\Support\Facades\Http;
$uri = UriTemplate::expand('/users/{id}', ['id' => $userId]);
$response = Http::get($uri)->json();
Route Generation:
Use in Laravel’s Route::get() with dynamic segments:
Route::get('/{user}/posts/{post}', function ($user, $post) {
$uri = UriTemplate::expand('/users/{user}/posts/{post}', [
'user' => $user,
'post' => $post
]);
// Use $uri for redirects or logging
});
{userId} with ../).try-catch for malformed templates:
try {
$uri = UriTemplate::expand($template, $data);
} catch (\InvalidArgumentException $e) {
Log::error("URI template error: " . $e->getMessage());
}
+, &, |) for query strings to control behavior (e.g., ?{+page} adds & if present).Empty String Expansion:
?{+var} with var = "" becomes ? instead of ?+var=).$template = '?{+filter}';
$query = UriTemplate::expandQuery($template, ['filter' => '']);
// Output: "?+filter=" (v1.0.7+ preserves `+`; older versions may drop it)
Non-Finite Floats (PHP 8.5+):
INF, NAN, or 0.0 may trigger warnings in PHP 8.5.@ or handle in code:
$data = ['limit' => INF];
$uri = @UriTemplate::expand('?{limit}', $data); // Suppress warning
// OR validate before expansion:
if (is_finite($data['limit'])) {
$uri = UriTemplate::expand('?{limit}', $data);
}
Double Encoding:
expandQuery() for arrays or manually encode:
$query = UriTemplate::expandQuery('?{filters}', [
'filters' => ['status' => 'active', 'page' => 1]
]);
// Output: "?filters[status]=active&filters[page]=1"
Reserved Characters:
#, ?, or & may break parsing.rawurlencode:
$data = ['query' => 'foo&bar'];
$uri = UriTemplate::expand('?{query}', [
'query' => rawurlencode($data['query'])
]);
Log::debug('Expanded URI:', ['uri' => $uri]);
'').INF, NAN).#, ?, &).v1.0.6 vs. v1.0.7 to catch operator/float changes.Custom Encoding:
Override default encoding by extending UriTemplate:
class CustomUriTemplate extends \GuzzleHttp\UriTemplate {
protected function encodeValue($value) {
return strtoupper($value); // Example: force uppercase
}
}
Query String Parsing:
Use parseQuery() to reverse-expand URIs (e.g., for logging):
$query = UriTemplate::parseQuery('?sort=name&page=2');
// Output: ['sort' => 'name', 'page' => '2']
Laravel Service Provider: Bind the template as a singleton for global use:
// app/Providers/AppServiceProvider.php
public function register() {
$this->app->singleton(UriTemplate::class, function () {
return new \GuzzleHttp\UriTemplate();
});
}
Then inject via constructor or app() helper.
config/uri-template.php exists.^1.0 for PHP 8.1+).strict_types=1 in composer.json to match the package’s defaults.How can I help you explore Laravel packages today?