stevebauman/purify
Laravel wrapper for HTMLPurifier to sanitize user-submitted HTML and prevent XSS. Clean strings or arrays via the Purify facade, with support for per-call (dynamic) configuration and published config for app-wide rules.
Installation:
composer require stevebauman/purify
php artisan vendor:publish --provider="Stevebauman\Purify\PurifyServiceProvider"
config/purify.php.First Use Case:
use Stevebauman\Purify\Facades\Purify;
$cleaned = Purify::clean('<script>alert("XSS")</script><p>Hello</p>');
// Returns: '<p>Hello</p>'
config/purify.php for default settings and available configs (e.g., default, comments).Purify::clean() for basic sanitization.serializer settings in the config for performance tuning.Sanitizing User Input:
$title = Purify::clean(request('title'));
$data = Purify::clean(json_decode(request()->getContent(), true));
Dynamic Configurations:
$cleaned = Purify::config(['HTML.Allowed' => 'div,p,a[href]'])->clean($input);
comments):
$cleaned = Purify::config('comments')->clean(request('content'));
Eloquent Integration:
use Stevebauman\Purify\Casts\PurifyHtmlOnGet;
class Post extends Model {
protected $casts = ['content' => PurifyHtmlOnGet::class];
}
use Stevebauman\Purify\Casts\PurifyHtmlOnSet;
protected $casts = ['content' => PurifyHtmlOnSet::class];
Batch Processing:
$cleanedArray = Purify::clean([
'<script>alert("XSS")</script>',
'<b>Safe</b>'
]);
public function handle($request, Closure $next) {
$request->merge(array_map([Purify::class, 'clean'], $request->all()));
return $next($request);
}
class ContentService {
public function sanitize($content, $config = 'default') {
return Purify::config($config)->clean($content);
}
}
$validator = Validator::make($request->all(), [
'content' => 'required|string|purify', // Custom rule
]);
Caching Quirks:
php artisan purify:clear after updating definitions or configs causes stale purifier rules.serializer path (e.g., storage/app/purify) is writable by the web server.Cache::clear() on the default cache driver may unintentionally clear other cached data. Use a dedicated cache store for Purify.Performance:
serializer: null in production causes repeated serialization, degrading performance.Configuration Overrides:
Purify::config() replace defaults, not merge with them. Use array_merge if needed:
$config = array_merge(config('purify.configs.default'), ['HTML.Allowed' => '...']);
Purify::config($config)->clean($input);
HTMLPurifier Limitations:
HTML.Doctype (e.g., HTML5) will be stripped. Extend definitions via Html5Definition or custom classes.text-align: start) require custom CssDefinition classes.\Log::debug('Purified:', ['input' => $dirty, 'output' => $cleaned]);
$testInput = '<custom-tag attr="value">Content</custom-tag>';
$cleaned = Purify::clean($testInput);
ls -la storage/app/purify
'Core.DebugInfo' => true,
HTMLPurifier_Exception traces.Html5Definition for unsupported tags (e.g., Trix editor):
class TrixDefinition implements Definition {
public static function apply($definition) {
Html5Definition::apply($definition);
$definition->addElement('figure', 'Inline', 'Flow', 'Common');
// Add attributes...
}
}
class CustomCssDefinition implements CssDefinition {
public static function apply($definition) {
$definition->info['text-align'] = new \HTMLPurifier_AttrDef_Enum(
['start', 'end', 'left', 'right']
);
}
}
$dynamicConfig = DB::table('purify_configs')->where('name', 'editor')->first();
Purify::config(json_decode($dynamicConfig->settings, true))->clean($input);
Purify::extend(function ($purifier) {
$purifier->addListener('preClean', function ($input) {
// Pre-process input
});
});
Purify::shouldReceive('clean')->once()->andReturn('<p>Mocked</p>');
How can I help you explore Laravel packages today?