PHPackages                             joby/smol-url - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. joby/smol-url

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

joby/smol-url
=============

A simple and lightweight extensible URL library designed for working with URLs in human-scale applications.

v1.1.4(2mo ago)0312↓23.5%2MITPHPPHP &gt;=8.1CI passing

Since Dec 17Pushed 2mo agoCompare

[ Source](https://github.com/joby-lol/smol-url)[ Packagist](https://packagist.org/packages/joby/smol-url)[ RSS](/packages/joby-smol-url/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (7)Dependencies (3)Versions (11)Used By (2)

smolURL
=======

[](#smolurl)

A simple and lightweight extensible URL library designed for working with URLs in human-scale applications.

What is smolURL?
----------------

[](#what-is-smolurl)

smolURL is a modern PHP library for working with URLs using an immutable, component-based architecture. Unlike built-in PHP URL functions, smolURL represents URLs as composed objects where each component (path, query, fragment, host, etc.) is its own readonly class.

Key features:

- **Immutable design**: All URL components are readonly and use `with*()` methods to create modified copies
- **Type-safe**: Built with PHP 8.3+ features including readonly properties and typed parameters
- **Component-based**: Each URL part (Path, Query, Fragment, Scheme, Host, Port, User) is a separate class
- **Security-focused**: Intentionally limited to HTTP/HTTPS schemes to prevent arbitrary scheme parsing
- **Link resolution**: Built-in support for resolving relative links (like HTML `` tags)
- **Parsing &amp; factories**: Parse strings or globals and compose URLs against a base URL
- **Clean API**: All components implement `Stringable` for easy conversion to strings

Installation
------------

[](#installation)

```
composer require joby/smol-url
```

Basic usage
-----------

[](#basic-usage)

### Quick start

[](#quick-start)

```
use Joby\Smol\URL\{URL, Path, Query, Scheme, Host};

$url = new URL(
    Path::fromString('/docs'),
    new Query(['q' => 'smol']),
    scheme: Scheme::HTTPS,
    host: new Host('example.com')
);

echo $url; // "https://example.com/docs?q=smol"
```

### Creating URLs

[](#creating-urls)

```
use Joby\Smol\URL\{URL, Path, Query, Fragment, Scheme, Host, Port, User};

// Simple absolute path
$url = new URL(new Path(filename: 'page.html'));
echo $url; // "/page.html"

// Full URL with all components
$url = new URL(
    path: new Path(['dir1', 'dir2'], 'file.php'),
    query: new Query(['key' => 'value']),
    fragment: new Fragment('section'),
    scheme: Scheme::HTTPS,
    host: new Host('example.com'),
    port: new Port(8080),
    user: new User('username', 'password')
);
echo $url; // "https://username:password@example.com:8080/dir1/dir2/file.php?key=value#section"

// Relative paths
$url = new URL(new Path(filename: 'page.html', absolute: false));
echo $url; // "page.html"
```

### Parsing URLs

[](#parsing-urls)

```
use Joby\Smol\URL\{UrlFactory, URL};

$factory = new UrlFactory();

// From string
$url = $factory->fromString('https://example.com/path?x=1#frag');

// From globals (REQUEST_URI, HOST, etc.)
$current = $factory->fromGlobals();

// Merge a URL with the factory base URL
$base = $factory->baseUrl();
$composed = $factory->fromUrl(new URL(path: $base->path));
```

### Working with paths

[](#working-with-paths)

```
// Create from string
$path = Path::fromString('/dir1/dir2/file.php');

// Access components
$path->directory; // ['dir1', 'dir2']
$path->filename;  // 'file.php'
$path->absolute;  // true

// Get directory path
$path->dirname(); // "/dir1/dir2/"
```

### Manipulating query parameters

[](#manipulating-query-parameters)

```
$query = new Query(['page' => '1', 'sort' => 'name']);

// Access values with type safety
$page = $query->getInt('page');        // 1
$sort = $query->get('sort');           // 'name'
$missing = $query->get('foo', 'bar');  // 'bar' (default)

// Check for parameters
$query->has('page'); // true

// Require parameters (throws exception if missing)
$page = $query->requireInt('page');

// Create modified copies
$newQuery = $query->withArg('limit', 10);
$newQuery = $query->withArgs(['page' => 2, 'limit' => 10]);
$newQuery = $query->withoutArg('sort');
```

### Modifying URLs immutably

[](#modifying-urls-immutably)

```
$url = new URL(
    Path::fromString('/page'),
    new Query(['id' => '123']),
    scheme: Scheme::HTTP,
    host: new Host('example.com')
);

// Create modified versions
$https = $url->withScheme(Scheme::HTTPS);
$newPath = $url->withPath(Path::fromString('/other'));
$newQuery = $url->withQuery(new Query(['id' => '456']));

// Original URL is unchanged
echo $url;   // "http://example.com/page?id=123"
echo $https; // "https://example.com/page?id=123"
```

#### URL query helpers

[](#url-query-helpers)

To make query edits less verbose, URL exposes helper methods that delegate to the `Query` object and return new URLs:

```
$url = new URL(
    Path::fromString('/page'),
    new Query(['a' => '1']),
    scheme: Scheme::HTTP,
    host: new Host('example.com')
);

$url = $url->withArg('b', 2);            // adds/updates a single arg
$url = $url->withArgs(['c' => true]);    // adds/updates multiple args
$url = $url->withoutArg('a');            // removes one arg
$url = $url->withoutArgs(['b', 'c']);    // removes multiple args
```

#### Permissive `with*()` inputs

[](#permissive-with-inputs)

Several `with*()` methods accept additional input types for convenience:

```
$url = new URL(new Path(absolute: true));

$url = $url->withScheme('https');        // string or Stringable
$url = $url->withHost('example.com');    // string or Stringable
$url = $url->withPort(8080);             // int
$url = $url->withFragment('section');    // string or Stringable
$url = $url->withQuery(['a' => '1']);    // array
$url = $url->withPath('/docs');          // string or Stringable
```

### Resolving relative links

[](#resolving-relative-links)

URLs include a `withLinkStringApplied()` method that allows updating URLs using a variety of relative URL strings, including relative paths, fragments, and both partial and full query string updates.

```
$base = new URL(
    Path::fromString('/dir1/dir2/page.html'),
    new Query(['a' => '1'])
);

// Apply relative links (like HTML )
$url = $base->withLinkStringApplied('other.html');
echo $url; // "/dir1/dir2/other.html"

$url = $base->withLinkStringApplied('../file.html');
echo $url; // "/dir1/file.html"

$url = $base->withLinkStringApplied('?b=2');
echo $url; // "/dir1/dir2/page.html?b=2"

$url = $base->withLinkStringApplied('&b=2');
echo $url; // "/dir1/dir2/page.html?a=1&b=2"

$url = $base->withLinkStringApplied('#section');
echo $url; // "/dir1/dir2/page.html#section"
```

Validation and encoding
-----------------------

[](#validation-and-encoding)

- **Host validation**: `Host` validates IP addresses and domain names.
- **Path normalization**: `Path` resolves `.` and `..` segments and rejects `.` or `..` filenames.
- **Encoding**: `Path` and `Fragment` encode their values for safe URL output; `Query` uses `http_build_query()` for encoding.

Error handling
--------------

[](#error-handling)

Invalid inputs throw `URLException` or `QueryException` depending on the component. For example, invalid host names, query value types, or missing required query keys will raise exceptions.

Limitations
-----------

[](#limitations)

- **HTTP/HTTPS only**: The library intentionally only supports HTTP and HTTPS schemes. This is a security feature to prevent parsing of arbitrary schemes like `javascript:`, `data:`, etc. (It does strictly allow using any backed enum as the scheme, so you could extend it to support more schemes if you like.)
- **No query parameter arrays**: Query parameters are limited to scalar types (strings, integers, floats, booleans). Arrays and objects are not supported to keep the implementation simple and focused.
- **Immutable only**: All components are readonly and immutable. You cannot modify a URL or its components in place; you must use the `with*()` methods to create new instances with your changes.

Requirements
------------

[](#requirements)

Fully tested on PHP 8.3+, static analysis for PHP 8.1+.

License
-------

[](#license)

MIT License - See [LICENSE](LICENSE) file for details.

###  Health Score

42

—

FairBetter than 90% of packages

Maintenance84

Actively maintained with recent releases

Popularity15

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity50

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~7 days

Total

10

Last Release

84d ago

PHP version history (3 changes)v1.0.0-beta1PHP &gt;=8.3

v1.0.0PHP &gt;=8.2

v1.0.1PHP &gt;=8.1

### Community

Maintainers

![](https://www.gravatar.com/avatar/f4e606aa41ebfd7d82c365c6dc4ec158fb1b0bfff5f8dcbfad4ab53e3092e6a6?d=identicon)[jobyone](/maintainers/jobyone)

---

Top Contributors

[![joby-lol](https://avatars.githubusercontent.com/u/856610?v=4)](https://github.com/joby-lol "joby-lol (12 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/joby-smol-url/health.svg)

```
[![Health](https://phpackages.com/badges/joby-smol-url/health.svg)](https://phpackages.com/packages/joby-smol-url)
```

###  Alternatives

[dereuromark/cakephp-url-cache

CakePHP plugin to speed up URL reverse lookup

24117.2k](/packages/dereuromark-cakephp-url-cache)[james.xue/laravel-admin-breadcrumb

Breadcrumb Sinicization

171.7k](/packages/jamesxue-laravel-admin-breadcrumb)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
