Skip to content

v1.6.0

Latest

Choose a tag to compare

@calebdw calebdw released this 30 Aug 03:46
5b03e9a

v1.6.0 brings much deeper type inference to Laravel managers, authentication, arrays, and collections, alongside a new opt-in rule for catching invalid configured service names.

Manager and authentication inference

Laravel manager subclasses now expose the declared return types of their create*Driver() methods. This improves both driver() calls and methods forwarded to the selected driver without requiring PHPStan to instantiate it during analysis:

$manager->driver('stripe');  // StripeDriver
$manager->charge();          // inferred from the default driver's contract

Configured authentication types are now resolved from the booted application. Named guards, custom guards, unions of guard names, authenticated users, and calls through the Auth facade, auth() helper, manager, and request are all narrowed:

auth('web');                  // SessionGuard
auth('api');                  // TokenGuard
auth('admin')->user();        // Admin|null
Auth::guard('web')->user();   // User|null
$request->user('admin');      // Admin|null

authenticate() returns the configured model without null, matching its exception-throwing behavior. Unknown or dynamic guard names continue to fall back to Laravel's declared contracts.

Array and collection shapes

Arr::get() and Arr::pull() now infer values through direct and dotted paths, including integer keys, unions, and lazy defaults. Arr::pull() also updates the source array's type to describe the shape left behind.

Arr::select(), Collection::select(), and LazyCollection::select() now describe the rows they actually rebuild rather than returning the original item shape. Constant key lists produce exact shapes, missing or dynamically selected keys remain optional, and model selections become arrays of the selected attributes.

/** @var Collection<int, array{name: string, role: string, active: bool}> $users */
$users->select(['name', 'role']);
// Collection<int, array{name: string, role: string}>

Arrayable generics are preserved through toArray() as well, so implementations no longer lose their key and value types to array<mixed>.

Undefined configured services

A new opt-in laravel.undefinedConfigName rule detects constant manager names that are absent from application configuration:

Storage::disk('s3');         // valid when filesystems.disks.s3 exists
Storage::disk('s3-backup');  // Disk [s3-backup] does not have a configured driver.

The rule covers filesystem disks, cache stores, database and queue connections, mailers, log channels, broadcast connections, and auth guards. It supports facade and injected-manager calls and understands database read/write connection suffixes.

It is disabled by default enable it with:

parameters:
    laravel:
        rules:
            undefinedConfigName: true

Schema and file discovery

  • Migration analysis now follows schema builders assigned from Schema::connection(), preserving literal connection names when create(), table(), and related methods are called through a variable.
  • Imported class constants used as migration table names are now resolved correctly.
  • Directory scanning no longer rewinds directory handles. This prevents migrations, schema dumps, views, config files, and stubs from silently disappearing on filesystems with unreliable rewinddir() behavior, notably some WSL2 and Docker Desktop 9p mounts.
  • SQL columns without an explicit NULL or NOT NULL constraint are treated as nullable consistently by both supported parsers.

Fixes and compatibility

  • Added missing Eloquent types for createQuietly(), getAppends(), getMutatedAttributes(), builder passthru methods, and forwarded model counter methods.
  • Preserved PSR-7 methods mixed into Laravel HTTP client responses.
  • Corrected the Laravel argument order for Redis eval() and evalsha().
  • Added CarbonInterval support to retry delays and retry delay callbacks.
  • Preserved union types through higher-order tap() calls and deferred to native signatures whenever a dynamic return type extension cannot refine a call.
  • Recognized computed model properties whose Attribute return type omits generics, falling back to mixed instead of reporting the property as missing.
  • Allowed the framework's null|object values for PendingRequest::$data.

Full Changelog: v1.5.0...v1.6.0