blundergoat/gruff-php

Opinionated PHP code-quality analyzer with 120 rules, SARIF output, baselines, and a local dashboard.

Maintainers

Package info

github.com/blundergoat/gruff-php

Documentation

pkg:composer/blundergoat/gruff-php

Statistics

Installs: 3

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.0 2026-05-23 05:05 UTC

This package is auto-updated.

Last update: 2026-05-23 21:26:12 UTC


README

Latest Version on Packagist Total Downloads PHP Version Require CI License

gruff-php is an opinionated PHP code-quality analyzer. It scans PHP projects, scores findings across rule pillars, and emits reports for terminals, CI, GitHub annotations, SARIF consumers, static HTML, and a local dashboard.

It is heuristic tooling. Use it alongside PHPStan, PHPUnit, PHP-CS-Fixer, Psalm, PHPCS, or other project-specific gates; do not treat it as a replacement for type checking or tests.

Release Status

This repository is prepared for the public 0.1.0 tag. Development checkouts currently report 0.1.0-dev; scripts/bump-version.sh 0.1.0 updates the CLI version and stamps the changelog before tagging.

Current package facts:

  • Package name: blundergoat/gruff-php
  • Repository: https://github.com/blundergoat/gruff-php
  • Binary: bin/gruff-php
  • PHP requirement: ^8.3
  • Runtime dependencies: nikic/php-parser, Symfony Console/Finder/Process/Yaml
  • Rule catalogue: 120 registry rules across 11 pillars
  • Config format: YAML only (.yaml / .yml)
  • License: MIT (see LICENSE)

Installation

From a checkout:

git clone https://github.com/blundergoat/gruff-php.git
cd gruff-php
composer install
php bin/gruff-php --help

After Packagist publication:

composer require --dev blundergoat/gruff-php
vendor/bin/gruff-php --help

Quick Start

# Analyze the current project
php bin/gruff-php analyse

# Analyze selected paths
php bin/gruff-php analyse src tests

# Keep the process green while inspecting findings
php bin/gruff-php analyse --fail-on none

# JSON for automation
php bin/gruff-php analyse --format json --fail-on none > gruff-report.json

# Markdown for PR comments
php bin/gruff-php analyse --format markdown --fail-on none > gruff-report.md

# SARIF for code-scanning ingestion
php bin/gruff-php analyse --format sarif --fail-on none > gruff.sarif

# Static HTML report
php bin/gruff-php report --format html --output gruff-report.html

# Local dashboard
php bin/gruff-php dashboard

The default fail threshold is error, so warning and advisory findings do not fail the command unless you opt in:

php bin/gruff-php analyse --fail-on warning
php bin/gruff-php analyse --fail-on advisory

Commands

Command Purpose
analyse Run the analyzer and print findings in text, json, html, markdown, github, hotspot, or sarif format.
summary Print a compact score and top-offender digest without the full finding list.
report Render an HTML or JSON report to stdout or --output.
dashboard Serve a local dashboard on 127.0.0.1:8765 by default.
list-rules Print rule metadata as a table or JSON.

Symfony Console also provides list, help, and shell completion support:

php bin/gruff-php list
php bin/gruff-php analyse --help
php bin/gruff-php list-rules --format json

Output Formats

analyse --format accepts:

Format Intended use
text Human CLI output.
json Machine-readable gruff.analysis.v1 reports.
html Self-contained report output.
markdown PR comment or issue summary text.
github GitHub Actions workflow annotations.
hotspot JSON hotspot map keyed around file scores.
sarif SARIF 2.1.0 for code-scanning ingestion.

report --format currently accepts html and json.

Exit Codes

Code Meaning
0 The run completed and no finding met the --fail-on threshold.
1 At least one finding met the --fail-on threshold.
2 A run diagnostic occurred, such as config failure, missing path, parse error, baseline error, history-file error, diff failure, or mutation-tool failure.

Rule Pillars

list-rules --format json is the source of truth for rule metadata. The v0.1 catalogue currently contains 120 registry rules:

Pillar Rules
size 7
complexity 5
maintainability 2
dead-code 9
naming 12
documentation 14
modernisation 10
security 18
sensitive-data 9
test-quality 33
design 1

Representative rule IDs:

size.method-length
complexity.cyclomatic
waste.unused-import
naming.identifier-quality
docs.missing-param-tag
modernisation.readonly-property-candidate
security.unsafe-unserialize
security.path-traversal-file-access
sensitive-data.high-entropy-string
test-quality.no-assertions
design.single-implementor-interface

Some dead-code pillar rules keep the waste.* rule-id prefix for historical continuity. Use the pillar field from list-rules --format json when filtering by pillar.

Use this command to inspect the full catalogue:

php bin/gruff-php list-rules
php bin/gruff-php list-rules --format json

Configuration

Place .gruff-php.yaml in the project root. analyse, report, and dashboard auto-load it unless --no-config is supplied. Legacy .gruff.yaml files are still auto-loaded when .gruff-php.yaml is absent. You can also pass --config=path/to/file.yaml.

minimumPhpVersion: 8.3

paths:
  ignore:
    - tests/Fixtures/**
    - generated

selection:
  tiers: [v0.1]
  pillars: [security, sensitive-data]
  rules: []
  excludePillars: []
  excludeRules: []

allowlists:
  acceptedAbbreviations: [id, db]
  secretPreviews: []

rules:
  size.method-length:
    threshold: 80
    severity: error
  complexity.cyclomatic:
    enabled: false
  size.parameter-count:
    threshold: 10
    severity: error
    options:
      constructorMaxParameters: 0
      promotedConstructorMaxParameters: 25
  test-quality.magic-number-assertion:
    options:
      allowedLiterals: [200, 201, 404, 500]

Supported top-level keys:

Key Purpose
minimumPhpVersion Minimum PHP version used by version-gated modernisation rules.
paths.ignore Project-relative paths or glob patterns to skip.
selection Include or exclude rules by tier, pillar, or rule id.
allowlists.acceptedAbbreviations Naming tokens accepted by naming rules.
allowlists.secretPreviews Redacted sensitive-data previews to suppress after review.
rules.<id> Per-rule enabled, threshold, severity, thresholds, or options.

Unknown keys are rejected. Threshold and option names must match the rule's definition. Use php bin/gruff-php list-rules --format json to inspect the available defaults and option names. For size.parameter-count, constructorMaxParameters: 0 means constructors inherit the main threshold; set it above zero only when non-exempt constructors should use a separate cap. Promoted final readonly value-object constructors are bounded separately by promotedConstructorMaxParameters.

Baselines

Baselines suppress known findings by fingerprint without disabling rules.

# Write gruff-baseline.json in the project root
php bin/gruff-php analyse --generate-baseline

# Auto-apply gruff-baseline.json when it exists
php bin/gruff-php analyse

# Use an explicit baseline file
php bin/gruff-php analyse --baseline=baselines/release.json

# Skip auto-baseline for one run
php bin/gruff-php analyse --no-baseline

Baseline files use schema gruff.baseline.v1. Full-project scans report stale baseline entries when a stored finding no longer exists.

Diff And Branch Review

Filter findings to changed files or changed lines:

php bin/gruff-php analyse --diff
php bin/gruff-php analyse --diff=staged
php bin/gruff-php analyse --diff=unstaged
php bin/gruff-php analyse --diff=origin/main

Compare current findings against a base ref:

php bin/gruff-php analyse --diff-vs=origin/main
php bin/gruff-php analyse --diff-vs=origin/main --changed-only

See docs/gruff-cli-branch-review.md for agent-oriented branch review usage.

Display Filters

Display filters reduce report noise without changing what rules run or what fails the command:

php bin/gruff-php analyse --min-severity warning
php bin/gruff-php analyse --include-pillar security --include-pillar sensitive-data
php bin/gruff-php analyse --exclude-rule test-quality.mystery-guest
php bin/gruff-php analyse --paths-relative-to "$PWD"

Mutation Analysis

Mutation analysis is optional. gruff-php can ingest an Infection JSON report or run Infection before ingesting the report path you provide.

php bin/gruff-php analyse --infection-report=infection-report.json

php bin/gruff-php analyse \
  --infection-run \
  --infection-report=infection-report.json \
  --infection-bin=infection \
  --infection-config=infection.json5

php bin/gruff-php analyse \
  --infection-report=infection-report.json \
  --mutation-baseline=baseline-infection.json \
  --mutation-budget=3

The dashboard does not run mutation analysis.

Dashboard

php bin/gruff-php dashboard
php bin/gruff-php dashboard --host=0.0.0.0 --port=9000
php bin/gruff-php dashboard --project=/path/to/project
php bin/gruff-php dashboard --diff
php bin/gruff-php dashboard --scan-timeout=300

The dashboard serves a local control page and refresh endpoint. It is intended for local development, not as a public network service.

Development

composer install
composer check
composer test
bash scripts/preflight-checks.sh

Useful scripts:

Command Purpose
composer check Composer validation, shell syntax checks, PHP syntax checks, PHPStan.
composer test PHPUnit test suite.
composer perf Wall, peak-memory, per-rule timing vs. a local baseline. See below.
composer format Apply PHP-CS-Fixer formatting.
composer format:check Check PHP-CS-Fixer formatting.
scripts/mutation-test-diff.sh Diff-scoped Infection workflow.
scripts/mutation-test-full.sh Full Infection workflow.
scripts/start-dev.sh Start the local dashboard.
scripts/bump-version.sh Bump Application::VERSION and stamp the matching CHANGELOG.md entry.

Performance harness

composer perf runs php bin/gruff-php analyse against three corpora (src/Diff, src/, full self-scan), captures wall time and peak memory, and compares each result to .goat-flow/logs/perf/m50-baseline/baseline.json. Use composer perf -- --baseline --yes to overwrite the baseline after intentional rule or threshold changes. Baselines are machine- and PHP-version-specific - regenerate locally rather than committing a CI-host baseline. Tolerances default to wall +20%, peak +25%; override with GRUFF_PERF_WALL_TOLERANCE / GRUFF_PERF_MEM_TOLERANCE. Use --quick for a single-corpus, no-warmup run.

CI runs on PHP 8.3 and 8.4 via .github/workflows/ci.yml.

Public Release Checklist

Before tagging 0.1.0:

  • Confirm CHANGELOG.md has ## 0.1.0 - Unreleased; the release script will stamp the date.
  • Run scripts/bump-version.sh 0.1.0 to update src/Console/Application.php and stamp the CHANGELOG.md entry.
  • Confirm Packagist metadata, repository URL, issue tracker, and GitHub private vulnerability reporting for https://github.com/blundergoat/gruff-php.
  • Run composer validate --strict, composer check, composer test, and composer format:check.
  • Run php bin/gruff-php list-rules --format json and confirm the rule count and public IDs match this README and CHANGELOG.md.
  • Run php bin/gruff-php analyse and confirm the default self-scan exits 0.
  • Review CHANGELOG.md.
  • Review SECURITY.md and confirm the private reporting path is enabled for the public repository.
  • Confirm generated artifacts such as history.json, infection-report.json, and local caches are not part of the release archive.

More Documentation

Author

Built by Matthew Hansen.

License

MIT