zenstruck/uri
Immutable, fluent URI/URL value objects for PHP with easy parsing, building, and modification of URIs. Create, normalize, and manipulate components (scheme, host, path, query, fragment) safely, with helpers for encoding and query params.
Installation
composer require zenstruck/uri
No additional configuration is required—it’s a drop-in package.
First Use Case Parse and manipulate a URL as an object:
use Zenstruck\Uri\Uri;
$uri = Uri::fromString('https://example.com/path?query=value#fragment');
echo $uri; // Outputs: https://example.com/path?query=value#fragment
Key Initial Methods
Uri::fromString(string $url) – Parse a string into a Uri object.Uri::fromParts(array $parts) – Build from an associative array (e.g., parse_url() output).Uri::fromRequest(Request $request) – Parse from a Laravel Request object (includes query params).Where to Look First
getScheme(), getHost(), getPath(), etc.).URL Construction
$uri = Uri::fromString('https://example.com')
->withPath('/new-path')
->withQuery(['key' => 'value'])
->withFragment('section');
Uri instances).Query Parameter Handling
$uri = Uri::fromString('https://example.com?foo=bar&baz=qux');
$uri->getQuery('foo'); // 'bar'
$uri->withQuery(['foo' => 'updated']); // New Uri with updated query
$uri->withoutQuery('baz'); // Removes 'baz'
Relative URL Resolution
$base = Uri::fromString('https://example.com/base/');
$relative = Uri::fromString('subpath');
$absolute = $base->resolve($relative); // 'https://example.com/base/subpath'
Laravel Integration
$uri = Uri::fromRequest(request());
return redirect()->to(Uri::fromString('/new-path')->withQuery(['ref' => 'home']));
$uri = Uri::fromRoute('profile.show', ['user' => 1]);
Validation
if ($uri->isValid()) {
// Safe to use
}
$uri = Uri::fromString(' Http://EXAMPLE.COM/path ');
$uri->normalize(); // 'http://example.com/path'
$uri->withScheme('https')->withHost('api.example.com');
$uri->toString(); // Back to string
$uri->toArray(); // Associative array (like parse_url)
Immutable Updates
withPath() return new Uri instances. Avoid overwriting the original:
// Anti-pattern: Loses previous state
$uri = Uri::fromString('https://example.com');
$uri = $uri->withPath('/new'); // Correct
$uri->withQuery([]); // Overwrites if reused!
Query Parameter Conflicts
withQuery() replaces all query params. Use withAddedQuery() or withoutQuery() for granular control:
$uri->withAddedQuery(['page' => 2]); // Preserves existing params
Relative URL Edge Cases
resolve() may not handle all edge cases (e.g., ../ in paths). Test with:
$base = Uri::fromString('https://example.com/base/');
$relative = Uri::fromString('../../outside');
$absolute = $base->resolve($relative); // 'https://example.com/outside'
Case Sensitivity
normalize() if needed.Empty Components
getPath() return null for missing components (not empty string). Check for null explicitly.$uri->toArray(); // Debug all components
if (!$uri->isValid()) {
throw new \InvalidArgumentException('Invalid URI');
}
toString() for Logging:
\Log::debug('Generated URI', ['uri' => $uri->toString()]);
Custom Parsing Logic
Override Uri::fromString() behavior by extending the class:
class CustomUri extends Uri {
public static function fromString(string $url): self {
// Custom logic (e.g., default scheme)
return parent::fromString($url);
}
}
Add Custom Methods
$uri->withTld('com'); // Hypothetical; extend to add domain-specific logic
Integrate with Laravel Services
Bind the package to the container in AppServiceProvider:
$this->app->bind('uri', function () {
return Uri::fromRequest(request());
});
Handle Non-Standard URLs
For URLs with non-standard schemes (e.g., mailto:), ensure the package’s isValid() logic aligns with your needs (may require extending).
/path), the scheme defaults to null. Explicitly set it if needed:
$uri = Uri::fromString('/path')->withScheme('https');
How can I help you explore Laravel packages today?