Skip to content

PhpCodeArcheology/PhpCodeArcheology

MCP Developer Tools

PHP static analysis tool that measures code quality with over 60 metrics, detects architecture‑level issues, integrates Git churn analysis, and produces HTML/JSON/SARIF reports.

PHP Latest v2.11.0 · 1mo ago Security brief →

Features

  • Over 60 code quality metrics (complexity, maintainability index, coupling, cohesion, Halstead, etc.)
  • Built‑in rule set for detecting architecture problems (God Class, deep inheritance, SOLID violations, security smells)
  • Git integration: churn analysis, hotspot detection, author tracking and trend charts
  • Multiple report formats – interactive HTML, Markdown, JSON, SARIF, AI summary and Knowledge Graph

Recent releases

View all 34 releases →
v2.11.0 Breaking risk
⚠ Upgrade required
  • Use the new `ServiceFactory::createMetricsTriple()` to instantiate `MetricsRegistry`, `MetricsReader`, and `MetricsWriter` ensuring they share a single `MetricsContainer`.
  • `MetricsController` is deprecated as a thin facade; production code should transition to using the three separate classes directly. It will be removed once tests migrate.
  • Docker builds now pin PHP platform version (`config.platform.php = "8.2.0"`) and rely on the committed `composer.lock`; ensure CI uses Composer install with locked dependencies for deterministic builds.
Breaking changes
  • Removal of `CalculatorTrait`; replaced by `MetricsReaderWriterTrait` with constructor property promotion.
  • Removal of unused `MaintainabilityIndexVisitor` and its test file.
  • Decomposition of monolithic `MetricsController` into three focused classes (`MetricsRegistry`, `MetricsReader`, `MetricsWriter`) backed by narrow interfaces; all Calculator, Prediction, Visitor, DataProvider, and CLI-output code now typehints the specific interface it needs.
Notable features
  • PHAR release (`phpcodearcheology.phar`) as a new installation option with bundled dependencies for legacy environments.
  • Source code display in HTML report with PHP syntax highlighting, nesting‑depth heatmap, and problem badges (opt‑in via `--source-code` or config).
  • `startLine` / `endLine` metrics added for methods, functions, and classes; SARIF report now reports correct line numbers.
Full changelog

Changed

  • MetricsController split into MetricsRegistry / MetricsReader / MetricsWriter. The monolithic MetricsController is now decomposed into three focused classes backed by narrow interfaces (MetricsRegistryInterface, MetricsReaderInterface, MetricsWriterInterface). Every Calculator, Prediction, Visitor, DataProvider, and CLI-output class typehints the interface it actually needs — Calculators and Predictions declare reader / writer / registry separately, Visitors take writer + registry (plus reader when they read), and DataProviders take reader + registry. The AnalysisPipeline now returns the trio directly from runAnalysis() / runQuickAnalysis() instead of a controller. PredictionInterface::predict() loses its parameter; predictions receive the trio via constructor injection, analogous to Calculators. Closes #13.
  • New ServiceFactory::createMetricsTriple() is the only supported way to build the three objects so they share one MetricsContainer (invariant: registry, reader, and writer observe the same state).

Removed

  • CalculatorTrait — replaced by MetricsReaderWriterTrait (constructor property promotion for reader/writer/registry). Calculators with additional dependencies keep a custom constructor.
  • MaintainabilityIndexVisitor — unused dead code (never registered in Analyzer::getVisitorClassList()), removed together with its test file.

Deprecated

  • MetricsController — retained as a thin facade implementing all three narrow interfaces, so tests can still instantiate a single object. No production code path uses it anymore; it will be removed once the test suite migrates to the trio directly.

Added

  • PHAR release as a new installation option. A standalone phpcodearcheology.phar is now built automatically on every v*.*.* tag via GitHub Actions and attached to the corresponding GitHub Release as a draft, together with a phpcodearcheology.phar.sha256 checksum. The PHAR ships all runtime dependencies bundled, so it runs on legacy codebases where direct composer require conflicts with the tool's own dependencies (e.g. nikic/php-parser version collisions). Local builds are available via composer build-phar (uses Box 4.7.0, pinned SHA-256). Closes #18.

Changed

  • Metric definition discovery uses scandir() instead of glob() in HtmlReport::generateGlossaryPage(). glob() is unreliable inside phar:// streams across PHP versions; scandir() is not. Behavior is identical for regular installations.

Added

  • Source code display in HTML report. Method and function detail pages can now show the actual source code with PHP syntax highlighting (highlight.js). A nesting-depth heatmap highlights deeply nested lines progressively (depth 2: subtle yellow, depth 3: stronger yellow, depth 4+: red), making complexity drivers immediately visible. Problem badges above the code block and a colored left border (red/yellow/blue by severity) connect metrics to the code. Opt-in via --source-code CLI flag or sourceCode: { enable: true, display: "all"|"problems-only" } in config. Default display mode is problems-only (only methods/functions with detected problems). The highlight.js assets are only included in the report when the feature is enabled.
  • startLine / endLine metrics for methods, functions, and classes. Stored during AST analysis, also fixes the SARIF report which previously defaulted all line numbers to 1.
  • DocBlock summary display in HTML report detail views. Class and method/function detail pages now show the PHPDoc summary text (description before @-tags) below the title. Works for all visibility levels including private methods and __construct. Empty or missing docblocks are handled gracefully (no empty boxes). Closes #16.
  • Test coverage for TooComplexPrediction. 42 dedicated unit tests covering all code paths: CC thresholds (small/large code), Difficulty (trivial exit, framework adjustment), Effort and MI (relative-to-average, typed code tolerance, framework tolerance), LCOM (relative-to-average, floor behavior), the full shouldSkipLcom exclusion matrix, per-method cognitive complexity, classTooComplex flag, File/Function collection handling, and configurable thresholds. All expected values hand-calculated.

Fixed

  • Docker build broken by PHP version mismatch. The Dockerfile ran composer update in the composer:2 stage (now PHP 8.4), which resolved symfony/yaml to v8.0.8 (requires PHP 8.4). The resulting vendor/ was then copied into the php:8.2-cli-alpine runtime, where platform_check.php aborted with a fatal error. Fixed by pinning config.platform.php to 8.2.0 in composer.json (so lockfile resolution stays on the minimum supported PHP regardless of the host PHP version), regenerating composer.lock, and switching the Dockerfile to composer install with the committed lock. Builds are now deterministic. Closes #20.
  • Search input and filter dropdown invisible in dark mode. The search fields and problem-level dropdown in all table views (classes, methods, functions, files, git, tests, refactoring roadmap) used text-black without an explicit background, making them unreadable on dark backgrounds. Replaced with themed .search-input styling that works in both dark and light mode. Filter bar elements now have proper spacing.
  • Markdown report: empty class column in refactoring roadmap and AI summary. The fullName metric was never populated for class collections, so refactoring-roadmap.md showed empty cells in the Class column and ai-summary.md rendered classes as ****. IdentifyVisitor now sets fullName centrally, which also improves the HTML report (namespace prefix now rendered correctly in the roadmap) and the MCP tools. Closes #19.
  • Markdown report: broken footer navigation. The footer nav on every page had three problem-category links (File problems, Class problems, Function problems) all pointing to files.md, plus a duplicated **Packages** heading where **Problems** was intended. Links now resolve to their respective *-problems.md pages and the section heading is correct. Closes #19.
  • Markdown report: problems.md linked to .html files. The problem category links in problems.md were hardcoded to .html, breaking navigation in the Markdown report. All Markdown templates now consistently link to .md targets. Closes #19.
  • Security-smell false positive on SQL concat with class constants only. The SQL string concatenation detector flagged 'INSERT INTO ' . SomeClass::TABLE . ' …' even though the dynamic part is a compile-time constant and cannot carry attacker-controlled data. The detector now ignores concat expressions whose non-literal operands are exclusively class constants, enum cases, global constants, or magic constants. Common Doctrine subscriber / repository patterns no longer produce noise. Closes #19.
  • PHP Warning: Undefined array key 1 in HistoryService when an older history entry stored a plain numeric value for a metric that now carries the 'label: value' prefix. explode(': ', …)[1] was accessed unconditionally; it now falls back to [0] when no colon is present — same effective delta, no warning.
  • Trend chart missing on the second run after a fresh history. A regression introduced when the ReportOrchestrator was extracted from Application.php inverted the order of writeHistory() and report generation: the current run was only appended AFTER reports were rendered, so the HistoryDataProvider never saw it. The chart only surfaced on the third run. writeHistory() now runs before reports again, matching the original v2.7.1 fix.
v2.10.0 New feature
⚠ Upgrade required
  • Enable source code display via CLI flag `--source-code` or config key `sourceCode.enable: true`. Optional `display` setting accepts "all" or "problems-only" (default).
Notable features
  • Source code display with PHP syntax highlighting and nesting-depth heatmap in HTML report
  • `startLine` / `endLine` metrics for methods, functions, and classes (fixes SARIF line numbers)
  • DocBlock summary shown in class and method detail views
Full changelog

Highlights

Source code display with nesting heatmap — Method and function detail pages in the HTML report can now show the actual source code with PHP syntax highlighting. A nesting-depth heatmap highlights deeply nested lines progressively (yellow → red), making complexity hotspots visible at a glance. Problem badges and severity borders connect detected issues directly to the code.

Enable via --source-code CLI flag or config:

sourceCode:
  enable: true
  display: "all"        # or "problems-only" (default)

Added

  • Source code display in HTML report with PHP syntax highlighting and nesting-depth heatmap
  • startLine / endLine metrics for methods, functions, and classes (also fixes SARIF line numbers)
  • DocBlock summary display in class and method detail views (#16)
  • 42 unit tests for TooComplexPrediction

Fixed

  • Search input and filter dropdown invisible in dark mode across all table views
  • Filter bar elements now have proper spacing

Full Changelog: https://github.com/PhpCodeArcheology/PhpCodeArcheology/blob/main/CHANGELOG.md

v2.9.1 Bug fix

Fixed PHPUnit XML coverage exclusion handling and filtered "Untested Complex Classes" to require cyclomatic complexity ≥ 8.

Full changelog

Fixed

  • <source> section from phpunit.xml is now respected. The v2.9.0 phpunit.xml integration only parsed <testsuite> entries; the <source><include> and <source><exclude> sections — which tell PHPUnit which production files belong to the coverage scope — were ignored. As a result, classes explicitly excluded from coverage (DataFixtures, Kernel, migrations, …) were still counted as untested and showed up in the "Untested Complex Classes" table, and some triggered false-positive UntestedComplexCode problems. The parser now reads the full <source> tree, classes outside the scope get the new excludedByPhpunitSource flag, and the Tests page shows a dedicated "Excluded by phpunit.xml" stats card so the removal is visible. Tested/untested counts, test ratio, Health Score and Technical Debt Score improve correspondingly for projects that use <source><exclude>. Projects without a <source> section are byte-identical to 2.9.0.
  • "Untested Complex Classes" list now honors the "Complex" part of its title. The table listed any untested class (sorted by CC, top 20), even trivial ones like pass-through exceptions with CC=1 — while UntestedComplexCodePrediction used a CC threshold of 8 for the actual warnings. The two were inconsistent: the list could show 20 "complex" entries while only 2 were actually flagged. The Tests page now filters by the same CC ≥ 8 threshold, so trivial exception classes, one-liner DTOs and the like no longer dilute the list.
v2.9.0 Breaking risk
⚠ Upgrade required
  • Deprecation warning: `--coverage-file` CLI flag still works but config value in yaml/json takes precedence.
  • If test ratio drops after upgrade, verify that phpunit.xml includes all desired test files; the new behavior excludes files not listed in suites.
Breaking changes
  • PHPUnit xml (phpunit.xml, phpunit.xml.dist, phpunit.dist.xml) becomes the sole source of truth for test discovery; files outside configured suites are no longer counted.
Security fixes
  • dep: Twig updated to v3.24.0 — resolves sandbox bypass and __toString__/__isset() security advisories
  • dep: Tailwind CSS upgraded from v3 to v4 — resolves npm security advisories
Notable features
  • `coverageFile` config option to persist Clover XML path in `php-codearch-config.yaml` / `.phpcodearch.json`
  • Symfony-style Clover auto-detection adds `var/reports/clover.xml` and `var/coverage/clover.xml` to candidate locations
Full changelog

Added

  • PHPUnit testsuite configuration is now respected. Test discovery reads phpunit.xml / phpunit.xml.dist / phpunit.dist.xml and honors <testsuite> definitions including <directory> (with suffix and prefix attributes), explicit <file> entries, and <exclude> paths. Pest projects benefit automatically since Pest uses phpunit.xml under the hood. Projects without a phpunit config continue to use the existing heuristic (PSR-4 autoload-dev → common directory names), so Codeception and PHPUnit-less projects are unaffected.
  • coverageFile config option. The Clover XML path can now be set persistently in php-codearch-config.yaml / .phpcodearch.json instead of passing --coverage-file on every run. Relative paths are resolved against the project's running directory. The CLI flag still takes precedence over the config value.
  • Symfony-style Clover auto-detection. var/reports/clover.xml and var/coverage/clover.xml are now part of the auto-detection candidate list, alongside the existing root, coverage/, build/logs/, and build/coverage/ locations.
  • Project logo. New logo (compass-graph hybrid) added to README, HTML report header, and as favicon.
  • Knowledge Graph: focus selector. Searchable combobox to focus the graph on specific classes, packages, or authors. Type to search, click to select — the graph shows only the selected entities and their direct neighbors. Selected items appear as removable chips below the controls. Double-click any node in the graph to focus on it. Selecting an author automatically activates the authored_by edge type. Supports keyboard navigation (arrows, Enter, Escape).

Changed

  • Behavior change: phpunit.xml is now authoritative for PHPUnit/Pest test discovery. When a phpunit.xml (or .xml.dist / .dist.xml) with testsuites is present, PhpCodeArcheology uses it as the single source of truth instead of just scanning PSR-4 autoload-dev directories. Test files outside the configured suites, inside <exclude> paths, or not matching the configured suffix are no longer counted. If your test ratio drops unexpectedly after upgrading, check whether your phpunit.xml is narrower than your filesystem layout — the new count reflects what PHPUnit actually runs.
  • Metric tiles: dynamic font sizing. Long metric values (namespace lists, refactoring recommendations) now scale down automatically instead of overflowing. Word-break at backslashes for readable namespace wrapping.
  • Metric tiles: empty value indicator. Tiles with no value now show a subtle dash instead of being completely empty.
  • Knowledge Graph filter chips: accessibility overhaul. Replaced opacity-based active/inactive toggle with filled/outline pattern. Active chips now use darkened color backgrounds with white text, inactive chips are transparent with muted text. All combinations pass WCAG AA contrast (4.5:1+) in both dark and light themes. Added keyboard navigation (Tab + Enter/Space), focus-visible ring, and role="button". Fixed German labels ("Node-Typen"/"Edge-Typen" → "Nodes"/"Edges"). Previously invisible chips in light mode (depends_on, belongs_to, declares) now use visible slate colors.
  • Glossary page redesigned. Replaced the flat table layout with grouped panels per metric category (Class, File, Function, Method, Package, Project, Git). Each metric shows name, description, type badge, and better-direction badge. Threshold table restyled with severity badges. All colors pass WCAG AA contrast in both dark and light themes.
  • 17 metric descriptions improved. Replaced placeholder descriptions (e.g. "LCOM.", "Instability.") with proper explanations including formulas and interpretation guidance for LCOM, Instability, all Halstead metrics, Complexity Density, Maintainability Index, Comment Weight, and Package metrics (Abstractness, Distance, Coupling).
  • Tailwind CSS upgraded from v3 to v4. Migrated config from tailwind.config.js to CSS-native @theme and @utility directives. Resolves all npm security advisories.
  • Twig updated to v3.24.0. Resolves sandbox bypass and __toString()/__isset() security advisories.
  • Dependabot enabled. Automated dependency update PRs for Composer (weekly), npm (monthly), and GitHub Actions (weekly).
  • 3.0.0 roadmap announced. New ## Next Major (3.0) section in ROADMAP.md and 3.0.0 milestone for tracking. The next major will rework relative average thresholds (Effort, MI, LCOM) with statistical outlier detection — development happens on the 3.0.x branch; 2.x continues on main.
v2.8.0 Breaking risk
Notable features
  • Composer plugin `codearch:analyze` command with auto‑PSR-4 detection and CLI option forwarding
  • Automatic exclusion of `vendor/`, `node_modules/`, `.git/` directories from analysis
Full changelog

Added

  • Composer Plugin. PhpCodeArcheology now registers as a Composer plugin, adding the composer codearch:analyze command. When no path is given and no config file exists, it auto-detects PSR-4 source directories from composer.json. All CLI options are supported via composer codearch:analyze -- --quick, --report-type, --coverage-file, etc.
  • Default directory exclusions. vendor/, node_modules/, and .git/ are now excluded from analysis automatically, even without a config file. The init command also includes vendor and node_modules in the generated config. Fixes #9.
  • memoryLimit config option documented in sample config and README with usage examples.
  • 259 new tests. FileList exclusion logic (19), Config memory limit handling (13), YAML/JSON config file parsing (32), integration test for analysis pipeline (12), Problem value objects (96), Prediction tests for DeadCode/TooManyParams/DeepInheritance/TooLong/GodClass/DependencyCycle (87). Test count: 440 → 699.
  • /release slash command for automated releases (version detection, changelog update, tagging, GitHub release).

Changed

  • Architecture refactoring. Application.php split from 614 to 175 LLOC: AnalysisPipeline, BootstrapService, ReportOrchestrator, CalculatorRegistry, PredictionRegistry, ServiceFactory extracted. Interfaces added: OutputInterface, MetricsReaderInterface, MetricsWriterInterface, AnalysisConfigInterface. Typed Config getters replace raw get() calls.
  • Monster methods split. ImpactAnalysisTool CC 76→5, RefactoringTool CC 51→5, GetTestCoverageTool CC 44→5, HalsteadMetricsVisitor::countOperators CC 35→5, HtmlReport::generateIndexPage CC 38→1, HistoryService::setDeltas CC 22→9, HotspotsTool CC 26→8.
  • GraphDataProvider split into ClassNodeCollector, EdgeCollector, PackageNodeCollector.
  • Halstead Difficulty threshold recalibrated. Default raised from 20 to 30 (45 for framework projects).
  • Test mapping improved. Classes with Clover XML coverage are now recognized as tested.
  • Internal task files removed from repository.
  • History memory optimization. Stream-based reading (fseek instead of file()), selective metric loading for trend charts (8 metrics instead of full entries), last-run caching. New entries are written in compact format (project-level metrics only). Old full entries remain readable — no migration needed.

Fixed

  • memory_limit=-1 from php.ini was overridden. The tool always set memory_limit to 1G, ignoring the system setting. Now respects unlimited (-1) from php.ini and only bumps to 1G when the current limit is lower. The memoryLimit config option also accepts "-1" for unlimited. Fixes #9.
  • memoryLimit from config file was ignored. The YAML/JSON config parser did not map the memoryLimit key to the application config, so the default of 1G was always used regardless of the config file setting.
  • Duplicate memory limit code. Consolidated identical ini_set logic from Application, McpCommand, and BaselineCommand into Config::applyMemoryLimit().
  • Test File Ratio removed. Unreliable metric replaced by class-level tested/untested tracking.

Weekly OSS security release digest.

The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.

No spam, unsubscribe anytime.

About

Stars
77
Forks
6
Languages
PHP Twig JavaScript

Install & Platforms

Install via
docker binary

Alternative to

PHPMetrics PHPStan Psalm

Beta — feedback welcome: [email protected]