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

Purify Laravel Package

stevebauman/purify

A Laravel wrapper for HTMLPurifier that sanitizes user input. Clean strings or arrays of HTML through a simple Purify facade, with publishable configuration, caching, and custom profiles for flexible, safe markup filtering.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the wrapper and pull in the underlying HTMLPurifier library in one shot:

composer require stevebauman/purify
php artisan vendor:publish --provider="Stevebauman\Purify\PurifyServiceProvider"

The published config/purify.php is the first file to read. It has three areas that matter day-to-day:

  • configs.default — the settings applied when you call Purify::clean() with no arguments. This is an HTMLPurifier config array (HTML.Allowed, HTML.ForbiddenElements, CSS.AllowedProperties, AutoFormat.*, etc.).
  • definitions — the class that registers which HTML elements/attributes are recognised at all (HTML5 support, Trix, custom tags).
  • serializer — where the compiled definitions are cached between requests.

The first use case is almost always sanitising user-submitted HTML before rendering it back:

use Stevebauman\Purify\Facades\Purify;

$cleaned = Purify::clean($request->input('content'));

Purify::clean() also accepts arrays, returning an array of cleaned strings — convenient for bulk input without a loop.

Implementation Patterns

Split configuration by surface, not by controller. Use the named config sets instead of inline arrays. Define comments, posts, bios under configs and call Purify::config('comments')->clean($content). This keeps the allow-list visible in one file and lets you tighten rules later without touching call sites.

Sanitise on the way out, not on the way in. Store raw HTML in the database and clean at read time. The package ships Eloquent casts for this:

protected function casts(): array // or $casts for Laravel <= 10
{
    return [
        'content' => PurifyHtmlOnGet::class,
    ];
}

Bind a specific config set by suffixing it: PurifyHtmlOnGet::class.':comments'. If you prefer explicit control, an old-school accessor mutator (getContentAttribute) does the same thing. PurifyHtmlOnSet is the inverse if you must clean before write (e.g. exporting to email or a third-party API).

Wire it into validation flows. In a FormRequest, clean in prepareForValidation() or after validated() rather than sprinkling facade calls across controllers. A middleware that transforms a whole payload is a reasonable alternative for CMS-style endpoints.

Extend the definition for WYSIWYG editors. HTMLPurifier's schema predates HTML5, so elements like <figure> and data attributes get stripped silently. Create a class implementing Stevebauman\Purify\Definitions\Definition and call Html5Definition::apply($definition) first, then add your own elements:

$definition->addElement('figure', 'Inline', 'Inline', 'Common');
$definition->addAttribute('figure', 'data-trix-attachment', 'Text');

The README's Trix example is a solid template for any editor (Trix, TipTap, Quill) and saves you a debugging session.

Extend CSS separately. Inline styles are controlled by HTMLPurifier_CSSDefinition, not the HTML definition. If you need text-align: start or end, implement CssDefinition and register it under css-definitions:

$definition->info['text-align'] = new \HTMLPurifier_AttrDef_Enum(
    ['right', 'left', 'center', 'start', 'end'], false
);

Handle the cache deliberately. After the first request, HTMLPurifier serialises definitions into the configured cache. Any change to definitions (or the config set) requires:

php artisan purify:clear

Run this in your deploy script if your definitions are derived from code. During development, set 'serializer' => null to skip caching and see changes immediately — but never leave it off in production; recompiling definitions per request is measurably slow.

Gotchas and Tips

  • Purify::config($array) does not merge with your default config. Passing an array replaces the whole set. Copy the defaults you care about, or use named config sets instead.
  • purify:clear wipes the entire target. With CacheDefinitionCache it issues a Cache::clear() on that store; with FilesystemDefinitionCache it deletes the configured directory. Point Purify at a dedicated cache store or disk so you don't nuke unrelated data.
  • Silent stripping is the default failure mode. If an element or attribute disappears, it's almost always a missing Definition entry, not a bug in clean(). Test with Purify::clean('<your markup>') in tinker before assuming the config is wrong.
  • Attribute syntax is exact. HTML.Allowed uses a[href|title], img[width|height|alt|src]. Attribute types in custom definitions matter: Text, Number, Enum#true,false, Class. Wrong type = silently dropped attribute.
  • AutoFormat.AutoParagraph and AutoFormat.RemoveEmpty change output shape. AutoParagraph wraps loose text in <p>; RemoveEmpty strips empty tags — including ones your editor intentionally emits. Both default to false in the shipped config; keep them off unless you specifically want the reformatting.
  • HTML.Doctype controls the baseline schema. Switching to a stricter doctype silently removes elements. If you allow HTML5, ensure definitions includes Html5Definition, otherwise HTML.Doctype alone won't add HTML5 elements.
  • Don't double-sanitise. If a model uses PurifyHtmlOnGet and a Blade template also calls Purify::clean(), you pay the cost twice. Pick one layer.
  • Sanitisation is not URL validation. HTMLPurifier filters dangerous schemes in href/src, but validating that a link is an allowed destination is still your job.
  • Performance scales with volume. Cleaning is CPU-bound; avoid looping clean() over thousands of rows in a report. Consider batching, or caching rendered output once it's been cleaned.
  • Extension points to remember: Definition for HTML, CssDefinition for inline styles, named configs for per-context rules, and the serializer for cache strategy. Most "Purify doesn't support X" issues resolve through one of these three.
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.
takashato/laravel-vietqr
aitor24/laralang
kraenzle-ritter/entity-relations
dindigital/din-image
pomirleanu/image-colors
sawmainek/laravel-api-generator
delatbabel/jsonresponses
mouhamedfd/permissions-generator
eduplus/qr
laravel2580/idehelpers
delatbabel/viewpages
anavel/gettext
muhamadrezaar/highcharts
delatbabel/keylists
frankkessler/salesforce-laravel-oauth2-rest
anavel/foundation
wdmtech/enviricons
eii/laravel-captcha
muka/shape-reader
tdt/json