Use when adding new error messages to React, or seeing "unknown error code" warnings.
npx skills add kojiromike/dot-claude --skill "phpstan-pr"
Install specific skill from multi-skill repository
# Description
This skill should be used when the user asks to "run phpstan", "fix phpstan errors", "phpstan check", "lint php", "check types", or wants to run PHPStan analysis and fix errors for the current pull request.
# SKILL.md
name: phpstan-pr
description: This skill should be used when the user asks to "run phpstan", "fix phpstan errors", "phpstan check", "lint php", "check types", or wants to run PHPStan analysis and fix errors for the current pull request.
version: 1.0.0
PHPStan PR Check
Run PHPStan at level 10 on the entire OpenEMR codebase, filter results to files changed in the current PR, and fix any findings.
Why Full Codebase Analysis
PHPStan's type inference requires analyzing all files together. Running on a subset of files gives incorrect results because PHPStan can't see type information from other files. Always run on the full codebase and filter the output.
Workflow
1. Get list of changed files in the PR
First, determine which PHP files are changed in the current PR:
# Get the base branch (usually main or master)
base_branch=$(gh pr view --json baseRefName --jq '.baseRefName' 2>/dev/null || echo "main")
# Get list of changed PHP files compared to base branch
git diff --name-only "origin/${base_branch}...HEAD" -- '*.php' | sort -u
If not in a PR context, get changed files from uncommitted changes and recent commits:
# Uncommitted changes
git diff --name-only HEAD -- '*.php'
# Or files changed in recent commits on this branch
git log --name-only --pretty=format: "origin/main..HEAD" -- '*.php' | sort -u
Store the file list for filtering:
changed_files=$(git diff --name-only "origin/${base_branch}...HEAD" -- '*.php' | sort -u)
2. Run PHPStan with JSON output
Run PHPStan at level 10 on the entire codebase with JSON output:
# Prefer composer script if available
composer phpstan -- --level=10 --error-format=json --no-progress 2>/dev/null > /tmp/phpstan-output.json
# Or direct invocation with memory limit
vendor/bin/phpstan analyse --level=10 --memory-limit=8G --error-format=json --no-progress > /tmp/phpstan-output.json
Important: PHPStan returns exit code 1 when errors are found. This is expected - don't treat it as a failure.
3. Filter results to changed files
Use jq to filter the JSON output to only show errors in files changed by the PR:
# Create a jq filter file for the changed files
echo "$changed_files" | jq -R -s 'split("\n") | map(select(length > 0))' > /tmp/changed-files.json
# Filter PHPStan output to only changed files
jq --slurpfile files /tmp/changed-files.json '
.files | to_entries | map(
select(.key as $path | $files[0] | map(. == $path or ($path | endswith("/" + .))) | any)
) | from_entries
' /tmp/phpstan-output.json > /tmp/phpstan-filtered.json
Alternative single-command approach using grep patterns:
# Build a grep pattern from changed files
pattern=$(echo "$changed_files" | paste -sd'|' -)
# Filter JSON to matching files
jq --arg pattern "$pattern" '
.files | to_entries | map(
select(.key | test($pattern))
) | from_entries
' /tmp/phpstan-output.json
4. Display filtered errors
Format the filtered errors for review:
jq -r '
to_entries[] |
.key as $file |
.value.messages[] |
"\($file):\(.line): \(.message)"
' /tmp/phpstan-filtered.json
Or get a summary count:
jq '[.[] | .messages | length] | add // 0' /tmp/phpstan-filtered.json
5. Fix the errors
For each error in the filtered output:
- Read the file and understand the context
- Fix the type error according to PHPStan's suggestion
- Common fixes include:
- Adding type hints to parameters and return types
- Adding null checks before accessing nullable values
- Using proper type assertions or instanceof checks
- Fixing incorrect property/method access
- Adding
@varannotations for complex types
Do NOT fix errors in files that weren't changed in the PR. Only fix errors in the filtered list.
6. Re-run to verify fixes
After making fixes, re-run the full analysis and filter again to verify:
composer phpstan -- --level=10 --error-format=json --no-progress 2>/dev/null > /tmp/phpstan-output.json
jq --arg pattern "$pattern" '
.files | to_entries | map(
select(.key | test($pattern))
) | from_entries | [.[] | .messages | length] | add // 0
' /tmp/phpstan-output.json
Report remaining errors or confirm all fixed.
Error Categories
Common PHPStan errors and how to fix them:
| Error | Fix |
|---|---|
| Parameter $x has no type | Add type hint: function foo(string $x) |
| Return type missing | Add return type: function foo(): string |
| Cannot access property on null | Add null check or use null-safe operator |
| Method X not found | Check class hierarchy, add @method annotation if dynamic |
| Property has no type | Add @var annotation or typed property |
| Undefined variable | Initialize variable or check logic flow |
Notes
- Always level 10 - This is the strictest level and catches the most issues
- Expect many pre-existing errors - OpenEMR has 100,000+ PHPStan errors at level 10; this is normal for a legacy codebase
- Only fix what you changed - Ignore pre-existing errors in unchanged code
- Don't add strict_types - OpenEMR doesn't use
declare(strict_types=1) - Prefer
?typeovertype|null- Use nullable syntax per project conventions
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.