Loading...
Loading...
Fix PHPStan static analysis errors by adding type annotations and PHPDocs. Use when encountering PHPStan errors, type mismatches, missing type hints, or static analysis failures. Never ignores errors without user approval.
npx skill4agent add marcelorodrigo/agent-skills phpstan-fixerphpstan.neonignoreErrorsvendor/# Check for Laravel
grep laravel/framework composer.json
# Check for Symfony
grep symfony/symfony composer.json
# Check for PHPStan extensions
grep phpstan composer.json
# Read PHPStan config
cat phpstan.neon
# Check project guidelines
cat AGENTS.md------ ----------------------------------------------
Line /path/to/File.php
------ ----------------------------------------------
42 Parameter $user of method foo() has invalid
type App\User.
💡 Identifier: parameter.type
------ ----------------------------------------------parameter.typemissingType.returnvendor/bin/phpstan analysemissingType.parameterParameter $name has no type specified.// Before
function greet($name) {
return "Hello, $name";
}
// After
function greet(string $name): string {
return "Hello, $name";
}// Before
function processUsers($users) { ... }
// After
/**
* @param array<int, User> $users
*/
function processUsers(array $users): void { ... }missingType.returnMethod foo() has no return type specified.// Before
public function getUser() {
return $this->user;
}
// After
public function getUser(): User {
return $this->user;
}// Before
public function findUser($id) { ... }
// After
/**
* @return User|null
*/
public function findUser(int $id): ?User { ... }argument.typeParameter #1 $id of method find() expects int, string given.// Before
$user = $repository->find($request->input('id'));
// After
$user = $repository->find((int) $request->input('id'));// Better approach
$id = $request->integer('id'); // Laravel helper
$user = $repository->find($id);return.typeMethod foo() should return User but returns User|null.// Before
public function getUser(): User {
return $this->user ?? null;
}
// After
public function getUser(): ?User {
return $this->user ?? null;
}public function getUser(): User {
assert($this->user !== null);
return $this->user;
}property.notFoundAccess to an undefined property User::$name.class User {
private string $name;
public function __construct(string $name) {
$this->name = $name;
}
}/**
* @property string $name
*/
class User {
public function __get($key) { ... }
}# Generate PHPDocs for Eloquent models
php artisan ide-helper:modelsproperty.onlyWrittenProperty User::$name is never read, only written.// If truly unused, remove it
// If needed, add usage:
public function getName(): string {
return $this->name;
}method.notFoundCall to an undefined method App\User::getFullName().class User {
public function getFullName(): string {
return $this->first_name . ' ' . $this->last_name;
}
}/**
* @method string getFullName()
*/
class User {
public function __call($method, $args) { ... }
}@mixin/**
* @mixin \Illuminate\Database\Eloquent\Builder
*/
class User extends Model { ... }offsetAccess.notFoundOffset 'email' does not exist on array./**
* @param array{email: string, name: string} $data
*/
function createUser(array $data): void {
echo $data['email']; // PHPStan knows this exists
}if (isset($data['email'])) {
echo $data['email'];
}$email = $data['email'] ?? 'default@example.com';missingType.genericsClass Collection has @template T but does not specify it.// Before
/** @var Collection $users */
$users = User::all();
// After
/** @var Collection<int, User> $users */
$users = User::all();deadCode.unreachableUnreachable statement - code above always terminates.// Before
function foo() {
return true;
echo "This never runs"; // Error
}
// After
function foo() {
return true;
}identical.alwaysTrueidentical.alwaysFalseStrict comparison using === between int and string will always evaluate to false.// Before
if ($id === '123') { ... } // $id is int
// After
if ($id === 123) { ... }composer require --dev larastan/larastanphpstan.neonincludes:
- vendor/larastan/larastan/extension.neon// 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();composer require --dev phpstan/phpstan-symfonyphpstan.neonincludes:
- vendor/phpstan/phpstan-symfony/extension.neon
parameters:
symfony:
containerXmlPath: var/cache/dev/App_KernelDevDebugContainer.xml// 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();I found a PHPStan error that cannot be easily fixed:
Error: [describe error]
Location: [file:line]
Reason: [explain why it can't be fixed]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"{
"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"
}
]
}]
}// 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# Don't add this to phpstan.neon without user consent
parameters:
ignoreErrors:
- '#.*#' # NEVER\PHPStan\dumpType()$user = User::find($id);
\PHPStan\dumpType($user); // Reports: App\User|null
// Remove before committing!composer dump-autoloadpathsphpstan.neon@varcomposer require --dev barryvdh/laravel-ide-helper
php artisan ide-helper:generate
php artisan ide-helper:models --write
php artisan ide-helper:metaargument.*return.*missingType.*property.*method.*offsetAccess.*class.*deadCode.*identical.*equal.*