Repository navigation
Labels
bugSomething isn't workingSomething isn't workingenhancementNew feature or requestNew feature or requestgood first issueGood for newcomersGood for newcomers
Milestone
Description
Description
PHPStan level 7 introduces stricter union type checking. Functions that return type|false or type|null must have their failure cases explicitly handled. Upgrading from level 6 to 7 surfaces 324 errors across 19 packages.
Motivation
Level 7 catches real bugs — unhandled false from fopen(), null dereferences on findOneBy(), unchecked json_encode() failures, etc. Fixing these improves runtime safety and code quality.
Scope
All errors fall into these categories:
| Category | Description | Example |
|---|---|---|
| Null/false return handling | PHP functions like fopen(), glob(), parse_url(), json_encode(), preg_replace(), preg_split(), openssl_encrypt(), file_get_contents(), md5_file(), getimagesize(), strtotime(), realpath(), proc_get_status() return false on failure |
fopen() result assigned to resource property without false check |
| Nullable property access | Calling methods on properties typed as ?Type without null checks |
$this->fs->isDirectory() where $fs is ?FileSystem |
| Dynamic instantiation | Factory patterns using new $adapterClass(...) produce object, not the expected interface |
new $adapterClass(...) passed where CacheInterface expected |
| PHPDoc type corrections | Array key types, nullable annotations, return type mismatches | array<class-string, ...> should be array<string, ...> |
| Cross-method type tracking | PHPStan can't track property nullability across method calls | ensureArchiveOpen() guarantees $this->archive is non-null, but callers still see ?Phar |
Approach
- Update
phpstan.neon.distlevel from6to7 - Work package by package — after fixing each package, run PHPStan on that package and then run its unit tests before moving on
- Prefer minimal, non-breaking fixes:
- Null coalescing (
?? '',?? []) for safe defaults - Explicit guards (
if ($x === null) return;) for nullable properties instanceofchecks for dynamic instantiation in factories@phpstan-assertannotations for methods that guarantee type state (e.g.,ensureArchiveOpen(),isMultiRequest())- Local variables to help PHPStan track property state after lazy initialization or recursive calls
@phpstan-ignoreonly when the error is a known PHPStan limitation (e.g.,parse_stroutput type, dynamic class-string from validation rules)
- Null coalescing (
- Do not change existing logic or control flow
- Do not widen return types just to silence errors
- Preserve cross-PHP-version compatibility (project targets PHP 7.4+)
Special notes
- Mailer package requires a structural refactor: move adapter-specific properties (
$httpClient,$mailer) out ofMailerTraitinto individual adapters, and replace$this->name == 'SMTP'branching with abstract/hook methods (prepare(),sendEmail(),getTransportErrors(),resolveMessageId(), etc.) ob_implicit_flush()inHtmlAdapter.phpandMailerTrait.phpuses a cross-version-compatible call (PHP_VERSION_ID >= 80000 ? false : 0) — suppress with@phpstan-ignore argument.type, do not simplifyHttpClient.phpuses@phpstan-assertonisMultiRequest()andensureSingleRequest()to avoid redundantinstanceofguards throughout the class- Archive adapters use
@phpstan-assertonensureArchiveOpen()for the same reason - Console commands (
DebugBarCommand,ResourceCacheClearCommand) should initializeFileSystemin the constructor (non-nullable) rather than lazily inexec()
Acceptance criteria
-
vendor/bin/phpstan analyse src --level 7reports 0 errors - Full PHPUnit suite passes with no new failures
- No existing logic or control flow is changed
- No return types widened just to suppress errors
- PHP 7.4 compatibility preserved
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't workingenhancementNew feature or requestNew feature or requestgood first issueGood for newcomersGood for newcomers
Activity