# Purify - HTML Sanitization for Laravel Purify is a Laravel wrapper for HTMLPurifier that provides robust HTML sanitization and XSS protection for user-generated content. The package integrates seamlessly with Laravel's service container, offering a fluent API for cleaning HTML strings and arrays while removing malicious scripts, unsafe styles, and unwanted markup. It supports multiple configuration profiles, custom HTML/CSS definitions, and flexible caching strategies. Built on top of ezyang's battle-tested HTMLPurifier library, Purify extends it with Laravel-specific features including Eloquent model casts, configurable definition extensions for HTML5 elements, and support for both filesystem and cache-based serialization. The package is designed to handle various sanitization requirements across different contexts within an application, from strict comment filtering to permissive rich-text content. ## API Reference and Usage Examples ### Clean HTML String Sanitizes a single HTML string by removing scripts, unsafe attributes, and unwanted elements according to configured rules. ```php use Stevebauman\Purify\Facades\Purify; // Input with malicious script and disallowed styles $input = '

Hello World

'; // Clean the input - removes script tags and disallowed attributes $cleaned = Purify::clean($input); // Output: '

Hello World

' echo $cleaned; ``` ### Clean HTML Array Processes multiple HTML strings in a single operation, useful for batch sanitization of form data. ```php use Stevebauman\Purify\Facades\Purify; // Array of user-submitted content $inputs = [ '

First post

', '

Second post

', 'Click me

Third post

' ]; // Clean all entries $cleaned = Purify::clean($inputs); // Output: ['

First post

', '

Second post

', '

Third post

'] var_dump($cleaned); ``` ### Dynamic Inline Configuration Applies custom HTMLPurifier settings for a single sanitization operation without modifying global configuration. ```php use Stevebauman\Purify\Facades\Purify; $input = '
Bold Link
'; // Custom config: only allow specific elements and attributes $config = [ 'HTML.Allowed' => 'b,a[href]', 'AutoFormat.AutoParagraph' => true ]; $cleaned = Purify::config($config)->clean($input); // Output: '

Bold Link

' // Note: div removed, title attribute stripped, script removed, auto-paragraph applied echo $cleaned; ``` ### Named Configuration Profiles Defines and uses multiple sanitization profiles for different contexts (e.g., comments vs. blog posts). ```php // config/purify.php return [ 'default' => 'default', 'configs' => [ 'default' => [ 'HTML.Allowed' => 'p,b,i,a[href]', 'AutoFormat.AutoParagraph' => false, ], 'comments' => [ 'HTML.Allowed' => 'p,b,i', 'HTML.ForbiddenElements' => 'a', 'AutoFormat.AutoParagraph' => true, ], 'posts' => [ 'HTML.Allowed' => 'h1,h2,h3,p,b,i,ul,ol,li,a[href|title],img[src|alt]', 'CSS.AllowedProperties' => 'color,background-color,font-size', ], ], ]; // Usage in application use Stevebauman\Purify\Facades\Purify; $commentInput = 'Link

Great post!

'; $cleanedComment = Purify::config('comments')->clean($commentInput); // Output: '

Great post!

' (link removed per ForbiddenElements) $postInput = '

Title

Content

Photo'; $cleanedPost = Purify::config('posts')->clean($postInput); // Output: '

Title

Content

Photo' ``` ### Eloquent Cast - Sanitize on Retrieval Automatically purifies HTML content when reading from the database using Laravel's cast system. ```php use Illuminate\Database\Eloquent\Model; use Stevebauman\Purify\Casts\PurifyHtmlOnGet; class Post extends Model { // Laravel 11+ syntax protected function casts(): array { return [ 'content' => PurifyHtmlOnGet::class, 'excerpt' => PurifyHtmlOnGet::class, ]; } // Laravel 10 and earlier syntax // protected $casts = [ // 'content' => PurifyHtmlOnGet::class, // ]; } // Usage $post = new Post(); $post->content = '

Safe content

'; $post->save(); // When retrieved, content is automatically sanitized $retrieved = Post::find($post->id); echo $retrieved->content; // Output: '

Safe content

' (script removed on read) // Raw database value remains unchanged echo $retrieved->getRawOriginal('content'); // Output: '

Safe content

' ``` ### Eloquent Cast with Named Configuration Uses a specific purifier configuration profile when casting model attributes. ```php use Illuminate\Database\Eloquent\Model; use Stevebauman\Purify\Casts\PurifyHtmlOnGet; // First, define configuration in config/purify.php // 'configs' => [ // 'user-bio' => [ // 'HTML.Allowed' => 'b,i,em,strong,a[href]', // 'AutoFormat.AutoParagraph' => true, // ], // ] class User extends Model { protected function casts(): array { return [ 'bio' => PurifyHtmlOnGet::class.':user-bio', ]; } } // Usage $user = User::find(1); $user->bio = '

Not allowed

Bold text and link

'; $user->save(); $retrieved = User::find(1); echo $retrieved->bio; // Output: '

Bold text and link

' // (h1 and script removed, only allowed elements kept) ``` ### Eloquent Cast - Sanitize on Storage Purifies HTML content before saving to the database, ensuring only clean data is persisted. ```php use Illuminate\Database\Eloquent\Model; use Stevebauman\Purify\Casts\PurifyHtmlOnSet; class Comment extends Model { protected function casts(): array { return [ 'body' => PurifyHtmlOnSet::class, ]; } } // Usage $comment = new Comment(); $comment->body = '

Nice article!

'; $comment->save(); // Content is sanitized BEFORE saving to database // Database stores: '

Nice article!

' $retrieved = Comment::find($comment->id); echo $retrieved->body; // Output: '

Nice article!

' // Raw value is already clean echo $retrieved->getRawOriginal('body'); // Output: '

Nice article!

' ``` ### Custom HTML Definition Extends HTMLPurifier with custom HTML elements and attributes for specialized content (e.g., WYSIWYG editors). ```php // app/Definitions/TrixDefinition.php namespace App\Definitions; use HTMLPurifier_HTMLDefinition; use Stevebauman\Purify\Definitions\Definition; class TrixDefinition implements Definition { public static function apply(HTMLPurifier_HTMLDefinition $definition) { // Add figure element for attachments $definition->addElement('figure', 'Inline', 'Inline', 'Common'); $definition->addAttribute('figure', 'class', 'Class'); $definition->addAttribute('figure', 'data-trix-attachment', 'Text'); $definition->addAttribute('figure', 'data-trix-attributes', 'Text'); // Add figcaption for attachment captions $definition->addElement('figcaption', 'Inline', 'Inline', 'Common'); $definition->addAttribute('figcaption', 'class', 'Class'); $definition->addAttribute('figcaption', 'data-trix-placeholder', 'Text'); // Extend anchor tag with Trix-specific attributes $definition->addAttribute('a', 'data-trix-attachment', 'Text'); $definition->addAttribute('a', 'data-trix-content-type', 'Text'); $definition->addAttribute('a', 'contenteditable', 'Enum#true,false'); } } // config/purify.php return [ 'definitions' => \App\Definitions\TrixDefinition::class, // ... other config ]; // Usage use Stevebauman\Purify\Facades\Purify; $trixContent = '
My Image
'; $cleaned = Purify::clean($trixContent); // Output preserves Trix-specific attributes and elements echo $cleaned; ``` ### Custom CSS Definition Customizes allowed CSS properties and values, useful for supporting modern CSS values not in HTMLPurifier defaults. ```php // app/Definitions/ModernCssDefinition.php namespace App\Definitions; use HTMLPurifier_CSSDefinition; use HTMLPurifier_AttrDef_Enum; use Stevebauman\Purify\Definitions\CssDefinition; class ModernCssDefinition implements CssDefinition { public static function apply(HTMLPurifier_CSSDefinition $definition) { // Extend text-align to support start/end values $definition->info['text-align'] = new HTMLPurifier_AttrDef_Enum( ['left', 'right', 'center', 'justify', 'start', 'end'], false ); // Add display property support $definition->info['display'] = new HTMLPurifier_AttrDef_Enum( ['block', 'inline', 'inline-block', 'flex', 'grid', 'none'], false ); } } // config/purify.php return [ 'css-definitions' => \App\Definitions\ModernCssDefinition::class, // ... other config ]; // Usage use Stevebauman\Purify\Facades\Purify; $input = '

Modern CSS

Grid layout

'; $cleaned = Purify::clean($input); // Output: '

Modern CSS

//

Grid layout

' echo $cleaned; ``` ### Extending HTML5 Definition Combines built-in HTML5 support with custom definitions for comprehensive modern HTML handling. ```php // app/Definitions/ExtendedHtml5Definition.php namespace App\Definitions; use HTMLPurifier_HTMLDefinition; use Stevebauman\Purify\Definitions\Definition; use Stevebauman\Purify\Definitions\Html5Definition; class ExtendedHtml5Definition implements Definition { public static function apply(HTMLPurifier_HTMLDefinition $definition) { // Apply built-in HTML5 definitions first Html5Definition::apply($definition); // Add custom elements for your application $definition->addElement('custom-widget', 'Block', 'Flow', 'Common'); $definition->addAttribute('custom-widget', 'data-widget-id', 'Number'); $definition->addAttribute('custom-widget', 'data-config', 'Text'); // Extend video with additional attributes $definition->addAttribute('video', 'playsinline', 'Bool'); $definition->addAttribute('video', 'muted', 'Bool'); } } // config/purify.php return [ 'definitions' => \App\Definitions\ExtendedHtml5Definition::class, ]; // Usage use Stevebauman\Purify\Facades\Purify; $html5Content = '

Title

Widget content

'; $cleaned = Purify::clean($html5Content); // Output preserves HTML5 semantic elements and custom extensions echo $cleaned; ``` ### Clear Definition Cache Clears the HTMLPurifier serialized definition cache after configuration changes. ```bash # Clear cache when definitions or configuration changes php artisan purify:clear # Output: "HTML Purifier serializer cache cleared successfully." ``` ```php // When to clear cache: // 1. After updating config/purify.php definitions // 2. After modifying custom Definition classes // 3. After changing CSS definition classes // 4. After changing HTML.Allowed or other HTMLPurifier settings // Filesystem cache configuration (config/purify.php) 'serializer' => [ 'disk' => env('FILESYSTEM_DISK', 'local'), 'path' => 'purify', 'cache' => \Stevebauman\Purify\Cache\FilesystemDefinitionCache::class, ], // Cache driver configuration (Redis, Memcached, etc.) 'serializer' => [ 'driver' => env('CACHE_DRIVER', 'redis'), 'cache' => \Stevebauman\Purify\Cache\CacheDefinitionCache::class, ], // Disable caching for development (not recommended for production) 'serializer' => null, ``` ### Form Request Validation Integration Integrates Purify with Laravel form requests for automatic sanitization during validation. ```php // app/Http/Requests/CreatePostRequest.php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; use Stevebauman\Purify\Facades\Purify; class CreatePostRequest extends FormRequest { public function authorize(): bool { return true; } public function rules(): array { return [ 'title' => 'required|string|max:255', 'content' => 'required|string', 'excerpt' => 'nullable|string', ]; } // Sanitize after validation passes protected function passedValidation(): void { $this->merge([ 'content' => Purify::config('posts')->clean($this->content), 'excerpt' => Purify::config('posts')->clean($this->excerpt), ]); } } // app/Http/Controllers/PostController.php namespace App\Http\Controllers; use App\Http\Requests\CreatePostRequest; use App\Models\Post; class PostController extends Controller { public function store(CreatePostRequest $request) { // Content is already sanitized by form request $post = Post::create($request->validated()); return response()->json($post, 201); } } ``` ### Middleware for Global HTML Sanitization Creates middleware to sanitize all incoming HTML content automatically. ```php // app/Http/Middleware/SanitizeHtmlInput.php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Stevebauman\Purify\Facades\Purify; class SanitizeHtmlInput { protected $except = [ 'password', 'password_confirmation', '_token', ]; public function handle(Request $request, Closure $next) { $input = $request->all(); array_walk_recursive($input, function (&$value, $key) { if (!in_array($key, $this->except) && is_string($value)) { $value = Purify::clean($value); } }); $request->merge($input); return $next($request); } } // Register in app/Http/Kernel.php protected $middlewareGroups = [ 'web' => [ // ... other middleware \App\Http\Middleware\SanitizeHtmlInput::class, ], ]; // Now all incoming HTML is automatically sanitized // POST /posts // { // "title": "My Post", // "content": "

Content

" // } // // After middleware, content becomes: "

Content

" ``` ## Integration and Use Cases Purify is designed for applications that accept user-generated HTML content and need protection against XSS attacks while preserving safe formatting. Common use cases include content management systems, blogging platforms, forum software, and any application with rich-text editors. The package excels in scenarios requiring different sanitization levels across contexts, such as strict filtering for user comments but permissive handling for administrator content. Integration patterns typically involve using the facade for manual sanitization in controllers or services, Eloquent casts for automatic model-level protection, and custom definitions for specialized HTML/CSS requirements. The package supports Laravel Vapor and serverless deployments through cache-based serialization, eliminating filesystem dependencies. For optimal performance, enable caching in production and use named configurations to avoid redundant instantiation. The middleware pattern works well for protecting entire application sections, while form request integration provides granular control over validation workflows.