league/uri
League URI provides simple, intuitive PHP 8.1+ classes to parse, validate, normalize, and manipulate URIs and related components. Supports PSR-7 interoperability, IDN hosts (intl/polyfill), IPv4 conversion, and HTML URI handling.
Installation:
composer require league/uri
Ensure PHP ≥ 8.1 and required extensions (intl, dom, GMP/BCMath for IPv4, or 64-bit PHP).
First Use Case: Parse and manipulate a URI:
use League\Uri\Uri;
$uri = Uri::new('https://example.com/path?query=value#fragment');
echo $uri->getScheme(); // "https"
echo $uri->getHost(); // "example.com"
Key Entry Points:
Uri::new(): Parse a string into a URI object.Uri::fromComponents(): Build from components (e.g., scheme, host, path).Uri::tryNew(): Safe parsing (returns null on failure).Http class: PSR-7 compliant HTTP URIs (e.g., Http::new('https://api.example.com')).Documentation:
Uri and Http classes for 90% of use cases.URI Construction:
// From string
$uri = Uri::new('https://user:pass@example.com:8080/path?query=1');
// From components
$uri = Uri::fromComponents([
'scheme' => 'https',
'host' => 'example.com',
'port' => 443,
'path' => '/api/v1',
]);
URI Manipulation:
// Immutable updates (returns new instance)
$newUri = $uri->withPath('/new-path')->withQuery(['page' => 2]);
// Conditional building
$uri->when($uri->getScheme() === 'https', fn($u) => $u->withPort(443));
Validation and Checks:
if ($uri->isAbsolute()) {
echo "Absolute URI";
}
if ($uri->isSameOrigin($otherUri)) {
echo "Same origin";
}
URI Templates (for dynamic paths):
$template = UriTemplate::new('https://api.example.com/{version}/users/{id}');
$uri = $template->expand(['version' => 'v1', 'id' => '123']);
PSR-7 Integration:
use League\Uri\Http;
$psr7Uri = Http::new('https://example.com')->toPsr7Uri();
Laravel Requests:
Use Uri::fromServer($_SERVER) to parse the current request URI.
$uri = Uri::fromServer($_SERVER);
$path = $uri->getPath(); // e.g., "/products/123"
Routing: Combine with Laravel’s router to validate or normalize incoming URIs:
$requestUri = Uri::new(request()->getUri());
if ($requestUri->isSameOrigin($baseUri)) {
// Proceed with trusted request
}
API Clients:
Use Http for constructing absolute URLs from relative paths:
$baseUri = Http::new('https://api.example.com');
$relativeUri = Uri::new('/users/1');
$absoluteUri = $baseUri->resolve($relativeUri);
File URIs:
Handle local files with Uri::fromUnixPath('/var/www/file.txt') or Uri::fromWindowsPath('C:\\file.txt').
IDN (Internationalized Domain Names): Convert between ASCII and Unicode:
$uri = Uri::new('https://例子.测试'); // Unicode
$asciiUri = $uri->toAsciiString(); // "https://xn--fsq.xn--0zwm56d"
Immutable Objects:
withPath(), withQuery()) return a new instance. Avoid chaining without assignment:
// ❌ Loses changes
$uri->withPath('/new')->withQuery(['key' => 'value']);
// ✅ Correct
$uri = $uri->withPath('/new')->withQuery(['key' => 'value']);
Path Normalization:
getPath() returns a normalized path (e.g., /a//b → /a/b). Use toString() for raw output.withPath() only if the original path had them.Query Parameters:
getQuery() to get a raw string or withQuery() to set:
$uri->withQuery(['page' => 2, 'sort' => 'asc']);
IPv6 Hosts:
[] (e.g., [2001:db8::1]). The library auto-compresses them (e.g., 2001:db8::1 → 2001:db8::).GMP/BCMath).Fragment Handling:
#hash) are not included in isSameDocument() or equals() by default. Use equals() to compare including fragments.Deprecated Methods:
BaseUri (deprecated in favor of Uri or Modifier).Uri::tryNew() instead of Uri::new() for safe parsing.IDN Requirements:
例子.测试) require the intl extension or symfony/polyfill-intl-idn. Without it, parsing throws an exception.PSR-7 Compliance:
Http class implements Psr\Http\Message\UriInterface, but not all methods are 100% compatible with other PSR-7 libraries. Test edge cases (e.g., authority components).Validation Errors:
Uri::tryNew() to catch invalid URIs gracefully:
$uri = Uri::tryNew('invalid:uri');
if (!$uri) {
// Handle error
}
Component Inspection:
dd([
'scheme' => $uri->getScheme(),
'host' => $uri->getHost(),
'port' => $uri->getPort(),
'path' => $uri->getPath(),
'query' => $uri->getQuery(),
'fragment' => $uri->getFragment(),
]);
URI Template Issues:
{123} is invalid). Use {var123} instead.expandOrFail() to catch errors early:
try {
$uri = $template->expandOrFail(['invalid' => 'value']);
} catch (TemplateCanNotBeExpanded $e) {
// Handle missing variables
}
Performance:
Uri instances where possible or use Uri::parse() for one-off parsing.Custom URI Classes:
Uri or Http to add domain-specific logic:
class ApiUri extends Uri {
public function isApiEndpoint(): bool {
return $this->getPath() === '/api';
}
}
URI Normalization:
normalize() to enforce project-specific rules (e.g., lowercase hosts):
$normalized = $uri->normalize()->withHost(strtolower($uri->getHost()));
URI Validation:
use League\Uri\Uri;
$validator->rule(function ($attribute, $value, $fail) {
$uri = Uri::tryNew($value);
if (!$uri || !$uri->isAbsolute()) {
$fail('The :attribute must be a valid absolute URI.');
}
});
URI Templates:
$userTemplate = UriTemplate::new('https://api.example.com/users/{id}');
$uri = $userTemplate->expand(['id' => $user->id]);
PSR-17 Factories:
How can I help you explore Laravel packages today?