PHP’s tooling ecosystem has been dominated by tools written in PHP: PHP-CS-Fixer for formatting, PHPStan and Psalm for static analysis, PHP_CodeSniffer for coding standards. They work well, but they share PHP’s speed characteristics—acceptable for small projects, noticeably slow on large codebases.
Mago is a new PHP linter and formatter written in Rust. It parses PHP files at native speed, applies lint rules, and formats code in a fraction of the time PHP-based tools take. This is not a theoretical difference—on a 500-file Laravel project, mago check completes in under a second while phpcs takes 8-12 seconds.
What Mago does
Mago provides two core features:
- Linting: Catches code quality issues, potential bugs, and style violations
- Formatting: Reformats PHP code to a consistent style
It does not replace PHPStan or Psalm for type-level static analysis. Mago operates at the syntax and style level, not the type system level.
Installation
1 | # Via Composer (downloads the Rust binary) |
Basic usage
1 | # Lint a directory |
Configuration
Mago uses a mago.toml file in your project root:
1 | [check] |
The configuration surface is intentionally smaller than PHP-CS-Fixer’s. Mago prefers convention over configuration—there is one way to format most constructs, similar to how gofmt works in Go or rustfmt works in Rust.
Speed comparison
Benchmarked on a 2,400-file Laravel application (M2 MacBook Pro):
| Tool | Task | Time |
|---|---|---|
| Mago | Lint | 0.4s |
| PHP_CodeSniffer | Lint | 14.2s |
| Mago | Format | 0.6s |
| PHP-CS-Fixer | Format | 22.8s |
| PHPStan (level 6) | Analyze | 18.5s |
Mago is roughly 30-40x faster than the PHP-based equivalents for linting and formatting. This makes it practical to run on every file save in your IDE, not just in CI.
What Mago catches
Bug-prone patterns
1 | // Mago flags: comparison always false |
Style issues
1 | // Mago suggests: use match instead of switch with simple returns |
Missing type declarations
1 | // Mago flags: missing return type |
Formatting philosophy
Mago’s formatter follows a deterministic approach: given the same input, it always produces the same output regardless of the original formatting. This eliminates formatting debates in code review.
1 | // Input (various styles) |
Where Mago differs from PHP-CS-Fixer
| Aspect | PHP-CS-Fixer | Mago |
|---|---|---|
| Config options | ~200 rules | ~30 rules |
| Approach | Highly configurable | Opinionated defaults |
| Speed | Slow on large codebases | Very fast |
| PHP version awareness | Yes | Yes |
| Custom rules | PHP plugins | Not yet supported |
IDE integration
VS Code
1 | // .vscode/settings.json |
PhpStorm
Install the Mago plugin from the JetBrains Marketplace. Configure it as an external tool or file watcher:
1 | Program: /path/to/mago |
CI integration
GitHub Actions
1 | - name: Lint PHP |
GitLab CI
1 | lint: |
Pre-commit hook
1 |
|
Because Mago is fast enough to run on every commit without annoying developers, pre-commit hooks become practical.
Combining Mago with PHPStan
Mago and PHPStan are complementary, not competing:
1 | # In CI: run Mago for style + basic bugs, PHPStan for types |
Mago catches what PHPStan does not (formatting, style, trivial bugs) and PHPStan catches what Mago does not (type errors, interface compliance, generics).
Common mistakes
Replacing PHPStan with Mago: Mago does not do type analysis. You still need PHPStan or Psalm for catching type errors, missing method calls, and interface violations.
Running Mago on generated code: If you have generated files (IDE helpers, Telescope stubs), exclude them from Mago checks or you will get thousands of false positives.
Forcing Mago formatting on a legacy codebase in one commit: Apply formatting incrementally—per-directory or per-module—to keep git blame useful.
Production tradeoffs
Adoption strategy: Run Mago alongside existing tools for a sprint, then gradually retire PHP_CodeSniffer or PHP-CS-Fixer if Mago covers your needs.
Team buy-in: The opinionated formatting means less bike-shedding about style. But it also means less flexibility if your team has strong preferences about specific formatting rules.
Maturity: Mago is newer than PHP-CS-Fixer (which has been stable since 2015). Edge cases exist, especially around complex heredoc syntax and unusual PHP constructs. Report issues when you find them.
FAQ
Does Mago support PHP 8.5 syntax?
Yes, Mago’s parser is updated for PHP 8.5 including the pipe operator. Parser updates typically ship within weeks of PHP releases.
Can I migrate from PHP-CS-Fixer to Mago incrementally?
Yes. Run both in CI initially with Mago in non-blocking mode. Once you are satisfied with coverage, switch to Mago as the primary tool.
Does Mago work on Windows?
Yes. Pre-built binaries are available for Windows, macOS, and Linux.
Next steps
Install Mago, run mago check on your project, and review the output. Do not auto-fix everything on day one—understand what Mago flags and verify the suggestions match your team’s standards. Then add it to CI as a non-blocking check before making it mandatory.
For complementary PHP tooling, the FrankenPHP guide covers runtime performance improvements, and the AI-assisted PHP development guide covers how AI tools interact with linters and formatters in your workflow.