PHP deep integration: test coverage, security, imports, tree-sitter#178
PHP deep integration: test coverage, security, imports, tree-sitter#178ikx94 wants to merge 4 commits intopeteromallet:mainfrom
Conversation
…fixes WIP — coded with Claude Code. New: - languages/php/test_coverage.py — full test coverage hooks (PHPUnit/Pest assertions, use-statement parsing incl group use, has_testable_logic, is_runtime_entrypoint, map_test_to_source) - languages/php/review_data/dimensions.override.json Fixes across 10 files addressing ~20 PHP gaps: - SECRET_NAME_RE: match $variable assignments (peteromallet#14) - ENV_LOOKUPS: add env(), getenv(), $_ENV[], config() (peteromallet#11) - LOG_CALLS: add Log::info/error/etc, error_log() (peteromallet#13) - RANDOM_CALLS: add rand(), mt_rand() (peteromallet#12) - Import query: group use App\Models\{User, Post} (peteromallet#5) - resolve_php_import: actually read composer.json PSR-4 (peteromallet#4) - Unused imports: handle aliased use Foo as Bar (peteromallet#6) - class_query: add enum_declaration (peteromallet#8) - _CLOSURE_NODE_TYPES: add anonymous_function_creation_expression (peteromallet#9) - _BRANCHING_NODE_TYPES: add match_conditional_expression (peteromallet#10) - Signature _ALLOWLIST: __construct, boot, register, render, etc (peteromallet#22) - _infer_lang_name: add .php -> php (peteromallet#2) - generic_lang: entry_patterns parameter (peteromallet#3) - PHP __init__: wire test_coverage, entry_patterns, zone_rules, exclude storage/bootstrap/cache/IDE helpers (peteromallet#1,peteromallet#16,peteromallet#19,peteromallet#20,peteromallet#24) - Remediation text: add random_bytes() for PHP (peteromallet#15) Deferred: peteromallet#17 (barrel_names — N/A), peteromallet#18 (dynamic_import_finder) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Trait `use` inside class bodies (use_declaration) was invisible to the dep graph — trait files only referenced this way appeared orphaned. - Add use_declaration query patterns for both bare names and FQNs - Strip leading backslash from FQN trait uses (\App\Traits\X) - Resolve bare trait names (HasUuid) via filesystem search in app/src/lib - Cache bare-name lookups to avoid repeated walks Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Hey, nice work on this! Looks like a solid implementation. I don't have any PHP projects to test against on my end — would you be able to run it against a real Laravel codebase and verify those tree-sitter edge cases you mentioned in the test plan? That would go a long way toward getting this merged. |
`orphaned.py` checks `any(p in r for p in entry_patterns)` where `r = rel(filepath)` returns relative paths like `app/Http/Controllers/Foo.php`. All 24 PHP entry patterns had leading `/` — substring check always failed, zero patterns matched. Every Laravel file flagged orphaned. Also added missing Laravel convention-loaded directories: Models, Enums, Actions, Filament, Livewire, View/Components, Http/Requests, Exceptions, bootstrap, public, lang. Extended test_coverage entrypoint patterns and content-based detection (FormRequest, Resource). Tested against two real Laravel codebases (sologaming, zalon). Code quality: 51% → 97% on both projects. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Tested against two real Laravel codebases — a large e-commerce app (~500 production files) and a SaaS booking platform (~760 files). Found and fixed a bug that was tanking PHP scores everywhere. The bugAll This is why code quality was stuck at ~50% on every Laravel project. The fix (just pushed)Stripped leading
Extended Real-world resultsProject A (large e-commerce, ~500 production files):
Project B (SaaS platform, ~760 files):
Note: same entry pattern bug exists in other languagesDart, Go, TypeScript, C#, GDScript all have leading Tree-sitter edge casesThe tree-sitter queries handled everything I threw at them — standard |
|
Really appreciate the thoroughness here — the real-world testing against two Laravel codebases is exactly what was needed, and the entry pattern slash bug is a great catch. The before/after numbers speak for themselves (51% → 98% code quality is huge). I'm in the middle of pushing a big batch of changes right now so I don't want to merge this on top of that and create a mess. Going to add this in once things settle down on my end. Thanks again for the work on this — it's a solid contribution. |
|
@ikx94 Great work here, and very convincing Laravel validation! Drive-by comment from a newcomer: I only discovered this project recently, so my understanding is still limited. This PR introduces many Laravel-specific conventions into the PHP layer. My concern is that others framework projects could get noisier results (false positives/negatives on entrypoints and architecture signals) if these conventions are applied by default. Would moving to a framework-agnostic PHP design in this PR require a larger refactor, or would you prefer keeping this Laravel-first now and adding framework support for PHP in a follow-up? I’m not trying to block this PR, I’m trying to align on direction early so I can try to contribute Symfony support later :) |
I would encorage you to try it on Symfony and to make it less Laravel specific, that’s definitely the way to go |
Summary
WIP — coded with Claude Code.
Upgrades PHP from a minimal phpstan-only plugin to near-parity with Python/TypeScript for framework-aware analysis. Addresses ~20 distinct gaps across security detection, import resolution, test coverage mapping, and tree-sitter queries.
The prompt that started this:
New files
languages/php/test_coverage.py— PHPUnit/Pest assertion patterns,use-statement parsing (incl groupuse A\{B,C}),has_testable_logic(excludes interfaces/config/migration stubs),is_runtime_entrypoint(controllers/commands/jobs/routes),map_test_to_source,strip_test_markers,strip_commentslanguages/php/review_data/dimensions.override.json— PHP/Laravel abstraction fitness review guidanceFixes (12 files, ~20 issues)
$var =security.py\$?to char classsecurity.pyenv(,getenv(,$_ENV[,config(security.pyLog::info/error/...,error_log()security.pyrand(,mt_rand(_specs.py,_imports.py_imports.pycomposer.jsonuse Foo as BarFP_unused_imports.py_specs.pyenum_declaration_complexity.pyanonymous_function_creation_expression_complexity.pymatch_conditional_expressionsignature.py__construct,boot,register,render,toArray,rules,authorizemapping.py.php→phpgeneric.pyentry_patternsparamphp/__init__.pyrules.pyrandom_bytes()for PHPdimensions.override.jsonDeferred
Test plan
🤖 Generated with Claude Code