micro-module/dsn
micro-module/dsn is a small PHP package for parsing, building, and handling DSN (Data Source Name) connection strings, making database or service connection details easier to manage in Laravel and PHP applications.
Because micro-module/dsn has no meaningful documentation trail (0 stars, single 2020 release, no dependents), treat the repository as the spec. Before wiring it into an app:
composer require micro-module/dsnsrc/ end-to-end — a DSN parser is typically one small class (Dsn, Parser, or similar) implementing parse(string $dsn): array or returning a value object. Identify the exact method names and the shape of the return value (array vs. object with scheme(), host(), port(), user(), password(), path(), query() accessors).tests/ — the fixtures usually encode the supported URL shapes (scheme://user:pass@host:port/db?options), which is the fastest way to understand behavior.composer.json for the PHP constraint. A December 2020 release will likely require PHP 7.2–7.4 and may not declare support for 8.x; if you're on PHP 8.2+, run the tests locally before trusting it.First use case — normalizing a DATABASE_URL env var into Laravel's config/database.php:
// config/database.php
use MicroModule\Dsn\Dsn; // confirm namespace in src/
$url = env('DATABASE_URL');
if ($url) {
$dsn = Dsn::parse($url); // adjust to real API
return [
'default' => $dsn->scheme(),
'connections' => [
$dsn->scheme() => [
'driver' => $dsn->scheme() === 'postgres' ? 'pgsql' : $dsn->scheme(),
'host' => $dsn->host(),
'port' => $dsn->port(),
'database' => ltrim($dsn->path(), '/'),
'username' => $dsn->user(),
'password' => $dsn->password(),
],
],
];
}
That single pattern — "one env var, one connection" — is the reason to reach for this package over hand-rolled parse_url() calls. Start there and only expand to other services (Redis, Mail, S3) once you've validated parsing behaves correctly for your credentials.
Wrap it in a typed value object. Don't pass raw arrays from the parser around your app. Normalize once at the edge:
final class ConnectionConfig
{
public function __construct(
public readonly string $scheme,
public readonly string $host,
public readonly ?int $port,
public readonly ?string $user,
public readonly ?string $password,
public readonly ?string $database,
public readonly array $options,
) {}
public static function fromDsn(string $dsn): self
{
$p = Dsn::parse($dsn); // adapt to real return shape
return new self(
scheme: $p->scheme(),
host: $p->host(),
port: $p->port(),
user: $p->user(),
password: $p->password(),
database: ltrim((string) $p->path(), '/'),
options: $p->query(),
);
}
}
This isolates your codebase from the package's API, which matters given how unmaintained it is. If you ever need to swap it out for league/uri or a native parse_url() fallback, the blast radius is one class.
Map DSN schemes to Laravel drivers explicitly. DSN scheme names (postgres, mysql, rediss) rarely match Laravel drivers (pgsql, mysql, redis). Keep a lookup table rather than leaning on scheme() directly:
private const DRIVER_MAP = [
'postgres' => 'pgsql',
'postgresql'=> 'pgsql',
'mysql' => 'mysql',
'maria' => 'mysql',
'redis' => 'redis',
'rediss' => 'redis',
];
Use it for side-channel services, not just the DB. Redis URLs (redis://:pw@host:6379/0), S3-compatible endpoints (s3://key:[email protected]/prefix), and mail transport URLs (smtp://user:[email protected]:2525) all benefit from the same parsing. Centralize it in a small DsnFactory service and inject that, so every service gets identical credential handling.
Multi-tenant / dynamic connections. If tenants each provide a DSN, parse on demand and register the connection at runtime:
config(['database.connections.tenant' => $tenant->dsnConfigArray()]);
DB::purge('tenant');
Cache parsed results keyed by the DSN string — parsing is cheap but you'll re-parse on every request otherwise.
Bootstrapping a testing config from DATABASE_URL. Because CI commonly injects a single URL, this package lets one env var drive both phpunit.xml DB settings and local .env without duplication.
@, :, /, or ? must be percent-encoded in the DSN. Verify the parser runs rawurldecode() on user, password, and query values — if it doesn't, you'll authenticate with literal %40 characters. Write a test asserting round-trip with a password like p@ss:w/rd.PostgreSQL:// vs postgres:// vs pgsql:// will not all be normalized identically. Case-fold scheme and path handling yourself rather than trusting the parser.null for absent ports. Do not pass null to Laravel's database config — it expects an integer or env('DB_PORT', 3306). Apply driver-appropriate defaults at the mapping layer.[::1]:5432). This is the single most common bug in hand-written and small-package DSN parsers. Test it. If the package strips brackets or splits on the first :, patch around it or fall back to parse_url() for the host portion.?sslmode=require&charset=utf8mb4 may return strings, arrays for repeated keys, or drop them entirely. Check what query() (or equivalent) returns before relying on it for things like sslmode.mysql://host:3306/ should yield an empty database, not /. ltrim($path, '/') handles the common case; double-check there's no null vs '' distinction leaking into config.composer's strict PSR-4 autoloading changes. Vendor-lock the exact commit or fork it into app/Support/ if you only need ~60 lines.dd(Dsn::parse($url))) once in a tinker session per service type. Because behavior is undocumented, this is faster than reading source twice.query() accessor, you can shim it with parse_str(parse_url($dsn, PHP_URL_QUERY), $opts). In practice, wrapping the package in your own ConnectionConfig/DsnFactory is the extension point — treat the package as an implementation detail you can replace.How can I help you explore Laravel packages today?