Skip to content

Compatibility with phpmd 3.x #1209

Closed
Closed

Description

@guvra

Hi,

phpmd 2.x is not compatible with php 8.5. The phpmd maintainers recommend to use the 3.x-dev branch instead.

Unfortunately, GrumPHP is not compatible with this branch because the phpmd command was overhauled.

I might be able to provide a PR that adds support for this version, but I'm wondering how to implement it:

  • Option 1: keep the current phpmd task untouched, add a new task for 3.x (what would be the name?)
  • Option 2: refactor the current phpmd task to support both phpmd 2.x and 3.x (how to consistently detect the version? maybe an config option?)

Activity

  1. guvra commented on Mar 25, 2026

    @guvra
    ContributorAuthor

    Example implementation with a separate task:

    <?php
    
    declare(strict_types=1);
    
    namespace GrumPHP\Task;
    
    use GrumPHP\Formatter\ProcessFormatterInterface;
    use GrumPHP\Runner\TaskResult;
    use GrumPHP\Runner\TaskResultInterface;
    use GrumPHP\Task\Config\ConfigOptionsResolver;
    use GrumPHP\Task\Context\ContextInterface;
    use GrumPHP\Task\Context\GitPreCommitContext;
    use GrumPHP\Task\Context\RunContext;
    use Symfony\Component\OptionsResolver\OptionsResolver;
    
    /**
     * @extends AbstractExternalTask<ProcessFormatterInterface>
     */
    class PhpMd3 extends AbstractExternalTask
    {
        public static function getConfigurableOptions(): ConfigOptionsResolver
        {
            $resolver = new OptionsResolver();
            $resolver->setDefaults([
                'whitelist_patterns' => [],
                'exclude' => [],
                'report_format' => 'text',
                'ruleset' => ['phpmd.xml.dist'],
                'triggered_by' => ['php'],
            ]);
    
            $resolver->addAllowedTypes('whitelist_patterns', ['array']);
            $resolver->addAllowedTypes('exclude', ['array']);
            $resolver->addAllowedTypes('report_format', ['string']);
            $resolver->addAllowedValues('report_format', ['text', 'ansi']);
            $resolver->addAllowedTypes('ruleset', ['array']);
            $resolver->addAllowedTypes('triggered_by', ['array']);
    
            return ConfigOptionsResolver::fromOptionsResolver($resolver);
        }
    
        /**
         * {@inheritdoc}
         */
        public function canRunInContext(ContextInterface $context): bool
        {
            return $context instanceof GitPreCommitContext || $context instanceof RunContext;
        }
    
        /**
         * {@inheritdoc}
         */
        public function run(ContextInterface $context): TaskResultInterface
        {
            $config = $this->getConfig()->getOptions();
    
            $whitelistPatterns = $config['whitelist_patterns'];
            $extensions = $config['triggered_by'];
    
            $files = $context->getFiles();
            if (\count($whitelistPatterns)) {
                $files = $files->paths($whitelistPatterns);
            }
            $files = $files->extensions($extensions);
    
            if (0 === \count($files)) {
                return TaskResult::createSkipped($this, $context);
            }
    
            $arguments = $this->processBuilder->createArgumentsForCommand('phpmd');
            $arguments->add('analyze');
            $arguments->addOptionalArgument('--format=%s', $config['report_format']);
    
            foreach ($config['ruleset'] as $ruleset) {
                $arguments->addOptionalArgument('--ruleset=%s', $ruleset);
            }
    
            foreach ($config['exclude'] as $exclude) {
                $arguments->addOptionalArgument('--exclude=%s', $exclude);
            }
    
            foreach ($extensions as $extension) {
                $arguments->addOptionalArgument('--suffixes=%s', $extension);
            }
    
            $arguments->addFiles($files);
    
            $process = $this->processBuilder->buildProcess($arguments);
            $process->run();
    
            if (!$process->isSuccessful()) {
                return TaskResult::createFailed($this, $context, $this->formatter->format($process));
            }
    
            return TaskResult::createPassed($this, $context);
        }
    }
  2. veewee commented on Mar 25, 2026

    @veewee
    Contributor

    Thanks for reporting.

    phpmd v3 is still under development?
    What's the plan of the project?

    I'm not sure what the best approach is. We once did a php-cs-fixer2 task which we eventually merged back to php-cs-fixer.
    But of course there will be a period in between where both need to be available.
    So maybe just adding a task phpmd3 would be fine for now? How do you see this?

  3. guvra commented on Mar 25, 2026

    @guvra
    ContributorAuthor

    phpmd v3 is still under development? What's the plan of the project?

    Apparently so, I discussed with the maintainers here: phpmd/phpmd#1280

    What's the plan of the project?

    From my understanding 3.x-dev will be released to 3.0.0 when it's ready.
    In the link provided above the maintainers said that the command-line is already stable enough in 3.x-dev.

    I'm not sure what the best approach is. We once did a php-cs-fixer2 task which we eventually merged back to php-cs-fixer.
    But of course there will be a period in between where both need to be available.
    So maybe just adding a task phpmd3 would be fine for now? How do you see this?

    It's an option, yes.
    e.g. merging phpmd3 into phpmd when 3.0.0 is officially released.

    But it would break all projects:

    • Projects that run phpmd3 would need to rename the task from phpmd3 to phpmd
    • Projects that run phpmd2 would need to upgrade to phpmd3
  4. guvra commented on Mar 31, 2026

    @guvra
    ContributorAuthor

    @veewee are you OK with a new task temporarily named phpmd3? If so I can finalize my PR #1210

  5. veewee commented on Mar 31, 2026

    @veewee
    Contributor

    I'm not opposed to it, but ad long as there is no stable release I can't really merge given it's a BC liability.
    So idd say just finish it and I'll merge once it's stable?

  6. guvra commented on Apr 2, 2026

    @guvra
    ContributorAuthor

    I'm not opposed to it, but ad long as there is no stable release I can't really merge given it's a BC liability. So idd say just finish it and I'll merge once it's stable?

    The 2.x branch does not work with recent php versions (8.4+ iirc) and the maintainers don't want to continue updating it.

    So for projects that use PHP >=8.4, the 3.x-dev branch already is the most stable version unfortunately (it is the only one that can be used).

  7. veewee commented on Apr 2, 2026

    @veewee
    Contributor

    I know, but that sounds more of a phpmd problem than it is a grumphp problem from my point of view ...

  8. novikor commented on May 18, 2026

    @novikor

    Fair enough that it's the phpmd problem, but there will be a collision anyway when 3.0.0 is released (e.g. will have to create phpmd2 and phpmd main will be for version 3, it won't work for everyone who didn't upgrade in time otherwise)

  9. veewee commented on May 19, 2026

    @veewee
    Contributor

    @novikor, you might misunderstand: I am good with the PR, but I will only merge it once v3 is released.

  10. arnedesmedt commented on Oct 5, 2026

    @arnedesmedt

    @veewee Looks like phpmd 3.0 is released 🎉

  11. veewee commented on Oct 16, 2026

    @veewee
    Contributor

    Fixed in #1210

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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