phanalist

Fast static analyzer for PHP, written in Rust. Finds maintainability and runtime risks with zero configuration required.


πŸ“– Table of Contents


πŸ€” Why Phanalist?

PHP codebases grow. As they grow, they collect technical debt: classes that do too much, methods that are hard to follow, and hidden risks that only appear under real traffic. Most tools check syntax, style, or types. Phanalist checks whether the code will stay maintainable and safe as it grows.

It looks at two core things:

In plain terms, Phanalist helps you answer questions like:

Each finding explains what is wrong, why it matters, and how to fix it. You do not need to be a static-analysis expert to use it.


✨ Features


πŸš€ Quickstart (5 Minutes)

Step 1: Install Phanalist

Run the installation script in your terminal:

curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/denzyldick/phanalist/main/bin/init.sh | sh

Step 2: Run Your First Scan

Navigate to your PHP project root and run:

~/phanalist

(On the first run, phanalist.yaml is automatically created with sensible defaults).

Step 3: Review Findings

Phanalist outputs actionable findings with file paths, line numbers, and clear explanations. To see violations per rule summary only, run:

~/phanalist --summary-only

Where Phanalist Fits in Your Pipeline

Tool category What it usually checks What Phanalist adds Run together?
Type analysis Types, nullability, and contracts Architecture, runtime/resource, and security findings Yes
Style and formatting Coding standard and layout Design and runtime risk Yes
Architecture and metrics Coupling, complexity, and dependency rules Actionable findings with explanations and fix guidance Yes, or use instead
Security scanning Broad source-to-sink analysis Targeted PHP-specific hazards Yes
Quality dashboard History, trends, and team reporting SARIF and Code Climate output for your existing dashboard Yes

GitHub Action

Add inline SARIF annotations to your PRs using the Phanalist GitHub Action:

name: Phanalist
on: [pull_request]
permissions:
  security-events: write
jobs:
  phanalist:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: denzyldick/phanalist-action@v1

By default, the action appends each rule’s full documentation to inline pull request annotations.


Usage

Additional CLI flags:

Flag Description Default
--config, -c Path to configuration file ./phanalist.yaml
--src, -s Path(s) to project sources (repeatable, e.g. -s src -s tests) ./src
--rules, -r Only run these rules (overrides config) from config
--output-format, -o Output format: text, json, sarif, codeclimate text
--summary-only Show only violation counts per rule β€”
--quiet, -q Suppress all output β€”
--verbose, -v Increase verbosity; repeat for more (-v main pass, -vv parsing, -vvv indexing) β€”
--debug-rule-timing Print per-rule per-file timing β€”
--debug-rule-stats Print per-rule cost/coverage stats β€”
--use-baseline Filter results against a baseline file, reporting only new violations β€”
--update-baseline Regenerate the baseline from the current scan β€”
--blame Attribute violations to engineers via git blame and show a quality report β€”
--since Only count violations from commits after this date (e.g. "30 days", "2025-01-01") β€”
--until Only count violations from commits before this date β€”
--export-chart Export engineer chart as PNG/SVG image (requires --blame) β€”
--exclude-author Exclude authors from the report (repeatable, e.g. --exclude-author dependabot) β€”
--min-violations Minimum total violations to include an engineer in the report 0
--lsp Start as a Language Server (LSP) for editor integrations β€”

Engineer Quality Report

Use the --blame flag to see who is introducing and fixing violations in your codebase:

# Who has violations in their code currently
~/phanalist --blame

# See what changed in the last 30 days (Net column shows who is improving vs degrading)
~/phanalist --blame --since "30 days"

# Exclude bots and sort by net improvements
~/phanalist --blame --since "30 days" --exclude-author dependabot --sort net
Column Meaning
Engineer Git author name
Fixed (βœ“) Violations that disappeared compared to the --since snapshot
Introduced (βœ—) Violations that appeared compared to the --since snapshot
Net Fixed - Introduced β€” positive means improving, negative means degrading

Baseline

A baseline lets you adopt Phanalist on an existing codebase without fixing every finding at once. It freezes current violations; later runs report only new ones so CI stays green on known debt but fails on regressions.

# Generate baseline
~/phanalist --use-baseline phanalist-baseline.json --update-baseline

# Run against baseline in CI
~/phanalist --use-baseline phanalist-baseline.json

βš™οΈ Configuration & Rule Sections

Phanalist organizes rules into four internal analysis sections. You can configure thresholds or disable rules in phanalist.yaml:

  1. AST & Code Quality (ast): Checks syntax structure, formatting conventions, naming rules, method complexity metrics, and class design.
  2. Formal Verification (verification): Analyzes control flow to prove runtime safety (such as preventing division by zero, null errors, and array bounds violations).
  3. Resource Analysis (resource): Measures time complexity, memory allocation growth, and untrusted loop cardinality.
  4. Security & Lifecycle (security): Detects security vulnerabilities, unclosed file/stream resources, and dangerous execution sinks.
enabled_rules: []   # empty = all rules active
disable_rules: []
exclude_paths:
  - vendor
  - node_modules
rules:
  E0007:
    max_parameters: 5
  E0009:
    max_complexity: 10
  E0031:
    php_version: "8.4"
    timeout_ms: 1000
  E0044:
    runtime_threshold: quadratic

πŸ“œ Rules Reference

New to the terminology? Check the Rule Glossary. Every rule links to detailed, junior-friendly documentation with bad/good examples and junior rules of thumb.

Section 1: AST & Code Quality Rules (E0000–E0030)

Type: Syntax, metrics, structure, and design checks.

Code Name Primary Option / Threshold
E0000 Example rule β€”
E0001 Opening tag position β€”
E0002 Empty catch block β€”
E0003 Method visibility modifiers β€”
E0004 Uppercase constants β€”
E0005 Capitalized class name β€”
E0006 Property visibility modifiers β€”
E0007 Method parameters count max_parameters: 5
E0008 Return type signature β€”
E0009 Cyclomatic complexity max_complexity: 10
E0010 Npath complexity max_paths: 200
E0011 Error suppression symbol (@) β€”
E0012 Service Shared Memory Model compatibility include_namespaces, reset_interfaces
E0013 Unused private method β€”
E0014 Law of Demeter β€”
E0015 Method Cohesion (LCOM4) threshold: 1
E0016 Cognitive complexity max_complexity: 15
E0017 Coupling Between Objects (CBO) max_coupling: 10
E0018 Weighted Methods per Class (WMC) max_wmc: 50
E0019 Response For a Class (RFC) max_rfc: 50
E0020 Depth of Inheritance Tree (DIT) max_depth: 4
E0021 Number of Children (NOC) max_children: 15
E0022 Afferent/Efferent Coupling (Ca/Ce) max_ca: 20, max_ce: 20
E0023 Instability, Abstractness, Distance max_distance: 0.5
E0024 Lines of Code per Method max_loc: 30
E0025 Lines of Code per File max_loc: 500
E0026 Comment Ratio min_ratio: 0.1, max_ratio: 0.5
E0027 God Class (Brain Class) max_methods: 15, max_fields: 10
E0028 Data Class min_methods: 3
E0029 Fan-in / Fan-out max_fan_out: 10, max_fan_in: 20
E0030 Cyclomatic Complexity Density max_density: 0.3

Section 2: Formal Verification Rules (E0031–E0043)

Type: Path analysis and runtime safety checks.

Code Name What it Proves / Checks
E0031 Division by Zero Proves that divisors are never zero
E0032 Modulo by Zero Proves that modulo divisors are never zero
E0033 intdiv() by Zero Proves integer division divisors are valid
E0034 intdiv() Overflow Prevents integer overflow during division
E0035 Missing Array Key Proves that required array keys exist before access
E0036 Array Index Out of Bounds Proves array index access stays within bounds
E0037 String Offset Out of Bounds Proves string character offsets are valid
E0038 Method Call on Null Proves objects are not null before calling methods
E0039 Property Access on Null Proves objects are not null before accessing properties
E0040 Return Contract Violation Verifies return types match documented contracts
E0041 Unreachable Code Detects dead statements after returns or throws
E0042 Impossible Assertion Detects assertions that can never hold true
E0043 Stream Resource Used After Close Detects reading/writing streams after closing them

Section 3: Resource Analysis Rules (E0044–E0046)

Type: Scalability, memory growth, and time complexity checks.

Code Name What it Analyzes
E0044 Input-Dependent Runtime Growth Flags loops whose time complexity scales poorly with input size
E0045 Input-Dependent Live-Heap Growth Flags memory allocation patterns that grow with input size
E0046 Input-Controlled Cardinality Flags collections sized directly from untrusted user input

Section 4: Security & Lifecycle Rules (E0047–E0059)

Type: Security hazards and resource leak checks.

Code Name Security / Lifecycle Risk
E0047 Resource Lifecycle Leak Unclosed file handles or database connections
E0048 Unbounded Direct Recursion Infinite recursion without base condition changes
E0049 Builtin Argument Validation Invalid literal arguments passed to built-in functions
E0050 Position Check Used as Boolean Using strpos() without strict comparison (!== false)
E0051 Count of Non-Countable Value Calling count() on non-array scalars
E0052 Unchecked json_decode Result Missing validation after JSON decoding
E0053 Unserialize Without allowed_classes PHP object injection risk
E0054 Dynamic Variable Creation Variable variables ($$x) or extract() usage
E0055 Dynamic Include or Require Unsafe dynamic file inclusion paths
E0056 Weak Randomness for Security Using rand() or mt_rand() for security tokens
E0057 MD5 or SHA1 for Passwords Insecure hashing algorithms for credentials
E0058 Cookie Missing Secure or HttpOnly Cookies missing security flags
E0059 Dynamic Eval or Assert Dangerous code execution sinks (eval())

Adding a new rule is straightforward β€” this tutorial explains how.


πŸ”Œ Language Server Protocol (LSP)

Phanalist includes built-in LSP support to provide real-time, as-you-type diagnostics directly in your editor (phanalist --lsp).


Articles

Read a series of chapters on https://dev.to/denzyldick to understand the project’s internals.