phpstan-fixer

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

PHPStan Error Fixer

PHPStan错误修复工具

Fix PHPStan static analysis errors through proper type annotations, PHPDocs, and code improvements. This skill teaches agents how to resolve errors without suppressing them, respecting the project's configured strictness level.
通过正确的类型注解、PHPDocs和代码优化来修复PHPStan静态分析错误。本技能指导Agent如何在不抑制错误的情况下解决问题,同时尊重项目配置的严格性级别。

Core Principles

核心原则

  1. Never suppress errors as first resort — Fix the root cause with proper types and annotations
  2. Respect user configuration — Never modify
    phpstan.neon
    settings (level, paths, parameters)
  3. No silent ignoring — Never add
    ignoreErrors
    to config without explicit user approval
  4. Context-aware fixes — Understand the project type (Laravel, Symfony, vanilla PHP) before proposing solutions
  5. Ask before ignoring — If a legitimate ignore is needed, explain why and get user approval first
  6. Don't fix third-party code — Never modify files in
    vendor/
    . Use stub files instead to override wrong types
  1. 绝不优先抑制错误 — 使用正确的类型和注解修复根本原因
  2. 尊重用户配置 — 绝不修改
    phpstan.neon
    设置(级别、路径、参数)
  3. 不静默忽略错误 — 未经用户明确批准,绝不向配置中添加
    ignoreErrors
  4. 上下文感知修复 — 在提出解决方案前,先了解项目类型(Laravel、Symfony、原生PHP)
  5. 忽略前先询问 — 如果确实需要忽略错误,先说明原因并获得用户批准
  6. 不修改第三方代码 — 绝不修改
    vendor/
    目录下的文件。可使用存根文件来覆盖错误的类型定义

Workflow

工作流程

Step 1: Understand the Project Context

步骤1:了解项目上下文

Before fixing errors, identify the project type:
bash
undefined
在修复错误前,先确定项目类型:
bash
undefined

Check for Laravel

检查是否为Laravel项目

grep laravel/framework composer.json
grep laravel/framework composer.json

Check for Symfony

检查是否为Symfony项目

grep symfony/symfony composer.json
grep symfony/symfony composer.json

Check for PHPStan extensions

检查已安装的PHPStan扩展

grep phpstan composer.json
grep phpstan composer.json

Read PHPStan config

读取PHPStan配置

cat phpstan.neon
cat phpstan.neon

Check project guidelines

查看项目规范

cat AGENTS.md

**Key information to extract:**
- PHPStan level (0-10, or max)
- Installed PHPStan extensions (larastan, phpstan-strict-rules, etc.)
- Framework-specific helpers (Laravel IDE Helper, Symfony plugin)
- Project-specific type conventions
cat AGENTS.md

**需要提取的关键信息:**
- PHPStan级别(0-10,或max)
- 已安装的PHPStan扩展(larastan、phpstan-strict-rules等)
- 框架专属工具(Laravel IDE Helper、Symfony插件)
- 项目专属的类型约定

Step 2: Analyze the Error

步骤2:分析错误

PHPStan errors have this structure:
text
------ ----------------------------------------------
Line   /path/to/File.php
------ ----------------------------------------------
42     Parameter $user of method foo() has invalid
       type App\User.
       💡 Identifier: parameter.type
------ ----------------------------------------------
Extract:
  1. Error identifier (e.g.,
    parameter.type
    ,
    missingType.return
    )
  2. Error location (file, line number)
  3. Context (what's the code trying to do?)
PHPStan错误的结构如下:
text
------ ----------------------------------------------
Line   /path/to/File.php
------ ----------------------------------------------
42     Parameter $user of method foo() has invalid
       type App\User.
       💡 Identifier: parameter.type
------ ----------------------------------------------
提取内容:
  1. 错误标识符(例如:
    parameter.type
    missingType.return
  2. 错误位置(文件、行号)
  3. 上下文(代码的意图是什么?)

Step 3: Apply the Right Fix

步骤3:应用正确的修复方案

Use the error identifier to determine the fix strategy:
根据错误标识符确定修复策略:

Step 4: Verify the Fix

步骤4:验证修复结果

After applying fixes, run PHPStan again to confirm:
bash
vendor/bin/phpstan analyse
Important:
  • If new errors appear, the fix may have been incorrect. Re-analyze the error and try a different approach.
  • If the same error persists, the fix wasn't applied correctly. Double-check the code.
  • If errors are resolved, mark the fix as successful and move to the next error.

应用修复后,重新运行PHPStan确认:
bash
vendor/bin/phpstan analyse
注意事项:
  • 如果出现新错误,说明修复方案可能有误。重新分析错误并尝试其他方法。
  • 如果相同错误仍然存在,说明修复未正确应用。仔细检查代码。
  • 如果错误已解决,标记修复成功并处理下一个错误。

Common Error Fixes

常见错误修复方案

Type-Related Errors

类型相关错误

missingType.parameter
— Missing parameter type

missingType.parameter
— 缺少参数类型

Error:
text
Parameter $name has no type specified.
Fix — Add native type:
php
// Before
function greet($name) {
    return "Hello, $name";
}

// After
function greet(string $name): string {
    return "Hello, $name";
}
Fix — Use PHPDoc for complex types:
php
// Before
function processUsers($users) { ... }

// After
/**
 * @param array<int, User> $users
 */
function processUsers(array $users): void { ... }

错误信息:
text
Parameter $name has no type specified.
修复方案 — 添加原生类型:
php
// 修复前
function greet($name) {
    return "Hello, $name";
}

// 修复后
function greet(string $name): string {
    return "Hello, $name";
}
修复方案 — 为复杂类型使用PHPDoc:
php
// 修复前
function processUsers($users) { ... }

// 修复后
/**
 * @param array<int, User> $users
 */
function processUsers(array $users): void { ... }

missingType.return
— Missing return type

missingType.return
— 缺少返回类型

Error:
text
Method foo() has no return type specified.
Fix — Add native return type:
php
// Before
public function getUser() {
    return $this->user;
}

// After
public function getUser(): User {
    return $this->user;
}
Fix — Use PHPDoc for union/intersection types:
php
// Before
public function findUser($id) { ... }

// After
/**
 * @return User|null
 */
public function findUser(int $id): ?User { ... }

错误信息:
text
Method foo() has no return type specified.
修复方案 — 添加原生返回类型:
php
// 修复前
public function getUser() {
    return $this->user;
}

// 修复后
public function getUser(): User {
    return $this->user;
}
修复方案 — 为联合/交叉类型使用PHPDoc:
php
// 修复前
public function findUser($id) { ... }

// 修复后
/**
 * @return User|null
 */
public function findUser(int $id): ?User { ... }

argument.type
— Wrong argument type

argument.type
— 参数类型错误

Error:
text
Parameter #1 $id of method find() expects int, string given.
Fix — Cast the argument:
php
// Before
$user = $repository->find($request->input('id'));

// After
$user = $repository->find((int) $request->input('id'));
Fix — Narrow the type earlier:
php
// Better approach
$id = $request->integer('id'); // Laravel helper
$user = $repository->find($id);

错误信息:
text
Parameter #1 $id of method find() expects int, string given.
修复方案 — 转换参数类型:
php
// 修复前
$user = $repository->find($request->input('id'));

// 修复后
$user = $repository->find((int) $request->input('id'));
修复方案 — 提前缩小类型范围:
php
// 更优方案
$id = $request->integer('id'); // Laravel辅助方法
$user = $repository->find($id);

return.type
— Wrong return type

return.type
— 返回类型错误

Error:
text
Method foo() should return User but returns User|null.
Fix — Adjust return type:
php
// Before
public function getUser(): User {
    return $this->user ?? null;
}

// After
public function getUser(): ?User {
    return $this->user ?? null;
}
Fix — Ensure non-null with assertion:
php
public function getUser(): User {
    assert($this->user !== null);
    return $this->user;
}

错误信息:
text
Method foo() should return User but returns User|null.
修复方案 — 调整返回类型:
php
// 修复前
public function getUser(): User {
    return $this->user ?? null;
}

// 修复后
public function getUser(): ?User {
    return $this->user ?? null;
}
修复方案 — 使用断言确保非空:
php
public function getUser(): User {
    assert($this->user !== null);
    return $this->user;
}

Property Errors

属性相关错误

property.notFound
— Undefined property access

property.notFound
— 访问未定义属性

Error:
text
Access to an undefined property User::$name.
Fix — Add property declaration:
php
class User {
    private string $name;
    
    public function __construct(string $name) {
        $this->name = $name;
    }
}
Fix — Document magic property:
php
/**
 * @property string $name
 */
class User {
    public function __get($key) { ... }
}
Fix (Laravel) — Use IDE Helper:
bash
undefined
错误信息:
text
Access to an undefined property User::$name.
修复方案 — 添加属性声明:
php
class User {
    private string $name;
    
    public function __construct(string $name) {
        $this->name = $name;
    }
}
修复方案 — 记录魔术属性:
php
/**
 * @property string $name
 */
class User {
    public function __get($key) { ... }
}
修复方案(Laravel)— 使用IDE Helper:
bash
undefined

Generate PHPDocs for Eloquent models

为Eloquent模型生成PHPDoc

php artisan ide-helper:models

---
php artisan ide-helper:models

---

property.onlyWritten
— Property written but never read

property.onlyWritten
— 属性仅被写入但未被读取

Error:
text
Property User::$name is never read, only written.
Fix — Remove unused property or add getter:
php
// If truly unused, remove it
// If needed, add usage:
public function getName(): string {
    return $this->name;
}

错误信息:
text
Property User::$name is never read, only written.
修复方案 — 删除未使用属性或添加获取方法:
php
// 如果确实未使用,直接删除
// 如果需要保留,添加使用逻辑:
public function getName(): string {
    return $this->name;
}

Method Errors

方法相关错误

method.notFound
— Undefined method call

method.notFound
— 调用未定义方法

Error:
text
Call to an undefined method App\User::getFullName().
Fix — Add method:
php
class User {
    public function getFullName(): string {
        return $this->first_name . ' ' . $this->last_name;
    }
}
Fix — Document magic method:
php
/**
 * @method string getFullName()
 */
class User {
    public function __call($method, $args) { ... }
}
Fix (Laravel) — Add to
@mixin
for query builders:
php
/**
 * @mixin \Illuminate\Database\Eloquent\Builder
 */
class User extends Model { ... }

错误信息:
text
Call to an undefined method App\User::getFullName().
修复方案 — 添加方法:
php
class User {
    public function getFullName(): string {
        return $this->first_name . ' ' . $this->last_name;
    }
}
修复方案 — 记录魔术方法:
php
/**
 * @method string getFullName()
 */
class User {
    public function __call($method, $args) { ... }
}
修复方案(Laravel)— 为查询构建器添加
@mixin
php
/**
 * @mixin \Illuminate\Database\Eloquent\Builder
 */
class User extends Model { ... }

Array/Offset Errors

数组/偏移量相关错误

offsetAccess.notFound
— Undefined array offset

offsetAccess.notFound
— 访问未定义数组偏移量

Error:
text
Offset 'email' does not exist on array.
Fix — Use array shape PHPDoc:
php
/**
 * @param array{email: string, name: string} $data
 */
function createUser(array $data): void {
    echo $data['email']; // PHPStan knows this exists
}
Fix — Add existence check:
php
if (isset($data['email'])) {
    echo $data['email'];
}
Fix — Use null coalescing:
php
$email = $data['email'] ?? 'default@example.com';

错误信息:
text
Offset 'email' does not exist on array.
修复方案 — 使用数组结构PHPDoc:
php
/**
 * @param array{email: string, name: string} $data
 */
function createUser(array $data): void {
    echo $data['email']; // PHPStan会识别该键存在
}
修复方案 — 添加存在性检查:
php
if (isset($data['email'])) {
    echo $data['email'];
}
修复方案 — 使用空合并运算符:
php
$email = $data['email'] ?? 'default@example.com';

Generics Errors

泛型相关错误

missingType.generics
— Missing generic type

missingType.generics
— 缺少泛型类型

Error:
text
Class Collection has @template T but does not specify it.
Fix — Specify generic type in PHPDoc:
php
// Before
/** @var Collection $users */
$users = User::all();

// After
/** @var Collection<int, User> $users */
$users = User::all();
Fix (Laravel) — Use IDE Helper stubs for collections.

错误信息:
text
Class Collection has @template T but does not specify it.
修复方案 — 在PHPDoc中指定泛型类型:
php
// 修复前
/** @var Collection $users */
$users = User::all();

// 修复后
/** @var Collection<int, User> $users */
$users = User::all();
修复方案(Laravel)— 为集合使用IDE Helper存根文件。

Dead Code Errors

死代码相关错误

deadCode.unreachable
— Unreachable code

deadCode.unreachable
— 不可达代码

Error:
text
Unreachable statement - code above always terminates.
Fix — Remove dead code:
php
// Before
function foo() {
    return true;
    echo "This never runs"; // Error
}

// After
function foo() {
    return true;
}

错误信息:
text
Unreachable statement - code above always terminates.
修复方案 — 删除死代码:
php
// 修复前
function foo() {
    return true;
    echo "This never runs"; // 错误
}

// 修复后
function foo() {
    return true;
}

identical.alwaysTrue
/
identical.alwaysFalse
— Condition is always true/false

identical.alwaysTrue
/
identical.alwaysFalse
— 条件始终为真/假

Error:
text
Strict comparison using === between int and string will always evaluate to false.
Fix — Remove useless condition or fix type:
php
// Before
if ($id === '123') { ... } // $id is int

// After
if ($id === 123) { ... }

错误信息:
text
Strict comparison using === between int and string will always evaluate to false.
修复方案 — 删除无用条件或修正类型:
php
// 修复前
if ($id === '123') { ... } // $id是int类型

// 修复后
if ($id === 123) { ... }

Framework-Specific Fixes

框架专属修复方案

Laravel

Laravel

Install Larastan for Laravel-aware analysis:
bash
composer require --dev larastan/larastan
Check
phpstan.neon
includes Larastan
(ask user to add if missing):
yaml
includes:
    - vendor/larastan/larastan/extension.neon
Common Laravel fixes:
php
// Eloquent relationships - use @property PHPDoc
/**
 * @property-read \Illuminate\Database\Eloquent\Collection<int, Post> $posts
 */
class User extends Model {
    public function posts() {
        return $this->hasMany(Post::class);
    }
}

// Collections - specify generic types
/** @var \Illuminate\Support\Collection<int, User> $users */
$users = User::all();

// Request input - use typed helpers
$id = $request->integer('id'); // Not $request->input('id')
$email = $request->string('email')->toString();
安装Larastan以支持Laravel专属分析:
bash
composer require --dev larastan/larastan
检查
phpstan.neon
是否包含Larastan
(如果缺失,请求用户添加):
yaml
includes:
    - vendor/larastan/larastan/extension.neon
常见Laravel修复方案:
php
// Eloquent关联 - 使用@property PHPDoc
/**
 * @property-read \Illuminate\Database\Eloquent\Collection<int, Post> $posts
 */
class User extends Model {
    public function posts() {
        return $this->hasMany(Post::class);
    }
}

// 集合 - 指定泛型类型
/** @var \Illuminate\Support\Collection<int, User> $users */
$users = User::all();

// 请求输入 - 使用类型化辅助方法
$id = $request->integer('id'); // 而非$request->input('id')
$email = $request->string('email')->toString();

Symfony

Symfony

Install Symfony PHPStan extension:
bash
composer require --dev phpstan/phpstan-symfony
Check
phpstan.neon
includes Symfony extension
(ask user to add if missing):
yaml
includes:
    - vendor/phpstan/phpstan-symfony/extension.neon
parameters:
    symfony:
        containerXmlPath: var/cache/dev/App_KernelDevDebugContainer.xml
Common Symfony fixes:
php
// Service container - use proper type hints
public function __construct(
    private UserRepository $userRepository, // Not mixed
) {}

// Forms - type the data
/** @var array{email: string, password: string} $data */
$data = $form->getData();

安装Symfony PHPStan扩展:
bash
composer require --dev phpstan/phpstan-symfony
检查
phpstan.neon
是否包含Symfony扩展
(如果缺失,请求用户添加):
yaml
includes:
    - vendor/phpstan/phpstan-symfony/extension.neon
parameters:
    symfony:
        containerXmlPath: var/cache/dev/App_KernelDevDebugContainer.xml
常见Symfony修复方案:
php
// 服务容器 - 使用正确的类型提示
public function __construct(
    private UserRepository $userRepository, // 而非mixed
) {}

// 表单 - 为数据指定类型
/** @var array{email: string, password: string} $data */
$data = $form->getData();

When Ignoring is Acceptable (Last Resort)

何时可以忽略错误(最后手段)

Sometimes a legitimate ignore is needed. Always ask the user first using the Question tool:
Step 1: Explain the situation
text
I found a PHPStan error that cannot be easily fixed:

Error: [describe error]
Location: [file:line]
Reason: [explain why it can't be fixed]
Step 2: Use Question tool to get user choice
text
Use the Question tool with these options:
- Header: "PHPStan Error Resolution"
- Question: "How would you like to handle this error?"
- Options:
  1. "Use @phpstan-ignore with comment" - description: "Add inline ignore with explanation (recommended for third-party type issues)"
  2. "Add to baseline" - description: "Generate baseline file (recommended for legacy code migration)"
  3. "Refactor code" - description: "Modify code to satisfy PHPStan (most robust but may require significant changes)"
  4. "Skip for now" - description: "Leave unfixed and continue with other errors"
Example Question tool usage:
json
{
  "questions": [{
    "header": "PHPStan Error Resolution",
    "question": "File src/Service.php:42 has argument type mismatch with third-party API. How should I handle this?",
    "options": [
      {
        "label": "Use @phpstan-ignore (Recommended)",
        "description": "Add inline ignore with explanation"
      },
      {
        "label": "Add to baseline",
        "description": "Include in baseline file for tracking"
      },
      {
        "label": "Refactor code",
        "description": "Modify to satisfy PHPStan"
      },
      {
        "label": "Skip for now",
        "description": "Continue with other errors"
      }
    ]
  }]
}
Valid reasons for ignoring:
  • Third-party library with wrong types (and no stub file available)
  • Reflection-based code that's correct but PHPStan can't understand
  • Complex business logic that's type-safe at runtime but not provably so statically
  • Temporary during large refactoring (use baseline)
How to ignore (if approved):
php
// Inline ignore with explanation
/** @phpstan-ignore argument.type (API returns string|int, we handle both) */
$result = $api->getValue();

// Baseline for legacy code
vendor/bin/phpstan analyse --generate-baseline
Never do this without approval:
yaml
undefined
有时确实需要忽略错误。务必先使用Question工具询问用户
步骤1:说明情况
text
我发现一个无法轻松修复的PHPStan错误:

错误:[描述错误内容]
位置:[文件:行号]
原因:[说明无法修复的原因]
步骤2:使用Question工具获取用户选择
text
使用Question工具,参数如下:
- 标题:"PHPStan错误处理"
- 问题:"您希望如何处理此错误?"
- 选项:
  1. "使用@phpstan-ignore并添加注释" - 描述:"添加行内忽略并附上说明(推荐用于第三方类型问题)"
  2. "添加到基线文件" - 描述:"生成基线文件(推荐用于遗留代码迁移)"
  3. "重构代码" - 描述:"修改代码以满足PHPStan要求(最稳健,但可能需要大量修改)"
  4. "暂时跳过" - 描述:"留待后续处理,继续修复其他错误"
Question工具使用示例:
json
{
  "questions": [{
    "header": "PHPStan错误处理",
    "question": "文件src/Service.php:42存在与第三方API的参数类型不匹配问题。应如何处理?",
    "options": [
      {
        "label": "使用@phpstan-ignore(推荐)",
        "description": "添加行内忽略并附上说明"
      },
      {
        "label": "添加到基线文件",
        "description": "纳入基线文件以便跟踪"
      },
      {
        "label": "重构代码",
        "description": "修改代码以满足PHPStan要求"
      },
      {
        "label": "暂时跳过",
        "description": "继续修复其他错误"
      }
    ]
  }]
}
可忽略错误的合理原因:
  • 第三方库存在类型错误(且无可用存根文件)
  • 基于反射的代码逻辑正确,但PHPStan无法识别
  • 复杂业务逻辑在运行时类型安全,但无法通过静态分析证明
  • 大型重构期间的临时方案(使用基线文件)
如何忽略错误(获得批准后):
php
// 行内忽略并添加说明
/** @phpstan-ignore argument.type (API返回string|int,我们已处理两种情况) */
$result = $api->getValue();

// 为遗留代码生成基线文件
vendor/bin/phpstan analyse --generate-baseline
未经批准绝不能执行此操作:
yaml
undefined

Don't add this to phpstan.neon without user consent

未经用户同意,绝不要向phpstan.neon添加此配置

parameters: ignoreErrors: - '#.*#' # NEVER

---
parameters: ignoreErrors: - '#.*#' # 绝对禁止

---

Debugging Types

类型调试

Use
\PHPStan\dumpType()
to see what PHPStan thinks:
php
$user = User::find($id);
\PHPStan\dumpType($user); // Reports: App\User|null

// Remove before committing!

使用
\PHPStan\dumpType()
查看PHPStan识别的类型:
php
$user = User::find($id);
\PHPStan\dumpType($user); // 输出:App\User|null

// 提交代码前请删除此代码!

Troubleshooting

故障排除

PHPStan doesn't recognize a valid type

PHPStan无法识别有效类型

Check:
  1. Is the class autoloadable? (
    composer dump-autoload
    )
  2. Does PHPStan scan the file? (Check
    paths
    in
    phpstan.neon
    )
  3. Is there a typo in the namespace?
检查项:
  1. 类是否可自动加载?(执行
    composer dump-autoload
  2. PHPStan是否扫描该文件?(检查
    phpstan.neon
    中的
    paths
    配置)
  3. 命名空间是否存在拼写错误?

Type inference doesn't work

类型推断失效

Check:
  1. Are you using inline
    @var
    too much? (Fix at source instead)
  2. Is the function/method return type specified?
  3. Are you using dynamic features PHPStan can't analyze?
检查项:
  1. 是否过度使用行内
    @var
    ?(应从源头修复)
  2. 函数/方法是否指定了返回类型?
  3. 是否使用了PHPStan无法分析的动态特性?

Laravel magic methods not recognized

Laravel魔术方法未被识别

Install and run:
bash
composer require --dev barryvdh/laravel-ide-helper
php artisan ide-helper:generate
php artisan ide-helper:models --write
php artisan ide-helper:meta

安装并运行:
bash
composer require --dev barryvdh/laravel-ide-helper
php artisan ide-helper:generate
php artisan ide-helper:models --write
php artisan ide-helper:meta

Error Identifier Reference

错误标识符参考

Most common categories:
  • argument.*
    — Function/method argument issues
  • return.*
    — Return type mismatches
  • missingType.*
    — Missing type declarations
  • property.*
    — Property access/declaration issues
  • method.*
    — Method call issues
  • offsetAccess.*
    — Array/ArrayAccess issues
  • class.*
    — Class inheritance/usage issues
  • deadCode.*
    — Unreachable code
  • identical.*
    /
    equal.*
    — Comparison issues

最常见类别:
  • argument.*
    — 函数/方法参数问题
  • return.*
    — 返回类型不匹配
  • missingType.*
    — 缺少类型声明
  • property.*
    — 属性访问/声明问题
  • method.*
    — 方法调用问题
  • offsetAccess.*
    — 数组/ArrayAccess访问问题
  • class.*
    — 类继承/使用问题
  • deadCode.*
    — 不可达代码
  • identical.*
    /
    equal.*
    — 比较操作问题

Resources

参考资源