league/uri-components
Immutable value-object URI components for PHP. Build, validate, normalize and convert parts like scheme, authority, host, path, query and fragment with PSR-7 compatibility. Supports IDN hosts (intl/polyfill) and IPv4 conversion.
Installation:
composer require league/uri-components
Requires PHP 8.1+ and extensions like intl for IDN support (or a polyfill).
First Use Case: Parse and manipulate a URL:
use League\Uri\Components\Uri;
use League\Uri\Components\Query;
$uri = Uri::create('https://example.com/search?q=laravel&page=1');
$query = $uri->query();
// Access query parameters
$searchTerm = $query->get('q'); // 'laravel'
$page = $query->get('page'); // '1'
// Modify query
$modifiedQuery = $query->with('page', 2);
$modifiedUri = $uri->withQuery($modifiedQuery);
Key Classes to Explore:
Uri: Root class for URI manipulation.Query: Handle query strings (e.g., ?key=value).Modifier: Chainable URI modifications (e.g., appending paths, modifying queries).Domain, Path, Fragment: Component-specific classes.// Parse from string
$uri = Uri::create('https://api.example.com/v1/users');
// Build from components
$uri = Uri::create()
->withScheme('https')
->withHost('api.example.com')
->withPath('/v1/users');
$query = Query::createFrom('?sort=desc&limit=10');
// Add/Modify Parameters
$query->with('limit', 20)->with('filter', 'active');
// Remove Parameters
$query->without('filter');
// Convert to array
$params = $query->toArray();
Modifier$modifier = Modifier::createFrom('https://example.com');
$modifiedUri = $modifier
->appendPath('/api/v1')
->appendQueryPairs(['page' => 1, 'sort' => 'asc'])
->toUri();
$domain = Domain::createFrom('sub.example.com');
$domain->isSubdomainOf('example.com'); // true
$path = Path::create('/api/v1/users');
$path->append('/profile'); // '/api/v1/users/profile'
use League\Uri\Components\Uri;
use Illuminate\Http\Request;
$requestUri = Uri::create(request()->getUri());
$query = $requestUri->query();
return redirect()->to(
Modifier::createFrom('/dashboard')
->appendQueryPairs(['tab' => 'settings'])
->toString()
);
$uri = Uri::create('https://example.com');
$uri->isValid(); // true
$uri->withHost('invalid host'); // Throws exception if invalid
Register a helper for URI manipulation:
// app/Providers/AppServiceProvider.php
use League\Uri\Components\Uri;
use Illuminate\Support\Facades\Blade;
public function boot()
{
Blade::directive('uri', function ($expression) {
return "<?php echo (string) League\Uri\Components\Uri::create({$expression}); ?>";
});
}
use League\Uri\Components\Uri;
use Illuminate\Validation\Rule;
$validator->addRules([
'url' => [
'required',
function ($attribute, $value, $fail) {
if (!Uri::create($value)->isValid()) {
$fail('The '.$attribute.' must be a valid URL.');
}
},
],
]);
use League\Uri\Components\Modifier;
return response()->json($data, 200, [
'Location' => Modifier::createFrom('/api/resource/123')
->appendQueryPairs(['action' => 'update'])
->toString(),
]);
Immutable Objects:
Query, Path) are immutable. Use modifier methods (e.g., with(), append()) to create new instances.$query->set('key', 'value'); // ❌ Throws error (no setter)
$newQuery = $query->with('key', 'value'); // ✅ Correct
Query String Encoding:
Query::encoded() or Query::decoded() for raw string manipulation.Query::toString() always returns URL-encoded output.
$query = Query::createFrom('?key=value with spaces');
$query->toString(); // 'key=value+with+spaces'
$query->decoded(); // 'key=value with spaces'
Domain Handling:
Domain::createFrom() throws exceptions for invalid domains (e.g., example..com).tryNew() for safe creation:
$domain = Domain::tryNew('invalid..domain'); // Returns null
Path Segments:
Path::create('/api//users')->toString(); // '/api/users'
Query Parameter Order:
Query::sort() ensures WHATWG compliance (alphabetical order by key).Query::unsorted() to preserve insertion order.IPv4/IPv6 Hosts:
GMP, BCMath, or 64-bit PHP for IPv4 conversion.symfony/polyfill-php80.Inspect Components:
$uri = Uri::create('https://example.com/path?query=value');
dump($uri->scheme()); // 'https'
dump($uri->host()); // 'example.com'
dump($uri->query()); // Query object
Validate URIs:
if (!$uri->isValid()) {
throw new \InvalidArgumentException('Invalid URI');
}
Handle Exceptions:
League\Uri\Exception\SyntaxError for malformed URIs.League\Uri\Exception\RuntimeException for runtime issues (e.g., missing extensions).Query Debugging:
$query = Query::createFrom('?key=value&key=other');
$query->all(); // ['key' => ['value', 'other']]
$query->get('key'); // ['value', 'other'] (array for multiple values)
Custom Query Parsing:
Override Query::fromString() or use Query::fromPairs() for custom formats:
$query = Query::fromPairs(['custom_key' => 'custom_value']);
Domain Logic:
Extend Domain for custom subdomain checks:
$domain = Domain::createFrom('api.example.com');
$domain->isSubdomainOf('example.com'); // true
Modifier Chaining: Create custom modifiers:
$modifier = Modifier::createFrom('/base')
->appendPath('/custom')
->appendQueryPairs(['debug' => true]);
PSR-7 Integration:
Convert to/from PSR-7 UriInterface:
use League\Uri\Components\Modifier;
use Psr\Http\Message\UriInterface;
$psr7Uri = new \GuzzleHttp\Psr7\Uri('https://example.com');
$modifier = Modifier::wrap($psr7Uri);
BackedEnum Support:
Use BackedEnum for type-safe schemes/ports:
use League\Uri\Components\Scheme;
$scheme = Scheme::from('https'); // Returns BackedEnum instance
$scheme->value(); // 'https'
Avoid Repeated Parsing: Cache parsed URIs if reused frequently:
static $cachedUri = null;
if (!$cachedUri) {
$cachedUri = Uri::create('https://example.com');
}
Query Operations:
How can I help you explore Laravel packages today?