This document explains the PHPStan static analysis errors that were discovered and fixed in the Laravel Model Schema Checker package. These fixes improve code quality, type safety, and prevent runtime errors.
PHPStan is a static analysis tool for PHP that focuses on finding bugs in your code without actually running it. It performs type checking, detects potential issues, and enforces coding standards.
- Early Bug Detection: Catches type errors and potential bugs before they reach production
- Improved Code Quality: Enforces better coding practices and type safety
- Better IDE Support: Provides better autocomplete and error detection
- Documentation: Serves as inline documentation for expected types
Error: Method has parameter $param with no type specified
Examples Found:
checkModel($file)- parameter should be\SplFileInfogetNamespaceFromFile($filePath)- parameter should bestringcheckModelFillableProperties($model, string $className)- first parameter should be\Illuminate\Database\Eloquent\Model
Error: Parameter #1 $objectOrClass of class ReflectionClass constructor expects class-string<T>|T, string given
Problem: PHPStan couldn't verify that string parameters passed to ReflectionClass were valid class names.
Error: Class Filament\Resources\Resource not found
Problem: Code was checking is_subclass_of() against Filament classes without verifying Filament was installed.
Error: Parameter #1 $haystack of function str_contains expects string, array|string|true given
Problem: Command options can return various types, not just strings.
Error: Parameter expects string, string|null given
Problems:
json_encode()can returnfalsepreg_replace()can returnnull
// Before
protected function checkModel($file): void
// After
protected function checkModel(\SplFileInfo $file): voidWhy: Type hints document expected parameter types and enable static analysis.
// Before
$reflection = new ReflectionClass($className);
// After
if (!class_exists($className)) {
$this->error("Class {$className} does not exist");
return;
}
$reflection = new ReflectionClass($className);Why: Ensures the class exists before attempting reflection, preventing runtime errors.
// Before
if (is_subclass_of($class, \Filament\Resources\Resource::class)) {
// After
if (class_exists(\Filament\Resources\Resource::class) &&
is_subclass_of($class, \Filament\Resources\Resource::class)) {Why: Prevents errors when optional dependencies like Filament are not installed.
// Before
$specificResource = $this->option('filament-resource');
if (!str_contains($specificResource, '\\')) {
// After
$specificResource = $this->option('filament-resource');
if ($specificResource) {
$specificResource = (string) $specificResource;
if (!str_contains($specificResource, '\\')) {Why: Command options can return various types; casting ensures string operations work correctly.
// Before
$this->line(json_encode($output, JSON_PRETTY_PRINT));
// After
$jsonOutput = json_encode($output, JSON_PRETTY_PRINT);
if ($jsonOutput !== false) {
$this->line($jsonOutput);
} else {
$this->error('Failed to encode JSON output');
}Why: Functions like json_encode() can fail and return false, which would cause type errors.
// Before
$content = preg_replace(...);
File::put($filePath, $content);
// After
$content = preg_replace(...);
if ($content !== null) {
File::put($filePath, $content);
} else {
$this->error("Failed to update file");
}Why: preg_replace() returns null on failure, which File::put() doesn't accept.
parameters:
level: 8
paths:
- src
excludePaths:
- vendor/*
checkMissingIterableValueType: falseLevel 8: Strictest analysis level Paths: Only analyze source code Exclusions: Skip vendor dependencies
<ruleset name="LaravelModelSchemaChecker">
<rule ref="PSR12">
<exclude name="PSR12.Files.FileHeader"/>
</rule>
<rule ref="Generic.Files.LineLength">
<properties>
<property name="lineLimit" value="120"/>
</properties>
</rule>
</ruleset>PSR-12: Modern PHP coding standard Line Length: 120 characters (reasonable limit)
<ruleset name="LaravelModelSchemaChecker">
<rule ref="rulesets/codesize.xml"/>
<rule ref="rulesets/unusedcode.xml"/>
<rule ref="rulesets/naming.xml"/>
</ruleset>Code Size: Detects overly complex code Unused Code: Finds dead code Naming: Enforces naming conventions
The workflow now runs:
- name: Run PHPStan static analysis
run: vendor/bin/phpstan analyse --error-format=github
- name: Run PHPCS code style check
run: vendor/bin/phpcs --standard=phpcs.xml --report=checkstyle
- name: Run PHPMD mess detector
run: vendor/bin/phpmd src text phpmd.xml --reportfile=phpmd-report.xml || true- Prevents type-related runtime errors
- Better IDE support and autocomplete
- Self-documenting code
- Catches bugs before they reach production
- Reduces debugging time
- Improves code reliability
- Enforces consistent coding standards
- Removes dead/unused code
- Prevents code complexity issues
- Easier refactoring with confidence
- Better code documentation
- Reduced technical debt
// Good
public function processUser(User $user): bool
// Bad
public function processUser($user)if (class_exists($className)) {
$reflection = new ReflectionClass($className);
}if (class_exists(\Optional\Package\Class::class)) {
// Use optional functionality
}$result = json_encode($data);
if ($result !== false) {
// Safe to use $result
}- Run static analysis on every PR
- Fail builds on type errors
- Use appropriate strictness levels
# Install dependencies
composer install
# Run PHPStan
vendor/bin/phpstan analyse
# Run PHPCS
vendor/bin/phpcs --standard=phpcs.xml
# Run PHPMD
vendor/bin/phpmd src text phpmd.xml
# Run all checks
composer test:allThese fixes demonstrate the value of static analysis in modern PHP development. By catching type errors, enforcing coding standards, and validating optional dependencies, we've significantly improved the code quality and reliability of the Laravel Model Schema Checker package.
The investment in static analysis pays dividends in:
- Fewer runtime bugs
- Easier maintenance
- Better developer experience
- Higher code quality standards
Remember: Static analysis is not a replacement for testing, but a powerful complement that catches different types of issues early in development. /home/nickd/projects/laravel-model-schema-checker/PHPSTAN_FIXES_GUIDE.md