league/uri-components
Immutable value objects for URI components (host, path, query, etc.) from The PHP League. PHP 8.1+. Supports IDN hosts (intl or polyfill) and IPv4 conversion (GMP/BCMath/64-bit). Built atop league/uri, uri-interfaces, and PSR-7.
Installation:
composer require league/uri-components
Requires PHP 8.1+ and intl extension (or symfony/polyfill-intl-idn) for IDN support.
First Use Case: Parse and manipulate a URI in a Laravel controller or service:
use League\Uri\Components\Uri;
use League\Uri\Components\Query;
$uri = Uri::createFromString('https://example.com/search?q=laravel&page=1');
$query = $uri->query();
// Get a single parameter
$searchTerm = $query->get('q'); // 'laravel'
// Modify the query
$modifiedQuery = $query->with('page', 2);
$modifiedUri = $uri->withQuery($modifiedQuery);
Key Classes to Explore:
Uri (root class for URI manipulation)Query (for query string handling)Modifier (for fluent URI modifications)Domain, Path, Fragment (for granular component access)Parse incoming HTTP requests (e.g., from Laravel's Request object):
use League\Uri\Components\Uri;
use Illuminate\Http\Request;
$request = app(Request::class);
$uri = Uri::createFromString($request->getUri());
// Extract query parameters
$query = $uri->query();
$filters = $query->all(); // Associative array of all query params
Use Modifier for chainable URI updates:
use League\Uri\Components\Modifier;
$uri = Uri::createFromString('https://api.example.com/v1/users');
$modifiedUri = Modifier::from($uri)
->withPath('/v2/users/123')
->withQuery('sort', 'desc')
->withFragment('section-1')
->toUri();
Leverage Query methods for complex query logic:
$query = Query::fromString('?id=1&tags[]=php&tags[]=laravel');
// Check for list parameters
if ($query->hasList('tags')) {
$tags = $query->getList('tags'); // ['php', 'laravel']
}
// Filter and transform
$filtered = $query->filter(fn($key, $value) => str_contains($key, 'tag'));
Work with subdomains or path segments:
$domain = $uri->domain();
if ($domain->isSubdomainOf('example.com')) {
$subdomain = $domain->first(); // 'api'
}
$path = $uri->path();
$segments = $path->segments(); // ['v1', 'users']
$newPath = $path->withSegments(['v2', 'users', '123']);
Generate URIs for named routes dynamically:
use Illuminate\Support\Facades\Route;
$routeUri = Route::getUriFor('users.show', ['user' => 123]);
$uri = Uri::createFromString($routeUri);
// Append query params
$uriWithParams = $uri->withQuery('with', 'profile');
Ensure URI components are valid before use:
$uri = Uri::createFromString($userInput);
if (!$uri->isValid()) {
throw new \InvalidArgumentException('Invalid URI');
}
// Normalize components
$normalizedHost = $uri->host()->normalized();
Bind the package for dependency injection:
// app/Providers/AppServiceProvider.php
public function register()
{
$this->app->bind(Uri::class, function () {
return Uri::createFromString(request()->getUri());
});
}
Validate URI components in Laravel form requests:
use League\Uri\Components\Uri;
use Illuminate\Validation\Rule;
public function rules()
{
return [
'callback_url' => [
'required',
function ($attribute, $value, $fail) {
$uri = Uri::createFromString($value);
if (!$uri->isValid() || !$uri->scheme()->isHttp()) {
$fail('The '.$attribute.' must be a valid HTTP URL.');
}
},
],
];
}
Construct paginated API responses with query parameters:
$uri = Uri::createFromString(request()->getUri());
$nextPageUri = $uri->withQuery('page', $currentPage + 1);
return response()->json([
'data' => $users,
'links' => [
'next' => $nextPageUri->toString(),
],
]);
Use the package to assert URI behavior in tests:
use League\Uri\Components\Uri;
use Tests\TestCase;
public function testUriConstruction()
{
$uri = Uri::createFromString('https://example.com/path?query=value');
$this->assertEquals('example.com', $uri->host()->toString());
$this->assertEquals('value', $uri->query()->get('query'));
}
All components (e.g., Query, Domain) are immutable. Always use modifier methods (e.g., with(), append()) to create new instances:
// ❌ Wrong: Attempting to mutate
$query->set('key', 'value'); // Throws error
// ✅ Correct: Create a new instance
$newQuery = $query->with('key', 'value');
getList() for multi-value parameters (e.g., ?tags[]=php&tags[]=laravel).Query::fromString() with $coercionMode to normalize:
$query = Query::fromString('?Key=Value', Query::COERCION_MODE_LOWERCASE);
$query->get('key'); // 'Value'
intl extension is installed for Internationalized Domain Names (IDN).Host::normalized() to convert between IDN and ASCII:
$host = $uri->host();
$asciiHost = $host->normalized(); // 'xn--example-123.com'
HierarchicalPath for proper handling:
$path = HierarchicalPath::createFromString('/api/v1/users');
$segments = $path->segments(); // ['api', 'v1', 'users']
// Append a segment
$newPath = $path->withSegments(['api', 'v1', 'users', '123']);
Modifier::from() (use Modifier::wrap() instead).GMP/BCMath extensions.Port::defaultScheme() to check defaults:
$port = $uri->port();
if ($port->isDefaultForScheme($uri->scheme())) {
// Port is implicit (e.g., :80 for HTTP)
}
Use toString() or debug() for debugging:
$uri = Uri::createFromString('https://example.com');
dd($uri->domain()->toString()); // 'example.com'
Customize how query strings are parsed/composed:
$query = Query::fromString('?q=test', Query::EXTRACT_MODE_RFC3986);
$query->toString(Query::COMPOSE_MODE_RFC3986);
Catch UriException for invalid URIs:
How can I help you explore Laravel packages today?