Skip to content

Commit 3316730

Browse files
html:Purify added
1 parent ac05b06 commit 3316730

File tree

2 files changed

+147
-0
lines changed

2 files changed

+147
-0
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"vlucas/phpdotenv": "^5.4.1",
2222
"phpmailer/phpmailer": "^6.9",
2323
"filp/whoops": "^2.15",
24+
"ezyang/htmlpurifier": "^4.13",
2425
"composer-plugin-api": "^2.0",
2526
"dompdf/dompdf": "^3.0"
2627
},

src/Purify.php

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
<?php
2+
3+
namespace Tamedevelopers\Support;
4+
5+
use HTMLPurifier;
6+
use HTMLPurifier_Config;
7+
use Tamedevelopers\Support\Str;
8+
9+
class Purify
10+
{
11+
private static $purifierHtml;
12+
private static $purifierDev;
13+
private static $purifierString;
14+
15+
/**
16+
* Init CMS-safe purifier
17+
*/
18+
private static function initHtml()
19+
{
20+
if (!self::$purifierHtml) {
21+
$config = HTMLPurifier_Config::createDefault();
22+
23+
// Core settings
24+
$config->set('HTML.SafeIframe', true);
25+
$config->set('AutoFormat.Linkify', true);
26+
$config->set('AutoFormat.AutoParagraph', true);
27+
$config->set('Core.EscapeInvalidTags', false);
28+
29+
// Set unique ID for extended definition (required for maybeGetRawHTMLDefinition)
30+
$config->set('HTML.DefinitionID', 'cms-html5-purifier'); // unique name
31+
$config->set('HTML.DefinitionRev', 1); // revision number
32+
33+
// Allowed tags and attributes for CMS/blog content
34+
$config->set('HTML.Allowed', implode(',', [
35+
// Links & references
36+
'a[href|title|target]', // links with optional title and target
37+
'abbr[title]', // abbreviations
38+
'acronym[title]', // acronyms
39+
'b', 'strong', // bold text
40+
'i', 'em', // italic text
41+
'u', // underline
42+
'strike', // strikethrough
43+
'sub', 'sup', // subscripts/superscripts
44+
'mark', // highlighted text
45+
'p', 'br', 'hr',
46+
'h1','h2','h3','h4','h5','h6',
47+
'blockquote[cite]',
48+
'code', 'pre',
49+
'ul','ol','li','dl','dt','dd',
50+
'table','thead','tbody','tfoot','tr','th','td',
51+
'img[src|alt|title|width|height]',
52+
'audio[src|controls|width|height|preload]',
53+
'video[src|controls|width|height|preload|poster]',
54+
'header','footer','main','section','article','aside','figure','figcaption','nav',
55+
'div[style|class|id]',
56+
'span[style|class|id]',
57+
'iframe[src|width|height|frameborder|allow|allowfullscreen]',
58+
]));
59+
60+
// Extend HTML5 support for semantic tags and media
61+
$def = $config->maybeGetRawHTMLDefinition();
62+
if ($def) {
63+
// Semantic blocks
64+
$def->addElement('header', 'Block', 'Flow', 'Common');
65+
$def->addElement('footer', 'Block', 'Flow', 'Common');
66+
$def->addElement('main', 'Block', 'Flow', 'Common');
67+
$def->addElement('section', 'Block', 'Flow', 'Common');
68+
$def->addElement('article', 'Block', 'Flow', 'Common');
69+
$def->addElement('aside', 'Block', 'Flow', 'Common');
70+
$def->addElement('figure', 'Block', 'Optional: (figcaption, Flow)', 'Common');
71+
$def->addElement('figcaption', 'Inline', 'Flow', 'Common');
72+
$def->addElement('nav', 'Block', 'Flow', 'Common');
73+
74+
// Multimedia
75+
$def->addElement('audio', 'Block', 'Optional: Flow', 'Common', ['src' => 'URI', 'controls' => 'Bool', 'width' => 'Length', 'height' => 'Length', 'preload' => 'Enum#auto,metadata,none']);
76+
$def->addElement('video', 'Block', 'Optional: Flow', 'Common', ['src' => 'URI', 'controls' => 'Bool', 'width' => 'Length', 'height' => 'Length', 'poster' => 'URI', 'preload' => 'Enum#auto,metadata,none']);
77+
}
78+
79+
self::$purifierHtml = new HTMLPurifier($config);
80+
}
81+
}
82+
83+
/**
84+
* Init Dev-safe purifier (allow all tags, attributes, JS, style)
85+
*/
86+
private static function initDev()
87+
{
88+
if (!self::$purifierDev) {
89+
$config = HTMLPurifier_Config::createDefault();
90+
91+
// Allow all HTML, including script/style for dev usage
92+
$config->set('HTML.Allowed', null);
93+
$config->set('HTML.SafeEmbed', true);
94+
$config->set('HTML.SafeObject', true);
95+
$config->set('HTML.SafeIframe', true);
96+
$config->set('CSS.AllowTricky', true);
97+
$config->set('HTML.Trusted', true); // keep script/style for dev
98+
99+
self::$purifierDev = new HTMLPurifier($config);
100+
}
101+
}
102+
103+
/**
104+
* Init plain string purifier
105+
*/
106+
private static function initString()
107+
{
108+
if (!self::$purifierString) {
109+
$config = HTMLPurifier_Config::createDefault();
110+
111+
// Strip all HTML safely including scripts
112+
$config->set('HTML.Allowed', '');
113+
$config->set('HTML.Trusted', false);
114+
115+
self::$purifierString = new HTMLPurifier($config);
116+
}
117+
}
118+
119+
/**
120+
* Purify HTML for CMS/blog posts
121+
*/
122+
public static function html(string $content): string
123+
{
124+
self::initHtml();
125+
return self::$purifierHtml->purify($content);
126+
}
127+
128+
/**
129+
* Purify for plain string (strip all HTML, scripts, unsafe content)
130+
*/
131+
public static function string(string $content): string
132+
{
133+
self::initString();
134+
$clean = self::$purifierString->purify($content);
135+
return Str::trim($clean);
136+
}
137+
138+
/**
139+
* Purify for developer usage (keep all content including JS/style)
140+
*/
141+
public static function dev(string $content): string
142+
{
143+
self::initDev();
144+
return self::$purifierDev->purify($content);
145+
}
146+
}

0 commit comments

Comments
 (0)