Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Dsn Laravel Package

zenstruck/dsn

A small PHP library for parsing and composing DSN (data source name) strings into structured objects, with support for custom schemes, validation, and clean access to connection credentials and options. Useful for configuring database and service connections from environment variables.

View on GitHub
Deep Wiki
Context7

Getting Started

zenstruck/dsn is a small, focused library for turning DSN strings (redis://user:pass@host:6379/0?timeout=10, smtp://..., https://...) into immutable value objects, and back again. v1.0.0 is the stability milestone: the public API is now frozen, Symfony 8 is supported, and deprecations are cleared. If you pin ^1.0, you can rely on the surface described below.

composer require zenstruck/dsn

Where to look first, in order:

  1. Zenstruck\Dsn — the value object. Read its scheme(), user(), password(), host(), port(), path(), query(), fragment(), with() and __toString() methods; that list is the package.
  2. Zenstruck\Dsn\Parser — the one-method interface (parse(string $dsn): Dsn) you bind in the container.
  3. Zenstruck\Dsn\GenericParser — the default implementation; Dsn::parse() is the static convenience wrapper around it.

First use case: stop hand-rolling parse_url() in a service provider. Anywhere Laravel hands you a URL-ish env var — DATABASE_URL, REDIS_URL, MAIL_DSN, AWS_*, a third-party SDK endpoint — parse it once, then read typed pieces:

use Zenstruck\Dsn;

$dsn = Dsn::parse(config('database.connections.pgsql.url'));

$dsn->host();          // "db.internal"
$dsn->port();          // 5432 (int) or null when omitted
$dsn->path();          // "app" (leading slash stripped)
$dsn->query();         // ['sslmode' => 'require'] — parsed, not a raw string

The payoff over parse_url() is the parsed query string, consistent handling of credentials, and a round-trippable object.

Implementation Patterns

Bind the parser, don't call the static. Dsn::parse() is fine in a tinker session or a test, but in application code type-hint Zenstruck\Dsn\Parser and let the container inject it. It keeps call sites swappable and lets you compose parsers later.

final class RedisConnectionFactory
{
    public function __construct(private \Zenstruck\Dsn\Parser $parser) {}

    public function make(string $url): array
    {
        $dsn = $this->parser->parse($url);

        return [
            'host' => $dsn->host(),
            'port' => $dsn->port() ?? 6379,
            'password' => $dsn->password(),
            'database' => (int) ltrim((string) $dsn->path(), '/'),
        ];
    }
}

Normalise config at the boundary. The strongest pattern is a single "configuration DSN" resolver that runs in a service provider and returns plain arrays for Laravel's config. Everything downstream then deals with scalars, and the DSN string only exists at the edge of the app. This makes config:cache safe and keeps DSN parsing out of request hot paths.

Scheme-aware dispatch. When you have several backends behind one config key (cache://, redis://, array://), switch on $dsn->scheme() to select a driver. This mirrors how Laravel's own CacheManager/MailManager behave and reads naturally:

$dsn = $this->parser->parse($url);

return match ($dsn->scheme()) {
    'redis' => $this->redisStore($dsn),
    'array' => $this->arrayStore($dsn),
    default => throw new \InvalidArgumentException("Unsupported scheme [{$dsn->scheme()}]"),
};

Use with() for derived DSNs. Because the object is immutable, $dsn->with(host: 'replica.internal') gives you a modified copy — useful for read-replica routing, per-tenant host swapping, or building a test DSN from a production one without string surgery.

Round-trip for storage and logging. (string) $dsn returns a DSN string, so you can persist it, put it in a queued job payload, or log it. Just remember credentials may be embedded — redact before logging.

Testing. In feature tests, assert on parsed values rather than raw strings ($this->assertSame('db.internal', Dsn::parse($url)->host())), and use with() in fixtures to derive variants from one canonical URL instead of copy-pasting env strings.

Gotchas and Tips

  • v1.0.0 drops older platform support. Symfony 8 support in the same release as the 1.0 tag almost certainly means the composer constraints widened to ^6.4|^7.0|^8.0 (or similar) and older Symfony/PHP lines fell out. Before upgrading from 0.2, run composer why-not zenstruck/dsn 1.0 and check php / symfony/* constraints in your lock file. The deprecation fixes are cosmetic; the constraint bump is the real upgrade risk.
  • Don't confuse it with parse_url(). They disagree on edge cases: no scheme, no authority, empty path, IPv6 brackets. If you migrate existing code, diff the two outputs on your real DSNs before deleting the old parser.
  • Query values are strings. ?timeout=10 yields '10', not 10. Cast deliberately, and remember parse_str semantics apply — dots and spaces in keys get mangled, and a[]=1 becomes an array.
  • port() is nullable. Don't assume a default; apply your own (?? 5432) at the call site so the intent is visible.
  • Credentials are percent-encoded. p%40ss in the DSN is p@ss out of the object. Conversely, never interpolate a raw password back into a DSN string — go through the object or rawurlencode it yourself.
  • Unknown schemes. The generic parser is permissive by design; it won't validate that rediss:// or pgsql:// is meaningful to your app. Do that validation yourself in the dispatch match — it's the only place that knows your drivers.
  • Custom parsers are the extension point. If a scheme needs richer validation or typed sub-objects, implement Dsn\Parser (returning a Dsn subclass) and compose it in front of the generic parser. That's the intended way to add behaviour; there's no plugin registry or config file to fight with.
  • Bind Parser once. Add $this->app->bind(\Zenstruck\Dsn\Parser::class, \Zenstruck\Dsn\GenericParser::class) (or your chain) in a provider; without it, autowiring into constructors is a common first-run error.
  • Debugging tip. When a DSN misbehaves, dump((string) $dsn) immediately after parsing — comparing the normalised output against what you wrote in .env surfaces encoding and empty-segment surprises faster than inspecting individual getters.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle