Troubleshooting¶
Parts of Laravel remain genuinely too dynamic to analyse. This page collects the known limits and what to do about each. If your problem is a property or relation that is not resolving, start with the FAQ instead.
Ignoring what cannot be fixed¶
Where a limit is real, ignore it by identifier rather than by message or by line. Identifiers are covered by the backward compatibility policy and message wording is not, so a message-based ignore can silently stop matching in any release and start failing your build.
Leave reportUnmatchedIgnoredErrors at its default of true, so
that an ignore which no longer matches is reported rather than left to rot. The
cost is that a release which fixes a false positive fails your build until you
delete the now-unused entry, which is the right trade in most projects.
Backward compatibility covers when to turn it off.
Higher order messages on a support collection¶
The higher order proxy
is understood, but only as far as the collection's value type is known. On an
Eloquent\Collection that is a model and everything resolves. On a plain
Support\Collection whose values are mixed, the proxy has nothing to look the
method up on:
The better fix is to give the collection a value type, which resolves it properly rather than hiding it:
/** @var Collection<int, User> $users */
$users->groupBy->email; // Collection<string, Collection<int, User>>
Collection value types are invariant¶
Collection declares TValue invariantly, so a collection of a narrower value
type is not accepted where a wider one is annotated, even though reading from it
would be perfectly safe:
/** @param Collection<int, string|null> $values */
public function accept(Collection $values): void {}
The error is easy to misread, because PHPStan prints the widened description on
both sides and the two types look identical. The Tip about TValue not being
covariant is the only part that tells you what happened. If you find yourself
suspecting a broken return type extension, check for that line first.
The fix is use-site variance, not an ignore:
covariant on a parameter says you only read from the collection. Prefer it to
suppressing argument.type, which would hide real mismatches in the same
signature. For keys, array-key does the same job, being a benevolent union;
a plain int|string does not, since it is matched invariantly.
This is a genuine limit rather than a missing feature. Collection::push(),
put(), prepend(), add() and offsetSet() all take a TValue, so the
collection consumes its own value type and cannot be covariant in it. The
framework annotates it as covariant anyway, but nothing enforces that annotation
against the framework's own source—see
the FAQ for why the stubs
cannot repeat it.
Macros¶
Macros registered while the application boots are discovered automatically. A
registration that does not run during analysis needs an @method annotation.
The macros guide covers discovery, static and instance
macros, and classes whose macros have a static-facing API.
Models without resolvable columns¶
A model whose table this extension cannot see, because the table belongs to
another service or is created outside migrations, will have every column
reported as missing once
modelPropertyType is
on. Annotate those models with @property and they behave normally.
Analysis is quiet on a package¶
If no application is found and Testbench is not installed, analysis runs but
every container lookup returns nothing, so config types, app() resolution and
view names all fall back to their widest types. Nothing errors, which makes it
look like the extension is doing very little. See analysing a
package.
The run fails while booting¶
Booting is fatal by design: nothing useful can be analysed once a service provider has failed, so the underlying exception is reported rather than a wall of unrelated errors. The message points at the real failure, which is usually a missing environment variable or a provider that expects a service the analysis environment does not have. Booting your application covers what runs and what to keep out of it.
A schema dump cannot be parsed¶
An unreadable dump fails the run rather than being skipped, because the tables
it defines would otherwise go missing from your model properties with nothing
to say so. Options, in order of preference: fix the dump, choose the other
parser with sqlParser, or set
scanSchema: false to opt out of dumps entirely.
The iamcal and phpmyadmin drivers parse MySQL dumps. PostgreSQL plain-text
dumps require calebdw/pg-schema-parser; use sqlParser: postgres when a MySQL
parser is also installed. Custom and directory-format dumps cannot be scanned.