Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Phpmd Config Laravel Package

irstea/phpmd-config

A ready-made PHPMD ruleset for PHP projects, so you can drop in consistent mess-detection rules without hand-writing XML. Simplifies setup of PHP Mess Detector in CI or local tooling and keeps code-quality checks uniform across projects.

View on GitHub
Deep Wiki
Context7

Getting Started

Know what you are installing before you install it. irstea/phpmd-config is not a Laravel package. There is no service provider, no facade, no vendor:publish, no config array, no autoloaded PHP. The entire payload is a phpmd.xml ruleset for PHP Mess Detector. Day-to-day, that means two things: you will reference a file path in a CLI command, and you will edit XML when the defaults don't fit. Budget an afternoon, not a sprint.

Step 1 — get the ruleset into your project. The package is published on gitlab.irstea.fr, not Packagist, so composer require irstea/phpmd-config will fail unless you register a VCS repository and have SSH/token access to that host:

{
  "repositories": [
    { "type": "vcs", "url": "[email protected]:pole-is/tools/phpmd-config.git" }
  ],
  "require-dev": {
    "irstea/phpmd-config": "dev-main"
  }
}

Check for a composer.json in the upstream repo first. If there isn't one (likely for a pure-XML artifact), Composer cannot install it at all — clone the repo once and copy phpmd.xml into tools/phpmd/phpmd.xml in your own tree. For most teams this is the better path anyway; see Implementation Patterns.

Step 2 — install the actual analyser. The package does not depend on or ship PHPMD:

composer require --dev phpmd/phpmd

Pin it ("phpmd/phpmd": "^2.15") — the ruleset's <rule ref="..."> entries are resolved against the rule names of the installed PHPMD version.

Step 3 — first run. Point it at one directory, text output, no CI gate:

vendor/bin/phpmd app text tools/phpmd/phpmd.xml --exclude vendor,tests,storage,bootstrap/cache

If the ruleset came from Composer instead, the path is vendor/irstea/phpmd-config/phpmd.xml.

Step 4 — read the XML. It is the documentation. There is no README worth relying on. Open the file and inventory three things:

  1. Which rulesets are referenced (codesize, design, unusedcode, naming, cleancode, controversial).
  2. Which rules override <properties> with non-default thresholds (CyclomaticComplexity, NPathComplexity, TooManyMethods, CouplingBetweenObjects, ExcessiveParameterList).
  3. Whether any <exclude> patterns bake in someone else's directory layout.

First use case: run it against app/ only for a week, capture the violation count, and look at the top 50 findings by hand. That eyeball pass — not the score — tells you whether the thresholds are sane for your codebase. Only then wire it into CI.


Implementation Patterns

Pattern 1 — Vendor the XML (default recommendation)

Copy phpmd.xml to tools/phpmd/phpmd.xml, add a header comment crediting upstream and the commit SHA you took it from, and delete the VCS repository entry. You get reproducible builds, no SSH keys in CI, and no build-time dependency on a research institute's GitLab. Updates become a deliberate diff-and-merge, which is what you want for a quality gate anyway.

Pattern 2 — phpmd.xml.dist with a local override

Follow the PHPUnit convention:

tools/phpmd/phpmd.xml.dist   # committed, shared
tools/phpmd/phpmd.xml        # gitignored, per-developer experimentation

Resolve the path in a Composer script so nobody types it:

"scripts": {
  "analyse:pmd": "phpmd app text tools/phpmd/phpmd.xml --exclude vendor,tests,storage,bootstrap/cache,database/migrations",
  "analyse:pmd:ci": "@analyse:pmd --reportfile build/phpmd.txt --ignore-violations-on-exit"
}

The :ci variant with --ignore-violations-on-exit is how you roll out non-blocking for two to four weeks without red builds.

Pattern 3 — Tune thresholds, don't disable rules

The ruleset's value is its threshold choices; change them rather than deleting rules:

<rule ref="rulesets/codesize.xml/CyclomaticComplexity">
  <properties>
    <property name="reportLevel" value="12" />
    <property name="showClassesComplexity" value="true" />
    <property name="showMethodsComplexity" value="true" />
  </properties>
</rule>

Typical adjustments for a Laravel app: raise TooManyMethods (fat Eloquent models are normal), raise CouplingBetweenObjects (facades and container bindings inflate it), and lower or drop ExcessivePublicCount.

Pattern 4 — Prune overlapping rules to keep signal clean

If you already run Pint and PHPStan/Larastan, PHPMD's naming and cleancode rulesets mostly produce noise that duplicates them. A pragmatic trimmed ruleset keeps complexity and dead code:

<ruleset name="project-phpmd">
  <description>Derived from irstea/phpmd-config @ &lt;sha&gt;</description>

  <rule ref="rulesets/codesize.xml" />
  <rule ref="rulesets/unusedcode.xml" />

  <rule ref="rulesets/design.xml/ExitExpression" />
  <rule ref="rulesets/design.xml/EvalExpression" />
  <!-- deliberately omitted: design/WeightedMethodCount, naming/*, cleancode/* -->
</ruleset>

Rule of thumb: one gate per concern. Pint owns formatting, PHPStan owns types, PHPMD owns complexity and unused code. Overlapping gates get suppressed into oblivion within a quarter.

Pattern 5 — Staged enforcement by directory

Do not turn on a blocking gate for a legacy codebase in one commit. New code strict, old code exempt:

# blocking, in CI
vendor/bin/phpmd app/Http/Controllers,app/Services,app/Actions text tools/phpmd/phpmd.xml

# advisory, tracked as a burn-down metric
vendor/bin/phpmd app/Legacy text tools/phpmd/phpmd.xml --ignore-violations-on-exit

Shrink the advisory set as directories get cleaned. This is the only strategy that survives contact with a 10-year-old repo.

Pattern 6 — Inline suppression at the narrowest scope

When a finding is genuinely a false positive, suppress the exact rule at the method, not the class:

/**
 * @SuppressWarnings(PHPMD.CyclomaticComplexity)
 */
public function applyDiscount(Order $order): Money

@SuppressWarnings(PHPMD) (bare) suppresses everything for that scope and should fail code review. Track these annotations — a growing count in a file is itself a maintenance signal.

Pattern 7 — CI wiring

# GitHub Actions
- uses: shivammathur/setup-php@v2
  with: { php-version: '8.3', tools: composer }
- run: composer install --no-interaction --prefer-dist
- run: composer analyse:pmd:ci
- uses: actions/upload-artifact@v4
  with: { name: phpmd, path: build/phpmd.txt }

Use --format github (or gitlab / sarif on recent PHPMD) so violations render as inline PR annotations instead of a wall of text. That single change is the difference between developers reading the report and developers ignoring it.

For monorepos, shard by package in a matrix job; PHPMD is single-threaded, so parallelism comes from the CI matrix, not the tool.


Gotchas and Tips

No composer.json upstream means no composer require. Composer VCS repositories require a package manifest. If it's absent, you must clone and vendor. Verify before you write any install docs.

Private-host credentials leak into every build. If you do use the VCS route, every CI runner needs an SSH deploy key or token for gitlab.irstea.fr. That's an auth dependency on an external organisation's infrastructure sitting in your composer install path — a rotation or outage breaks all builds, including hotfixes. Vendoring removes this entirely.

Pin to a commit SHA, not dev-main. Zero stars, zero dependents, no tags: there is no release cadence and no downstream pressure to keep history stable. Record the SHA in the vendored file header.

You cannot cleanly "extend" a custom ruleset. PHPMD's <rule ref="..."> resolves names like rulesets/codesize.xml/CyclomaticComplexity from its bundled standards. There is no <include> that pulls in another custom XML file the way PHP_CodeSniffer standards compose. Fork-and-edit is the only sane extension story — which is another reason to vendor.

ref names must exist in your PHPMD version. Rules are added and removed between PHPMD minors. An unknown ref aborts the run with a non-zero exit and a config error, not a violation. If CI suddenly fails with "Cannot find rule", check PHPMD version drift before touching the XML. Add a scheduled job that runs the ruleset against your codebase before you bump PHPMD.

Exit codes: 0 clean, 1 violations found, 2 error. Do not treat 1 and 2 the same in CI — a broken config masquerading as "just violations" is how gates silently rot.

PHPMD has no baseline file. Unlike PHPStan, there is no --generate-baseline. Your options are --exclude path patterns, --minimumpriority (numeric; higher = fewer findings — start at 5 and lower it as you clean up), and inline @SuppressWarnings. Plan for this; teams routinely assume a baseline feature exists.

Parse failures follow the PHP version PHPMD runs under. Running PHPMD on PHP 8.1 against an 8.3 codebase will choke on readonly classes, enums, and DNF types. Match the analyser's PHP to your highest target version, and use PHPMD ≥ 2.15.

--exclude needs to be more aggressive than you think on Laravel. At minimum: vendor, node_modules, storage, bootstrap/cache, database/migrations, tests. Migrations trigger naming and codesize rules constantly and nobody fixes them.

Framework-agnostic means framework-blind. PHPMD will flag idiomatic Laravel as CouplingBetweenObjects (facades, container-resolved services) and TooManyMethods (Eloquent models). Do not "fix" the code — tune the rule. This is the single largest source of first-week rejection.

Naming rules clash with Pint/PSR-12. Both have opinions on method and constant naming. Running both means duplicate findings with different wording on the same line. Disable rulesets/naming.xml in PHPMD if Pint is already gating.

Iterate on one file. vendor/bin/phpmd app/Models/Order.php text tools/phpmd/phpmd.xml is the fastest debug loop; no need to re-analyse the whole tree to test a threshold change.

Cache if available. Recent PHPMD releases ship --cache / --cache-file; on a few thousand files it cuts CI time meaningfully. Check vendor/bin/phpmd --help for your pinned version rather than assuming.

Nobody owns config by default. A static file cannot page anyone, so it rots silently — until a PHP bump makes it error out or the thresholds are so stale that everything is suppressed. Put the vendored ruleset under a CODEOWNERS entry and review it quarterly.

License is unresolved. No license is declared in the package metadata. Vendoring an unlicensed file and shipping it in a company repo is a compliance question, not a hypothetical. Confirm redistribution rights with IRSTEA before you copy, or reimplement the thresholds as your own XML with a comment noting the inspiration rather than the source.

Don't add PHPMD and this ruleset in the same PR. Introducing a new analyser and someone else's opinionated thresholds at once makes it impossible to tell whether a failure is a tooling problem or a rule problem — and gives reviewers an easy reason to reject both.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings