PHPackages                             pdxapps/preflight - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Testing &amp; Quality](/categories/testing)
4. /
5. pdxapps/preflight

ActiveLibrary[Testing &amp; Quality](/categories/testing)

pdxapps/preflight
=================

A framework-agnostic, AI/CI-native runner for PHP code-quality tools (format, lint, static analysis, tests) in check or fix mode.

00PHPCI passing

Since Jun 7Pushed 2d agoCompare

[ Source](https://github.com/PDX-Apps/preflight)[ Packagist](https://packagist.org/packages/pdxapps/preflight)[ RSS](/packages/pdxapps-preflight/feed)WikiDiscussions main Synced 2d ago

READMEChangelogDependenciesVersions (1)Used By (0)

Preflight
=========

[](#preflight)

[![CI](https://github.com/PDX-Apps/preflight/actions/workflows/ci.yml/badge.svg)](https://github.com/PDX-Apps/preflight/actions/workflows/ci.yml)[![codecov](https://camo.githubusercontent.com/a9f8ba971495ec11107a672dc2e05477b9da0642ea3d6ebb9af537ceaa37eedb/68747470733a2f2f636f6465636f762e696f2f67682f5044582d417070732f707265666c696768742f67726170682f62616467652e737667)](https://codecov.io/gh/PDX-Apps/preflight)[![Packagist Version](https://camo.githubusercontent.com/b776a53c89a2d40c73b241dad2e56689c64ca3f012f9b500336b905d2d646d20/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f706478617070732f707265666c69676874)](https://packagist.org/packages/pdxapps/preflight)[![Packagist Downloads](https://camo.githubusercontent.com/03bd5b51c94bb42ea428a4f9062595d541e19c1da231632dadd390bd4607ff00/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f706478617070732f707265666c69676874)](https://packagist.org/packages/pdxapps/preflight)[![PHP Version](https://camo.githubusercontent.com/f86b50ddaade6636602e8d4869088740323aba9398f06d8902f9edd3727a35c8/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f706478617070732f707265666c696768743f6c6f676f3d706870266c6f676f436f6c6f723d7768697465)](composer.json)[![License: MIT](https://camo.githubusercontent.com/5525b7425146098a91331694603182f006fd3139b76e250d1cabe4858d875d21/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f5044582d417070732f707265666c69676874)](LICENSE)

A framework-agnostic, AI/CI-native runner for PHP code-quality tools. One command runs your formatter, linters, static analysis, refactoring checks, and tests — in **check** or **fix** mode — and reports the results for humans, CI, or AI agents.

```
 PASS   Pint (0.57s)
 PASS   PHPCS (0.78s)
 PASS   PHPStan (1.40s)
 PASS   Rector (0.71s)
 PASS   Psalm (2.77s)
 PASS   PHPMD (0.51s)
 PASS   Composer Audit (1.14s)
 PASS   Tests (1.44s)

8 passed, 0 failed, 0 skipped  (9.32s)
✓ All checks passed.

```

Why
---

[](#why)

Every PHP project ends up with a pile of quality tools, each with its own CLI, output format, and exit-code quirks. Preflight gives them **one interface**: a single command, a unified result schema, consistent exit codes, and output formats tuned for who's reading — a human at a terminal, a CI annotation on a PR, or an AI agent fixing its own work.

Documentation
-------------

[](#documentation)

This README is the overview. For depth, see the guides in [`docs/`](docs/):

- **[Installing tools (`preflight install`)](docs/install.md)** — set up tools and scaffold config in one confirm-first command.
- **[Steps reference](docs/steps.md)** — every built-in step, its options, and how scope flags affect it.
- **[Configuration](docs/configuration.md)** — `preflight.php`, precedence, run defaults, the agent preset, modules, custom steps, and the programmatic API.

Requirements
------------

[](#requirements)

- PHP 8.4+
- The tools you want to run, installed in your project (`composer require --dev`). Preflight runs whatever it finds and skips the rest.

Install
-------

[](#install)

```
composer require --dev pdxapps/preflight
```

Setup
-----

[](#setup)

Already have your tools installed? Skip this — Preflight auto-detects them. Starting fresh, `preflight install` adds the missing ones and scaffolds their config:

```
vendor/bin/preflight install            # interactive: previews, then asks before changing anything
vendor/bin/preflight install --dry-run  # just show what it would do
vendor/bin/preflight install --yes      # non-interactive (CI / agents)
```

It previews the exact `composer require --dev …` and the config files it will create, then acts only on confirmation. It never mutates silently: tools needing an explicit decision are surfaced, not assumed —

- **PHPMD** has no stable 3.x yet (and 2.x can't parse PHP 8.4), so it's **opt-in**: pass `--with-phpmd` (or accept the prompt) to install the dev branch, which also sets `minimum-stability: dev` in your `composer.json`.
- **Test runner**: choose with `--runner=phpunit|pest|none` (or at the prompt).
- Other flags: `--no-configs` (don't scaffold), `--force` (overwrite existing configs).

Scaffolded configs point at your real source dirs (detected from `app`/`src`/`tests`); Psalm is set up via its own `psalm --init`. `preflight doctor` tells you what's still missing.

Usage
-----

[](#usage)

```
vendor/bin/preflight              # run all installed checks
vendor/bin/preflight --fix        # apply fixes where the tool supports it
vendor/bin/preflight app src      # only these paths
vendor/bin/preflight --dirty      # only files changed in your working tree
vendor/bin/preflight doctor       # what's installed and what would run
vendor/bin/preflight init         # scaffold a preflight.php config
```

It works **zero-config**: with no `preflight.php`, Preflight runs the [default steps](#default-steps) — auto-detecting which apply from the tools you have installed — using your existing root config (`phpstan.neon`, `pint.json`, `phpcs.xml`, …), the standard locations the tools already use.

### Commands

[](#commands)

CommandDescription`run` (default)Run the checks (or fixes with `--fix`).`install`Install missing tools for your steps and scaffold their config (see [Setup](#setup)).`doctor`Show project root, config, and per-tool installed / config-found / would-run.`steps`List the configured steps and whether each will run.`init`Create a `preflight.php` config (`--force` to overwrite).### Options (for `run`)

[](#options-for-run)

OptionDescription`--fix`Apply fixes instead of only checking (fixable steps).`--check`Force check-only — overrides a `fixByDefault()` config.`--format=`Console output: `auto` (default), `human`, `json`, `agent`, `github`, `sarif`, `markdown`.`--write=:`**Also** render to a file (repeatable). Run once, emit many — e.g. `--write=sarif:preflight.sarif --write=markdown:summary.md`.`--fail-fast`Stop at the first failing step.`--files=a.php,b.php`Check only these files.`--dirty`Check only working-tree changes (staged + unstaged + untracked).`--all`Check the whole project — overrides a `dirtyByDefault()` config.`--since=`Check only files changed since a git ref (e.g. `main`).`-m, --module=`Check only a module's `app`/`tests` dirs (see modules below).`--skip-if-fresh`Skip the run entirely if inputs are unchanged since the last passing run (see [Freshness cache](#freshness-cache)).`--report=`Also write a durable run report to this file (see [Reports](#reports)).`--report-format=`Report file format: `json` (default).`--report-include=`Report sections: `findings,steps,passing,output` (or `all`). Default `findings,steps`.`[paths...]`Positional paths to scope the run.`--format=auto` (the default) prints the **human** table on an interactive terminal and the **agent** format when piped — so `preflight` reads nicely by hand and pipes cleanly into scripts. Exit code is `0` on pass and non-zero on failure in every format.

`--fix`/`--check` and `--dirty`/`--all` are the explicit overrides for the matching config defaults (`fixByDefault()` / `dirtyByDefault()`); a CLI flag always wins over config.

Default steps
-------------

[](#default-steps)

These are the steps Preflight runs out of the box, in this order (fast to slow). With no `preflight.php`, a run **auto-detects** which of them apply: a step runs only when its tool is installed in the project, and the rest are silently skipped. `composer-audit` is the exception — `composer` ships its `audit` command, so it always runs.

\#StepToolCategoryCheckFixRuns when1`pint`Laravel PintFormatting✓✓`laravel/pint` installed2`phpcs`PHP\_CodeSnifferCoding standard✓✓ (phpcbf)`squizlabs/php_codesniffer` installed3`phpstan`PHPStanStatic analysis✓`phpstan/phpstan` installed4`rector`RectorRefactoring✓✓`rector/rector` installed5`psalm`PsalmStatic analysis✓`vimeo/psalm` installed6`phpmd`PHPMD (3.x)Mess detection✓`phpmd/phpmd` installed7`composer-audit`Composer AuditDependency security✓**always** (built into Composer)8`test`PHPUnit / Paratest / PestTests✓a supported runner installed`vendor/bin/preflight doctor` shows, for your project, exactly which of these are installed and would run; `vendor/bin/preflight steps` lists the steps the current config resolves to. To change the set, see [Configuration](#configuration) (`withSteps()`, `tune()`, `without()`).

### Optional built-in steps

[](#optional-built-in-steps)

These ship with Preflight but aren't in the default set — add them in `preflight.php`:

StepToolCategoryCheckFixAdd with`composer-normalize`Composer Normalize`composer.json` hygiene✓✓`->withSteps([..., ComposerNormalize::class])``deptrac`DeptracArchitecture boundaries✓`->withSteps([..., Deptrac::class])``composer-normalize` runs the `ergebnis/composer-normalize` Composer plugin to keep `composer.json` sorted and consistently formatted. It's opt-in because it needs that plugin installed (`composer require --dev ergebnis/composer-normalize`).

`deptrac` enforces architectural layer boundaries from a `deptrac.yaml` depfile; each violation is an error. It's opt-in because it only does something once you've defined an architecture (`composer require --dev deptrac/deptrac` + a depfile).

Either skips with an install hint if its tool isn't installed, like any missing tool.

The `test` step auto-detects the runner: Paratest if installed, then Pest, then PHPUnit. All three are driven through the same JUnit output, so findings carry file, line, and the failing test's message. Coverage is off by default; opt in with `Tests::make()->coverage(['clover' => 'build/coverage.xml'])` and gate on it with `->minCoverage(90)` (whole project) or `->minPatchCoverage(90)` (only the lines a `--since`/`--dirty` run changed — it names the exact uncovered lines, ideal for PR checks and AI agents). Without a coverage driver (PCOV/Xdebug) the tests still run and a non-failing warning is attached — see [the steps reference](docs/steps.md#coverage).

The `composer-audit` step needs **no install** — `composer` ships the `audit` command — so it runs by default in every project, scanning `composer.lock` for known CVEs. A known advisory fails the run; abandoned packages are surfaced as non-failing warnings by default. Tune it with `ComposerAudit::make()->abandoned('ignore'|'report'|'fail')` and `->locked(false)` (to audit installed packages instead of the lock file). Like the other whole-project steps, a narrowed run (`--dirty`, `--files`) skips it.

Configuration
-------------

[](#configuration)

Zero-config is fine. To customise, run `vendor/bin/preflight init` and edit `preflight.php`. Steps are immutable, fluent, and referenced **by class**:

```
