carthage-software/mago
Mago is an extremely fast PHP linter, formatter, and static analyzer written in Rust. It brings Rust-inspired speed and reliability to PHP projects with a modern toolchain and great developer experience, plus multiple install options (script, Homebrew, Composer).
title: Analyzer configuration Reference
Mago's analyzer is highly configurable, allowing you to tailor the analysis to your project's specific needs. All settings go under the [analyzer] table in your mago.toml file.
[analyzer]
# Ignore a specific error code across the whole project
ignore = ["mixed-argument"]
# Ignore an error code only in specific paths
ignore = [
"mixed-argument",
{ code = "missing-return-type", in = "tests/" },
]
# Use a baseline file to ignore existing issues
baseline = "analyzer-baseline.toml"
| Option | Type | Default | Description |
|---|---|---|---|
excludes |
string[] |
[] |
A list of paths or glob patterns to exclude from analysis. |
ignore |
(string | object)[] |
[] |
Issue codes to ignore, optionally scoped to specific paths. See Path-scoped ignoring. |
baseline |
string |
null |
Path to a baseline file to ignore listed issues. When specified, the analyzer will use this file as the default baseline, eliminating the need to pass --baseline on every run. Command-line --baseline arguments will override this setting. |
baseline-variant |
string |
"loose" |
The baseline format variant to use when generating new baselines. Options: "loose" (count-based, resilient to line changes) or "strict" (exact line matching). See Baseline Variants for details. |
minimum-fail-level |
string |
"error" |
Set the minimum issue severity that causes the command to exit with a non-zero status. Options: "note", "help", "warning", "error". Can be overridden by the --minimum-fail-level CLI flag. |
:::tip Tool-Specific Excludes
The excludes option here is additive to the global source.excludes defined in the [source] section of your configuration. Files excluded globally will always be excluded from analysis, and this option allows you to exclude additional files from the analyzer specifically.
For example:
[source]
excludes = ["cache/**"] # Excluded from ALL tools
[analyzer]
excludes = ["tests/**/*.php"] # Additionally excluded from analyzer only
:::
The ignore option accepts three formats:
Plain string — ignore a code everywhere:
ignore = ["missing-return-type"]
Object with single path — ignore a code only in a specific directory or file:
ignore = [
{ code = "missing-return-type", in = "tests/" },
]
Object with multiple paths — ignore a code in several locations:
ignore = [
{ code = "missing-return-type", in = ["tests/", "src/Legacy/"] },
]
All three formats can be mixed in the same ignore list:
ignore = [
"mixed-argument",
{ code = "missing-return-type", in = "tests/" },
{ code = "unused-parameter", in = ["tests/", "src/Generated/"] },
]
Paths are matched as prefixes against relative file paths from the project root. Both "tests" and "tests/" will match all files under the tests directory.
:::tip
Path-scoped ignoring is different from excludes:
excludes removes files from analysis entirely — they won't be parsed for type information.ignore with in still analyzes the files but suppresses specific issue codes in the output.
:::These flags control specific, powerful analysis capabilities.
| Option | Default | Description |
|---|---|---|
find-unused-expressions |
true |
Find and report expressions whose results are not used (e.g., $a + $b;). |
find-unused-definitions |
true |
Find and report unused definitions (e.g., private methods that are never called). |
analyze-dead-code |
false |
Analyze code that appears to be unreachable. |
memoize-properties |
true |
Track the literal values of class properties. Improves type inference but may increase memory usage. |
allow-possibly-undefined-array-keys |
true |
Allow accessing array keys that may not be defined without reporting an issue. |
check-throws |
false |
Check for unhandled thrown exceptions that are not caught or documented with [@throws](https://github.com/throws). |
check-missing-override |
false |
Check for missing #[Override] attributes on overriding methods (PHP 8.3+). |
find-unused-parameters |
false |
Find and report unused function/method parameters. |
strict-list-index-checks |
false |
When true, requires any integer used as a list index to be provably non-negative. |
no-boolean-literal-comparison |
false |
When true, disallows direct comparison to boolean literals (e.g., $a === true). |
check-missing-type-hints |
false |
When true, reports missing type hints on parameters, properties, and return types. |
check-closure-missing-type-hints |
false |
When true, checks closures for missing type hints when check-missing-type-hints is enabled. |
check-arrow-function-missing-type-hints |
false |
When true, checks arrow functions for missing type hints when check-missing-type-hints is enabled. |
register-super-globals |
true |
Automatically register PHP superglobals (e.g., $_GET, $_POST) for analysis. |
trust-existence-checks |
true |
When true, narrows types based on method_exists(), property_exists(), function_exists(), and defined() checks. |
check-property-initialization |
false |
When true, checks that typed properties are initialized in constructors or class initializers. |
check-use-statements |
false |
When true, reports use statements that import non-existent classes, functions, or constants. |
check-name-casing |
false |
When true, reports incorrect casing when referencing classes, functions, etc. (e.g., new fooBar() when defined as FooBar). Helps prevent autoloading failures on case-sensitive file systems. |
enforce-class-finality |
false |
When true, reports classes that are not final, abstract, or annotated with [@api](https://github.com/api) and have no children. |
require-api-or-internal |
false |
When true, requires abstract classes, interfaces, and traits to have [@api](https://github.com/api) or [@internal](https://github.com/internal) annotations. |
check-experimental |
false |
When true, reports usage of classes, interfaces, traits, and functions marked with [@experimental](https://github.com/experimental) from non-experimental contexts. |
allow-side-effects-in-conditions |
true |
When false, reports calls to impure functions (not marked [@pure](https://github.com/pure) or [@mutation-free](https://github.com/mutation-free)) inside if, while, for, ternary, or match conditions. Helps catch surprising evaluation-order bugs. |
These options control how the analyzer checks property initialization.
| Option | Type | Default | Description |
|---|---|---|---|
check-property-initialization |
bool |
false |
Enable/disable property initialization checking entirely. |
class-initializers |
string[] |
[] |
Method names treated as class initializers (like __construct). |
When check-property-initialization is enabled, the analyzer reports:
missing-constructor: Classes with typed properties that have no constructor to initialize themuninitialized-property: Typed properties not initialized in the constructorThe class-initializers setting allows you to specify additional methods that should be treated as initializers. Properties initialized in these methods count as "definitely initialized", just like in __construct. This is useful for frameworks that use lifecycle methods like:
setUp() method for test classesboot() or initialize() methods[analyzer]
# Treat setUp as a class initializer (for PHPUnit tests)
class-initializers = ["setUp", "initialize", "boot"]
With this configuration, the following code won't trigger false positives:
class MyTest extends TestCase
{
private string $name;
protected function setUp(): void
{
// Property initialized in setUp - no error reported
$this->name = "test";
}
}
Property initialization checking is disabled by default. To enable it:
[analyzer]
check-property-initialization = true
This enables both missing-constructor and uninitialized-property issues.
When check-throws is enabled, you can fine-tune which exceptions should be ignored using these options:
| Option | Type | Default | Description |
|---|---|---|---|
unchecked-exceptions |
string[] |
[] |
Exceptions to ignore including all their subclasses (hierarchy-aware). |
unchecked-exception-classes |
string[] |
[] |
Exceptions to ignore only as exact class matches (not subclasses or parent classes). |
unchecked-exceptions: When you add an exception class here, that exception and all classes that extend it will be ignored. This is useful for ignoring entire exception hierarchies, like all logic exceptions.
unchecked-exception-classes: When you add an exception class here, only that exact class is ignored. Subclasses and parent classes are still checked. This is useful when you want to ignore a specific exception without affecting related exceptions.
[analyzer]
check-throws = true
# Ignore LogicException and ALL its subclasses:
# - InvalidArgumentException
# - OutOfRangeException
# - DomainException
# - etc.
unchecked-exceptions = [
"LogicException",
"Psl\\Type\\Exception\\ExceptionInterface",
]
# Ignore ONLY this specific exception class (not its parent or child classes)
unchecked-exception-classes = [
"Psl\\File\\Exception\\FileNotFoundException",
]
In this example:
LogicException or its subclasses (like InvalidArgumentException) won't be reportedPsl\Type\Exception\ExceptionInterface won't be reportedPsl\File\Exception\FileNotFoundException is ignored, but other file exceptions would still be reported:::tip When to use which option
Use unchecked-exceptions when you want to ignore an entire category of exceptions, such as logic errors that indicate programming mistakes rather than runtime issues.
Use unchecked-exception-classes when you want to ignore a specific exception but still want to track its parent or sibling exceptions.
:::
When check-experimental is enabled, the analyzer reports warnings when code marked with [@experimental](https://github.com/experimental) is used from a non-experimental context. This helps teams manage unstable APIs that may change or be removed without notice. Available since Mago 1.19.0.
Mark classes, interfaces, traits, or functions with the [@experimental](https://github.com/experimental) PHPDoc tag:
/** [@experimental](https://github.com/experimental) */
class UnstableApi {
public function doSomething(): void {}
}
/** [@experimental](https://github.com/experimental) */
function beta_feature(): void {}
The analyzer will then warn when these symbols are used from non-experimental code:
// Warning: Usage of experimental class-like `UnstableApi`.
new UnstableApi();
// Warning: Usage of experimental function `beta_feature`.
beta_feature();
// Warning: Usage of experimental class-like `UnstableApi`.
class MyService extends UnstableApi {}
Usage from an experimental context is allowed without warnings:
/** [@experimental](https://github.com/experimental) */
function also_experimental(): void {
// OK — both caller and callee are experimental
new UnstableApi();
beta_feature();
}
/** [@experimental](https://github.com/experimental) */
class ExperimentalConsumer extends UnstableApi {
// OK — the class itself is experimental
}
class StableService {
/** [@experimental](https://github.com/experimental) */
public function experimentalMethod(): void {
// OK — the method is experimental
new UnstableApi();
}
}
[analyzer]
check-experimental = true
Plugins extend the analyzer with specialized type information for libraries and frameworks. They provide accurate type inference for functions that would otherwise return generic types.
| Option | Type | Default | Description |
|---|---|---|---|
disable-default-plugins |
bool |
false |
Disable all default plugins. Only explicitly listed plugins will be used. |
plugins |
string[] |
[] |
List of plugins to enable (by name or alias). |
| Plugin ID | Aliases | Default | Description |
|---|---|---|---|
stdlib |
standard, std, php-stdlib |
Enabled | Type providers for PHP built-in functions (strlen, array_*, json_*, etc.) |
psl |
php-standard-library, azjezz-psl |
Disabled | Type providers for php-standard-library package |
flow-php |
flow, flow-etl |
Disabled | Type providers for flow-php/etl package |
psr-container |
psr-11 |
Disabled | Type providers for psr/container package |
Plugins provide "type providers" that give the analyzer precise type information for library functions. For example, the stdlib plugin knows that:
array_filter($array) returns the same array type but potentially with fewer elementsjson_decode($json, true) returns array<string, mixed> when the second argument is truestrlen($string) returns int<0, max>Without plugins, these functions would return less precise types like mixed or array.
By default, the stdlib plugin is enabled:
[analyzer]
# No configuration needed - stdlib is enabled by default
[analyzer]
plugins = ["psl", "flow-php", "psr-container"]
[analyzer]
disable-default-plugins = true
[analyzer]
disable-default-plugins = true
plugins = ["psl"] # Only enable PSL, not stdlib
:::tip Plugin aliases
You can use any of the plugin aliases for convenience. For example, plugins = ["std"] is equivalent to plugins = ["stdlib"].
:::
The analyzer can be configured to be more or less strict depending on your project's needs. This section describes how to configure the analyzer for maximum strictness.
For the strictest possible analysis, use the following configuration:
[analyzer]
# Enable all checks
find-unused-expressions = true
find-unused-definitions = true
analyze-dead-code = true
check-throws = true
check-missing-override = true
find-unused-parameters = true
check-missing-type-hints = true
check-closure-missing-type-hints = true
check-arrow-function-missing-type-hints = true
enforce-class-finality = true
require-api-or-internal = true
check-experimental = true
# Enable strict checks
strict-list-index-checks = true
no-boolean-literal-comparison = true
# Disable lenient behaviors
allow-possibly-undefined-array-keys = false
trust-existence-checks = false
| Option | Strict Value | Effect |
|---|---|---|
check-missing-type-hints |
true |
Reports missing type hints on function parameters, return types, and class properties. |
check-closure-missing-type-hints |
true |
Also checks closures for missing type hints (requires check-missing-type-hints). |
check-arrow-function-missing-type-hints |
true |
Also checks arrow functions for missing type hints (requires check-missing-type-hints). |
| Option | Strict Value | Effect |
|---|---|---|
allow-possibly-undefined-array-keys |
false |
Reports errors when accessing array keys that may not exist. |
strict-list-index-checks |
true |
Requires list indices to be provably non-negative (int<0, max>). |
| Option | Strict Value | Effect |
|---|---|---|
trust-existence-checks |
false |
Ignores method_exists(), property_exists() checks; requires explicit type hints. |
When trust-existence-checks is enabled (the default), the analyzer narrows types based on runtime existence checks:
function process(object $obj): mixed
{
// With trust-existence-checks = true (default):
// No warning - method existence is verified at runtime
if (method_exists($obj, 'toArray')) {
return $obj->toArray();
}
// With trust-existence-checks = false:
// Warning reported - explicit type hints required
return null;
}
| Option | Strict Value | Effect |
|---|---|---|
find-unused-expressions |
true |
Reports expressions whose results are discarded (e.g., $a + $b;). |
find-unused-definitions |
true |
Reports unused private methods, variables, and other definitions. |
analyze-dead-code |
true |
Analyzes and reports on unreachable code paths. |
check-missing-override |
true |
Reports missing #[Override] attributes on overriding methods (PHP 8.3+). |
find-unused-parameters |
true |
Reports unused function/method parameters. |
no-boolean-literal-comparison |
true |
Disallows comparisons like $a === true or $b == false. |
enforce-class-finality |
true |
Reports classes not declared final, abstract, or annotated with [@api](https://github.com/api). |
require-api-or-internal |
true |
Requires abstract classes, interfaces, and traits to have [@api](https://github.com/api) or [@internal](https://github.com/internal). |
check-experimental |
true |
Reports usage of [@experimental](https://github.com/experimental) APIs from non-experimental contexts. |
allow-side-effects-in-conditions |
false |
Reports impure function calls inside conditions. |
| Option | Strict Value | Effect |
|---|---|---|
check-throws |
true |
Reports unhandled exceptions not caught or documented with [@throws](https://github.com/throws). |
For a more lenient analysis (useful for legacy codebases or gradual adoption), use:
[analyzer]
# Disable strict checks
check-missing-type-hints = false
strict-list-index-checks = false
no-boolean-literal-comparison = false
enforce-class-finality = false
require-api-or-internal = false
# Enable lenient behaviors
allow-possibly-undefined-array-keys = true
trust-existence-checks = true
# Optionally disable some checks
check-throws = false
:::tip Gradual adoption When introducing Mago to an existing codebase, start with lenient settings and a baseline. Gradually enable stricter options as you improve the codebase. :::
The analyzer uses internal thresholds to balance analysis depth against performance. These thresholds control how deeply the type inference engine explores complex logical formulas. All settings go under [analyzer.performance] in your mago.toml file.
| Option | Type | Default | Description |
|---|---|---|---|
saturation-complexity-threshold |
u16 |
8192 |
Maximum clauses during CNF saturation. |
disjunction-complexity-threshold |
u16 |
4096 |
Maximum clauses per side in OR operations. |
negation-complexity-threshold |
u16 |
4096 |
Maximum cumulative complexity when negating formulas. |
consensus-limit-threshold |
u16 |
256 |
Upper limit for consensus optimizati... |
How can I help you explore Laravel packages today?