Purifier::_createDocument()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
c 0
b 0
f 0
ccs 4
cts 4
cp 1
rs 10
cc 1
nc 1
nop 1
crap 1
1
<?php
2
namespace Redaxscript\Html;
3
4
use DOMDocument;
5
use DOMNode;
6
use Redaxscript\Model;
7
use function in_array;
8
use function is_object;
9
use function is_string;
10
use function mb_convert_encoding;
11
use function range;
12
use function strlen;
13
use function trim;
14
15
/**
16
 * parent class to purify html against xss
17
 *
18
 * @since 3.0.0
19
 *
20
 * @package Redaxscript
21
 * @category Html
22
 * @author Henry Ruhs
23
 */
24
25
class Purifier
26
{
27
	/**
28
	 * array of allowed tags
29
	 *
30
	 * @var array
31
	 */
32
33
	protected $_allowedTags =
34
	[
35
		'#text',
36
		'br',
37
		'caption',
38
		'div',
39
		'dd',
40
		'dl',
41
		'dt',
42
		'em',
43
		'h1',
44
		'h2',
45
		'h3',
46
		'h4',
47
		'h5',
48
		'h6',
49
		'li',
50
		'p',
51
		'pre',
52
		'ol',
53
		'span',
54
		'strike',
55
		'strong',
56
		'sub',
57
		'sup',
58
		'table',
59
		'tbody',
60
		'tfoot',
61
		'thead',
62
		'td',
63
		'th',
64
		'tr',
65
		'strong',
66
		'u',
67
		'ul',
68
		'wbr'
69
	];
70
71
	/**
72
	 * purify the html
73
	 *
74
	 * @since 3.0.0
75
	 *
76
	 * @param string $html html to be purified
77
	 * @param bool $filter optional filter
78
	 *
79
	 * @return string|null
80
	 */
81
82 11
	public function purify(string $html = null, bool $filter = true) : ?string
83
	{
84 11
		$settingModel = new Model\Setting();
85 11
		$charset = $settingModel->get('charset');
86 11
		$html = mb_convert_encoding($html, 'html-entities', $charset);
87
88
		/* filter html */
89
90 11
		if ($filter && strlen($html))
91
		{
92 11
			$html = $this->_process($html);
93
		}
94 11
		return $html;
95
	}
96
97
	/**
98
	 * process the html
99
	 *
100
	 * @since 3.0.0
101
	 *
102
	 * @param string|object $html html to be processed
103
	 *
104
	 * @return string|null
105
	 */
106
107 11
	protected function _process($html = null) : ?string
108
	{
109 11
		if (is_object($html))
110
		{
111
			/* process children */
112
113 11
			if ($html->hasChildNodes())
114
			{
115 5
				$range = range($html->childNodes->length - 1, 0);
116 5
				foreach ($range as $i)
117
				{
118 5
					$this->_process($html->childNodes->item($i));
119
				}
120
			}
121
122
			/* strip tags and attributes */
123
124 11
			$this->_stripTags($html);
125 11
			$this->_stripAttributes($html);
126
		}
127 11
		else if (is_string($html))
128
		{
129 11
			$doc = $this->_createDocument($html);
130 11
			$this->_process($doc->documentElement);
131 11
			return trim($doc->saveHTML());
132
		}
133 11
		return null;
134
	}
135
136
	/**
137
	 * strip the tags
138
	 *
139
	 * @since 3.0.0
140
	 *
141
	 * @param DOMNode $node document node
142
	 */
143
144 11
	protected function _stripTags(DOMNode $node = null) : void
145
	{
146 11
		if (!in_array($node->nodeName, $this->_allowedTags))
147
		{
148 10
			$fragment = $this->_createFragment($node);
149 10
			$node->parentNode->replaceChild($fragment, $node);
150
		}
151 11
	}
152
153
	/**
154
	 * strip the attributes
155
	 *
156
	 * @since 3.0.0
157
	 *
158
	 * @param DOMNode $node document node
159
	 */
160
161 11
	protected function _stripAttributes(DOMNode $node = null) : void
162
	{
163 11
		while ($node->hasAttributes())
0 ignored issues
show
Bug introduced by
It seems like $node is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
164
		{
165 10
			$node->removeAttributeNode($node->attributes->item(0));
166
		}
167 11
	}
168
169
	/**
170
	 * create the document
171
	 *
172
	 * @since 2.4.0
173
	 *
174
	 * @param string $html html to be loaded
175
	 *
176
	 * @return DOMDocument
177
	 */
178
179 11
	protected function _createDocument(string $html = null) : DOMDocument
180
	{
181 11
		$doc = new DOMDocument();
182 11
		$doc->loadHTML($html, LIBXML_HTML_NODEFDTD | LIBXML_HTML_NOIMPLIED);
183 11
		return $doc;
184
	}
185
186
	/**
187
	 * create the fragment
188
	 *
189
	 * @since 3.0.0
190
	 *
191
	 * @param DOMNode $node document node
192
	 *
193
	 * @return DOMNode
194
	 */
195
196 10
	protected function _createFragment(DOMNode $node = null) : DOMNode
197
	{
198 10
		$fragment = $node->ownerDocument->createDocumentFragment();
199
200
		/* process nodes */
201
202 10
		while ($node->childNodes->length)
203
		{
204 1
			$fragment->appendChild($node->firstChild);
205
		}
206 10
		return $fragment;
207
	}
208
}
209