PHPStan readable macros
CommunityPHPStan and Larastan understand macros registered on Filament components.
filament/
namespace. Review the source and install at your own risk. Found
malware or an unresolved security issue the author won't
address?
Report it
.
Author:
Happenv sp. z o.o.
Documentation
- Why it matters: everything after the macro goes dark
- Key features
- Requirements
- Installation
- Usage
- Development
- Upgrading
- Changelog
- Contributing
- Security vulnerabilities
- Credits
- License

A PHPStan extension that makes PHPStan and Larastan understand macros registered on Filament components — TextInput, TextColumn, TextEntry, Grid, Action and everything else built on Filament's Macroable.
Filament ships its own macro trait, Filament\Support\Concerns\Macroable, instead of Laravel's Illuminate\Support\Traits\Macroable. Larastan only knows Laravel's trait, so every Filament macro call is an "undefined method" — and, as shown below, ignoring that error switches off analysis of everything chained after the macro.
// A service provider
Field::macro('translatableLabel', function (string $key): Field {
/** @var Field $this */
return $this->label(__("fields.{$key}"));
});
// Anywhere in the app
TextInput::make('title')->translatableLabel('title')->maxLength(100);
#Why it matters: everything after the macro goes dark
Without this package PHPStan reports every macro call as Call to an undefined method. The usual fix is an ignoreErrors entry — and that hides far more than the macro: the macro's result is unknown, so every method chained after it is no longer checked at all.
TextInput::make('title')
->translatableLabel('title') // the macro — its error is ignored
->copyable(copyMessage: 42) // wrong argument type
->maxLenght(100); // typo
| Analysis (level 8) | Reported |
|---|---|
without this package, macro error in ignoreErrors |
nothing — both bugs pass |
| with this package | Parameter $copyMessage of method TextInput::copyable() expects Closure|string|null, 42 given.Call to an undefined method TextInput::maxLenght(). |
At level 9 and above the ignored macro only turns into Cannot call method copyable() on mixed for every following call — noise that tends to get ignored as well, with the same result. With this package the macro is a typed method, so the whole chain is analysed like any other Filament code.
#Key features
- The code after a macro is checked again. No more
ignoreErrorsfor macros — which silently switched off analysis of everything chained after them. - Macros become real methods for PHPStan. Parameters and return type are read from the registered closure, so a wrong argument or a misused result is reported like for any native method.
- Fluent chains keep their type. A macro typed to return one of the caller's ancestors (e.g.
Fieldwhen called onTextInput) returnsstatic, so subclass-only methods after it are still known. - Filament's own lookup rules. A macro registered on the class itself wins over one registered on a parent, exactly as Filament resolves it at runtime.
- Any callable. Closures, invokable objects and array callables registered with
macro()ormixin()are all understood. - Zero configuration. With
phpstan/extension-installerthe extension registers itself; no Filament dependency is added to your production install. - Filament 3, 4 and 5. Tested against every major on PHP 8.3 – 8.5, with the lowest and the newest installable PHPStan 2.x.
#Requirements
| Package | Versions |
|---|---|
| PHP | 8.3 – 8.5 |
| PHPStan | 2.1+ |
| Filament | 3, 4, 5 |
Macros are read from Filament at analysis time, so they must be registered when PHPStan runs. Larastan does that for you: it boots your Laravel application, which runs the service providers that register them.
#Installation
Install the package as a development dependency:
composer require --dev happenv-com/filament-phpstan-macros
With phpstan/extension-installer (Larastan setups usually have it) there is nothing else to do. Otherwise include the extension in your phpstan.neon:
includes:
- vendor/happenv-com/filament-phpstan-macros/extension.neon
#Usage
Register macros as usual — typically in a service provider's boot() — and type the closure: its parameter and return types are what PHPStan will use.
use Filament\Forms\Components\Field;
use Filament\Forms\Components\TextInput;
use Filament\Tables\Columns\Column;
use Filament\Tables\Columns\TextColumn;
// Fluent: typed to return an ancestor, so the chain keeps the caller's type.
Column::macro('sortableAndSearchable', function (): Column {
/** @var Column $this */
return $this->sortable()->searchable();
});
// Returning a value: kept as declared.
Field::macro('translationKey', function (): string {
/** @var Field $this */
return "fields.{$this->getName()}";
});
TextColumn::make('name')->sortableAndSearchable()->limit(50); // still a TextColumn: limit() is known
TextInput::make('title')->translationKey(); // string
#Without Larastan
Register the macros before analysis with a bootstrap file:
parameters:
bootstrapFiles:
- phpstan-macros.php # calls TextInput::macro(...) and friends
#Good to know
- A closure without a return type is analysed as returning
mixed, just like with Larastan's macros — add the return type. - Macros are exposed as instance methods. Calling a Filament macro statically (
TextInput::myMacro()) is still reported. - Only Filament's
Macroableis handled here; Laravel'sMacroable(collections, requests, Eloquent builders, …) stays Larastan's job, so both work side by side.
#Development
composer test # PHPStan test cases: unit tests and type inference on tests/Types/data
composer phpstan # static analysis of the package itself (level max)
composer cs # fix code style: composer normalize, Rector, Pint
composer ci # everything CI checks, locally
The macros the type-inference tests analyse are registered in tests/bootstrap.php; add a case there and an assertType() to tests/Types/data/macros.php.
#Upgrading
Breaking changes and how to migrate are described in UPGRADING for every major version.
#Changelog
See CHANGELOG and GitHub releases for what has changed recently.
#Contributing
See CONTRIBUTING for details.
#Security vulnerabilities
Please review our security policy on how to report security vulnerabilities.
#Credits
#License
The MIT License (MIT). See License File for more information.
The author
Happenv is a software development company specializing in e-commerce solutions, logistics systems, and Order Management Systems (OMS). We design, build, and maintain scalable business applications that help companies streamline operations, automate workflows, and improve customer experiences. Our expertise includes custom development, system integrations, and long-term support of solutions built with Laravel and Filament, delivering reliable and efficient platforms tailored to modern commerce and logistics needs.
From the same author
Translatable Fields
ilament Translatable is a flexible package that provides a complete solution for managing multilingual content in Filament admin panels. It allows you to easily create translatable form fields with an intuitive tabbed interface, supporting multiple locales and translation packages.
Author:
Happenv sp. z o.o.
Enhanced Charts
Apache ECharts for Filament panels - from sankeys to calendar heatmaps, every chart built from typed PHP objects.
Author:
Happenv sp. z o.o.
User Presence
See who else is on the page - live avatars next to every heading, with online / away status and a durable visit log.
Author:
Happenv sp. z o.o.
Multi Source Upload
A drop-in replacement for Filament's FileUpload field that lets users add a file from their disk or from a URL.
Author:
Happenv sp. z o.o.
Featured Plugins
A selection of plugins curated by the Filament team
Custom Dashboards
Let your users build and share their own dashboards with a drag-and-drop interface. Define your data sources in PHP and let them do the rest.
Filament
Compact Theme
A theme that makes data-heavy panels easier to scan by fitting more useful information on each screen.
Filament
Custom Fields
Eliminate custom field migrations forever. Let your users create and manage form fields directly in Filament admin panels with 20+ built-in field types, validation, and zero database changes.
Relaticle