deinte/laravel-blade-validator

Validate Blade templates for syntax errors, security issues, and best practices

Installs: 725

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/deinte/laravel-blade-validator

v0.0.1 2026-01-06 13:00 UTC

This package is auto-updated.

Last update: 2026-01-06 13:01:47 UTC


README

Validate Blade templates for syntax errors, security issues, and best practices.

Installation

You can install the package via composer:

composer require --dev deinte/laravel-blade-validator

You can publish the config file with:

php artisan vendor:publish --tag="blade-validator-config"

Usage

Basic Usage

Validate all Blade templates in resources/views:

php artisan blade:validate

Validate specific paths:

php artisan blade:validate resources/views/components resources/views/layouts

Command Options

php artisan blade:validate [options] [path...]

Options:
  --fix                Automatically fix safe issues
  --dry-run            Preview fixes without applying them
  --ignore=*           Patterns to ignore (fnmatch format)
  --format=text        Output format: text, json, github
  --no-fail            Exit with 0 even if errors found
  --rules=             Comma-separated rules to run
  --exclude-rules=     Comma-separated rules to skip
  --severity=error     Minimum severity: error, warning, info

Auto-Fix

The validator can automatically fix certain safe issues:

# Preview what would be fixed
php artisan blade:validate --dry-run

# Apply fixes
php artisan blade:validate --fix

Fixable rules:

  • legacy-php-tags: Converts <?= ?> to {{ }}
  • deprecated-syntax: Fixes {{{ }}}, {{ e() }}, @else if
  • use-statement-in-php-block: Moves use to @use() directive

Examples

# JSON output for CI/CD
php artisan blade:validate --format=json

# GitHub Actions annotations
php artisan blade:validate --format=github

# Ignore patterns
php artisan blade:validate --ignore="**/cache/**" --ignore="**/vendor/**"

# Run only specific rules
php artisan blade:validate --rules=raw-output-usage,sensitive-data-exposure

# Exclude specific rules
php artisan blade:validate --exclude-rules=deprecated-syntax

# Show warnings and errors
php artisan blade:validate --severity=warning

Validation Rules

directive-in-component-attribute (error)

Detects Blade directives used inside component tag attributes.

{{-- Invalid --}}
<x-button @if($active) color="primary" @endif />

{{-- Valid --}}
<x-button @class(['primary' => $active]) />

use-statement-in-php-block (error)

Detects PHP use statements inside @php blocks.

{{-- Invalid --}}
@php
    use App\Models\User;
    $users = User::all();
@endphp

{{-- Valid --}}
@use('App\Models\User')

@php
    $users = User::all();
@endphp

{{-- Or use fully qualified class names --}}
@php
    $users = \App\Models\User::all();
@endphp

raw-output-usage (warning)

Detects potentially unsafe raw output {!! !!} that bypasses escaping.

{{-- Flagged --}}
{!! $userContent !!}
{!! request()->input('html') !!}

{{-- Suppress with comment --}}
{{-- @security-ignore raw-output --}}
{!! $trustedHtml !!}

unclosed-directive (error)

Detects unclosed block directives.

{{-- Invalid --}}
@if($show)
    <div>Content</div>
{{-- Missing @endif --}}

{{-- Valid --}}
@if($show)
    <div>Content</div>
@endif

inline-javascript (warning)

Detects inline JavaScript patterns with Blade expressions.

{{-- Flagged --}}
<button onclick="{{ $action }}">Click</button>
<a href="javascript:{{ $code }}">Link</a>

{{-- Valid alternatives --}}
<button x-on:click="action">Click</button>
<button data-action="{{ $action }}">Click</button>

sensitive-data-exposure (error)

Detects potential exposure of sensitive data.

{{-- Flagged --}}
{{ $user->password }}
{{ config('app.key') }}
{{ env('DB_PASSWORD') }}
{{ $request->bearerToken() }}

{{-- Safe --}}
{{ $user->name }}
{{ $user->email }}

deprecated-syntax (warning)

Detects deprecated Blade syntax.

{{-- Flagged --}}
{{{ $variable }}}           {{-- Use {{ }} instead --}}
{{ e($variable) }}          {{-- Double escaping --}}
@else if($condition)        {{-- Use @elseif --}}
{{ str_limit($text, 100) }} {{-- Use Str::limit() --}}

{{-- Valid --}}
{{ $variable }}
@elseif($condition)
{{ Str::limit($text, 100) }}

legacy-php-tags (error)

Detects legacy PHP tags (<?= and <?php) that should be converted to Blade syntax.

{{-- Flagged --}}
<td><?= $payment->name ?></td>
<td><?= $payment->iban ?></td>
<td><?= "" . number_format($amount, 2) ?></td>
<?php echo $variable; ?>

{{-- Valid Blade equivalents --}}
<td>{{ $payment->name }}</td>
<td>{{ $payment->iban }}</td>
<td>€{{ number_format($amount, 2) }}</td>
{{ $variable }}

This is especially useful when migrating legacy PHP templates to Blade.

Configuration

Publish the configuration file:

php artisan vendor:publish --tag="blade-validator-config"

Configuration Options

return [
    // Default paths to validate
    'paths' => [
        resource_path('views'),
    ],

    // Patterns to ignore
    'ignore' => [
        '**/vendor/**',
        '**/node_modules/**',
    ],

    // Enable/disable rules
    'rules' => [
        'directive-in-component-attribute' => true,
        'use-statement-in-php-block' => true,
        'raw-output-usage' => true,
        'unclosed-directive' => true,
        'inline-javascript' => true,
        'sensitive-data-exposure' => true,
        'deprecated-syntax' => true,
        'legacy-php-tags' => true,
    ],

    // Raw output settings
    'raw_output' => [
        'severity' => 'warning',
        'allowed_patterns' => [],
    ],

    // Sensitive data patterns
    'sensitive_patterns' => [
        'password', 'secret', 'token', 'key', 'credential',
        'bearerToken', 'api_key', 'apiKey', 'private',
    ],
];

GrumPHP Integration

Setup

  1. Install GrumPHP:
composer require --dev phpro/grumphp
  1. Copy the configuration file:
cp vendor/deinte/laravel-blade-validator/grumphp.yml.dist grumphp.yml
  1. Initialize git hooks:
vendor/bin/grumphp git:init

Configuration

# grumphp.yml
grumphp:
    extensions:
        - Deinte\BladeValidator\GrumPHP\ExtensionLoader

    tasks:
        blade_validation:
            triggered_by: ['blade.php']
            severity: error
            ignore:
                - '**/cache/**'

Now Blade validation will run automatically on every commit!

Programmatic Usage

Using the Facade

use Deinte\BladeValidator\Facades\BladeValidator;

// Validate a single file
$result = BladeValidator::validateFile('/path/to/template.blade.php');

// Validate multiple files
$result = BladeValidator::validateFiles([
    '/path/to/template1.blade.php',
    '/path/to/template2.blade.php',
]);

// Validate a directory
$result = BladeValidator::validateDirectory(resource_path('views'));

// Check results
if (!$result->valid) {
    foreach ($result->errors as $error) {
        echo "{$error->file}:{$error->line} - [{$error->rule}] {$error->message}\n";
    }
}

Using Dependency Injection

use Deinte\BladeValidator\BladeValidator;

class MyController
{
    public function validateViews(BladeValidator $validator)
    {
        $result = $validator->validateDirectory(resource_path('views'));

        return response()->json($result->toArray());
    }
}

Creating Custom Rules

Implement the BladeRuleInterface:

<?php

namespace App\BladeRules;

use Deinte\BladeValidator\Data\BladeValidationError;
use Deinte\BladeValidator\Rules\BladeRuleInterface;

class NoTodoComments implements BladeRuleInterface
{
    public function getName(): string
    {
        return 'no-todo-comments';
    }

    public function getDescription(): string
    {
        return 'Detects TODO comments in Blade templates.';
    }

    public function getDefaultSeverity(): string
    {
        return 'warning';
    }

    public function validate(string $content, string $filePath): array
    {
        $errors = [];

        if (preg_match_all('/\{\{--.*TODO.*--\}\}/i', $content, $matches, PREG_OFFSET_CAPTURE)) {
            foreach ($matches[0] as $match) {
                $line = substr_count(substr($content, 0, $match[1]), "\n") + 1;

                $errors[] = new BladeValidationError(
                    file: $filePath,
                    line: $line,
                    rule: $this->getName(),
                    message: 'TODO comment found in template.',
                    severity: $this->getDefaultSeverity(),
                );
            }
        }

        return $errors;
    }
}

Register your rule in a service provider:

use Deinte\BladeValidator\BladeValidator;
use App\BladeRules\NoTodoComments;

public function boot(BladeValidator $validator)
{
    $validator->addRule(new NoTodoComments());
}

CI/CD Integration

GitHub Actions

name: Blade Validation

on: [push, pull_request]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'

      - name: Install dependencies
        run: composer install

      - name: Validate Blade templates
        run: php artisan blade:validate --format=github

GitLab CI

blade-validation:
  script:
    - composer install
    - php artisan blade:validate --format=json
  only:
    changes:
      - "resources/views/**/*.blade.php"

Testing

composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.