---
layout: default
title: RFC3986 URI Object API
---

RFC3986 URI
=======

The `League\Uri\Uri` class implements:

- League's own [UriInterface](https://github.com/thephpleague/uri-interfaces/blob/master/src/Contracts/UriInterface.php) contracts
- PHP's [JsonSerializable](https://php.net/jsonserializable) interfaces.

The class handles all URI schemes and default to [RFC3986](https://tools.ietf.org/html/rfc3986) rules if the scheme is not present and not recognized as special.

## Instantiation

<p class="message-warning">The default constructor is private and can not be accessed to instantiate a new object.</p>

The `League\Uri\Uri` class comes with the following named constructors to ease instantiation.

<p class="message-info">All named constructors supports parameter widening. When a string is expected, scalar values and objects implementing the <code>__toString</code> method can be used.</p>

### Using a string

~~~php
<?php

use League\Uri\Uri;

$uri = Uri::createFromString(
	'http://example.com/path/to?q=foo%20bar#section-42'
);
~~~

<p class="message-info">If no input is provided a instance representing the empty string is returned.</p>

### Using Uri components

~~~php
use League\Uri\Uri;
use League\Uri\UriString;

$uri = Uri::createFromComponents(
	UriString::parse("http://uri.thephpleague/5.0/uri/api")
);
~~~

<p class="message-warning">If you supply your own hash to <code>createFromComponents</code>, you are responsible for providing well parsed components without their URI delimiters.</p>

### Using environment variables

~~~php
//don't forget to provide the $_SERVER array
$uri = Uri::createFromServer($_SERVER);
~~~

<p class="message-warning">The method only relies on the server's safe parameters to determine the current URI. If you are using the library behind a proxy the result may differ from your expectation as no <code>$_SERVER['HTTP_X_*']</code> header is taken into account for security reasons.</p>

### Using a base URI

Resolves an URI as a browser would for a relative URI.

~~~php
$uri = Uri::createFromBaseUri(
	"./p#~toto",
	"http://www.example.com/path/to/the/sky/"
);

echo $uri; //displays "http://www.example.com/path/to/the/sky/p#~toto"
~~~

This method expect at most two variables. The URL to resolve and the base URL to use for resolution. If not base URL is provided, the URL to resolve **must** be absolute. On the other hand, if a base URL is provided it **must** be absolute. 

### Using an URI object

~~~php
use Zend\Diactoros\Uri as ZendUri;
use League\Uri\Uri;

$zendUri = new ZendUri("http://www.example.com/path/to/the/sky");
$zendUri->getQuery();   //return '';

$leagueUri = Uri::createFromUri($zendUri);
$leagueUri->getQuery(); //return null;
~~~

This named constructor accepts PSR-7 or League UriInterface implementing objects as its sole argument.

### Using the content of a file

Returns a new URI object with the `data` scheme and using the content of the file located at the provided path. Because the function uses internally PHP's file streaming capabilities an optional [context resource](https://www.php.net/manual/en/stream.contexts.php) can be used to better access the file content.

~~~php
$uri = Uri::createFromDataPath('path/to/my/png/image.png');
echo $uri; //returns 'data:image/png;charset=binary;base64,...'
//where '...' represent the base64 representation of the file
~~~

### Using a Windows file path

Returns a new URI object using the `file` scheme for a Windows file path.

~~~php
$uri = Uri::createFromWindowsPath('c:\windows\My Documents\my word.docx');
echo $uri; //returns 'file://localhost/c:My%20Documents/my%20word.docx'
~~~

### Using a Unix file path

Returns a new URI object using the `file` scheme for a Unix file path.

~~~php
$uri = Uri::createFromUnixPath('/path/to/my/file.xml');
echo $uri; //returns 'file://localhost/path/to/my/file.xml'
~~~

URI validation
-------

A `League\Uri\Contracts\UriException` exception is triggered if an invalid URI is given.

~~~php
$uri = Uri::createFromString(':');
// throws a League\Uri\Exceptions\SyntaxError
// because the URI string is invalid
~~~

<p class="message-info">Because the <code>League\Uri\Exceptions\SyntaxError</code> exception extends <code>League\Uri\Contracts\UriException</code> you should catch any exception triggered by the package using the following code.</p>

~~~php
use League\Uri\Uri;
use League\Uri\Contracts\UriException;

try {
	$uri = Uri::createFromString(':');
} catch (UriException $e) {
}
~~~

### Basic validations

By default, if the URI scheme is not recognized, the URI object wil only validate [RFC3986](https://tools.ietf.org/html/rfc3986) rules. This means that depending on the URI scheme the returned URI may not be valid.

~~~php
$mailto_uri = 'mailto://thephpleague.com/path/to?here#content';

$uri = Uri::createFromString($mailto_uri);
//this will not throw an error because this URI satified RFC3986 rules
~~~

For the following special schemes (order alphabetically) extra validation are take into account.

- data
- file
- ftp
- http(s)
- ws(s)

These extra validation rules are triggerd only when the URI is absolute and the scheme is detected. Otherwise only basic [RFC3986](https://tools.ietf.org/html/rfc3986) rules are taken into account.

### http(s) scheme validation

#### Authority presence

If a scheme is present and the scheme specific part of a Http URI is not empty the URI can not contain an empty authority. Thus, some Http URI modifications must be applied in a specific order to preserve the URI validation.

~~~php
$uri = Uri::createFromString('http://uri.thephpleague.com/');
echo $uri->withHost(null)->withScheme(null);
// will throw an League\Uri\Exceptions\SyntaxError
// you can not remove the Host if a scheme is present
~~~

Instead you are required to proceed as below

~~~php
$uri = Uri::createFromString('http://uri.thephpleague.com/');
echo $uri->withScheme(null)->withHost(null); //displays "/"
~~~

<p class="message-notice">When an invalid URI object is created an <code>SyntaxError</code> exception is thrown</p>

#### Path validity

According to [RFC3986](https://tools.ietf.org/html/rfc3986), if an HTTP URI contains a non empty authority part, the URI path must be the empty string or absolute. Thus, some modification may trigger an <code>SyntaxError</code>.

~~~php
$uri = Uri::createFromString('http://uri.thephpleague.com/');
echo $uri->withPath('uri/schemes/http');
// will throw an League\Uri\Exceptions\SyntaxError
~~~

Instead you are required to submit an absolute path

~~~php
$uri = Uri::createFromString('http://uri.thephpleague.com/');
echo $uri->withPath('/uri/schemes/http');
// displays 'http://uri.thephpleague.com/uri/schemes/http'
~~~

Of course this does not mean that rootless path are forbidden, the following code is fine.

~~~php
$uri = Uri::createFromString('?foo=bar');
echo $uri->withPath('uri/schemes/http'); // displays 'uri/schemes/http?foo=bar'
~~~

### ftp scheme validation

It can not contains a query and or a fragment component.

<p class="message-notice">Adding contents to the fragment or query components throws an <code>UriException</code> exception</p>

~~~php
$uri = Uri::createFromString('ftp://thephpleague.com/path/to/image.png;type=i');
$uri->withQuery('p=1'); // will throw an League\Uri\Exceptions\SyntaxError
~~~

<p class="message-info">Apart from the fragment requirements, FTP URIs share the same [validation rules](/5.0/uri/schemes/http/#validation) as Http URIs.</p>

### ws(s) scheme validation

It can not contain a fragment component as per [RFC6455](https://tools.ietf.org/html/rfc6455#section-3).

<p class="message-notice">Adding contents to the fragment component throws an <code>UriException</code> exception</p>

~~~php
$uri = Uri::createFromString('wss://thephpleague.com/path/to?here#content');
// will throw an League\Uri\Exceptions\SyntaxError
~~~

<p class="message-info">Apart from the fragment requirements, FTP URIs share the same [validation rules](/5.0/uri/schemes/http/#validation) as Http URIs.</p>

### data scheme validation

Even though all URI properties are defined and accessible attempt to set any component other than the path will result in the object throwing a `SyntaxError` exception. As adding data to theses URI parts will generate an invalid Data URI.

~~~php
$uri = Uri::createFromPath('path/to/my/png/image.png');
$uri->getHost(); //returns null
$uri->withHost('example.com'); // will throw an League\Uri\Exceptions\SyntaxError
~~~

Accessing URI properties
-------

The [RFC3986](https://tools.ietf.org/html/rfc3986) URI object exposes the following methods.

~~~php
public function Uri::__toString(): string
public function Uri::getScheme(void): ?string
public function Uri::getUserInfo(void): ?string
public function Uri::getHost(void): ?string
public function Uri::getPort(void): ?int
public function Uri::getAuthority(void): ?string
public function Uri::getPath(void): string
public function Uri::getQuery(void): ?string
public function Uri::getFragment(void): ?string
public function Uri::jsonSerialize(void): string
~~~

<p class="message-info">Apart from the path component, which is always a string, all the other URI components can be <code>null</code></p>

You can access the URI string, its individual parts and components using their respective getter methods. This lead to the following result for a simple HTTP URI:

~~~php
$uri = Uri::createFromString("http://foo:bar@www.example.com:81/how/are/you?foo=baz#title");
echo $uri->getScheme();    //displays "http"
echo $uri->getUserInfo();  //displays "foo:bar"
echo $uri->getHost();      //displays "www.example.com"
echo $uri->getPort();      //displays 81 as an integer
echo $uri->getAuthority(); //displays "foo:bar@www.example.com:81"
echo $uri->getPath();      //displays "/how/are/you"
echo $uri->getQuery();     //displays "foo=baz"
echo $uri->getFragment();  //displays "title"
echo $uri;
//displays "http://foo:bar@www.example.com:81/how/are/you?foo=baz#title"
echo json_encode($uri);
//displays "http:\/\/foo:bar@www.example.com:81\/how\/are\/you?foo=baz#title"
~~~

Modifying URI properties
-------

To replace one of the URI component you can use the modifying methods exposed by all URI object. If the modifications do not alter the current object, it is returned as is, otherwise, a new modified object is returned.

<p class="message-notice">Any modification method can trigger a <code>League\Uri\Contracts\UriException</code> exception if the resulting URI is not valid. Just like with the instantiation methods, validition is scheme dependant.</p>

~~~php
<?php

public function Uri::withScheme(?string $scheme): self
public function Uri::withUserInfo(?string $user [, ?string $password = null]): self
public function Uri::withHost(?string $host): self
public function Uri::withPort(?int $port): self
public function Uri::withPath(string $path): self
public function Uri::withQuery(?string $query): self
public function Uri::withFragment(?string $fragment): self
~~~

<p class="message-info">Apart from the path component, which is always a string, to delete a URI component you need to specify its content as being <code>null</code></p>

Since all URI objects are immutable you can chain each modifying methods to simplify URI creation and/or modification.

~~~php
$uri = Uri::createFromString("ws://thephpleague.com/fr/")
    ->withScheme("wss")
    ->withUserInfo("foo", "bar")
    ->withHost("www.example.com")
    ->withPort(81)
    ->withPath("/how/are/you")
    ->withQuery("foo=baz");

echo $uri; //displays wss://foo:bar@www.example.com:81/how/are/you?foo=baz
~~~

URI normalization
-------

Out of the box the package normalizes any given URI according to the non destructive rules of [RFC3986](https://tools.ietf.org/html/rfc3986).

These non destructives rules are:

- scheme and host components are lowercased;
- the host is converted to its ascii representation using punycode if needed
- query, path, fragment components are URI encoded if needed;
- the port number is removed from the URI string representation if the standard port is used;

~~~php
$uri = Uri::createFromString(
    "hTTp://www.ExAmPLE.com:80/hello/./wor ld?who=f 3#title"
);
echo $uri; //displays http://www.example.com/hello/./wor%20ld?who=f%203#title

$uri = Uri::createFromComponent(parse_url("hTTp://www.bébé.be?#"));
echo $uri; //displays http://xn--bb-bjab.be?#
~~~

<p class="message-info">The last example depends on the presence of the <code>ext-intl</code> extension. Otherwise the code will trigger a <code>IdnSupportMissing</code> exception</p>
