# Configuration

The package has two config files to extend from `mago.toml`, see [Install](<https://project.pages.drupalcode.org/mago/setup/install/index.md>):

- `drupal.mago.toml` makes Mago match Drupal's standard, for a site, a module or a theme.
- `drupal-core.mago.toml` adds what core's `phpcs.xml.dist` checks, for a core checkout.

Each setting in the files has a comment that says why it's there.

## What drupal.mago.toml sets

- Drupal's PHP file extensions, and `yml` for the rules for [info and routing files](<https://project.pages.drupalcode.org/mago/rules/info-and-routing-files/index.md>). Other YAML files aren't read, and the formatter and the analyzer stay out of the info and routing files.
- The worker, with Xdebug and pcov off, see [Xdebug and pcov](<https://project.pages.drupalcode.org/mago/setup/worker/index.md#xdebug-and-pcov>).
- Mago's `drupal` formatter preset, and its `drupal` linter integration, which accepts `TRUE`, `FALSE` and `NULL`.
- The `Interface` and `Trait` suffixes, and the `file-name` rule, as Coder 9 checks them.
- `minimum-fail-level = "warning"` for the linter, so that a warning fails the run as it does in phpcs, see [What fails a run](<#what-fails-a-run>).
- Coder's level for the Mago rules that stand in for a sniff, such as `class-name` and `array-style`. Mago reports them as help or as a note, which doesn't fail a run.
- `check-name-casing` for the analyzer, which ports `Squiz.PHP.LowercasePHPFunctions`. The analyzer stays out of `*.api.php` files and test fixtures, as phpstan-drupal does.
- Mago's rules that contradict the standard or repeat a rule of this extension, turned off, such as `tagged-todo` and `function-name`. The same goes for advice that the standard doesn't ask for, such as `no-else-clause` and `strict-types`. Turning them off doesn't lose any check done by phpcs.
- Excludes for the paths where a Mago rule reports what Drupal does on purpose, such as the fake credentials in tests. The linter also skips `.ht.router.php`, as phpcs skips a file whose name starts with a dot.

It doesn't set `php-version`, since that's up to the project. Without it, Mago assumes its newest PHP version, and the formatter writes syntax that older versions reject, such as `new Foo()->bar()`.

## Changing a setting

The project's `mago.toml` wins over the file that it extends. A table is merged into the one in the file, so one line changes one rule. An array is added to the one in the file. The worker command is replaced as a whole.

```
extends = "vendor/drupal/mago/drupal.mago.toml"
php-version = "8.3"

[source]
# Coder also checks .test files, a Drupal 7 format.
extensions = ["test"]

[linter.rules]
strict-types = { enabled = true }

[analyzer]
# Let @phpstan-ignore comments drop the Mago issues that report the same finding.
plugins = ["phpstan-ignores"]
```

See [PHPStan ignores](<https://project.pages.drupalcode.org/mago/analyzer/phpstan-ignores/index.md>) for the plugin.

## Turning rules off

Mago takes this extension's rule codes under `[linter.rules]`, with the same `enabled`, `level` and `exclude` settings as its own rules. Quote the code, because it holds a `/`:

```
[linter.rules]
"drupal/inline-comment-punctuation" = { enabled = false }
"drupal/missing-function-comment" = { exclude = ["tests/"] }
"drupal/else-if" = { level = "warning" }
```

`enabled = false` turns a rule off. `exclude` leaves the rule out of the matching paths, which take the same globs and directory prefixes as the excludes of Mago's own rules. `level` sets the level of every issue that the rule reports.

A rule that is turned off still runs when `mago lint --only` names it, and `mago lint --pedantic` doesn't read these settings. Mago warns about a code that doesn't name a rule, and the run goes on.

A project that turns phpcs sub-codes off in its `phpcs.xml` can turn off the matching rules this way. [Coming from Coder](<https://project.pages.drupalcode.org/mago/coder/index.md>) says which rule ports each sub-code.

`[linter.rules]` takes only linter rules. To turn off an [analyzer check](<https://project.pages.drupalcode.org/mago/analyzer/index.md#checks>), use Mago's own `[analyzer] ignore`, for the whole project or for some paths:

```
[analyzer]
ignore = [
    "drupal/global-drupal-call",
    { code = "drupal/entity-storage-injection", in = "src/Legacy/" },
]
```

An entry with a `pattern` drops the issues whose text matches a regular expression. The text is the message, the notes, the help and the annotations of the issue. `code` and `in` narrow the entry further:

```
[analyzer]
ignore = [
    { code = "unsafe-instantiation", pattern = "new static\\(\\)" },
]
```

## What fails a run

phpcs exits with an error when it reports a warning or an error. `mago lint` on its own fails only on an error, and many of its rules report a warning, help or a note. `drupal.mago.toml` sets `minimum-fail-level = "warning"`, and gives the Mago rules that stand in for a Coder sniff the level used by Coder. So a report that fails phpcs fails `mago lint` too.

The Mago rules that are on and don't port any sniff, such as `strict-behavior` and `str-contains`, fail the run as well when they report a warning. Turn a rule off, or set its level, in the project's `mago.toml`:

```
[linter]
# Fail on errors only.
minimum-fail-level = "error"

[linter.rules]
strict-behavior = { level = "help" }
```

`mago analyze` has its own `minimum-fail-level` under `[analyzer]`, which isn't set by `drupal.mago.toml`.

## Checking Drupal core

`drupal-core.mago.toml` is for a core checkout. It extends `drupal.mago.toml` and adds:

- The files that core's `phpcs.xml.dist` checks: `core/` and `composer/`, with `.test` files, and `vendor/` read for its symbols.
- The files that phpcs doesn't check, left out of the formatter and the linter: the paths excluded by `phpcs.xml.dist`, and the files marked with `// phpcs:ignoreFile` or `// @codingStandardsIgnoreFile`, which Mago's own rules don't read.
- The classes that core's `phpstan.neon.dist` leaves out because they're broken on purpose, left out of the analyzer. The one that Mago reports a `semantics` error for is left out of the linter too, since that error doesn't have a rule to turn off.
- The worker with `--phpcs-ignores` and `--phpstan-ignores`, so that core's phpcs and PHPStan comments drop the issues of the extension's rules, see [phpcs and PHPStan comments](<https://project.pages.drupalcode.org/mago/setup/worker/index.md#phpcs-and-phpstan-comments>).
- The rules and the analyzer checks that core doesn't run, turned off.

### Which rules are off

Core's own `phpcs.xml.dist` asks for less than Drupal's standard. `drupal-core.mago.toml` follows the file of Drupal 11.x, which runs Coder 9, and turns off the rules whose checks core turns off or doesn't run, Mago's own rules included. That covers Mago's general advice, such as `strict-behavior` and `inline-variable-return`, which doesn't port any sniff that core runs. Where `phpcs.xml.dist` leaves paths out of a sniff, such as the tests directories out of the line length check, the file leaves them out of the rules that port it. It also ignores five analyzer checks that are about modules that use core's API. The comments in the file say why.

A rule that stays on can check more than the sniff that it ports, such as `drupal/hook-comment` on a `#[Hook]` method and `drupal/doc-see-tag` in a class docblock. Those reports fail the run on core, where phpcs passes.

A rule that the file turns off can be turned back on in the checkout's `mago.toml`:

```
extends = "vendor/drupal/mago/drupal-core.mago.toml"
php-version = "8.3"

[linter.rules]
"drupal/inline-comment-punctuation" = { enabled = true }
```

An analyzer check stays ignored, since Mago adds the `ignore` list of `mago.toml` to the one of the file that it extends.

Two rules follow core's directories by themselves, since a rule's `exclude` setting can't name the only directories to check. In the files of Drupal core itself, `missing-summary` and `missing-function-comment` run only in the directories where core's `phpcs.xml.dist` runs them. A file is core's when one of its directories is named `core` and holds `lib/Drupal.php`. [`drupal/unknown-service`](<https://project.pages.drupalcode.org/mago/analyzer/services/index.md#drupalunknown-service>) checks every service id in core's files, since core looks up only its own services.

A project that starts from core's `phpcs.xml.dist` but isn't core can copy the `[linter.rules]` entries of `drupal-core.mago.toml`, see [Turning rules off](<#turning-rules-off>).
