zicht/url-bundle
zicht/url-bundle is a Symfony bundle for flexible URL handling in PHP applications, with tools for URL generation, aliases and redirects so links stay clean, stable and easy to integrate with routed content.
zicht/url-bundle is a Symfony bundle — not a Laravel package. Before reaching for composer require, be clear on what you're actually getting: a small set of URL utilities (URL value objects, URL signing/verification, and Twig helpers) wired through Symfony's DI container. With zero dependents and no stars, treat it as a reference implementation you may lift from rather than a drop-in dependency.
Minimal path if you're evaluating it:
composer require zicht/url-bundle in a scratch Symfony app (not your Laravel app — there's no service provider, facade, or config/url.php).src/ — look for the bundle class, the URL/signer services, and any Twig extension. The namespace is Zicht\Bundle\UrlBundle\….DependencyInjection/Configuration.php to see the real config keys (secret, defaults). That file is the source of truth, not the README.If you want the behavior in Laravel today, plan to copy the signer/URL classes into app/Support/ and skip the bundle entirely.
Signed-URL round trip (Symfony native). Inject the signer service into a controller, sign the absolute URL for a route, return it in a response or email. On the receiving controller, pull the signature from the request, run verification, and reject on failure. Keep signing and verification in the same class so the parameter name and hashing algorithm can't drift.
Twig integration. If the bundle ships a Twig extension (likely url_sign or similar filter), use it in templates for outbound links (password resets, magic links, download links). This is convenient in Symfony; in Laravel you'd replicate it with a Blade directive wrapping your copied signer service.
Compose with the router, don't hardcode. Generate the base URL with Symfony's UrlGeneratorInterface, then sign. Hardcoded paths will break across environments and signed URLs will silently mismatch on host changes.
Bridge pattern for Laravel. Register a UrlSignerServiceProvider that binds a small interface to a copied signer class, and read the secret from config('services.url_signer.secret'). This gives you testability and keeps the origin of the code obvious. Prefer an HMAC (hash_hmac('sha256', $url, $secret)) over anything clever — the bundle's value is the shape, not exotic crypto.
Use cases that fit cleanly:
Testing pattern. For every signed-URL feature, write two tests: one that asserts a freshly signed URL verifies, and one that asserts a URL with a tampered query parameter or expired timestamp is rejected. Freeze the clock with Carbon::setTestNow() in Laravel (or Symfony's clock) so expiration tests are deterministic.
UrlBundleServiceProvider, no facade. Installing it in a Laravel app will register classes but wire nothing — you'll get "target class does not exist" errors the moment you try to resolve a signer from the container. Either run it in Symfony or vendor the code.composer.json with an exact constraint and audit the signer code yourself before production use.%env(URL_SIGNER_SECRET)% in Symfony or env() in Laravel, and fail loudly at boot if it's missing.hash_equals() rather than ===. The bundle may already do this; verify before trusting it in an auth-adjacent flow.Request::getSchemeAndHttpHost() can differ between signing and verification — configure trusted proxies or sign only the path + query, not the host.zicht_url: in config/packages/). Don't assume url: — check the bundle class's getContainerExtension().services.yaml after the bundle loads; that's the cleanest way to swap in your own implementation without forking.hash_hmac on a route URL, the bundle is overhead. A ~20-line UrlSigner class in app/Support/ gives you the same result with no Symfony dependency and no supply-chain surface.How can I help you explore Laravel packages today?