laravel-sanitizer maintained by hadikhanzadeh
Laravel Sanitizer
Recursive, filter-based input sanitization for Laravel applications — with per-field rules, dot-notation support for nested data, and a FormRequest trait for zero-boilerplate integration.
Why this package
Cleaning request input in Laravel usually ends up as ad-hoc calls to trim() and strip_tags() scattered across controllers, or a single monolithic sanitizer class that's hard to extend. This package instead treats each sanitization step as a small, testable, swappable class — registered through config, resolved through the container, and safe for config:cache.
It's a spiritual successor to the now-unmaintained waavi/sanitizer, rebuilt for PHP 8.4 and Laravel 12/13 with an interface-based filter architecture.
Requirements
- PHP 8.4+
- Laravel 12.x or 13.x
Installation
composer require hadikhanzadeh/laravel-sanitizer
The service provider is auto-discovered — no manual registration needed.
Optionally publish the config file to customize registered filters or defaults:
php artisan vendor:publish --tag=sanitizer-config
Basic usage
Resolve the Sanitizer service directly:
use HadiKhanzadeh\LaravelSanitizer\Sanitizer;
$sanitizer = app(Sanitizer::class);
$clean = $sanitizer->clean([
'name' => ' <b>Hadi</b> ',
'bio' => '<script>alert(1)</script><p>Hello</p>',
]);
// ['name' => 'Hadi', 'bio' => 'Hello']
Or use the facade:
use HadiKhanzadeh\LaravelSanitizer\Facades\Sanitizer;
$clean = Sanitizer::clean($request->all());
Automatic sanitization in FormRequests
Add the SanitizesInput trait to any FormRequest to sanitize its payload automatically before vHadidation runs:
use HadiKhanzadeh\LaravelSanitizer\Concerns\SanitizesInput;
use Illuminate\Foundation\Http\FormRequest;
final class StoreProductRequest extends FormRequest
{
use SanitizesInput;
public function rules(): array
{
return [
'name' => ['required', 'string'],
];
}
}
Per-field rules
Override sanitizationRules() to control which filters run on specific fields:
protected function sanitizationRules(): array
{
return [
'description' => ['editor'],
'title' => ['trim', 'strip_tags'],
];
}
Nested fields (dot notation)
protected function sanitizationRules(): array
{
return [
'address.postal_code' => ['trim'],
];
}
Changing the default pipeline
Fields without an explicit rule fall back to config('sanitizer.default_filters') (trim, strip_tags by default). Override per request:
protected function defaultSanitizationFilters(): ?array
{
return ['trim'];
}
Opting out
protected bool $sanitizeInput = false;
Before/after hooks
protected function beforeSanitization(): void
{
// runs before the Sanitizer touches the payload
}
protected function afterSanitization(): void
{
// runs after sanitization, before vHadidation
}
Built-in filters
| Name | Class | Description |
|---|---|---|
trim |
TrimFilter |
Trims leading/trailing whitespace. |
strip_tags |
StripTagsFilter |
Removes all HTML/PHP tags. |
stripslashes |
StripSlashesFilter |
Removes backslashes. Registered but not in the default pipeline — a legacy filter, opt in per-field only if needed. |
editor |
EditorFilter |
Sanitizes rich-text HTML via mews/purifier, stripping dangerous attributes (onclick, javascript: URLs) that a plain tag allow-list would miss. Requires composer require mews/purifier and a purifier.editor config preset. |
Note:
htmlspecialcharsis intentionally not included. Escaping is an output-layer concern (Blade, API resources) — encoding on input causes double-encoding when the value is escaped again later.
Adding a custom filter
Implement SanitizationFilter:
namespace App\Sanitization\Filters;
use HadiKhanzadeh\LaravelSanitizer\Contracts\SanitizationFilter;
final readonly class LowercaseFilter implements SanitizationFilter
{
public function apply(mixed $value): mixed
{
return is_string($value) ? mb_strtolower($value) : $value;
}
}
Register it in config/sanitizer.php:
'filters' => [
// ...
'lowercase' => \App\Sanitization\Filters\LowercaseFilter::class,
],
Use it like any other filter name in your rules.
Testing
composer test
License
MIT. See LICENSE.md.