The PHP Mess Detector just shipped PHPMD 3.0.0, its first release since 2.15.0 in December 2023. It raises the minimum PHP version from 5.3.9 to 8.1 and replaces the command line interface. It also adds a new way to configure and suppress rules.
- A
#[SuppressWarnings]attribute replaces the docblock annotation - A new
UnusedSuppressionrule finds suppressions that no longer do anything - Configuration in YAML, JSON, or PHP, with
phpmd initandphpmd migratecommands - A
--threadsoption for parsing files in parallel - Two new rules,
IfStatementWithoutLogicandLongMethodName - A renderer for GitHub Check Runs
What's New
Suppressing Warnings With an Attribute
PHPMD 2 used a @SuppressWarnings docblock annotation. PHPMD 3 adds a PHP attribute that takes the rule's class name:
use PHPMD\Attribute\SuppressWarnings; use PHPMD\Rule\UnusedLocalVariable; #[SuppressWarnings(UnusedLocalVariable::class)] public function example() { $unused = 42; }
Because the rule is a class reference, your editor can autocomplete it and catch a typo. The docblock annotation still works, but it's now deprecated.
The new UnusedSuppression rule reports #[SuppressWarnings] attributes that no longer suppress a warning. That catches the suppression someone added years ago for code that has since changed. It only checks attributes, not docblock annotations. The upgrade guide suggests adding existing reports to a baseline with --generate-baseline if you want to adopt it gradually.
Issue: #1230
New Configuration Formats and a Setup Wizard
Rule sets can now be written in YAML, JSON, or PHP as well as XML, and YAML is the recommended format. PHPMD looks for a file like phpmd.yml, phpmd.json, or phpmd.xml in the current directory, so you don't have to pass a rule set on every run.
The config file can also hold settings that used to be CLI options, including paths, the output format, cache settings, the baseline file, and the thread count.
Two new commands help you get there:
phpmd initbuilds aphpmd.ymlthrough an interactive wizardphpmd migrateconverts a PHPMD 2 config to YAML, renaming the rule classes and threshold properties that changed
phpmd migrate keeps the original file and lists every change it made. Add --dry-run to print the result without writing it.
Parallel Parsing and a Progress Bar
The new --threads option parses files in parallel, which helps on large codebases. PHPMD also shows a progress bar on stderr during analysis. Use --no-progress to hide it in CI.
New Rules and Options
IfStatementWithoutLogic flags conditions that contain only literals, like if (true). LongMethodName flags method and function names over a set length.
A few existing rules got smarter too. UnusedFormalParameter now skips methods marked with #[Override], since those parameters come from the parent signature. StaticAccess ignores built-in enum methods like from() and cases(). UnusedPrivateField, UnusedFormalParameter, and ExcessiveParameterList accept an exceptions list to skip named methods.
GitHub Check Runs Output
A new renderer outputs results in the GitHub Check Runs format, so violations can show up as annotations on a pull request.
PR: #965
Upgrade Notes
PHPMD 3 has breaking changes, and the CLI change will affect most CI scripts. The upgrade guide covers each one.
PHP 8.1 or newer is required.
The command signature changed. PHPMD now uses Symfony Console, and the format and rule set are options instead of positional arguments:
# PHPMD 2 phpmd src/ text codesize,unusedcode # PHPMD 3 phpmd analyze --ruleset codesize --ruleset unusedcode src/
Several options were renamed. --ignore is now --exclude, --extensions is now --suffixes, and --reportfile is split into --reportfile-text, --reportfile-xml, and so on.
Thresholds now mean "highest allowed value." Size and complexity rules use a single maximum property, and they only report when a value goes over it. In PHPMD 2, many of them also reported when the value equaled the threshold. With the same config, you may see fewer warnings after upgrading. phpmd migrate --preserve-behavior lowers those thresholds by one to keep the old results. The old property names still work as aliases.
A new exit code. Files that fail to process now exit with code 3 instead of 1. Check any CI step that looks for specific exit codes.
Baseline updates report new violations. --update-baseline used to drop violations that weren't in the baseline. It now reports them and exits with code 2.
Custom rule authors should also check the guide. Several rule classes were renamed, and suppressions are now applied after rules run, so custom rules no longer need to check for them.
Install
composer require --dev phpmd/phpmd:^3.0