Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Purifier Laravel Package

luketowers/purifier

A Laravel wrapper around HTMLPurifier that brings clean, secure HTML sanitization to your application. It offers a Purifier facade, Blade directive, service provider, and publishable config so you can safely filter user-supplied HTML and prevent XSS with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Install via Composer:

composer require luketowers/purifier

On Laravel 5.5+ the Luketowers\Purifier\PurifierServiceProvider will be auto-discovered. For older 5.x versions, register it in config/app.php and add the facade alias:

'providers' => [
    Luketowers\Purifier\PurifierServiceProvider::class,
],

'aliases' => [
    'Purifier' => Luketowers\Purifier\Facades\Purifier::class,
],

Publish the config file so you can define allowed HTML:

php artisan vendor:publish --provider="Luketowers\Purifier\PurifierServiceProvider"

This creates config/purifier.php with a settings array containing a default profile and room for custom profiles (commonly ugc, strict, forum).

First use case: sanitizing user-generated HTML — blog comments, rich text descriptions, forum posts — before storing or rendering. The minimal call is:

$clean = Purifier::clean($request->input('body'));

Look first at:

  1. config/purifier.php → settings.default.HTML.Allowed (decides what tags survive).
  2. settings.default.Cache.SerializerPath (must point to a writable directory — this is the #1 trip-up).
  3. The facade call signature Purifier::clean($html, $configName).

Implementation Patterns

Per-context profiles. Don't run one loose config everywhere. Define named profiles for each trust level and pick them at call sites:

// config/purifier.php
'settings' => [
    'default' => [ /* ... */ ],
    'ugc'     => [
        'HTML.Allowed'  => 'p,b,strong,em,a[href|title],ul,ol,li,blockquote,code,pre',
        'AutoFormat.AutoParagraph' => true,
        'URI.AllowedSchemes' => ['http' => true, 'https' => true, 'mailto' => true],
    ],
    'strict'  => [
        'HTML.Allowed' => 'b,strong,em,i,p',
        'AutoFormat.RemoveEmpty' => true,
    ],
]
Purifier::clean($comment->body, 'ugc');
Purifier::clean($bio, 'strict');

Form request integration. Sanitize on the way in so validation and storage both see safe values:

class StoreCommentRequest extends FormRequest
{
    protected function prepareForValidation()
    {
        $this->merge([
            'body' => Purifier::clean($this->input('body', ''), 'ugc'),
        ]);
    }
}

Global helper. The package ships a clean() helper (auto-loaded) that mirrors Purifier::clean(). Use it in Blade views and controllers where the facade feels heavy, but prefer the facade in services so the test double is unambiguous.

Markdown → Purify pipeline. If you accept Markdown, convert first, then purify. Purifying Markdown source is wrong (the raw Markdown is harmless text); purifying the rendered HTML is what protects you:

$html = (new CommonMarkConverter())->convertToHtml($input)->getContent();
$safe = Purifier::clean($html, 'ugc');

Model mutator. Centralize sanitization where the model owns its own trust level:

public function setBodyAttribute($value)
{
    $this->attributes['body'] = Purifier::clean($value, 'ugc');
}

Be aware this makes direct DB writes unsafe-by-default in tests — seed via the model, not DB::table().

Rendering. Once purified, output with {!! $model->body !!} (raw). Anything not routed through Purifier should still use {{ }}.

Gotchas and Tips

  • Cache.SerializerPath must exist and be writable. HTMLPurifier serializes its runtime definition into that directory; without it you get a HTMLPurifier_Exception: Cache.SerializerPath ... is not writable at boot. Set it explicitly in config/purifier.php (e.g. storage_path('app/purifier')) and create the directory in your deploy script (mkdir -p ...). This is the single most common failure with any HTMLPurifier wrapper.

  • Empty vs null input. Purifier::clean(null) and Purifier::clean('') return an empty string, not null. If your column is nullable, coerce: $value === '' ? null : $value.

  • Defaults are permissive. The shipped default profile allows most safe HTML with href/src. For user content, tighten URI.AllowedSchemes (drop data:, javascript:, ftp:) and consider HTML.SafeIframe => false. HTMLPurifier is conservative by default but "conservative" still means <a href="http://evil">.

  • Attribute filtering is subtle. a[href|title] in HTML.Allowed means those attributes are permitted on <a>, not that they're allowed globally. If a tag disappears entirely, check that it's in HTML.Allowed — the blacklist/whitelist model is not obvious to newcomers.

  • Purify after Markdown, never before. Purifying raw Markdown strips nothing useful but can mangle [text](url) syntax into nothing. Convert to HTML first, then hand it to Purifier. Reverse order is a common bug.

  • Debugging output. Add 'Core.CollectErrors' => true to a temp config and check Purifier::getInstance()->getInstance()->context->getRaw('Core', 'Errors') when HTML is disappearing unexpectedly — it tells you which tokens were removed and why. HTMLPurifier_Config::createDefault() + HTMLPurifier::getInstance() work as an escape hatch if you need to bypass the wrapper.

  • Testing. Fake the facade in tests (Purifier::shouldReceive('clean')->andReturn(...)) for unit tests, but keep at least one integration test that exercises the real config — otherwise a broken config/purifier.php ships silently. Also test null and empty input explicitly.

  • Maintenance signal. This package is a low-activity fork of mews/purifier (32 stars). Pin the dependency and watch for mews/purifier or stevebauman/purify as drop-in successors if you hit compatibility issues with newer Laravel versions. The facade and helper APIs are intentionally the same, so migrating is mostly a namespace swap.

  • Extension points. If you need custom HTMLPurifier behavior (custom filters, HTML.DefinitionID tweaks), the config array supports any HTMLPurifier directive — the wrapper just forwards the array to HTMLPurifier_Config::create(). There's no need to subclass the service for most cases: add a named profile and pass it to clean().

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core