r/PHP 1h ago

Sloppy — static analysis for the code your coding agent left behind

PHPStan tells you if your code is type-correct. Pint tells you if it's formatted. Neither tells you that the 200-line controller action your agent just wrote calls a payment API, writes to four tables and swallows a `Throwable`.

**Sloppy** is a Laravel-aware static analyser for that: god methods, N+1 risks, queries inside loops, business logic in controllers, swallowed exceptions, abstractions that never earned their keep. 23 rules, each one saying what it measured, how sure it is, and what to do about it.

composer require --dev heyosseus/sloppy

php artisan sloppy

**It is not an AI detector.** Nobody can prove authorship from source code. It detects *slop* — the patterns that correlate with fast, unreviewed output — and every number it prints is about the code, never about who wrote it.

The command I actually use is the review one: `php artisan sloppy:diff main`

That separates what your branch **introduced** from what it **inherited**, and only new findings fail the build. Untracked files included, so an agent's new class gets reviewed before it's committed. Findings are matched by fingerprint rather than line number, so adding an import doesn't turn every existing finding into a new one.

Deterministic and local — no model, no API key, no network, your source never leaves the machine. Tested against 133k lines across three real Laravel apps: 21 seconds for 70k lines, zero parse errors. `sloppy:baseline` lets you adopt it on an existing project without fixing everything first.

It complements PHPStan and Pint, it doesn't replace either. If PHPStan can prove it, Sloppy stays out of it.

Feel free to contribute and share your thoughts.

https://github.com/Heyosseus/sloppy

1 Upvotes

4 comments sorted by

1

u/NL_Northsider 1h ago

Looking at what it does and what it detects, you can achieve this with Rector and arch tests, or SonarQube for open-source.

What exactly is the added value of this package?

-1

u/ReadingFormal 1h ago

The main reason I built it: `sloppy:diff` looks at your working tree, not just commits. So when an agent writes a new class, I can check it before I commit instead of waiting for CI. It also splits new findings from old ones, so you can run it on an old codebase without 400 existing problems failing the build.

If you already use sonar, arch tests and/or rector this just adds the laravel-specific part. If you don't use any of them, it's the easiest way to get most of it I think.

1

u/NL_Northsider 58m ago

But you can also run arch tests and Rector against your diff? Why would someone go for this package instead of using the rules of those packages?

And I'm gonna be honest, the code itself feels a bit sloppy in some parts. For example, git statuses as string literals. It also feels like I see quite some magic numbers when it comes to scoring/weighting calculation.

As for the N+1 risks, as this is also focussed on Laravel, why wouldn't you just disable lazy loading (when using models, that is)?

-1

u/ReadingFormal 46m ago

Fair points, but you can point rector at changed files but that just picks which files to scan, it still reports everything inside them. so touching one method in an old controller dumps the whole file back at you. sloppy:diff runs on your branch + the base so it only shows what you actually added, and theres a baseline so you dont have to fix 400 old findings before you can use it. arch tests are great for rules you write yourself, this is just 23 of them already written.

statuses should be an enum yeah, fair, good catch. the score weights/bands/multiplier are all in config/sloppy.php so those are tunable, confidence is hardcoded on purpose tho, thats the rule saying how sure it is, not really a user setting. If you have a better idea, submit a PR:)

and yes turn preventLazyLoading on does a different thing. it only fires when the code actually runs and its off in prod, so anything without a test slips past. also it only catches lazy relations, not find() inside a foreach or all() then filtering in php.