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

Php Coverage Checker Laravel Package

michaelmoussa/php-coverage-checker

A PHP tool that reads Clover-format code coverage XML and fails the build when coverage drops below a configured threshold. Drop it into CI to enforce minimum coverage on every run.

Deep Wiki
Context7

Getting Started

This package is a single-purpose CLI gate: it reads a Clover-style XML coverage report and exits non-zero if line coverage falls below a threshold. It does not generate coverage — that is still PHPUnit/Pest + Xdebug or PCOV's job.

Minimal path from zero:

composer require --dev michaelmoussa/php-coverage-checker

Then produce a report and check it, in that order:

# 1. Generate Clover XML (PHPUnit)
XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-clover build/logs/clover.xml

# 2. Enforce a floor
vendor/bin/php-coverage-checker build/logs/clover.xml 80

That is genuinely the whole API surface: <clover.xml> <minimum-percentage>. Exit code 0 = at or above threshold, non-zero = below. Run vendor/bin/php-coverage-checker --help on the version you install before scripting against optional flags — the last release is from 2015, so argument/option details vary between tags.

Where to look first:

  1. vendor/michaelmoussa/php-coverage-checker/README.md — the examples are the de-facto spec.
  2. src/ — it is a handful of small classes; if you need to change behaviour (different metric, rounding), you will be reading these, not configuring them.
  3. Your phpunit.xml — confirm <coverage> / the CLI flag actually writes Clover, and that the path matches what you pass the checker.

First real use case: a single CI job that fails the build on a coverage regression, with the threshold committed in the repo rather than typed ad hoc.

Implementation Patterns

Composer script as the single entry point. Keep the threshold in composer.json so local and CI runs are identical:

{
  "scripts": {
    "test:coverage": [
      "XDEBUG_MODE=coverage phpunit --coverage-clover build/logs/clover.xml",
      "php-coverage-checker build/logs/clover.xml 80"
    ]
  }
}

Composer aborts the script chain on the first non-zero exit, so you get fail-fast for free.

CI gate (GitHub Actions shape):

- run: composer install --prefer-dist --no-progress
- run: composer test:coverage

Useful variants: pin the exact tool version (composer require --dev michaelmoussa/php-coverage-checker:1.0.3) so a CI image refresh cannot silently change the metric; upload clover.xml as an artifact on failure so you can diff it locally.

Ratchet pattern. The tool only knows a number, so encode progress in the pipeline:

  • Baseline job runs against the target branch, reads its threshold from a file, and the PR job requires max(baseline, absolute_floor).
  • Simpler and usually enough: a Makefile variable COVERAGE_MIN ?= 80 that you bump manually each release, checked into the repo and reviewed like code.

Multiple gates from multiple reports. The checker evaluates one file at a time, which is actually convenient: run PHPUnit per test suite (--testsuite=unit, --testsuite=integration), emit separate Clovers, and call the checker once per suite with a per-suite floor (e.g. 90 for pure unit tests, 50 for integration). This is the main reason to prefer it over a single aggregate number.

Structured output for dashboards. The command can emit a machine-readable summary of per-file coverage in addition to the pass/fail exit code (verify the option name against --help on your tag). If present, use it to post a PR comment or push a series to your metrics store rather than scraping stdout, which is human-formatted and unstable across versions.

Wrap it, don't fork it. For anything beyond pass/fail, write a thin bin/coverage-gate that calls the checker, captures the exit code, and prints a message with the actual percentage and the required one. Keeps the package as a dumb comparator and puts your policy (branch exemptions, [skip coverage] labels in the PR body, docs-only change detection) in your own code.

Gotchas and Tips

  • Archived and ancient. Last release 2015-09-30 means it depends on Symfony Console 2.x-era code and PHPUnit-era assumptions. On PHP 8.x, composer install may fail on platform requirements or emit deprecations; --ignore-platform-reqs is a workaround, not a fix. For a new project, prefer a maintained equivalent (rregeer/phpunit-coverage-check is the common spiritual successor) or a two-line script that reads metrics/@line-rate from the Clover XML and compares it yourself — the tool is small enough that reimplementing it is often cheaper than keeping it alive.
  • Clover only. Not PHPUnit XML, not Cobertura, not LCOV, not the --coverage-text stdout. If your toolchain (Pest, ParaTest, infection) writes a different format, you must still get PHPUnit to write Clover somewhere.
  • Metric mismatch with the HTML report. The pass/fail number comes from the Clover metrics attributes, typically line coverage. Your browser HTML report surfaces classes/methods/branches alongside it. Expect the two numbers to differ and do not file a bug about it; decide which one you actually want to gate on.
  • Empty / partial reports. A report with zero coverable lines, or one truncated because the test run crashed, can produce 0% or a parse error. Treat a missing or unparsable clover.xml as a hard failure in CI, not as a pass. Always generate the report before the check in the same script step and clean build/logs first so a stale file from a previous run can't produce a false green.
  • Coverage mode is not on by default. With Xdebug 3+, without XDEBUG_MODE=coverage you get a valid-looking report where everything is 0% — or no report at all. PCOV users need pcov.enabled=1. This is the single most common cause of "the gate is failing and I don't know why."
  • Round-trip paths. The argument is resolved relative to the current working directory. In CI, working-directory:, Docker WORKDIR, and Composer script cwd can all differ. Use an absolute path (${{ github.workspace }}/build/logs/clover.xml) in CI if you see "file not found" only in pipelines.
  • Boundary and precision semantics. Whether exactly-threshold passes, and how the percentage is rounded, is baked into the 2015 code with no config knob. Don't build a ratchet that depends on 80.0 vs 79.99 unless you've verified the comparison on your pinned version.
  • Exit code discipline. Under set -e in a bash script, the non-zero exit kills your step — intended, but it also means any post-processing (uploading artifacts, PR comments) must be wired to run before the check or with if: always().
  • Extension point is the source, not config. There is no plugin system or significant config file. If you need branch-coverage gating or per-directory thresholds, that's a fork (install from a VCS/local path repository) or your own wrapper. Given 24 stars and an archived repo, plan on owning the fork if you keep it.
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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle