Urlify is a lightweight, object-oriented PHP library designed for parsing, manipulating, and reconstructing URLs in a highly modular and intuitive way.
It provides fine-grained access to every component of a URL, such as scheme, authorization, host, path, query, and fragment through dedicated classes. Unlike typical URL parsers that only offer string-based access, Urlify structures these components as first-class objects, enabling more powerful and flexible manipulations.
URLs are often treated as plain strings, but in modern applications, we frequently need to:
- Add or remove query parameters
- Modify the fragment (
#
) portion of the URL - Update only the path, keeping the query and host untouched
- Serialize the full URL again from its parts
Urlify abstracts all of this with clean, readable, and immutable-friendly interfaces, while giving you full control over the inner structure of the URL.
Urlify is built around a component-based architecture, where each part of the URL is encapsulated in its own class:
Component | Class | Responsibility |
---|---|---|
Scheme | Scheme |
Handles URL scheme (e.g., http , https , ftp , etc.) |
Host | Host |
Handles domain or IP address, subdomains, and port |
Path | Path |
Manages the path portion of the URL |
Query | Query |
Represents the query string as a structured, iterable object |
Fragment | Fragment |
Represents the URL fragment (#... ), also compatible with Query |
Entry | QueryEntry |
Represents an individual key-value pair or flag in the query |
URL | Url |
Central orchestrator class that ties all parts together |
All of these classes are designed to be loosely coupled and easily testable.
- Full URL Parsing
Create aUrl
object from a string and instantly access all parts via method calls. - Immutable-Friendly API
Most methods support chaining, enabling fluent and predictable transformations. - Query Intelligence
Flags (?foo&bar=baz
) and key-value entries are separately handled viaQueryEntry
. - Path Segments as Query The path segments can be parsed as a query object.
- Fragment as Query
The fragment part (#...
) can be parsed as a query object as well, useful for hash-based routing in SPAs. - JSON Serializable
All components can be converted to structured arrays or serialized into JSON easily. - Object-Oriented Everything
No need to useparse_url()
andhttp_build_query()
manually anymore.
- Modify query parameters in a clean and testable way
- Dynamically generate URLs based on runtime conditions
- Convert fragments into navigable query structures
- Build router-like logic for backend or frontend integration
- Serialize or debug complex URLs in JSON format
Urlify can be installed via Composer, the standard PHP dependency manager.
composer require iceylan/urlify
After installation, make sure to include Composer’s autoloader in your project:
require 'vendor/autoload.php';
All Urlify classes are namespaced under Iceylan\Urlify
. You can either import specific classes or use them directly with their fully qualified names:
use Iceylan\Urlify\Url;
use Iceylan\Urlify\Query\Query;
use Iceylan\Urlify\Fragment;
ℹ️ Urlify is compatible with PHP 8.1
or higher.
Let’s say you have a URL where a query string is embedded inside a path segment. Let's make it more compicated and add another level of nesting with a different separation. Now let's say you want to extract a value from that messy URL.
use Iceylan\Urlify\Url;
use Iceylan\Urlify\Query\Query;
// The URL we're working with
$url = new Url(
'https://example.com/something/utm_medium=target:readme|foo:bar&utm_source=github/its-me'
);
// Get the dataset as a Query object (we can use negative indexes)
$segmentAsQuery = $url->path->getSegmentAsQuery( -2 );
// Extract the utm_medium value as a Query object
$mediumAsQuery = $segmentAsQuery->getAsQuery( 'utm_medium', '|', ':' );
// access the target key's value
echo $mediumAsQuery->get( 'target' ); // "readme"
We can easily chain the above code into a single liner:
echo $url
->path
->getSegmentAsQuery( -2 )
->getAsQuery( 'utm_medium', '|', ':' )
->get( 'target' );
// readme
This example shows how flexible Urlify can be:
It separates the URL structure cleanly. You can treat path segments as independent values. You can easily re-parse segments, fragments, or query strings as new Query objects.
The Url
class is the main entry point to working with URL components. Once instantiated, it provides clean access to each part of the URL, and allows full manipulation with method chaining.
use Iceylan\Urlify\Url;
$url = new Url( 'https://user:pass@example.com:8080/users//foo/../profile?view=full#section1' );
$url->scheme;
$url->auth;
$url->host;
$url->port;
$url->path;
$url->query;
$url->fragment;
We can set a value for a segment and the value we give it will be parsed.
All setters return $this, so they support method chaining:
echo $url
->setHost( 'iceylan.dev' )
->setScheme( 'http' )
->setPort( 3000 )
->setFragment( 'new-section' );
// Output: http://iceylan.dev:3000/users/profile?view=full#new-section
Each component (path, query, fragment) has its own powerful interface. In the next sections, we'll dive deeper into those.
You can also cast the URL object into string directly:
echo $url; // Outputs: https://example.com:8080/users/profile?view=full#section1
Or you can also convert it to an array:
var_dump( $url->toArray());
// Outputs:
// [
// 'scheme' => 'https://',
// 'user' => 'user',
// 'pass' => 'pass',
// 'host' => 'example.com',
// 'port' => ':8080',
// 'path' => '/user/profile',
// 'query' => '?view=full',
// 'fragment' => '#section1',
// ]
And finally, you can convert it to JSON:
echo json_encode( $url );
and output:
{
"scheme":{
"name": "https",
"isSecure": true,
"isKnown": true
},
"auth": {
"user": "user",
"pass": "pass"
},
"host": {
"subdomains": [],
"subdomainName": null,
"primaryDomainName": "example",
"topLevelDomain": "com",
"rootDomain": "example.com"
},
"port": {
"address": 8080,
"effective": 8080
},
"path": {
"rawSegments": [ "", "users", "", "foo", "..", "profile" ],
"resolvedSegments": [ "users", "profile" ]
},
"query": {
"view": [ "full" ]
},
"fragment": {
"fragment": "section1",
"asQuery": null
}
}
You can also use Urlify as a URL builder. It's a simple way to create URLs from scratch.
use Iceylan\Urlify\Url;
echo ( new Url )
->setScheme( 'ws' )
->setHost( 'example.com' )
->setPath( '/users/profile' )
->setQuery( 'view=full&flag' )
->setFragment( 'section1' );
// Outputs: wa://example.com/users/profile?view=full&flag#section1
You can also use component methods to modify them more precisely:
use Iceylan\Urlify\Url;
$url = ( new Url )
->setScheme( 'ws' )
->setHost( 'example.com' );
$url->path
->append( 'profile' )
->prepend( 'users' );
echo $url;
// Outputs: ws://example.com/users/profile
We can also keep the chain alive with builder methods, for example:
$url = ( new Url )
->setScheme( 'ws' )
->setHost( 'example.com' )
->buildPath( fn ( $path ) =>
$path
->append( 'profile' )
->prepend( 'users' )
)
->setQuery( 'view=full&flag' );
echo $url;
// Outputs: ws://example.com/users/profile?view=full&flag
The scheme
(also known as "protocol") represents the beginning of a URL and indicates how resources should be accessed, for example: http
, https
, ftp
, etc.
The Url::$scheme
property holds an instance of the Iceylan\Urlify\Scheme
class, which allows both manipulation and introspection of the scheme.
Accessing via Url
object:
use Iceylan\Urlify\Url;
$scheme = ( new Url( 'https://example.com' ))->scheme;
Using Scheme
class standalone:
use Iceylan\Urlify\Scheme;
$scheme = new Scheme( 'https' );
Without an initial value:
$scheme = new Scheme;
You can retrieve the current scheme:
$scheme->get(); // 'https'
(string) $scheme; // 'https://'
If not set, it returns null or an empty string on cast.
Set the scheme value:
echo $scheme->set( 'tel' ); // 'tel:'
Or set it through Url:
echo $url->setScheme( 'sms' ); // 'sms:'
Urlify
reconizes the known schemes and automatically appends the correct suffix.
Clear the scheme completely:
echo $scheme->clean(); // ''
Alternative methods:
echo $scheme->set( null ); // ''
echo $url->setScheme( null ); // ''
Check whether the scheme is marked as secure:
$scheme->set( 'ftp' )->isSecure(); // false
$scheme->set( 'ftps' )->isSecure(); // true
Determine if the scheme is one of the known/registered ones:
$scheme->set( 'mysql' )->isKnown(); // true
$scheme->set( 'asgardia' )->isKnown(); // false
Custom schemes can be registered globally:
Scheme::registerScheme( name: 'asgardia', suffix: '://', secure: true );
$scheme->set( 'asgardia' );
$scheme->isKnown(); // true
$scheme->isSecure(); // true
echo $scheme; // 'asgardia://'
Scheme
objects can be serialized into JSON.
json_encode( $scheme );
Yields:
{
"name": "asgardia",
"suffix": "://",
"isSecure": true,
"isKnown": true
}
The auth
property holds an instance of the Iceylan\Urlify\Auth
class which represents the authentication part of a URL (i.e., username:password@
).
You can access the auth part directly via the Url
instance:
use Iceylan\Urlify\Url;
$auth = ( new Url( 'https://username:password@example.com' ))->auth;
Or use the Auth
class directly:
use Iceylan\Urlify\Auth;
$auth = new Auth( 'username', 'password' );
Or even instantiate without any credentials:
$auth = new Auth;
You can retrieve the username and password values separately.
$auth->getUser(); // 'username'
$auth->getPass(); // 'password'
If either is not set, null is returned.
Credentials can be set individually or together:
// You can set simultaneously
$auth->set( 'username', 'password' );
// Or separately
$auth->setUser( 'root' );
$auth->setPass( '1234' );
echo $auth; // 'root:1234@'
Partial credentials are also supported:
echo $url->auth->set( 'username', null ); // 'username@'
echo $url->auth->set( null, 'password' ); // ':password@'
Clear all authentication data with:
$auth->clean();
Or reset via setters:
// equivalent to clean method
$auth->set( null, null );
// clear separately
$auth->setUser( null );
$auth->setPass( null );
On Url:
// echo triggers Url::__toString method
echo $url->setUsername( null )->setPassword( null ); // ''
Determine whether the auth section (both username and password) is currently empty:
$auth->isEmpty(); // true or false
Auth
objects can be converted into JSON.
json_encode( $auth );
Result:
{
"user": "username",
"pass": "password"
}
The host
component of a URL specifies the domain address that identifies the resource's location on the network. In Urlify
, the host
property is an instance of the Iceylan\Urlify\Host
class, providing methods for manipulation and inspection of the host part.
This library uses a list of top-level domain names to separate the top-level domain names. It doesn't just take the latest part of a string separated by dots and treat it as the main domain. This approach tells us that co.uk
is a top-level domain name.
Accessing host instance via Url
object:
use Iceylan\Urlify\Url;
$host = ( new Url( 'https://example.com' ))->host;
Using Host class standalone:
use Iceylan\Urlify\Host;
$host = new Host( 'www.foo.example.co.uk' );
Without an initial value:
$host = new Host;
After setting the host, you can retrieve host parts by meaningful methods.
$host->getSubdomainName(); // www.foo
$host->getSubdomains(); // ['www', 'foo']
$host->getPrimaryDomainName(); // example
$host->getTopLevelDomainName(); // co.uk
$host->getRootDomainName(); // example.co.uk
If any of these is not set, null
is returned.
You can manipulate the host parts with powerful methods.
Sometimes setting the host with a whole string is can be enough for you. We have a method for this:
echo $host->set( 'subdomain.example.com' );
// 'subdomain.example.com'
When the set method is called, parsing processes will be start for given host and all the getter methods will return the parsed values.
You can also set the host directly on the Url
object:
echo $url->setHost( 'api.example.com' );
// 'https://api.example.com'
Sometimes you need to set the subdomain as a whole string. We have a method for this:
echo $host->setSubdomain( null ); // example.com
echo $host->setSubdomain( 'bar.baz' ); // bar.baz.example.com
You may also need to append or prepend existing subdomains.
echo $host->appendSubdomain( 'zoo' ); // bar.baz.zoo.example.com
echo $host->prependSubdomain( 'chat' ); // chat.bar.baz.zoo.example.com
You can also set the primary domain name with the setPrimaryDomainName
method:
echo $host->setPrimaryDomainName( 'exam' ); // chat.bar.baz.zoo.exam.com
You can also set the top-level domain name with the setTopLevelDomainName
method:
echo $host->setTopLevelDomainName( 'co.uk' ); // chat.bar.baz.zoo.exam.co.uk
Clear the host completely:
echo $host->clean(); // ''
// or
echo $host->set( null ); // ''
Host
objects can be converted into JSON.
json_encode( $host );
Result:
{
"subdomainName": "chat.bar.baz.qux.zoo",
"subdomains": [ "chat", "bar", "baz", "qux", "zoo" ],
"primaryDomainName": "exam",
"rootDomain": "exam.co.uk",
"topLevelDomain": "co.uk"
}
The port
component of a URL specifies the port number used for communication with the resource.
Accessing port instance via Url
object:
use Iceylan\Urlify\Url;
$port = ( new Url( 'https://example.com:8001' ))->port;
Using Port class standalone:
use Iceylan\Urlify\Port;
$port = new Port( 8001 );
Without an initial value:
$port = new Port;
After setting the port, you can retrieve the port with the get
method:
$port->get(); // 8001
If the port is not set, null
is returned.
You can set the port with the set
method:
echo $port->set( 8001 ); // ':8001'
You can check if the port is defined with the isEmpty
method:
echo $port->isEmpty(); // false
You can retrieve the effective port with the getEffective
method. Effective port is the port set or the default port for the scheme if the port is not set.
echo $port->getEffective(); // 8001
You can use Port
class to query the default port for a given scheme:
use Iceylan\Urlify\Port;
echo Port::getDefaultPortForScheme( 'https' ); // 443
Clear the port completely:
echo $port->clean(); // ''
// or
echo $port->set( null ); // ''
Port
objects can be converted into JSON.
json_encode( $port );
Result:
{
"address" => null,
"effective" => 443
}