Changelog¶
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased¶
0.15.0 - 2026-04-04¶
Changed¶
- Strict configuration validation: unknown section sub-keys (
cache.typo), invalid value types (cache.enabled: "yes"), and unknown rule names (rules.complexty) now produce clear errors with "Did you mean?" suggestions - Warnings (e.g., unknown rule option keys) are now visible at default verbosity via stderr, without requiring
-v
Fixed¶
- Configuration warnings were invisible without
-vflag due toNullLoggerat default verbosity
0.14.0 - 2026-04-03¶
Changed¶
--exclude-namespaceCLI option for violation suppression by namespace (prefix or glob), merged withexclude_namespacesfromqmx.yaml
Fixed¶
- Computed metric names with underscores (e.g.,
computed.my_score) were incorrectly normalized to camelCase in YAML config
0.13.0 - 2026-04-03¶
Changed¶
--show-suppressednow lists each suppressed violation with file, line, message, and rule name (was count-only)exclude_pathsandexclude_namespacesnow support both prefix matching (src/Entity) and glob patterns (src/Metrics/*Visitor.php); simple directory/namespace names work without trailing/*--exclude-healthwith invalid dimension name now produces an error instead of silently ignoring
Fixed¶
- "No PHP files found" message shown when all files had parse errors — now shows "All N file(s) were skipped due to parse errors"
0.12.0 - 2026-04-03¶
Changed¶
- LCOM4 rule:
exclude_methodsoption to exclude specific methods from the cohesion graph (reduces false positives from interface-mandated methods likegetName,getDescription) - Partial scope warning when analysis paths don't cover all composer.json autoload entries
coupling.instability:min_afferentoption replacesskip_leaf— configurable minimum afferent coupling (Ca) threshold for skipping symbols (default: 1, skip Ca=0)code-smell.boolean-argument: parameters with common boolean prefixes (is*,has*,can*,should*,will*,did*,was*) are now allowed by default (configurable viaallowed_prefixes: [])code-smell.error-suppression:allowed_functionsoption to whitelist functions where@usage is acceptable (e.g.,fopen,unlink)- Per-rule
exclude_pathsoption for targeted violation suppression by file path patterns @qmx-ignoretags now work in regular comments (//,/* */), not just PHPDoc docblocks- JSON format (
--format=json) now outputs all violations by default (was limited to 50); use--format-opt=violations=50to restore the old behavior - Global
exclude_namespacesconfig option for suppressing violations by namespace prefix (likeexclude_pathsbut for namespaces) - Computed metric formulas referencing non-existent metrics now produce a clear error instead of silently failing
- Warnings (partial scope, unknown rules, missing composer.json) now go to stderr to avoid corrupting machine-readable output
- Exit codes: config/input errors now return exit code 3 (was 1, overlapping with "warnings found"). Scheme: 0=clean, 1=warnings, 2=errors, 3=config error
Fixed¶
graph:exportcommand crash due to-dshortcut conflict with global--working-dir
Removed¶
--analyzeoption — was misleading (analyzed all files regardless, only filtered violations like--report). Use--reportinsteadanalyzecommand alias — usecheckinsteadbaseline.json— replaced with properqmx.yamlconfiguration using new features
0.11.2 - 2026-04-02¶
Changed¶
- Project
qmx.yamlfor self-analysis with tuned coupling thresholds andexclude_namespacesfor Core value objects qmx.yaml.example— comprehensive annotated example with documentation links, default values, and all available options (replacesqmx.yaml.dist)parallelsection in config file for setting worker count (was CLI-only via--workers)
Fixed¶
couplingsection in config file was rejected as unknown key
0.11.1 - 2026-04-01¶
Changed¶
--memory-limitoption andmemory_limitconfig key to control PHP memory limit (e.g.,--memory-limit=1G)- Removed hidden 512M memory limit override — PHP's
memory_limitfrom php.ini is now respected by default
0.11.0 - 2026-04-01¶
Changed¶
- Cognitive Complexity violations include breakdown of top contributors:
Top: nested if +5 L12, foreach +4 L15, &&/|| +1 L22 - NPath Complexity violations include multiplicative chain:
Chain: ×6 if/else L25, ×4 match L31, ×3 switch L20
0.10.0 - 2026-03-29¶
Breaking¶
- Rule IDs
code-smell.god-classandcode-smell.data-classrenamed todesign.god-classanddesign.data-class --format=healthnow produces a text table (was HTML). Use--format=htmlfor the interactive HTML report
Changed¶
@qmx-thresholdannotations for per-class/method threshold overrides in source code- Framework CBO distinction:
cbo_appandce_frameworkmetrics separate application from framework coupling - Full dependency graph in
--analyze=git:*modes — coupling metrics now correct in partial analysis --group-by=class|namespacefor JSON output- Worst contributors per health dimension in
--format=health, configurable via--format-opt=contributors=N - Violation density metric (
violationDensity: violations per 100 LOC) in worst offenders - NPath violations include severity categories (low/moderate/high/very high/extreme)
- VO constructor exemption for
long-parameter-list— relaxed thresholds (vo-warning,vo-error) - LCOM4: stateless methods grouped together, reducing false positives on utility classes
- Duplication violations include content preview hint
- Martin Diagram view in HTML report with parent-namespace instability/abstractness/distance
- NamespaceTree: canonical namespace hierarchy replaces flat aggregator
- Warn when
@qmx-thresholdtargets rules that don't support overrides - Decomposed 13 large classes into focused components (SRP)
Fixed¶
- Health: complexity contributors always empty; recalibrated formulas for per-method aggregation
- Metrics: namespace
.max/.avg/.p95now aggregated from raw method values, not pre-aggregated class values - Reporting: aggregation suffixes stripped from metric keys in health text; uppercase metric keys fixed
- Git: absolute path mismatch in
GitScopeFilterfor--analyze=git:* - Security: hardcoded credentials no longer flag dot-notation identifiers (e.g.,
config.database.host) - Duplication: self-duplication for overlapping/adjacent ranges in same file eliminated
- Removed dead weighted average from aggregation, dead
GitFileDiscoveryclass
0.9.2 - 2026-03-26¶
Fixed¶
- CI: refactored
ConfigDataNormalizerto eliminate complexity violations (NPath 442K → 4), regenerated baseline
0.9.1 - 2026-03-26¶
Changed¶
- "Top issues by impact" redesigned: file path on the first line (clickable in terminal), rule name + message + symbol context on the second line. Shows
recommendationwhen available. Handles architectural violations ([project]) - HTML report: violations table now shows
Filecolumn, usesviolationCode(more specific thanruleName), and prefersrecommendationover technicalmessage
0.9.0 - 2026-03-26¶
Changed¶
- Analysis presets:
--preset=strict|legacy|cifor one-flag configuration. Multiple presets can be combined (--preset=strict,ci). Custom preset files supported via path (--preset=./team.yaml) ruleskey now uses deep merge across pipeline stages — partial rule overrides inqmx.yamlno longer replace entire preset rule configurations
0.8.0 - 2026-03-26¶
Changed¶
- Effort-aware prioritization: "Top issues by impact" section in summary and JSON output. Violations ranked by
classRank × severity × remediation time— answering "what should I fix first?" New--top=Noption (default 10,--top=0to disable)
0.7.1 - 2026-03-25¶
Changed¶
- CBO metric no longer counts PHP built-in classes (
Exception,DateTime,Iterator, etc.) — only project and third-party dependencies contribute to coupling scores. Dependency graph exports (graph:export) are also affected
0.7.0 - 2026-03-25¶
Changed¶
--fail-onnow defaults toerror— warnings are shown in output but don't cause non-zero exit code. Use--fail-on=warningorfail_on: warningin config for the old behaviorthresholdshorthand for rule configuration — sets both warning and error to the same value, making all violations errors at that threshold- Health score labels renamed to industry-standard terminology:
Excellent/Good/Fair/Poor/Critical(wasStrong/Good/Acceptable/Weak/Critical) - Line numbers shown only for violations with precise locations (method/class level), not for file-level violations
Fixed¶
- Technical debt breakdown now calculated from all violations, not just the truncated display list
Breaking¶
- Default
--fail-onchanged fromwarningtoerror. CI pipelines relying on exit code 1 for warnings must add--fail-on=warningexplicitly
0.6.0 - 2026-03-18¶
Fixed¶
- Baseline now correctly matches file-level violations (duplication, code smell, security rules) — previously ~150 violations passed through a freshly generated baseline
- Duplicate code block locations are now sorted deterministically, making baseline entries stable across runs
- File paths are normalized to relative (vs CWD) to prevent mismatches with absolute or
./-prefixed paths
Breaking¶
- Baseline version bumped to 5 — existing v4 baselines must be regenerated with
--generate-baseline
0.5.0 - 2026-03-18¶
Changed¶
exclude_namespacesis now a universal per-rule option available for any rule, not just coupling rules
Breaking¶
exclude_namespacesforcoupling.cboandcoupling.instabilitymoves from nestednamespace:to top-level rule configexclude_namespacesnow filters violations at all levels (class + namespace), not just namespace level
0.4.0 - 2026-03-18¶
Changed¶
- Health scores redesigned: 5-tier labels (
Excellent/Good/Fair/Poor/Critical), recalibrated formulas for complexity (avg + P95 + sqrt(max) penalties), coupling (efferent-based, P95 + sqrt-scaled max), cohesion (TCC neutral value for small classes), maintainability (MI anchor shifted to 30).--exclude-health=DIMENSIONto exclude dimensions from scoring - Computed metrics: 6 built-in
health.*scores plus user-definablecomputed.*metrics via Symfony Expression Language formulas, per-level formulas, threshold-based violations - Summary-first CLI:
--format=summaryis now the default output — health bars, worst offenders, violation summary, and contextual hints in one screen - Drill-down navigation:
--namespace=App\Serviceand--class=App\Service\UserServicefor progressive filtering with auto-enabled--detail. Namespace/class health scores shown in drill-down headers - Interactive HTML report:
--format=health— self-contained D3.js treemap, health coloring, search, metric selector, dark mode. Use--output/-oto write any format to a file - JSON output redesigned: summary-oriented with
meta,summary,healthdecomposition,worstNamespaces,worstClasses,violations(top 50 by default).--format-opt=violations=all|0|N,--format-opt=top=N - New rules:
code-smell.long-parameter-list,code-smell.unreachable-code,code-smell.identical-subexpression,design.god-class(Lanza & Marinescu),design.data-class,code-smell.constructor-overinjection,code-smell.unused-private,design.type-coverage,duplication.code-duplication(Rabin-Karp token hashing),coupling.class-rank(PageRank),security.sql-injection,security.xss,security.command-injection,security.sensitive-parameter,security.hardcoded-credentials - New output formats:
--format=metrics(raw metric values),--format=github(PR annotations) - Technical debt: remediation time estimates per violation, aggregated debt in reports,
--detailshows per-rule breakdown --fail-on=erroroption to allow warnings without failing the build--include-generatedto override automatic@generatedfile skipping--disable-rule=duplicationnow skips the memory-intensive detection phase entirely (not just violations). Same for circular dependency detection- Violation messages improved: actionable recommendations, parameter names in boolean-argument, coupling direction in CBO, CCN divergence hints, top-5 dependencies in coupling violations
bin/qmx graph:export --format=json— dependency graph as aggregated JSON adjacency listcomposer benchmark:checkregression suite — validates health scores against 15 open-source projectsllms.txtandllms-full.txt— machine-readable documentation for AI coding agents
Fixed¶
- Metric algorithm corrections: cognitive complexity nesting in closures, cyclomatic complexity for
matcharms, NPath formulas aligned with Nejmeh/PMD standards, Maintainability Index class-level aggregation, WOC formula, RFC for traits/enums, abstractness formula for interfaces - Anonymous class isolation: methods inside anonymous classes no longer attributed to enclosing class (CCN, NPath, Halstead, ParameterCount, UnreachableCode visitors)
- Suppression system (
@qmx-ignore): fully wired into pipeline,@qmx-ignore-next-linescoped to single line, file-level regex fixed, symbol-level no longer leaks to file-level - Output formatters: SARIF schema compliance (paths, locations, helpUri), Checkstyle/Text relative paths, GitLab project-level path, JSON NaN/Infinity handling
- Configuration:
--confignow functional,exclude_pathsaccepted, YAML key normalization preserves rule IDs, deep merge for CLI overrides,fromArray([])applies defaults - Security rules: XSS and command injection detect superglobals in interpolated strings
- Infrastructure: cache hit skips AST traversal, runtime state reset between runs, baseline v3 migration errors, parallel worker validation
Breaking¶
--format=htmlrenamed to--format=health;--format=metrics-jsonrenamed to--format=metrics--format=summaryis now the default (wastext). Use--format=textfor the previous behavior--format=jsonredesigned — no longer PHPMD-compatible. See documentation for new schema- JSON field
humanMessagerenamed torecommendationin violation objects - Health scores: 5-tier labels (was 4-tier), recalibrated formulas — baselines may need regeneration
- NPath values changed due to formula corrections — baselines may need regeneration
- Baseline version 3 no longer supported — regenerate with
--generate-baseline
0.3.0 - 2026-03-08¶
Changed¶
- CLI command renamed from
analyzetocheck, with aliases for backward compatibility - Canonical config file name is now
qmx.yaml exclude_pathsoption for violation suppression by file path patterns- MkDocs Material documentation website (EN/RU)
- Version derived from Composer/git tag instead of hardcoded constant
Fixed¶
- LCOM4 calculation aligned with original Hitz & Montazeri specification
- Maintainability Index accuracy: use ELOC instead of physical LOC
--workers=0semantics corrected
0.2.2 - 2026-03-05¶
Changed¶
- Rule NAME constants follow
group.rule-nameformat (kebab-case) SizeRulesplit intoMethodCountRuleandClassCountRuleCouplingRulesplit intoInstabilityRuleandCboRuleRuleMatcherutility for prefix-based rule matching- ANSI colors, grouping, and
FormatterContextfor formatters - Baseline v3 format with duplicate NAME validation
- Suppression system updated for dotted rule names and prefix matching
0.2.1 - 2026-03-05¶
Fixed¶
- TTY output written line by line to prevent macOS terminal truncation
0.2.0 - 2026-03-05¶
Changed¶
- Category filtering for rules
- Default thresholds calibrated
0.1.1 - 2026-03-04¶
Changed¶
violationCodefield inViolationfor stable baseline hashing- Improved violation messages with thresholds and actionable advice
Fixed¶
- Namespace-level violation display and
minClassCountfilter
0.1.0 - 2026-03-04¶
Initial release.
- PHP static analysis CLI tool
- Metrics: Cyclomatic Complexity, Cognitive Complexity, NPATH, Halstead, Maintainability Index
- Metrics: RFC, Instability, Abstractness, Distance from Main Sequence
- Metrics: TCC/LCC, LCOM4, WMC, LOC, DIT, NOC
- Rules with configurable thresholds
- Circular dependency detection with DOT graph export
- Output formats: Text, JSON, Checkstyle, SARIF, GitLab Code Quality
- Parallel file processing via amphp/parallel
- Git integration:
--staged,--diff - Baseline support with
@qmx-ignoresuppression tags - AST caching, progress bar, PSR-3 logging
- Git hook installation (
hook:install,hook:status) - Symfony DI with autowiring and autoconfiguration
- GitHub Actions workflow