Configuration
All options live under parameters.phpstanLaravelValidation. Defaults match
Laravel’s ordinary factory and validator behavior.
parameters:
phpstanLaravelValidation:
laravelVersion: auto
assumeHttpInputNormalization: false
includeUnvalidatedArrayKeys: false
experimentalConditionalPresenceInference: false
formRequests:
enabled: false
additionalClasses: []
trustedClasses: []
customRules:
classes: []
names: []
laravelVersion
Default: auto.
auto uses Composer’s installed-version data for the project root matching
PHPStan’s working directory. That follows the Laravel code actually installed
for analysis rather than a potentially stale lockfile. If no matching
installed-package data is available, the extension falls back to the analyzed
project’s composer.lock. It does not use Laravel versions from unrelated
Composer roots that happen to be loaded in the PHPStan process.
The detected laravel/framework version selects verified release boundaries
such as integer:strict, ascii, and Laravel’s default request-trimming
exceptions. A standalone illuminate/validation installation can select
rule-level behavior, but cannot establish full-framework middleware defaults.
It also cannot select version-sensitive rule-name whitespace behavior because
illuminate/support can have a different version. See
Validation Rule Reference.
For monorepos or other layouts where PHPStan’s working directory is not the relevant Composer project root, set the version explicitly:
parameters:
phpstanLaravelValidation:
laravelVersion: '13.4.0'
If the version is unavailable, malformed, or outside the supported Laravel 10–13 range, inference retains the conservative cross-version type. The effective version context participates in PHPStan’s result-cache metadata, so changing Laravel versions invalidates cached inference.
Boundaries are listed in Laravel Version Behavior.
assumeHttpInputNormalization
Default: false.
By default the extension models the validator itself and therefore includes
blank strings that can bypass optional non-implicit rules. Applications whose
request validation is guaranteed to run after Laravel’s standard
TrimStrings and ConvertEmptyStringsToNull middleware may opt into
narrower request types:
parameters:
phpstanLaravelValidation:
assumeHttpInputNormalization: true
This option affects Request::validate(), controller validate(), and
inferred FormRequest::validated() calls. It does not affect direct
validators, factories, facades, or validators passed to validateWith().
An optional array field normally has the value type array|string because
a blank string may bypass the rule. With this option it has type array;
nullable|array has type array|null. Laravel 11 through 13 exclude
current_password, password, and password_confirmation from trimming by
default, so those paths still include strings. Laravel 10 trims them and
receives the narrower type. If a supported full-framework version cannot be
established, the extension conservatively includes strings.
Enable this option only if neither middleware is skipped or removed and
validation cannot observe values introduced afterward by request mutation.
Projects with custom trimming exceptions or skipWhen() callbacks should
leave it disabled.
includeUnvalidatedArrayKeys
Default: false, matching Laravel’s factory default.
Laravel’s validation factory excludes unvalidated nested array keys by
default. An application that calls includeUnvalidatedArrayKeys() changes
the shape returned from bare array and, where supported by Laravel, list
parents with nested child rules.
parameters:
phpstanLaravelValidation:
includeUnvalidatedArrayKeys: true
When enabled, the extension conservatively widens affected nested parents
because unmentioned keys may survive in validated(). This applies to
inferred factory, facade, request, controller, validator-helper, and
FormRequest output. Affected bare array parents widen to array, so the
inferred types of their validated children are no longer retained. Bare
list parents retain only their listness unless a direct exclusion rule can
remove an element.
The extension does not boot the application or attempt to discover a call in
a service provider. Treat this option as an assertion about the factories
whose output the extension infers. It is not a conservative setting for mixed
factory modes. In particular, an excluding factory can reconstruct a bare
list parent with sparse keys. An including factory normally preserves the
parent, but nested exclusion rules mutate its data before validated()
reads it and can also make the result sparse. The extension widens parents
with direct exclusion rules to cover that behavior. A single global option
cannot precisely model mixed factory modes. A directly constructed
Illuminate\Validation\Validator retains Laravel’s broad declared return
type and is not narrowed from this assumption.
PHPStan reports laravelValidation.unvalidatedArrayKeysConfiguration when a
direct Factory method call or statically resolved Validator facade call
switches to the mode opposite this option. The diagnostic is call-local: it
does not execute service providers, follow arbitrary container aliases, or
claim to determine the final mode after later calls. The option remains the
source of truth for inferred output.
experimentalConditionalPresenceInference
Default: false.
Laravel’s dependent presence rules normally leave the affected output key optional because their result depends on another runtime value. This option recovers definite cases where a required top-level controlling field has a finite scalar-literal type.
parameters:
phpstanLaravelValidation:
experimentalConditionalPresenceInference: true
$validated = Validator::make($input, [
'mode' => 'required|string|in:create',
'name' => 'present_if:mode,create|string',
])->validated();
\PHPStan\dumpType($validated);
// array{mode: 'create', name: string}
The option handles definite outcomes for present_if, present_unless,
missing_if, and missing_unless. Active presence requires the key but
still permits blank strings to bypass adjacent non-implicit rules; it is not
treated as required. Active missing rules omit the key from successful
output. present_if and present_unless refinement requires a detected
Laravel version of 10.32 or later; earlier or unknown versions remain
conservative.
The first experimental slice supports only one conditional field whose controller is a required direct top-level sibling. The controller’s entire inferred domain must either match or not match the dependent values. Mixed matching and non-matching domains, boolean controllers, nested or wildcard paths, multiple conditional fields, exclusions, and custom or opaque rules retain the ordinary conservative optional shape.
See Presence and Output Projection.
Form requests
See FormRequest Inference for behavior. The keys are:
parameters:
phpstanLaravelValidation:
formRequests:
enabled: false
additionalClasses: []
trustedClasses: []
additionalClasses is an exact discovery-only class list for FormRequests
outside the ordinary analysed, scan, and root Composer source paths. Each
class still has to pass the lifecycle-safety checks described in the guide.
The manifest fingerprints the configured class’s Composer package PHP sources
and declared autoload paths. It does the same for parent classes, implemented
interfaces, recursively used traits, and statically referenced classes in the
rules() return expressions, including injected class-constant receivers and
transitive class-constant references. Statically referenced user constants add
their defining file and the runtime-loaded file set as a conservative boundary
for initializer dependencies; functions contribute their available source
files. Exact autoload.files entries are included even when they do not use a
.php extension. These additional sources do not make unlisted classes
discoverable.
trustedClasses is an exact class list. Subclasses are not trusted
implicitly. Trust also makes the class discoverable, but bypasses those
lifecycle checks and can therefore make inference unsound when asserted
incorrectly. Trust in a parent does not bypass checks on its descendants.
Receiver inference includes discovered descendants and assumes discovery covers the application’s request implementations. Hierarchy membership and polymorphic payload changes can invalidate the global result cache, because parent-typed callers do not otherwise depend on child classes. Literal-rule leaves and hierarchies whose combined payload matches the parent’s own exportable contract retain selective invalidation.
For a discovered FormRequest whose rules() method is a single literal return,
the extension records each relevant class or trait method body in PHPStan’s
exported cache state when the complete application-owned request hierarchy is
part of the current analysis selection and its relevant method markers are
exportable by PHPStan. The extension enumerates the current analysis roots
using PHPStan’s file-selection semantics, including its hidden and
version-control directory exclusions, rather than treating every path
descendant as analysed. A rule-body change can then invalidate that request
and its dependent callers without discarding unrelated cached files. A
redundant additionalClasses entry does not change this cache strategy.
Trusted requests, requests discovered only through additionalClasses,
sources omitted by a CLI path override, excluded or otherwise unanalysed
hierarchy sources, non-exportable private trait methods, and rule expressions
involving constants, builders, helpers, services, or other external behavior
retain the manifest’s global descriptor fallback. The manifest remembers
directly resolved external source fingerprints that are not part of its
ordinary project scan. This split is conservative: unsupported provenance
causes broader invalidation rather than risking a stale inferred type.
Custom rules
parameters:
phpstanLaravelValidation:
customRules:
classes: []
names: []
Diagnostics
Validator-contract invalidation operates independently of the inference
options. PHPStan reports mutations of existing inferred validators under
laravelValidation.validatorMutation while allowing conservative or precise
handling of supported fresh chains. See
Supported Entry Points.
Statically resolvable parsing rules on a detected laravel/framework version
below 10.7 are reported as
laravelValidation.parsingRuleLaravelVersion. The analyzer remains silent
when the framework version is unavailable; the parser’s runtime capability
guard still rejects validators without setValue().
Numeric parsing rules combined with min, max, between, or size without
Laravel’s integer, numeric, or decimal marker are reported as
laravelValidation.parsingNumericSize. Laravel otherwise measures the
original representation rather than the parsed numeric value. For a
float-producing parser, the diagnostic recommends numeric or an appropriate
decimal rule rather than integer, which rejects non-integral values. The
diagnostic inspects supported factory, facade, helper, request, and controller
validation calls, including validateWith() and validateWithBag(), as well
as returns from FormRequest::rules(). FormRequest rules are checked
independently of formRequests.enabled because the diagnostic concerns runtime
behavior rather than inferred FormRequest output. See
Parsing validated output.