Skip to main content

Checking the environment

sqlens:doctor answers one question: what can SQLens actually see from here? It is the command to run first on a new machine and the one to run when two machines disagree about a report.

It is read-only in the strictest sense the package can offer — no write, no lock, and no query beyond a handshake.

php artisan sqlens:doctor

What it reports

php: 8.4.3
laravel: 13.26.1
sqlens: 0.1.0
os: Darwin
connection pgsql: driver=pgsql server=undetermined (no connection is open — pass --probe to open one)
tool squawk: not_found — 17 check(s) nobody is running. It would add: PostgreSQL migration lint rules for locking, rewrites and unsafe column changes
tool pgls: not_found — 6 check(s) nobody is running. It would add: PostgreSQL schema security checks for RLS gaps, mutable function search paths and extension placement

Four kinds of line:

  • The runtime — PHP, Laravel, the SQLens version, the OS family. These are the reproducibility parameters every report carries, printed once where you can read them.
  • A line per configured connection — its driver and its real server version.
  • A line per optional external tool — its version, or its absence.
  • With --strict, a verdict — would a strict-tools run fail on what is missing here?

--probe: by default it opens nothing, and says so

php artisan sqlens:doctor # opens nothing; every server version is undetermined
php artisan sqlens:doctor --probe # the default connection
php artisan sqlens:doctor --probe=pgsql
php artisan sqlens:doctor --probe=all

Without --probe the command reads whatever handle is already open, and in a fresh CLI process nothing is. So every connection comes back undetermined — which is honest, and was also useless: a healthy setup and a broken one produced identical output from the one command that exists to tell them apart. The default now names the switch that answers the question.

--probe is scoped to the default connection unless you widen it, and that scope was measured rather than chosen for tidiness. A stock config/database.php feeds one DB_PORT to every entry, so probing all of them points the MySQL driver at a PostgreSQL port — where the TCP session establishes and the handshake never arrives. --probe=all is right when your connections genuinely differ; it is a slow way to learn nothing when they do not.

A version that cannot be had is reported as undetermined with a named reason, never as a silent "ok".

--strict: ask the question a pipeline asks

php artisan sqlens:doctor --strict

strict_tools turns a missing external tool from a degradation into an error. Turning it on in CI without knowing what is installed there is how a pipeline goes red on its first run for a reason that has nothing to do with the code in the pull request.

--strict answers that in advance: would a strict-tools run fail on what is missing here? Run it once on the machine that will run the gate.

The tool lines report a cost, not a status

An optional tool is an amplifier. SQLens reaches every one of its own verdicts without Squawk and without the Postgres language server; a tool that is present adds findings SQLens cannot reach alone.

So the line for an absent tool does not say not_found and stop. It counts the checks nobody is running and names what they would have covered. A diagnostic that reports an absence without naming its cost invites a shrug — and the only reason to mention an optional tool at all is that its absence is otherwise invisible in a report that simply contains fewer findings.

--format=json

php artisan sqlens:doctor --format=json

Two formats, and only two: console and json.

That is deliberately its own pair rather than the four the other commands accept. doctor reports the environment, not findings — there is no result for a GitHub annotation or a SARIF log to describe, so accepting those formats would be a promise it cannot keep.

Exit code

sqlens:doctor reports; it does not gate. It returns 0 for a reachable environment and uses the ordinary misconfiguration code when what it was asked to inspect does not exist — an unknown connection name, an unknown format.

An undetermined server version is not a failure here. Not being able to read a version is information, and this command's job is to hand it to you.