lumensistemas / lens
Lumen's PHP code-quality conventions, baked in. Wraps php-cs-fixer, Rector and PHPStan with shipped config.
Requires
- php: ^8.3
- ext-json: *
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.95.1
- humbug/box: ^4.7
- larastan/larastan: ^3.9.6
- pestphp/pest: ^4.6.3
- phpstan/phpstan: ^2.1.51
- rector/rector: ^2.4.2
- symfony/console: ^7.4.8
- symfony/finder: ^7.4.8
- symfony/process: ^7.4.8
README
Opinionated PHP code-quality conventions for Lumen Sistemas products. One package that wraps php-cs-fixer, Rector and PHPStan with shipped configs; installing it gives a project the canonical Lumen rules, and bumping its version rolls those rules across every product in lockstep.
The reference points are Laravel Pint (single tool, opinionated, baked-in config) and Tighten Duster (multi-tool orchestrator). lens is closer to Pint in philosophy and closer to Duster in shape.
Install
Requires PHP 8.3+ and git on PATH (for --dirty).
composer require --dev lumensistemas/lens
The package installs a self-contained PHAR at vendor/bin/lens.
None of the wrapped tools (php-cs-fixer, Rector, PHPStan,
Larastan, symfony components) land in your project's vendor/ —
they are bundled inside the PHAR and extracted on first run to
~/.cache/lens/<version>/. The cache is fingerprinted against the
PHAR's signature, so swapping builds/lens for a newer build
auto-invalidates without manual cleanup. There are no version
conflicts to resolve against your application's dependency tree.
Use
vendor/bin/lens check # run all linters in check mode vendor/bin/lens fix # apply automatic fixes, then verify with phpstan vendor/bin/lens cs-fixer [--fix] # single tool, --fix toggles write mode vendor/bin/lens rector [--fix] vendor/bin/lens phpstan
Useful flags:
--dirty only files changed vs. the merge-base with main
--ci GitHub-style annotations
--using=… comma-separated subset, e.g. --using=phpstan,rector
(an unknown name fails fast — no silent skip)
--base=<ref> git base ref for --dirty (default origin/main)
lens check returns the worst exit code across drivers, so a
single non-zero result fails CI without you needing to chain
commands.
--dirty is strict: missing git, missing repo, or an unfetched
base ref each fail loud. CI configurations using shallow clones
should git fetch origin <branch> (or use
actions/checkout@v4 with fetch-depth: 0).
One-time setup in a new project
vendor/bin/lens init # write lens.json + phpstan baseline vendor/bin/lens publish:workflow --package # drop .github/workflows/tests.yml (lint + test matrix)
init is opt-in. lens runs without lens.json — it auto-detects
the standard Laravel layout (app, bootstrap, config,
database, resources, routes, tests, src) and analyses
whichever of those exist in the project root.
lens.json — the override surface
By design, lens.json only configures boundaries, never rules.
The rules themselves live inside the package and change for every
product when lens is bumped.
{
"paths": ["app", "database", "routes", "tests"],
"phpstan": {
"baseline": "phpstan-baseline.neon"
}
}
| key | purpose |
|---|---|
paths |
dirs each tool analyses (default: detected) |
phpstan.baseline |
optional phpstan baseline file at the project root |
The shipped configs already exclude the universal cases (vendor,
storage, bootstrap/cache, node_modules) globally — there is
no per-project exclude knob today.
Any other key is rejected with a clear error. If you find yourself wanting to disable a rule, the path is to open a PR against lens itself — not to override locally. That is the point.
CI
lens publish:workflow --package drops a GitHub Actions workflow
at .github/workflows/tests.yml that installs PHP, runs lens check --ci for lint, and runs a composer test:coverage matrix
across supported PHP versions and stability profiles on Ubuntu and
Windows. Wire it into your pull-request branch protection.
--package is required: today this command only deploys the
package matrix workflow. The flag declares intent and reserves
room for other workflow shapes later.
Convention philosophy
Lumen's operating principles include "conventions over decisions"
and "consistency over cleverness". lens exists so the same PHP
code-quality decisions don't get re-litigated per project, and so
a single composer update lumensistemas/lens propagates the
canonical config to every product on the same day.
The corollary: if you find a real exception that the convention should permit, the right move is to update the convention, not to work around it locally.
Development
git clone https://github.com/lumensistemas/lens cd lens composer install composer test # pest, ~8s (4 subprocess-tagged tests) composer lens # run lens check against itself composer run build # rebuild builds/lens (the PHAR)
The PHAR is built from composer-build.json (runtime tools as
require, no dev) into a staged build/ working dir, then
written to builds/lens and committed. CI rebuilds on every push
and verifies via the freshly-built PHAR rather than source.
License
MIT. See LICENSE.