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).
Mago is configured using a mago.toml file. You can generate a default configuration file using the mago init command.
This page details the global configuration options and the [source] section. For tool-specific options, see the links at the bottom of this page.
When no --config flag is provided, Mago searches for a configuration file (mago.toml, mago.yaml, or mago.json) in the following locations, in order:
--workspace)$XDG_CONFIG_HOME — if the environment variable is set (e.g., $XDG_CONFIG_HOME/mago.toml)$HOME/.config — the default XDG config directory (e.g., ~/.config/mago.toml)$HOME — the user's home directory (e.g., ~/mago.toml)The first file found wins. This allows you to have a global configuration in ~/.config/mago.toml that applies when no project-level configuration exists.
These options are set at the root of your mago.toml file.
php-version = "8.2"
threads = 8
stack-size = 8388608 # 8 MB
editor-url = "phpstorm://open?file=%file%&line=%line%&column=%column%"
| Option | Type | Default | Description |
|---|---|---|---|
php-version |
string |
Latest stable | The version of PHP to use for parsing and analysis. Defaults to the latest stable PHP version supported by your Mago release. Use mago init to auto-detect from composer.json. |
allow-unsupported-php-version |
boolean |
false |
Allow Mago to run on unsupported PHP versions. Not recommended. |
threads |
integer |
(logical CPUs) | The number of threads to use for parallel tasks. |
stack-size |
integer |
(see below) | The stack size in bytes for each thread. Defaults to 2MB, with a minimum of 2MB and a maximum of 8MB. |
editor-url |
string |
(none) | Editor URL template for clickable file paths in terminal output. See Editor Integration below. |
[source] SectionThis section configures how Mago discovers and processes files in your project.
paths, includes, and excludesMago distinguishes between your code (what you want to check and format) and dependencies (code you need for context but don't want to modify):
paths = Your source code - files that Mago will actively process:
includes = Dependencies and vendor code - files that Mago will parse for context only:
vendor directory, third-party libraries, framework codeexcludes = Paths or patterns to completely skip:
:::tip
If a file matches both paths and includes, the more specific pattern takes precedence:
src/b.php) are most specificsrc/foo/bar/) are more specific than shallow ones (e.g., src/)src/*.php)If patterns have equal specificity, includes takes precedence. This allows you to explicitly override the file type for specific paths when needed.
:::
[source]
# Your application code - will be analyzed, linted, and formatted
paths = ["src", "tests"]
# Vendor dependencies - only parsed for type information
includes = ["vendor"]
# Completely ignored by all tools
excludes = ["cache/**", "build/**", "var/**"]
# File extensions to treat as PHP
extensions = ["php"]
Both paths, includes, and excludes support glob patterns:
[source]
# Use glob patterns to target specific files
paths = ["src/**/*.php"]
includes = ["vendor/symfony/**/*.php"] # Only Symfony from vendor
excludes = [
"**/*_generated.php", # Any generated file
"**/tests/**", # All test directories
"src/Legacy/**", # Specific legacy code
]
| Option | Type | Default | Description |
|---|---|---|---|
paths |
string[] |
[] |
Directories or glob patterns for your source code. These files will be analyzed, linted, and formatted. If empty, the entire workspace is scanned. |
includes |
string[] |
[] |
Directories or glob patterns for dependencies (e.g., vendor). These files are parsed for symbols and types but are NOT analyzed, linted, or formatted. |
excludes |
string[] |
[] |
Glob patterns or paths to completely exclude from all tools. These files won't be processed or parsed at all. |
extensions |
string[] |
["php"] |
File extensions to treat as PHP files. |
The [source.glob] section controls how glob patterns are matched. Available since Mago 1.19.0.
[source.glob]
# When true, `*` does not match `/` in paths. Use `**` for recursive matching.
# e.g., `src/*/Test` matches `src/foo/Test` but NOT `src/foo/bar/Test`.
literal-separator = true
# Match patterns case-insensitively.
case-insensitive = false
# Whether `\` escapes special characters in patterns.
backslash-escape = true
# Whether empty alternates are allowed, e.g., `{,a}` matches "" and "a".
empty-alternates = false
| Option | Type | Default | Description |
|---|---|---|---|
case-insensitive |
bool |
false |
Match patterns case-insensitively. |
literal-separator |
bool |
false |
When true, * does not match path separators. Use ** for recursive directory matching. |
backslash-escape |
bool |
false on Windows, true otherwise |
Whether \ escapes special characters in patterns. |
empty-alternates |
bool |
false |
Whether empty alternates are allowed (e.g., {,a} matches "" and "a"). |
:::tip
New projects created with mago init automatically set literal-separator = true, which is the recommended setting. It makes * behave like most users expect (matching a single directory level, like .gitignore).
:::
In addition to the global excludes option, each tool (linter, formatter, analyzer, guard) has its own excludes option for tool-specific exclusions.
Tool-specific excludes are additive - files are excluded if they match EITHER the global source.excludes OR the tool-specific excludes.
[source]
paths = ["src", "tests"]
excludes = ["cache/**"] # Excluded from ALL tools
[analyzer]
# Additionally exclude test files from analysis only
# (they'll still be linted and formatted)
excludes = ["tests/**/*.php", "src/**/tests/**"]
[formatter]
# Additionally exclude auto-generated code from formatting only
# (it will still be analyzed and linted)
excludes = ["src/**/AutoGenerated/**/*.php"]
[linter]
# Additionally exclude database migrations from linting only
excludes = ["database/migrations/**"]
The linter also supports per-rule path exclusions, allowing you to skip individual rules for specific files or directories while still applying other rules. See the Linter Configuration Reference for details.
[linter.rules]
# Don't enforce static closures in test files
prefer-static-closure = { exclude = ["tests/"] }
:::tip Using mago list-files
Use the mago list-files command to see which files will be processed:
# See all files in your project
mago list-files
# See which files the formatter will process
mago list-files --command formatter
# See which files the analyzer will process
mago list-files --command analyzer
This helps verify your paths, includes, and excludes configuration is working as expected.
:::
[parser] SectionThis section configures how Mago parses PHP code, including lexer-level settings that affect tokenization behavior.
[parser]
enable-short-tags = false
| Option | Type | Default | Description |
|---|---|---|---|
enable-short-tags |
boolean |
true |
Whether to enable PHP short open tags (<?). When disabled, only <?php and <?= are recognized as PHP open tags. Equivalent to PHP's short_open_tag ini directive. |
You might want to disable short open tags if:
.php extensions that use <?xml declarations<? sequences<?php tags for clarityshort_open_tag is disabled:::warning
When enable-short-tags is false, sequences like <?xml version="1.0"?> will be treated as inline text rather than causing parse errors. However, any code using <? as a PHP open tag will no longer be recognized as PHP code.
:::
Mago can make file paths in diagnostic output clickable using OSC 8 terminal hyperlinks. When configured, clicking a file path in the terminal opens the file directly in your editor at the correct line and column.
This works in terminals that support OSC 8 hyperlinks, including iTerm2, Wezterm, Kitty, Windows Terminal, Ghostty, and others.
Mago automatically detects your editor when running inside a supported terminal. On macOS, this uses the __CFBundleIdentifier environment variable set by the running application. On other platforms, the TERM_PROGRAM variable is checked.
The following editors are automatically detected:
When auto-detection succeeds, clickable file paths work out of the box with no configuration needed.
If auto-detection doesn't work for your setup, or you want to override it, you can set the editor URL explicitly.
The MAGO_EDITOR_URL environment variable takes the highest precedence:
export MAGO_EDITOR_URL="vscode://file/%file%:%line%:%column%"
The editor-url option in mago.toml takes precedence over auto-detection but is overridden by the environment variable:
editor-url = "phpstorm://open?file=%file%&line=%line%&column=%column%"
The editor URL is resolved in the following order (first match wins):
MAGO_EDITOR_URL environment variableeditor-url in mago.toml| Placeholder | Description |
|---|---|
%file% |
Absolute path to the file |
%line% |
Line number (1-based) |
%column% |
Column number (1-based) |
| Editor | Template |
|---|---|
| VS Code | vscode://file/%file%:%line%:%column% |
| VS Code Insiders | vscode-insiders://file/%file%:%line%:%column% |
| Cursor | cursor://file/%file%:%line%:%column% |
| Windsurf | windsurf://file/%file%:%line%:%column% |
| PhpStorm / IntelliJ | phpstorm://open?file=%file%&line=%line%&column=%column% |
| Zed | zed://file/%file%:%line%:%column% |
| Sublime Text | subl://open?url=file://%file%&line=%line%&column=%column% |
| Emacs | emacs://open?url=file://%file%&line=%line%&column=%column% |
| Atom | atom://core/open/file?filename=%file%&line=%line%&column=%column% |
:::tip
Hyperlinks are only rendered when output is sent to a terminal with colors enabled. They are automatically disabled when output is piped or when --colors=never is used, so they won't interfere with scripts or CI pipelines.
:::
Clickable file paths are supported in the following reporting formats:
rich (default), medium, short — file paths in diagnostic headersemacs — file paths at the start of each lineMachine-readable formats (json, github, gitlab, checkstyle, sarif) are not affected.
For details on configuring the linter, formatter, and analyzer, see their respective reference pages:
config CommandThe mago config command is a utility to display the final, merged configuration that Mago is using for the current project.
This is invaluable for debugging your setup, as it shows you the result of combining your mago.toml file, any environment variables, and the built-in defaults.
Running the command without any options will print the entire configuration object as a pretty-printed JSON object.
mago config
You can inspect a specific part of the configuration using the --show flag.
# Show only the [linter] configuration
mago config --show linter
# Show only the [formatter] configuration
mago config --show formatter
You can also output the JSON schema for the configuration using the --schema flag. This is useful for generating documentation, IDE integration, or validation tooling.
# Output the JSON schema for the entire configuration
mago config --schema
# Output the JSON schema for a specific section
mago config --schema --show linter
:::tip
For global options that can be used with any command, see the Command-Line Interface overview. Remember to specify global options before the config command.
:::
Usage: mago config [OPTIONS]
| Flag, Alias(es) | Description |
|---|---|
--show <SECTION> |
Display only a specific section of the configuration. Values: source, parser, linter, formatter, analyzer |
--default |
Show the default configuration values instead of the current merged configuration. |
--schema |
Output JSON schema instead of configuration values. Useful for documentation and IDE integration. |
-h, --help |
Print help information. |
How can I help you explore Laravel packages today?