URL Utility

Lightpack's Url utility class provides a comprehensive set of methods for URL generation, manipulation, and validation.

You can instantiate to Url utility:

$url = new Lightpack\Utils\Url();

Or you can call the utility function url() which returns an instance or Url class.

Basic URL Generation

Simple URLs

// Generate basic URL
url()->to('users'); // Returns: /users

// URL with multiple segments
url()->to('blog', 'posts', '123'); // Returns: /blog/posts/123

// URL with query parameters
// Returns: /users?sort=asc&status=active
url()->to('users', ['sort' => 'asc', 'status' => 'active']); 

If you set the environment variable APP_URL, then it returns fully qualified URL:

// If APP_URL=https://example.com
// Returns: https://example.com/api/v1
url()->to('api', 'v1'); 

URL Manipulation

Query Parameters

Add/update query parameters:

// Returns: https://example.com/search?q=php
url()->withQuery('https://example.com/search', ['q' => 'php']);

Array parameters:

// Returns: https://example.com/posts?tags[0]=php&tags[1]=mysql
url()->withQuery('https://example.com/posts', [
    'tags' => ['php', 'mysql']
]);

Remove query parameters:

// Returns: example.com?page=1
url()->withoutQuery('example.com?page=1&sort=desc', 'sort');

Remove multiple parameters:

// Returns: example.com?page=1
url()->withoutQuery('example.com?utm_source=fb&page=1', ['utm_source', 'utm_medium']);

Remove all query parameters:

// Returns: example.com
url()->withoutQuery('example.com?page=1&sort=desc');

URL Fragments

Add/update fragment:

// Returns: example.com#section1
url()->withFragment('example.com', 'section1');

Update existing fragment:

// Returns: example.com#new
url()->withFragment('example.com#old', 'new');

Remove fragment:

// Returns: example.com
url()->withoutFragment('example.com#section');

URL Normalization

Clean up URLs:

// Returns: https://example.com/api/users
url()->normalize('https://example.com//blog/../api/./users//');

Join URL segments:

// Returns: https://api.com/v1/users?sort=desc
url()->join('https://api.com', 'v1/', '/users', '?sort=desc');

Comprehensive Example Scenarios

The following document shows you some valid practical use case scenarios.

1. URL Generation Service

class UrlGenerator
{
    public function getProfileUrl(int $userId, array $params = []): string
    {
        return url()->route('user.profile', array_merge(
            ['id' => $userId],
            $params
        ));
    }

    public function getDownloadUrl(string $fileId, int $expiresIn = 3600): string
    {
        return url()->sign('file.download', [
            'id' => $fileId
        ], $expiresIn);
    }

    public function getAssetUrl(string $path, bool $versioned = false): string
    {
        $url = url()->asset($path);

        if ($versioned) {
            return url()->withQuery($url, [
                'v' => filemtime(public_path($path))
            ]);
        }

        return $url;
    }
}

2. URL Validation Service

class UrlValidator
{
    private array $defaultOptions = [
        'schemes' => ['https'],
        'require_scheme' => true,
        'max_length' => 2048
    ];

    public function isValidExternalUrl(string $url): bool
    {
        return url()->validate($url, array_merge(
            $this->defaultOptions,
            ['allowed_hosts' => $this->getAllowedHosts()]
        ));
    }

    public function isValidInternalUrl(string $url): bool
    {
        return url()->validate($url, array_merge(
            $this->defaultOptions,
            ['allowed_hosts' => [parse_url(get_env('APP_URL'), PHP_URL_HOST)]]
        ));
    }

    public function isValidAssetUrl(string $url): bool
    {
        $assetHost = get_env('ASSET_URL') 
            ? parse_url(get_env('ASSET_URL'), PHP_URL_HOST)
            : parse_url(get_env('APP_URL'), PHP_URL_HOST);

        return url()->validate($url, array_merge(
            $this->defaultOptions,
            ['allowed_hosts' => [$assetHost]]
        ));
    }

    private function getAllowedHosts(): array
    {
        // Load from configuration or environment
        return explode(',', get_env('ALLOWED_EXTERNAL_HOSTS', ''));
    }
}

3. URL Cleaner Service

class UrlCleaner
{
    public function clean(string $url): string
    {
        // Remove tracking parameters
        $url = url()->withoutQuery($url, [
            'utm_source',
            'utm_medium',
            'utm_campaign',
            'utm_term',
            'utm_content'
        ]);

        // Normalize the URL
        $url = url()->normalize($url);

        // Remove fragments if present
        return url()->withoutFragment($url);
    }

    public function cleanBatch(array $urls): array
    {
        return array_map([$this, 'clean'], $urls);
    }
}

4. URL Security Service

class UrlSecurity
{
    private array $sensitiveParams = [
        'token',
        'key',
        'password',
        'secret'
    ];

    public function sanitize(string $url): string
    {
        // Remove sensitive query parameters
        return url()->withoutQuery($url, $this->sensitiveParams);
    }

    public function createSecureDownloadUrl(string $path, array $params = []): string
    {
        // Generate a signed URL that expires in 5 minutes
        return url()->sign('secure.download', array_merge(
            ['path' => $path],
            $params
        ), 300);
    }

    public function verifySecureUrl(string $url): bool
    {
        // Verify the URL ignoring non-security related parameters
        return url()->verify($url, [
            'ref',
            'source',
            'campaign'
        ]);
    }
}

5. URL Analytics Service

class UrlAnalytics
{
    public function addTracking(string $url, array $params): string
    {
        return url()->withQuery($url, array_merge(
            [
                'utm_source' => $params['source'] ?? 'website',
                'utm_medium' => $params['medium'] ?? 'link',
                'utm_campaign' => $params['campaign'] ?? 'default'
            ],
            array_filter([
                'utm_term' => $params['term'] ?? null,
                'utm_content' => $params['content'] ?? null
            ])
        ));
    }

    public function extractTrackingParams(string $url): array
    {
        $parts = url()->parse($url);
        $tracking = [];

        foreach ($parts['query'] as $key => $value) {
            if (str_starts_with($key, 'utm_')) {
                $tracking[substr($key, 4)] = $value;
            }
        }

        return $tracking;
    }
}