rize/uri-template
RFC 6570 URI Template implementation for PHP. Expand templates into URLs and extract variables from matching URIs. Supports all expression types/levels, path segment and query expansions, plus base URI and default parameters—handy for building API endpoints.
Start by installing via Composer: composer require rize/uri-template. Then instantiate the class and use expand() for URL construction or extract() for parsing incoming URIs. The simplest use case is building a Twitter-style profile URL:
use Rize\UriTemplate;
$uri = new UriTemplate();
echo $uri->expand('/{username}/profile', ['username' => 'john']);
// Outputs: '/john/profile'
For API clients, initialize with a base URI and default parameters:
$api = new UriTemplate('https://api.example.com/{version}', ['version' => 'v1']);
echo $api->expand('/users/{id}', ['id' => 123]);
// Outputs: 'https://api.example.com/v1/users/123'
Dynamic API Client Generation: Build SDKs for external services that use RFC 6570 templates (common in Google Cloud, Stripe, etc.) by expanding templates with runtime parameters:
$template = '/v1/{projectId}/datasets/{datasetId}{?view,paginationToken}';
$api->expand($template, [
'projectId' => 'my-project',
'datasetId' => 'sales',
'view' => 'FULL',
'paginationToken' => 'next123'
]);
Strict Route Matching for API Gateways: Use extract() with strict mode to validate incoming requests against known API endpoints:
$router = new UriTemplate();
$extracted = $router->extract(
'/api/{version}/users/{id}',
'/api/v1/users/42',
strict: true
);
if ($extracted) {
// Route matches and all variables extracted
$version = $extracted['version'];
}
Complex Query Parameter Handling: Leverage the % modifier for PHP-style array queries (e.g., ?filter[name]=john&filter[role]=admin):
$template = '/search{?filter%}';
$uri = $api->expand($template, [
'filter' => ['name' => 'john', 'role' => 'admin']
]);
// Results in: '/search?filter[name]=john&filter[role]=admin'
Then extract these parameters back from requests:
$params = $api->extract($template, '/search?filter[name]=john');
// $params['filter'] = ['name' => 'john']
Nested Path Segment Expansion: Handle hierarchical paths efficiently with * and / operators:
$template = '/{host}{/segments*}/{file}{.extensions*}';
$uri = $api->expand($template, [
'host' => 'cdn.example.com',
'segments' => ['assets', 'js', 'main'],
'file' => 'bundle',
'extensions' => ['js', 'map']
]);
// 'https://cdn.example.com/assets/js/main/bundle.js.map'
^0.3.8 for older PHP versions.strict Mode Considerations: Extraction fails silently (returns null) if any variable is missing in strict mode. For partial matches, use strict: false (default) and check null values in results.:n): Extraction returns separate keys like term:1 for prefix capture (e.g., term:1 = 'j' when term = 'john' in {term:1}). Don’t assume keys match template names.% modifier produces [] notation (e.g., list[]=value) but expects URL-encoded input in extract(). Always pass raw URIs (e.g., '/path?list%5B%5D=a'), not decoded strings.$baseUri, $defaults) only apply when called on the same instance. Chaining calls won’t inherit defaults:
$api = new UriTemplate('https://api.com/{v}', ['v' => 'v1']);
$api->expand('/path'); // Works
$api->expand('/other'); // Still uses 'v1'
$api->expand('/new'); // Still works
expand().extract() returns unexpected null, check:
rawurlencode() on inputs)How can I help you explore Laravel packages today?