enqueue/dsn
Enqueue DSN is a small PHP library for parsing Data Source Name strings into structured connection configuration for the Enqueue messaging abstraction, making it easy to configure transports like AMQP, Redis, and others from a single URL.
The package is tiny and dependency-free: two public classes (Enqueue\Dsn\Dsn, Enqueue\Dsn\ParsedDsn) and one exception (Enqueue\Dsn\InvalidArgumentException). There is nothing to configure, no service provider to register, no facade to alias.
composer require enqueue/dsn
That's it. Dsn::parse() is static, so you can call it from anywhere — a config file, a service provider, a console command, an artisan command, a queued job.
Parse a broker DSN out of .env into the pieces you actually need:
use Enqueue\Dsn\Dsn;
$parsed = Dsn::parse(env('QUEUE_DSN', 'amqp://guest:guest@localhost:5672/%2f'));
$parsed->getScheme(); // "amqp"
$parsed->getHost(); // "localhost"
$parsed->getPort(); // "5672" (string, not int)
$parsed->getUser(); // "guest"
$parsed->getPassword(); // "guest"
$parsed->getPath(); // "/" (the %2f was decoded)
$parsed->getQueryParameters(); // [] unless you added ?foo=bar
src/Dsn.php and src/ParsedDsn.php — the entire public surface is ~80 lines. You can read it in five minutes and know everything the package does.tests/ — this is where edge cases (empty password, missing port, encoded vhost, query strings) are exercised.The first realistic use is normalizing a DSN for a queue connection factory (php-enqueue/amqp-ext, php-enqueue/sqs, etc.) or validating a user-supplied DSN before you hand it to a driver.
Because the object is a thin value object, the cleanest integration with Laravel is to translate it into a plain array in your queue config, so config:cache and container resolution stay simple:
// config/queue.php
use Enqueue\Dsn\Dsn;
$dsn = Dsn::parse(env('BROKER_DSN'));
return [
'connections' => [
'broker' => [
'scheme' => $dsn->getScheme(),
'host' => $dsn->getHost(),
'port' => (int) $dsn->getPort(),
'user' => $dsn->getUser(),
'pass' => $dsn->getPassword(),
'vhost' => ltrim((string) $dsn->getPath(), '/'),
'options' => $dsn->getQueryParameters(),
],
],
];
This is the single most useful day-to-day pattern. Instead of adding a dozen environment variables, put them in the DSN query and read them by name:
$timeout = (int) $parsed->getQueryParameter('read_timeout', 3);
$persistent = (bool) $parsed->getQueryParameter('persistent', false);
Note getQueryParameter() takes a default — reach for it before writing ?? null.
parseFirst()Dsn::parse() expects exactly one DSN. If your config allows comma-separated alternatives (amqp://a,amqp://b), use parseFirst() to take the first one and let your own logic handle rotation:
$primary = Dsn::parseFirst(config('queue.broker_dsn'));
Wrap parsing at any boundary where a human supplies the DSN — form requests, artisan commands, admin panels:
use Enqueue\Dsn\Dsn;
use Enqueue\Dsn\InvalidArgumentException;
try {
Dsn::parse($request->input('dsn'));
} catch (InvalidArgumentException $e) {
throw ValidationException::withMessages([
'dsn' => 'Not a valid DSN: '.$e->getMessage(),
]);
}
This is the sweet spot for the package in a Laravel app: a small validation helper around a queue-setup form or php artisan queue:configure.
ParsedDsn::getDsn() (and __toString()) rebuilds the DSN, which is handy for an admin dashboard that shows the normalized connection string without leaking the password:
$safe = preg_replace('/:[^:@]*@/', ':****@', (string) $parsed);
Since parsing is stateless, you rarely need this — but if several services consume the same DSN, bind the ParsedDsn itself:
$this->app->singleton(ParsedDsn::class, fn () => Dsn::parse(config('queue.connections.broker.dsn')));
Then inject ParsedDsn wherever a specific broker host/port is needed.
It's a parser, not a broker client. It knows nothing about AMQP vs SQS vs Redis semantics. amqp://foo and made-up://foo both parse fine. Any per-scheme validation is yours to write.
getPort() returns a string (straight from parse_url). Cast to (int) before passing into a driver that type-hints int.
Missing parts are null, not empty strings. getPassword() on redis://localhost returns null; don't rely on truthiness vs === '' interchangeably.
The path is URL-decoded. amqp://host/%2f gives you getPath() === '/'. If you need the raw vhost representation you have to re-encode it.
Password with special characters must be URL-encoded in the DSN. getPassword() returns the decoded value, so p@ss:word must be written as p%40ss%3Aword in the config string or you'll get a parse error / wrong split.
Dsn::parse() throws InvalidArgumentException on malformed input (missing scheme, bad syntax). It does not return null. Always try/catch at trust boundaries; a bad DSN in .env will otherwise blow up during config load with an unhelpful stack trace.
Comma-separated DSNs need parseFirst(). Passing amqp://a,amqp://b to parse() will fail — this is a common surprise if you're migrating from a Symfony/Enqueue config that allows failover lists.
Query parameters are always strings. ?persistent=1 gives you '1', not 1. Cast explicitly ((bool), (int)) at the read site.
config:cache caveat. Parsing inside config/queue.php runs during config caching, which is fine — but if you bind a ParsedDsn object into a service provider it won't survive the config cache the way arrays do. Prefer arrays in config, objects in services.
Don't use it as a general URL parser. It's fine for HTTP-ish URLs but it deliberately doesn't do deep scheme-specific rules. For general URL work, Symfony's Url component (or parse_url) gives you more and is actively maintained.
No maintenance churn. Last release is old, but the surface is frozen and MIT-licensed. If you need a behavior it doesn't have (e.g. SQS ARN parsing, IPv6 edge cases), fork it — it's small enough to own outright. Wrap it behind your own QueueDsnParser interface so a future swap is a one-file change.
Debugging tip. When a DSN misbehaves, var_export($parsed->getQueryParameters(), true) plus (string) $parsed tells you exactly what was recognized vs dropped — far faster than stepping through your queue driver's connection factory.
How can I help you explore Laravel packages today?