Skip to content

PHPMD version 3.x compatibility #273

@EduardoMorales-mrm

Description

@EduardoMorales-mrm

phpmd 3.x introduced a new CLI structure based on Symfony Console with an analyze subcommand. The current command generation in src/Tools/Analyzer/Phpmd.php produces a phpmd 2.x-style command that is incompatible with phpmd 3.x.

Current behavior

phpqa generates:

vendor/bin/phpmd "web/modules/custom","web/themes/custom" xml "/path/to/phpmd.xml.dist" --exclude /vendor/,/node_modules/,/tests/src/ --suffixes php,module,install,theme --reportfile "reports/codereview/phpmd.xml"

This fails with phpmd 3.x:

Command "web/modules/custom,web/themes/custom" is not defined.

phpmd 3.x interprets the first positional argument as a subcommand name.

Expected behavior

phpqa should generate a phpmd 3.x-compatible command:

vendor/bin/phpmd analyze
--exclude 'vendor/' --exclude 'node_modules/' --exclude 'tests/src/'
--suffixes php --suffixes module --suffixes install --suffixes theme
--ruleset "/path/to/phpmd.xml.dist"
--format xml
--reportfile-xml "reports/codereview/phpmd.xml"
-- "web/modules/custom" "web/themes/custom"

Breaking changes in phpmd 3.x CLI

Area phpmd 2.x phpmd 3.x
Subcommand none (positional source dirs first) analyze required as first arg
Source dirs first positional arg (comma-separated) trailing args after -- separator
Format second positional arg (xml/text) --format xml
Ruleset third positional arg --ruleset /path/to/ruleset.xml
Exclude --exclude /vendor/,/node_modules/ (comma-separated, slash-delimited) --exclude '*vendor/*' (one flag per pattern, glob-style with asterisks)
Suffixes --suffixes php,module,install (comma-separated, single flag) --suffixes php --suffixes module (one flag per extension)
Report file --reportfile file.xml --reportfile-xml file.xml

Proposed fix

Two files need updating:

src/IgnoredPaths.php — add a phpmd3() method that generates glob-style --exclude patterns:

public function phpmd3()
{
$parts = [];
foreach ($this->ignoreDirs as $dir) {
$dir = trim($dir, '/');
$parts[] = '--exclude ' . escapeshellarg('*' . $dir . '/*');
}
foreach ($this->ignoreFiles as $file) {
$file = ltrim($file, '/');
$parts[] = '--exclude ' . escapeshellarg('*' . $file);
}
return $parts ? ' ' . implode(' ', $parts) : '';
}

src/Tools/Analyzer/Phpmd.php — rewrite __invoke() to use the phpmd 3.x command structure. Version detection via $this->toolVersionIs('>=', '3.0.0') can be used to maintain backward compatibility with phpmd 2.x if needed:

public function __invoke()
{
$this->tool->errorsType = $this->config->value('phpmd.ignoreParsingErrors') === true;
$rulesets = $this->config->pathsOrValues('phpmd.standard');

$extensions = array_filter(array_map('trim', explode(',', $this->config->csv('phpqa.extensions'))));
$suffixArgs = ' ' . implode(' ', array_map(function ($ext) {
return '--suffixes ' . $ext;
}, $extensions));

$args = [];
$args[] = 'analyze';
$args[] = $this->options->ignore->phpmd3();
$args[] = $suffixArgs;
$args['ruleset'] = \Edge\QA\escapePath(implode(',', $rulesets));
$args['format'] = $this->options->isSavedToFiles ? 'xml' : 'text';
if ($this->options->isSavedToFiles) {
$args['reportfile-xml'] = $this->tool->getEscapedXmlFile();
}
$args[] = '-- ' . $this->options->getAnalyzedDirs(' ');
return $args;
}

Environment

  • phpmd/phpmd: 3.x
  • edgedesign/phpqa: dev-master
  • PHP: 8.4

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions