--- layout: default title: RFC 6570 compliant URI template description: The UriTemplate class enables expanding a URI template and its submitted parameters into an URI object. --- URI Template ======= The `League\Uri\UriTemplate` class enables expanding a URI object based on a URI template and its submitted parameters following [RFC 6570 URI Template](http://tools.ietf.org/html/rfc6570). ## Template Expansion ### RFC Expansion The `UriTemplate::expand` public method expands a URI template to generate a valid URI conforming to RFC3986. ~~~php '42', 'hotel' => 'Rest & Relax']; $uriTemplate = new UriTemplate($template); $uri = $uriTemplate->expand($params); // instance of League\Uri\Uri $rfc3986Uri = $uriTemplate->expandToUri($params); // instance of Uri\Rfc3986\Uri $whatWgUrl = $uriTemplate->expandToUrl($params); // instance of Uri\Whatwg\Url $psr7Uri = $uriTemplate->expandToPsr7Uri($params); // instance of Psr7 UriInterface echo $uri; //display https://example.com/hotels/Rest%20%26%20Relax/bookings/42" ~~~
~~~php '42', 'hotel' => 'Rest & Relax']; $uriTemplate = new UriTemplate($template); $uri = $uriTemplate->expand($params, 'https://example.com'); // instance of League\Uri\Uri $rfc3986Uri = $uriTemplate->expandToUri($params, 'https://example.com'); // instance of Uri\Rfc3986\Uri $whatWgUrl = $uriTemplate->expandToUrl($params, 'https://example.com'); // instance of Uri\Whatwg\Url $whatWgUrl = $uriTemplate->expandToPsr7Uri($params, 'https://example.com'); // instance of League\Uri\Http echo $uri; //display https://example.com/hotels/Rest%20%26%20Relax/bookings/42" ~~~ ### Strict Expansion By default, if variables are missing or are not provided an empty string is used as replacement string as per the RFC. If you want to force correct expansion you can use the `expandOrFail` method. It behaves exactly like the `expand` method but will additionnally throw an exception if there are missing required variables. ~~~php $template = 'https://api.twitter.com/{version}/search/{term}/{?q*,limit}'; $params = [ 'term' => ['john', 'doe'], 'q' => ['a', 'b'], 'limit' => '10', ]; $uriTemplate = new UriTemplate($template); echo $uriTemplate->expand($params), PHP_EOL; // display https://api.twitter.com//search/john,doe/?q=a&q=b&limit=10 with missing version echo $uriTemplate->expandOrFail($params); // will throw a TemplateCanNotBeExpanded exception with the following message // Missing variables `version` ~~~ ### Literal Expansion The characters located outside of the template expressions are literals. As per [RFC6570 section 3.1](https://www.rfc-editor.org/rfc/rfc6570#section-3.1) a literal character is copied as is when it is allowed anywhere in the URI syntax, in other words when it belongs to the `unreserved / reserved / pct-encoded` set. Any other character is percent encoded using its UTF-8 octets. ~~~php $uriTemplate = new UriTemplate('https://example.com/café/{hotel}'); echo $uriTemplate->expand(['hotel' => 'Rest & Relax']), PHP_EOL; // display https://example.com/caf%C3%A9/Rest%20%26%20Relax ~~~ Percent encoded triplets already present in the template are left untouched, they are never encoded a second time. ~~~php $uriTemplate = new UriTemplate('https://example.com/caf%C3%A9/{hotel}'); echo $uriTemplate->expand(['hotel' => 'Rest & Relax']), PHP_EOL; // display https://example.com/caf%C3%A9/Rest%20%26%20Relax ~~~ ## Template Variables ### Default Variables The constructor takes an optional set of default variables that can be applied by default when expanding the URI template. ~~~php $template = 'https://api.twitter.com/{version}/search/{term:1}/{term}/{?q*,limit}'; $params = [ 'term' => 'john', 'q' => ['a', 'b'], 'limit' => '10', ]; $uriTemplate = new UriTemplate($template, ['version' => 1.1]); echo $uriTemplate->expand($params); //displays https://api.twitter.com/1.1/search/j/john/?q=a&q=b&limit=10 ~~~ ### Runtime Variables The default variables are overwritten by those supplied to the `expand` method. ~~~php $template = 'https://api.twitter.com/{version}/search/{term:1}/{term}/{?q*,limit}'; $params = [ 'term' => 'john', 'q' => ['a', 'b'], 'limit' => '10', 'version' => '2.0' ]; $uriTemplate = new UriTemplate($template, ['version' => '1.1']); echo $uriTemplate->expand($params), PHP_EOL; //displays https://api.twitter.com/2.0/search/j/john/?q=a&q=b&limit=10 ~~~ The `expandToUri()`, `expandToUrl()` and `expandToPsr7Uri()` methods will act exactly like the `expand()` method but will instead return a `Uri\Rfc3986\Uri`, a `Uri\Whatwg\Url` or a `Psr7\Http\Message\UriInterface` object respectively. ~~~php $template = '/{version}/search/{term:1}/{term}/{?q*,limit}'; //the template represents a non-absolute URL $params = [ 'term' => 'john', 'q' => ['a', 'b'], 'limit' => '10', 'version' => '2.0' ]; $uriTemplate = new UriTemplate($template, ['version' => '1.1']); echo $uriTemplate->expandToUrl($params, 'https://api.twitter.com')->toAsciiString(); //works echo $uriTemplate->expandToUrl($params); //will throw ~~~ ~~~php use League\Uri\UriTemplate; use Laminas\Diactoros\UriFactory; $template = '/{version}/search/{term:1}/{term}/{?q*,limit}'; //the template represents a non-absolute URL $params = [ 'term' => 'john', 'q' => ['a', 'b'], 'limit' => '10', 'version' => '2.0' ]; $uriTemplate = new UriTemplate($template, ['version' => '1.1']); $uri = $uriTemplate->expandToPsr7Uri($params, 'https://api.twitter.com', new UriFactory()); $uri::class; // returns 'Laminas\Diactoros\Uri' ~~~ By default, if no factory is provided, the returned PSR-7 `UriInterface` object is a `League\Uri\Http` instance. ### Updating Variables At any given time you may update your default variables but since the `UriTemplate` is an immutable object instead of modifying the current instance, a new instance with the modified default variables will be returned. ~~~php $template = 'https://api.twitter.com/{version}/search/{term:1}/{term}/{?q*,limit}'; $params = [ 'term' => 'john', 'q' => ['a', 'b'], 'limit' => '10', 'version' => '2.0' ]; $uriTemplate = new UriTemplate($template, ['version' => '1.0', 'foo' => 'bar']); $uriTemplate->getDefaultVariables(); //returns new VariableBag(['version' => '1.0']) $newUriTemplate = $uriTemplate->withDefaultVariables(['version' => '1.1']); $newUriTemplate->getDefaultVariables(); //returns new VariableBag(['version' => '1.1']) ~~~ ~~~php $template = 'https://example.com/hotels/{hotel}/book{?query*}'; $params = [ 'hotel' => 'Rest & Relax', 'query' => [ 'period' => [ 'start' => '2020-01-12', 'end' => '2020-01-15', ], ], ]; $uriTemplate = new UriTemplate($template); $uriTemplate->expand($params); // will throw a League\Uri\UriTemplate\TemplateCanNotBeExpanded when trying to expand the `period` value. ~~~ ## Limitations ### Prefix Modifier While the RFC does not forbid this, the `UriTemplate` class will throw an exception if an attempt is made to use the prefix modifier with a list of value. Other implementations will silently ignore the modifier **but** this package will trigger the exception to alert the user that something might be wrong and that the generated URI might not be the one expected. ~~~php $template = 'https://api.twitter.com/{version}/search/{term:1}/{term}/{?q*,limit}'; $params = [ 'term' => ['john', 'doe'], 'q' => ['a', 'b'], 'limit' => '10', 'version' => '2.0' ]; $uriTemplate = new UriTemplate($template); echo $uriTemplate->expand($params), PHP_EOL; // throw a League\Uri\UriTemplate\TemplateCanNotBeExpanded because the term variable is a list and not a string. ~~~ ### Braces Usage The following implementation disallows the use of braces `{` or `}` outside of being URI template expression delimiters. If not used as the boundary of an expression an exception will be triggered. ~~~php $template = 'https://example.com/hotels/{/book{?query*}'; $uriTemplate = new UriTemplate($template); // will throw a League\Uri\Exceptions\SyntaxError on instantiation ~~~ If your template do require them you should URL encode them. ~~~php $template = 'https://example.com/hotels/%7B/{hotel}'; $params = ['booking' => 42, 'hotel' => 'Rest & Relax']; $uriTemplate = new UriTemplate($template); echo $uriTemplate->expand($params), PHP_EOL; // https://example.com/hotels/%7B/Rest%20%26%20Relax ~~~ ## Interoperability To allow easier integration with other PHP packages and especially [PSR-13](https://www.php-fig.org/psr/psr-13/) the `UriTemplate` class implements the `Stringable` interface. ~~~php use League\Uri\UriTemplate; use Symfony\Component\WebLink\Link; $uriTemplate = new UriTemplate('https://google.com/search{?q*}'); $link = (new Link()) ->withHref($uriTemplate) ->withRel('next') ->withAttribute('me', 'you'); // Once serialized will return // '