URN Value Object

Available since version 7.6.0

The League\Uri\Urn is a specific class developped around URN creation and manipulation as defined in RFC8141.

The class handles a subset of URI schemes as defined by RF8141. Because URNs have distinct requirements, the class is still considered generic, and additional rules may apply depending on their respective NID.

Instantiation

While the default constructor cannot be used as it is marked as private, the League\Uri\Urn provides several named constructors to help creating a new instance.

use League\Uri\Urn;

$urn = Urn::new('urn:example:animal:nose?+foo=bar&fo%26o=b%3Far');
$urnBis = Urn::fromString('urn:example:animal:nose?+foo=bar&fo%26o=b%3Far');
$urnRfc2141 = Urn::fromRfc2141(nid: 'example', nss: 'animal:nose');
Urn::parse('invalid uri'); // returns null on failure

The new() method allows creating a new instance from an encoded string. The method is an alias of the fromString() method. By default, the submitted URNs will be validated against RFC8141. While both methods support strings, you can also use the new native URI classes as input, as well as PSR-7 UriInterface implementing objects.

The fromRfc2141() named constructor allows creating URNs using the legacy RFC2141 rules.

While all the previous methods would throw a SyntaxError on malformed URN, the parse() method returns null to enable using the method during input validation.

URN String Representation

The Urn class handles URI according to RFC8141, as such you can retrieve its string representation using the toString method. the __toString() method is an alias of the toString() method.

$urn = Urn::fromString('urn:example:animal:nose?+foo=bar&fo%26o=b%3Far');
echo $urn->toString(); // displays 'urn:example:animal:nose?+foo=bar&fo%26o=b%3Far'
echo $uri;             // displays 'urn:example:animal:nose?+foo=bar&fo%26o=b%3Far'

An Urn instance can be JSON-encoded using its string representation to allow better interoperability with Javascript.

$urn = Urn::fromString('urn:example:animal:nose?+foo=bar&fo%26o=b%3Far');
json_encode($urn); //returns "urn:example:animal:nose?+foo=bar&fo%26o=b%3Far"

The toDisplayString() method returns a human-readable representation of the URI, corresponding to its IRI form as defined in RFC 3987. Although the resulting value may not constitute a syntactically valid URN, it is intended for presentation purposes — for example, as the textual content of an HTML <a> element.

$urn = Urn::fromString('urn:example:%F0%9F%98%88');
echo $urn->toDisplayString(); // displays 'urn:example:😈'

The toRfc2141() returns the URN legacy representation from the obsolete RFC2141 specification.

$urn = Urn::fromString('urn:example:animal:nose?+foo=bar&fo%26o=b%3Far');
echo $urn->toString();  // displays 'urn:example:animal:nose?+foo=bar&fo%26o=b%3Far'
echo $urn->toRfc2141(); // displays 'urn:example:animal:nose'

The optional components as defined by RFC8141 are stripped if present.

It is possible to convert your League\Uri\Urn instance into a League\Uri\Uri object using the toUri() method. The method returns a League\Uri\Uri instance.

$urn = Urn::fromString('urn:example:animal:nose?+foo=bar&fo%26o=b%3Far');
$uri = $urn->toUri(); // returns a League\Uri\Uri instance
$uri->equals($urn);   // returns true

There is no Uri::toUrn() method attached to the League\Uri\Uri class because every URN is an URI but not all URIs are URNs.

Accessing The URN properties

Let’s examine the result of building an URN:

use League\Uri\Urn;

$urn = Urn::fromString('urn:example:animal:ferret:nose?+weight=2.3;length=5.1?=profile=standard#section2');
echo $urn->getNid();        // displays 'example'
echo $urn->getNss();        // displays 'animal:ferret:nose'
echo $urn->getRComponent(); // displays 'weight=2.3;length=5.1'
echo $urn->getQComponent(); // displays 'profile=standard'
echo $urn->getFComponent(); // displays 'section2'

The returned value of each component is kept encoded.

URN information

The components related properties r-component, q-component and f-component are optional, as such, they can be null if they have no value or be a non-empty string. They can never be the empty string.

To ease gathering information about optional component presence the class exposes the following methods:

  • Urn::hasRComponent: returns true if the r-component value is not null;
  • Urn::hasQComponent: returns true if the q-component value is not null;
  • Urn::hasFComponent: returns true if the f-component value is not null;
  • Urn::hasOptionalComponent: returns true if at least one of the optional component is set;
$urn = Urn::fromString('urn:example:animal:nose?+foo=bar&fo%26o=b%3Far');
$urn->hasOptionalComponent(); // returns true
$urn->hasFComponent();        // returns false
$urn->hasRComponent();        // returns true

Modifying URN Properties

Use the modifying methods exposed by all URN instances to replace one of the URN part or component. If the modifications do not alter the current object, it is returned as is, otherwise, a new modified object is returned.

Any modification method can trigger a League\Uri\Contracts\UriException exception if the resulting URN is not valid. Just like with the instantiation methods, validation is NID-dependent.

$urn = Urn::fromString('urn:example:animal:nose')
            ->withQComponent('foo=bar')
            ->withFComponent('fragment');
echo $urn->toString(); // returns 'urn:example:animal:nose?=foo=bar#fragment'

The following modifier methods exist:

  • withNid : will update the URN namespace identifier part;
  • withNss : will update the URN namespace specific string part;
  • withRComponent : will update the URN r-component value;
  • withQComponent : will update the URN q-component value;
  • withFComponent : will update the URN f-component value;

All methods will correctly encode your input before updating the URN.

To remove any of the component you can give to their respective wither methods the null value. It will remove any non empty-string attached to the component if it exists.

Normalization and Comparison

Out-of-the-box, the only normalization that will occur it that the scheme will be lowercased to urn. But you can improve normalization by lowercasing the URN NIS part. This is done if you call the normalize() method. It will return a new instance fully normalized. This instance is used to compare URN.

use League\Uri\Urn;
use League\Uri\UrnComparisonMode;

$urn = Urn::fromString('UrN:Example:Animal:NOSE');
echo $urn; //returns "urn:Example:Animal:NOSE"
$newUrn = $urn->normalize();
echo $newUrn; //returns "urn:example:Animal:NOSE"

The NSS and the optional components are not affected by the normalization.

By default, when comparing two URN only the NIS and the NSS parts are considered as per the requirements of the RFC. However, depending on the specificity of some URN namespace, the optional component may be used. To cover both situations the UrnComparisonMode enum is used with the equals method.

use League\Uri\Urn;
use League\Uri\UrnComparisonMode;

$urn = Urn::fromRfc2141('example', 'animal:nose')->withQComponent('foo/bar');
$urnBis = Urn::fromRfc2141('example', 'animal:nose');

$urn->equals($urnBis, UrnComparisonMode::ExcludeComponents); // returns true
$urn->equals($urnBis, UrnComparisonMode::IncludeComponents); // returns false

By default, if no UrnComparisonMode is used, optional components are not taken into account during comparison.