tomasvotruba/bladestan
Bladestan adds PHPStan-powered static analysis for Laravel Blade templates. Install as a dev dependency and include its extension if needed. Provides a custom “blade” error formatter showing clickable template paths and where errors are rendered.
Installation:
composer require --dev tomasvotruba/bladestan
Add it to your composer.json under require-dev to ensure it runs in CI/CD pipelines.
Configuration:
phpstan.neon:
includes:
- ./vendor/tomasvotruba/bladestan/config/extension.neon
First Run:
vendor/bin/phpstan analyze --error-format=blade
This enables the custom Blade error formatter with clickable template links.
Run Bladestan on a controller returning a view with a Blade template:
// app/Http/Controllers/PostController.php
public function show(Post $post) {
return view('posts.show', ['post' => $post]);
}
If posts/show.blade.php calls $post->nonexistentMethod(), Bladestan will flag it with:
Line 15 posts/show.blade.php: Call to undefined method App\Models\Post::nonexistentMethod().
Click the link to jump directly to the error in your IDE.
Pre-Commit Hooks:
Add Bladestan to your pre-commit script (e.g., using husky or laravel-pint):
vendor/bin/phpstan analyze --error-format=blade --level=max
Fail the hook if errors exist, enforcing Blade quality before code review.
CI/CD Pipeline: Run Bladestan in your CI (e.g., GitHub Actions):
- name: Run Bladestan
run: vendor/bin/phpstan analyze --error-format=blade --level=max
Set it to fail the pipeline on errors, mirroring your PHPStan checks.
Onboarding New Developers:
Include Bladestan in your README.md under "Local Setup":
## Local Setup
1. Install dependencies: `composer install`
2. Run Blade template checks: `vendor/bin/phpstan analyze --error-format=blade`
Custom Error Levels:
Override default rule levels in phpstan.neon:
parameters:
level: max
rules:
Bladestan\Rules\UndefinedMethodRule: error
Bladestan\Rules\MissingVariableRule: warning
Ignoring False Positives:
Suppress specific errors in phpstan.neon:
includes:
- ./vendor/tomasvotruba/bladestan/config/extension.neon
ignoreErrors:
- '#Call to undefined method App\Models\User::legacyMethod\(\) in .*\.blade\.php#'
Livewire Component Analysis: Bladestan automatically detects Livewire components. For custom props, ensure your component class is type-hinted:
// app/Http/Livewire/MyComponent.php
public string $propName = '';
Bladestan will validate usage in Blade templates like @livewire('my-component', ['propName' => $value]).
Mail Template Validation:
Extend Bladestan to analyze mail templates by adding their paths to phpstan.neon:
parameters:
bladeTemplatePaths:
- resources/views
- resources/views/emails
Package Development:
If building a Laravel package with Blade templates, include Bladestan in your composer.json under require-dev and document its use in your README.md:
## Static Analysis
Run Blade template checks:
```bash
vendor/bin/phpstan analyze --error-format=blade
Cache Invalidation: Bladestan invalidates PHPStan’s result cache when Blade templates change (since v0.11.5). If errors persist after fixes:
vendor/bin/phpstan analyze --clear-cache
Dynamic Includes:
Errors may appear for @include directives with dynamic paths (e.g., @include($dynamicPath)). To debug:
Livewire Component Props: Bladestan may flag Livewire props as undefined if the component class lacks type hints. Add PHPDoc annotations as a workaround:
/** @property string $propName */
Facade Calls:
For Response::view() or View::make(), ensure the view name is a string literal or a resolved variable. Dynamic view names (e.g., View::make($dynamicView)) may trigger false positives.
Non-HTML Templates:
Bladestan supports non-HTML mail templates (since v0.11.0), but complex logic (e.g., @php blocks with dynamic includes) may require manual validation.
Verbose Output:
Run with --verbose to debug rule application:
vendor/bin/phpstan analyze --error-format=blade --verbose
Rule-Specific Debugging: Disable all rules except one to isolate issues:
parameters:
rules:
Bladestan\Rules\UndefinedMethodRule: error
*: disabled
Template Paths:
If Bladestan misses templates, explicitly define paths in phpstan.neon:
parameters:
bladeTemplatePaths:
- resources/views
- custom/template/path
IDE Integration: For VS Code, use the PHPStan extension with the Blade formatter enabled to get inline errors.
Custom Rules: Extend Bladestan by creating a custom PHPStan rule. Example:
// app/Rules/CustomBladeRule.php
namespace App\Rules;
use PHPStan\Rules\Rule;
use Bladestan\BladeNode;
class CustomBladeRule implements Rule
{
public function getNodeTypeNames(): array
{
return [BladeNode::class];
}
public function processNode(BladeNode $node): array
{
if ($node->isDangerousPattern()) {
return [$node->createError('Custom rule: Dangerous pattern detected.')];
}
return [];
}
}
Register it in phpstan.neon:
includes:
- ./vendor/tomasvotruba/bladestan/config/extension.neon
- ./app/Rules/CustomBladeRule.neon
Error Formatter: Override the default Blade formatter by creating a custom formatter class and updating the CLI command:
vendor/bin/phpstan analyze --error-format=custom
Configure in phpstan.neon:
parameters:
errorFormatter: App\CustomBladeErrorFormatter
Template Parsing:
For complex templates (e.g., custom directives), subclass Bladestan\BladeParser and override parsing logic. Example:
// app/Blade/CustomParser.php
namespace App\Blade;
use Bladestan\BladeParser;
class CustomParser extends BladeParser
{
protected function parseCustomDirective(string $content): void
{
// Custom logic for @customdirective
}
}
Register the parser in your service provider:
// app/Providers/AppServiceProvider.php
public function register()
{
$this->app->singleton(BladeParser::class, function () {
return new \App\Blade\CustomParser();
});
}
PHPStan Version:
Bladestan requires PHPStan 2.0+. Check compatibility in composer.json:
"require-dev": {
"phpstan/phpstan": "^2.0"
}
Laravel Version:
Ensure your laravel/framework version matches Bladestan’s support (Laravel 10–12 as of v0.11.5). For older versions, pin to a compatible release:
composer require tomasvotruba/bladestan:0.10.0
Livewire Support:
If Livewire components are not detected, verify config/livewire.php includes the correct namespace and that the component class extends \Livewire\Component.
Symfony Mailer:
For Symfony mail templates, ensure config/services.php includes the mailables path:
'mailables
How can I help you explore Laravel packages today?