|
| 1 | +# P1 – PHP Parser / Static Analysis Integration Plan |
| 2 | +**Status:** Not Started · **Created:** 2026-02-03 |
| 3 | + |
| 4 | +## Table of Contents |
| 5 | +- [Background](#background) |
| 6 | +- [High-Level Phased Checklist](#high-level-phased-checklist) |
| 7 | +- [Background & Goals](#background--goals) |
| 8 | +- [Tooling Options Overview](#tooling-options-overview) |
| 9 | +- [Recommended Tooling Choice](#recommended-tooling-choice) |
| 10 | +- [Phase 0 – Spike & Decision](#phase-0--spike--decision) |
| 11 | +- [Phase 1 – Local PHPStan Integration](#phase-1--local-phpstan-integration) |
| 12 | +- [Phase 2 – PHP-Parser AST Experiments for WPCC](#phase-2--php-parser-ast-experiments-for-wpcc) |
| 13 | +- [Phase 3 – Hardening & Developer Experience](#phase-3--hardening--developer-experience) |
| 14 | +- [Risk / Quagmire Avoidance](#risk--quagmire-avoidance) |
| 15 | +- [LLM Notes](#llm-notes) |
| 16 | + |
| 17 | +## Background |
| 18 | +WPCC today is a shell-based scanner that leans on grep-style rules, cached file lists, and small Python helpers to produce deterministic JSON logs and HTML reports. |
| 19 | +It is intentionally distributed without a Composer/vendor footprint, and its checks are primarily syntactic (e.g., unbounded queries, superglobals, magic strings) rather than type- or contract-aware. |
| 20 | +This plan explores how to layer PHP-Parser and dedicated static analysis tools (PHPStan/Psalm) on top of that foundation without breaking the lightweight distribution model. |
| 21 | + |
| 22 | +## High-Level Phased Checklist |
| 23 | +> **Note for LLMs:** Whenever you progress an item below, update its checkbox state in-place so humans can see progress without scrolling. |
| 24 | +- [ ] Phase 0 – Clarify goals, choose pilot use cases, decide tooling mix |
| 25 | +- [ ] Phase 1 – Run PHPStan/Psalm on a target plugin repo with simple IRL checks |
| 26 | +- [ ] Phase 2 – Implement first PHP-Parser-based AST rule inside WPCC |
| 27 | +- [ ] Phase 3 – Stabilize, document, and integrate into CI / WPCC flows |
| 28 | + |
| 29 | +## Background & Goals |
| 30 | +We want type- and shape-aware analysis that can catch: |
| 31 | +- Contract mismatches between producers (`search_customers()`) and consumers (filters/Ajax). |
| 32 | +- Misused settings from `get_option()` and similar APIs. |
| 33 | +- Nullability mistakes around `get_user_by()`, `get_post()`, `wc_get_order()`, etc. |
| 34 | + |
| 35 | +Constraints: |
| 36 | +- WPCC today is shell + grep + small Python helpers, with no Composer footprint. |
| 37 | +- We must avoid a quagmire where bundling a full static analyser into WPCC explodes complexity. |
| 38 | +- First wins must be small, IRL, and obviously useful to developers. |
| 39 | +- We already have in-house PHP-Parser plumbing: |
| 40 | + - `kissplugins/WP-PHP-Parser-loader` for loading/configuring PHP-Parser in WP. |
| 41 | + - A working harness in `KISS-woo-shipping-settings-debugger` for using AST analysis on real plugins. |
| 42 | + |
| 43 | +## Tooling Options Overview |
| 44 | +**PHPStan** |
| 45 | +- Mature static analyser with strong ecosystem. |
| 46 | +- Good WordPress support via `phpstan/wordpress` and community configs. |
| 47 | +- Excellent at cross-function type contracts and array shapes. |
| 48 | +- Assumes a Composer-managed project; heavy to embed directly into WPCC. |
| 49 | + |
| 50 | +**Psalm** |
| 51 | +- Very capable analyser with rich type system and taint analysis. |
| 52 | +- Similar Composer + bootstrap expectations as PHPStan. |
| 53 | +- Slightly smaller WP-specific ecosystem for our current needs. |
| 54 | + |
| 55 | +**nikic/PHP-Parser** |
| 56 | +- Low-level AST library; we get syntax trees and must build our own analysis. |
| 57 | +- Great for narrow, custom rules where grep is too blunt. |
| 58 | +- No built-in type inference, data flow, or WordPress awareness. |
| 59 | +- Fits WPCC’s distribution model better, especially given our existing loader + harness, but only if we keep scope tight. |
| 60 | + |
| 61 | +## Recommended Tooling Choice |
| 62 | +**Short answer** |
| 63 | +- For plugin development repos (e.g., Woo Fast Search), start with **PHPStan** as the primary static analysis tool. |
| 64 | +- For WPCC itself and its “no Composer” distribution, use **PHP-Parser** for a small set of targeted AST-based checks, not as a general type system. |
| 65 | + |
| 66 | +Rationale: |
| 67 | +- PHPStan/Psalm already solved the hard problems (types, inheritance, generics, data flow); recreating that on top of PHP-Parser would be a multi-month project. |
| 68 | +- WPCC can still benefit from lightweight AST rules where grep is too blunt, while keeping install friction low. |
| 69 | +- PHPStan has a slight edge over Psalm here due to WordPress extensions, docs, and recipes that match our IRL patterns. |
| 70 | + |
| 71 | +## Phase 0 – Spike & Decision |
| 72 | +**Goals** |
| 73 | +- Confirm the “easier” IRL use cases (options shape, nullability, list vs single) are lower effort and lower risk than the wholesale filter contract. |
| 74 | +- Decide on: (a) initial PHPStan configuration for a target plugin repo; (b) first AST rule worth building with PHP-Parser in WPCC, reusing our existing loader and harness patterns where possible. |
| 75 | + |
| 76 | +**Tasks** |
| 77 | +- [ ] Pick 1–2 IRL scenarios as pilots: |
| 78 | + - [ ] Settings/options shape via `get_option()`. |
| 79 | + - [ ] Nullability guards for `get_user_by()` / `get_post()` / `wc_get_order()`. |
| 80 | +- [ ] Run a manual PHPStan spike (level 1–3) on the plugin repo using Composer dev-dependency. |
| 81 | +- [ ] Document major friction points (WordPress stubs, bootstrap, performance). |
| 82 | +- [ ] Review `WP-PHP-Parser-loader` and KISS-woo-shipping-settings-debugger harness to understand existing AST patterns and APIs. |
| 83 | +- [ ] Sketch one candidate PHP-Parser rule where grep is not enough (e.g., verifying a specific Ajax response array shape) that can be implemented by reusing the loader/harness concepts. |
| 84 | +- [ ] Roughly sketch a JSON config schema for AST rules (e.g., for `ajax-response-shape`: function selectors, expected keys, severity/impact) before implementation. |
| 85 | +- [ ] Time-box Phase 0 spikes (e.g., 4–6 engineering hours) and add a “stop and reassess” checkpoint; if PHPStan WP stubs/bootstrap friction is too high, pivot or descope rather than pushing through. |
| 86 | + |
| 87 | +## Phase 1 – Local PHPStan Integration |
| 88 | +**Intent:** Keep this out of WPCC’s distribution; treat it as a per-repo dev tool. |
| 89 | + |
| 90 | +**Tasks** |
| 91 | +- [ ] Add PHPStan as a dev dependency to the target plugin repo. |
| 92 | +- [ ] Create a minimal `phpstan.neon` with: |
| 93 | + - [ ] WordPress extension / stubs if needed. |
| 94 | + - [ ] Baseline file to mute existing noise. |
| 95 | +- [ ] Encode 1–2 simple IRL checks: |
| 96 | + - [ ] `get_option()` wrapper returning a documented array shape. |
| 97 | + - [ ] One nullability wrapper (e.g., `find_customer_by_email(): ?WP_User`). |
| 98 | +- [ ] Run PHPStan in CI and locally; confirm it stays fast and stable. |
| 99 | + - [ ] Record a canonical IRL failure fixture for later regression tests: the Woo Fast Search "wholesale filter contract mismatch" bug at commit `9dec5a4cd713b6528673cc8a0561e6c4db925667` (https://github.com/kissplugins/KISS-woo-fast-search/commit/9dec5a4cd713b6528673cc8a0561e6c4db925667). |
| 100 | + |
| 101 | +## Phase 2 – PHP-Parser AST Experiments for WPCC |
| 102 | +**Intent:** Add one small AST-based rule to WPCC to prove value over grep, without changing WPCC’s installation story, and **leverage our existing loader + harness** so this remains a low-risk, low-effort experiment. |
| 103 | + |
| 104 | +### Proposed First AST Rule: Ajax Response Shape Checker |
| 105 | +**Scenario (example: Woo Fast Search, or similar search feature)** |
| 106 | +- Target a specific Ajax endpoint function (e.g. `ajax_search_customers()`). |
| 107 | +- Enforce that any returned array literal for the JSON response has a **fixed, documented shape**, for example: |
| 108 | + - `['customers' => list, 'total' => int, 'has_more' => bool]`. |
| 109 | + |
| 110 | +**What the rule does (AST-level)** |
| 111 | +- Parse target PHP files and locate: |
| 112 | + - Functions matching a configured name/pattern (e.g. `kiss_woo_ajax_search_customers`). |
| 113 | + - `return` statements that return an array literal. |
| 114 | +- Validate that those array literals: |
| 115 | + - Contain required keys (`customers`, `total`, `has_more`). |
| 116 | + - Do **not** contain obviously conflicting duplicate shapes for the same function. |
| 117 | + - Optionally: flag if the same function sometimes returns a bare list vs a keyed array literal. |
| 118 | + |
| 119 | +**Limitations (v1)** |
| 120 | +- Only inspects direct array literals in `return` statements. |
| 121 | +- Patterns like `$result = [...]; return $result;` or arrays built via helper functions are out of scope for the initial rule. |
| 122 | +- This is acceptable for v1; broader data-flow or variable-tracking can be revisited in later phases if this rule proves useful. |
| 123 | + |
| 124 | +**CLI contract (sketch)** |
| 125 | +- New helper, invoked from WPCC (names TBD), for example: |
| 126 | + - `php dist/bin/wpcc-ast-check.php --rule ajax-response-shape --config dist/config/ajax-response-shape.json --paths "${PATHS}"`. |
| 127 | +- Output: JSON object with a `findings` array compatible with WPCC’s log schema, e.g. each finding contains at minimum: |
| 128 | + - `id` (e.g. `ast-001-ajax-response-shape`) |
| 129 | + - `severity` (e.g. `warning` or `error`) |
| 130 | + - `impact` (e.g. `MEDIUM`) |
| 131 | + - `file`, `line`, `message`, `code`, and optional `context` lines (mirroring existing entries in `dist/logs/*.json`). |
| 132 | + |
| 133 | +**Tasks** |
| 134 | +- [ ] Decide and document how PHP-Parser will be distributed for WPCC (e.g., bundle loader/helper into `dist/` and rely on `WP-PHP-Parser-loader` to manage `nikic/php-parser`, keeping WPCC itself Composer-free). |
| 135 | +- [ ] Reuse or adapt `WP-PHP-Parser-loader` so WPCC can reliably load PHP-Parser in its own context. |
| 136 | +- [ ] Mirror or borrow minimal harness patterns from KISS-woo-shipping-settings-debugger for walking ASTs and emitting JSON findings. |
| 137 | +- [ ] Define a small JSON config format for this rule (e.g. function names and expected keys). |
| 138 | +- [ ] Implement the `ajax-response-shape` rule end-to-end: |
| 139 | + - [ ] CLI entry point callable from WPCC. |
| 140 | + - [ ] JSON output format consistent with existing `findings` entries (id/severity/impact/file/line/message/code/context). |
| 141 | + - [ ] Wiring into the scan pipeline behind a feature flag. |
| 142 | +- [ ] Measure performance impact and confirm it’s acceptable on medium-sized plugins. |
| 143 | + - [ ] Create small synthetic fixtures for this rule (e.g., one "good" and one "bad" Ajax endpoint file plus expected `findings` JSON) so we can exercise AST feedback without depending on live IRL plugins. |
| 144 | + |
| 145 | +## Phase 3 – Hardening & Developer Experience |
| 146 | +**Tasks** |
| 147 | +- [ ] Decide which AST-based rules graduate from “experiment” to “default on”. |
| 148 | +- [ ] Document how WPCC interacts with PHPStan in plugin repos (if at all). |
| 149 | +- [ ] Add docs / recipes in `~/bin/ai-ddtk/recipes/` for: |
| 150 | + - [ ] Running PHPStan on a plugin with WPCC. |
| 151 | + - [ ] Enabling/disabling AST-based checks. |
| 152 | +- [ ] Capture lessons learned to avoid future quagmires (what worked, what hurt). |
| 153 | + |
| 154 | +## Risk / Quagmire Avoidance |
| 155 | +- Keep PHPStan usage local to plugin repos, not bundled into WPCC. |
| 156 | +- Keep PHP-Parser usage narrowly scoped (one or a few high-value rules). |
| 157 | +- Regularly reassess: if a path starts requiring custom type inference or complex data flow, stop and reconsider before committing. |
| 158 | + |
| 159 | +## LLM Notes |
| 160 | +- When you complete or materially progress any task in this file, update the checklist(s) above rather than creating new documents. |
| 161 | +- Do not expand this document into a full design spec; keep it as a high-level plan plus checklists and link out to more detailed docs in other files if needed. |
| 162 | + |
0 commit comments