kojiromike

phpstan-pr

0
0
# Install this skill:
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:

  1. Read the file and understand the context
  2. Fix the type error according to PHPStan's suggestion
  3. Common fixes include:
  4. Adding type hints to parameters and return types
  5. Adding null checks before accessing nullable values
  6. Using proper type assertions or instanceof checks
  7. Fixing incorrect property/method access
  8. Adding @var annotations 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 ?type over type|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.