Purifier::purifyHtml()   B
last analyzed

Complexity

Conditions 7
Paths 8

Size

Total Lines 32
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 7

Importance

Changes 0
Metric Value
eloc 22
dl 0
loc 32
ccs 19
cts 19
cp 1
rs 8.6346
c 0
b 0
f 0
cc 7
nc 8
nop 2
crap 7
1
<?php
2
/**
3
 * Purifier file.
4
 *
5
 * @package App
6
 *
7
 * @license   YetiForce Public License 6.5 (licenses/LicenseEN.txt or yetiforce.com)
8
 * @copyright YetiForce S.A.
9
 * @author    Mariusz Krzaczkowski <[email protected]>
10
 * @author    Radosław Skrzypczak <[email protected]>
11
 */
12
13
namespace App;
14
15
/**
16
 * Purifier basic class.
17
 */
18
class Purifier
19
{
20
	/** @var string Purify type date in user format. */
21
	public const DATE_USER_FORMAT = 'DateInUserFormat';
22
23
	/** @var string Purify type integer. */
24
	public const INTEGER = 'Integer';
25
26
	/** @var string Purify type standard. */
27
	public const STANDARD = 'Standard';
28
29
	/** @var string Purify type sql. */
30
	public const SQL = 'Sql';
31
32
	/** @var string Purify type text. */
33
	public const TEXT = 'Text';
34
35
	/** @var string Purify type number. */
36
	public const NUMBER = 'Number';
37
38
	/** @var string Purify type html. */
39
	public const HTML = 'Html';
40
41
	/** @var string Purify type boolean. */
42
	public const BOOL = 'Bool';
43
44
	/** @var string Purify type url. */
45
	public const URL = 'Url';
46
47
	/** @var string Purify type Alnum. */
48
	public const ALNUM = 'Alnum';
49
50
	/** @var string Purify type Alnum 2 (A-Za-z0-9\/\+\-). */
51
	public const ALNUM2 = 'AlnumType2';
52
53
	/** @var string Purify type AlnumExtended. */
54
	public const ALNUM_EXTENDED = 'AlnumExtended';
55
56
	/** @var string Purify type Digits. */
57
	public const DIGITS = 'Digits';
58
59
	/** @var string Purify type HTML text parser */
60
	public const HTML_TEXT_PARSER = 'HtmlTextParser';
61
62
	/** @var string Purify type Path. */
63
	public const PATH = 'Path';
64
65
	/** @var string Purify type email. */
66
	public const EMAIL = 'Email';
67
68
	/**
69
	 * Default charset.
70
	 *
71
	 * @var string
72
	 */
73
	public static $defaultCharset;
74
75
	/**
76
	 * Cache for purify instance.
77
	 *
78
	 * @var bool|\HTMLPurifier
79
	 */
80
	private static $purifyInstanceCache = false;
81
82
	/**
83
	 * Cache for Html purify instance.
84
	 *
85
	 * @var bool|\HTMLPurifier
86
	 */
87
	private static $purifyHtmlInstanceCache = false;
88
89
	/** @var bool|\HTMLPurifier Cache for Html template purify instance. */
90
	private static $purifyTextParserInstanceCache = false;
91 52
92
	/**
93 52
	 * Html events attributes.
94 3
	 *
95
	 * @var string
96 51
	 */
97 51
	private static $htmlEventAttributes = 'onerror|onblur|onchange|oncontextmenu|onfocus|oninput|oninvalid|onreset|onsearch|onselect|onsubmit|onkeydown|onkeypress|onkeyup|' .
98 51
	'onclick|ondblclick|ondrag|ondragend|ondragenter|ondragleave|ondragover|ondragstart|ondrop|onmousedown|onmousemove|onmouseout|onmouseover|onbeforepaste|onresizestart|onactivate|' .
99 51
	'onmouseup|onmousewheel|onscroll|onwheel|oncopy|oncut|onpaste|onload|onselectionchange|onabort|onselectstart|ondragdrop|onmouseleave|onmouseenter|onunload|onresize|onmessage|' .
100 9
	'onpropertychange|onfilterchange|onstart|onfinish|onbounce|onrowsinserted|onrowsdelete|onrowexit|onrowenter|ondatasetcomplete|ondatasetchanged|ondataavailable|oncellchange|' .
101
	'onbeforeupdate|onafterupdate|onerrorupdate|onhelp|onbeforeprint|onafterprint|oncontrolselect|onfocusout|onfocusin|ondeactivate|onbeforeeditfocus|onbeforedeactivate|onbeforeactivate|' .
102
	'onresizeend|onmovestart|onmoveend|onmove|onbeforecopy|onbeforecut|onbeforeunload|onhashchange|onoffline|ononline|onreadystatechange|onstop|onlosecapture';
103
104 46
	/**
105
	 * Remove unnecessary code list.
106
	 *
107
	 * @var string[]
108
	 */
109
	private static $removeUnnecessaryCode = [
110
		'href="javascript:window.history.back();"',
111
		'href="javascript:void(0);"',
112 46
	];
113
114 46
	/**
115 2
	 * Purify (Cleanup) malicious snippets of code from the input.
116 2
	 *
117 2
	 * @param string $input
118
	 * @param bool   $loop  Purify values in the loop
119 46
	 *
120 45
	 * @return string
121 43
	 */
122 43
	public static function purify($input, $loop = true)
123 43
	{
124 43
		if (empty($input)) {
125 43
			return $input;
126 43
		}
127
		$value = $input;
128
		if (!\is_array($input)) {
0 ignored issues
show
introduced by
The condition is_array($input) is always false.
Loading history...
129 43
			$cacheKey = md5($input);
130
			if (Cache::has('purify', $cacheKey)) {
131
				return Cache::get('purify', $cacheKey);
132 44
			}
133
		}
134
		// Initialize the instance if it has not yet done
135
		if (!static::$purifyInstanceCache) {
0 ignored issues
show
Bug introduced by
Since $purifyInstanceCache is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $purifyInstanceCache to at least protected.
Loading history...
136
			$config = \HTMLPurifier_Config::createDefault();
137
			$config->set('Core.Encoding', static::$defaultCharset);
138
			$config->set('Cache.SerializerPermissions', 0775);
139
			$config->set('Cache.SerializerPath', ROOT_DIRECTORY . \DIRECTORY_SEPARATOR . 'cache' . \DIRECTORY_SEPARATOR . 'vtlib');
140
			$config->set('HTML.Allowed', '');
141
			static::$purifyInstanceCache = new \HTMLPurifier($config);
142
		}
143 22
		if (static::$purifyInstanceCache) {
144
			// Composite type
145 22
			if (\is_array($input)) {
0 ignored issues
show
introduced by
The condition is_array($input) is always false.
Loading history...
146 1
				$value = [];
147
				foreach ($input as $k => $v) {
148 21
					$value[$k] = static::purify($v);
149 21
				}
150 21
			} elseif (\is_string($input)) {
0 ignored issues
show
introduced by
The condition is_string($input) is always true.
Loading history...
151 8
				$input = str_replace(["\r\n"], "\n", $input);
152
				static::purifyHtmlEventAttributes($input);
153
				$value = static::$purifyInstanceCache->purify(static::decodeHtml($input));
154 19
				if ($loop) {
155 1
					$last = '';
156 1
					while ($last !== $value) {
157
						$last = $value;
158 19
						$value = static::purify($value, false);
159 19
					}
160 19
				}
161 16
				Cache::save('purify', $cacheKey, $value, Cache::SHORT);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $cacheKey does not seem to be defined for all execution paths leading up to this point.
Loading history...
162 16
			}
163 16
		}
164 14
		return $value;
165 14
	}
166
167
	/**
168 16
	 * Purify HTML (Cleanup) malicious snippets of code from the input.
169 16
	 *
170
	 * @param string $input
171 16
	 * @param bool   $loop  Purify values in the loop
172
	 *
173
	 * @return string
174
	 */
175
	public static function purifyHtml(string $input, $loop = true): string
176
	{
177
		if (empty($input)) {
178
			return $input;
179 61
		}
180
		$value = $input;
181 61
		$cacheKey = md5($input);
182 5
		if (Cache::has('purifyHtml', $cacheKey)) {
183 5
			return Cache::get('purifyHtml', $cacheKey);
184
		}
185 56
186
		// Initialize the instance if it has not yet done
187
		if (!static::$purifyHtmlInstanceCache) {
0 ignored issues
show
Bug introduced by
Since $purifyHtmlInstanceCache is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $purifyHtmlInstanceCache to at least protected.
Loading history...
188
			$config = static::getHtmlConfig();
189
			static::$purifyHtmlInstanceCache = new \HTMLPurifier($config);
190
		}
191
		if (static::$purifyHtmlInstanceCache) {
192 1
			$input = str_replace(["\r\n"], "\n", $input);
193
			$value = static::$purifyHtmlInstanceCache->purify($input);
194 1
			$value = static::removeUnnecessaryCode($value);
195 1
			if ($loop) {
196 1
				$last = '';
197 1
				while ($last !== $value) {
198 1
					$last = $value;
199 1
					$value = static::purifyHtml($value, false);
200 1
				}
201 1
			}
202 1
			static::purifyHtmlEventAttributes(static::decodeHtml($value));
203 1
			$value = preg_replace("/(^[\r\n]*|[\r\n]+)[\\s\t]*[\r\n]+/", "\n", $value);
204 1
			Cache::save('purifyHtml', $cacheKey, $value, Cache::SHORT);
205 1
		}
206 1
		return $value;
207 1
	}
208 1
209
	/**
210
	 * Purify HTML (Cleanup) malicious snippets of code from text parser.
211
	 *
212
	 * @param string $input
213
	 * @param bool   $loop  Purify values in the loop
214
	 *
215
	 * @return string
216
	 */
217 1
	public static function purifyTextParser($input, $loop = true): string
218 1
	{
219 1
		if (empty($input)) {
220 1
			return $input;
221 1
		}
222 1
		$cacheKey = md5($input);
223 1
		if (Cache::has('purifyTextParser', $cacheKey)) {
224 1
			return Cache::get('purifyTextParser', $cacheKey);
225 1
		}
226 1
		if (!static::$purifyTextParserInstanceCache) {
0 ignored issues
show
Bug introduced by
Since $purifyTextParserInstanceCache is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $purifyTextParserInstanceCache to at least protected.
Loading history...
227 1
			$config = static::getHtmlConfig(['directives' => ['HTML.AllowedCommentsRegexp' => '/^(\s+{% |{% )[\s\S]+( %}| %}\s+)$/u']]);
228 1
			static::$purifyTextParserInstanceCache = new \HTMLPurifier($config);
229 1
		}
230
		$value = static::$purifyTextParserInstanceCache->purify($input);
231
		$value = static::removeUnnecessaryCode($value);
232
		static::purifyHtmlEventAttributes($value);
233
		if ($loop) {
234
			$last = '';
235
			while ($last !== $value) {
236
				$last = $value;
237 1
				$value = static::purifyTextParser($value, false);
238 1
			}
239
		}
240
		$value = preg_replace("/(^[\r\n]*|[\r\n]+)[\\s\t]*[\r\n]+/", "\n", $value);
241
		Cache::save('purifyTextParser', $cacheKey, $value, Cache::SHORT);
242
		return $value;
243 1
	}
244 1
245
	/**
246
	 * To purify malicious html event attributes.
247 1
	 *
248 1
	 * @param string $value
249 1
	 */
250 1
	public static function purifyHtmlEventAttributes(string $value): void
251 1
	{
252 1
		if (preg_match('#(<[^><]+?[\x00-\x20"\'])([^a-z_\\-]on\\w*|xmlns)(\\s*=\\s*[^><]*)([><]*)#i', $value, $matches)) {
253 1
			\App\Log::error('purifyHtmlEventAttributes: ' . $value, 'IllegalValue');
254 1
			throw new Exceptions\IllegalValue('ERR_NOT_ALLOWED_VALUE|1|' . print_r($matches, true) . "||$value", 406);
0 ignored issues
show
Bug introduced by
Are you sure print_r($matches, true) of type string|true can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

254
			throw new Exceptions\IllegalValue('ERR_NOT_ALLOWED_VALUE|1|' . /** @scrutinizer ignore-type */ print_r($matches, true) . "||$value", 406);
Loading history...
255
		}
256 1
		if (preg_match('#<([^><]+?)(' . static::$htmlEventAttributes . ')(\\s*=\\s*[^><]*)([>]*)#i', $value, $matches)) {
0 ignored issues
show
Bug introduced by
Since $htmlEventAttributes is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $htmlEventAttributes to at least protected.
Loading history...
257 1
			\App\Log::error('purifyHtmlEventAttributes: ' . $value, 'IllegalValue');
258
			throw new Exceptions\IllegalValue('ERR_NOT_ALLOWED_VALUE|2|' . print_r($matches, true) . "||$value", 406);
259 1
		}
260 1
		if (preg_match('#<([^><]+?)javascript:[\w\.]+\(([>]*)#i', $value, $matches)) {
261 1
			\App\Log::error('purifyHtmlEventAttributes: ' . $value, 'IllegalValue');
262 1
			throw new Exceptions\IllegalValue('ERR_NOT_ALLOWED_VALUE|3|' . print_r($matches, true) . "||$value", 406);
263 1
		}
264 1
	}
265 1
266 1
	/**
267 1
	 * Remove unnecessary code.
268
	 *
269 1
	 * @param string $value
270 1
	 *
271 1
	 * @return string
272
	 */
273
	public static function removeUnnecessaryCode(string $value): string
274
	{
275
		foreach (self::$removeUnnecessaryCode as $code) {
276
			if (false !== stripos($value, $code)) {
277
				$value = str_ireplace($code, '', $value);
278
			}
279
		}
280
		return $value;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $value could return the type array which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
281
	}
282 3
283
	/**
284 3
	 * Get html config.
285 3
	 *
286
	 * @param array $options
287 1
	 *
288 1
	 * @return \HTMLPurifier_Config
289
	 */
290
	public static function getHtmlConfig(array $options = [])
291
	{
292
		$config = \HTMLPurifier_Config::createDefault();
293
		$config->set('Core.Encoding', static::$defaultCharset);
294
		$config->set('Cache.SerializerPermissions', 0775);
295
		$config->set('Cache.SerializerPath', ROOT_DIRECTORY . \DIRECTORY_SEPARATOR . 'cache' . \DIRECTORY_SEPARATOR . 'vtlib');
296
		$config->set('HTML.Doctype', 'HTML 4.01 Transitional');
297
		$config->set('CSS.AllowTricky', true);
298
		$config->set('CSS.Proprietary', true);
299
		$config->set('CSS.Trusted', true);
300
		$config->set('Core.RemoveInvalidImg', true);
301
		$config->set('HTML.SafeIframe', true);
302
		$config->set('HTML.SafeEmbed', true);
303
		$config->set('URI.SafeIframeRegexp', '%^(http:|https:)?//(www\.youtube(?:-nocookie)?\.com/embed/|player\.vimeo\.com/video/)%');
304
		$config->set('HTML.DefinitionRev', 1);
305
		$config->set('HTML.TargetBlank', true);
306 48
		$config->set('Attr.EnableID', true);
307
		$config->set('CSS.MaxImgLength', null);
308 48
		$config->set('URI.AllowedSchemes', [
309 1
			'http' => true,
310 1
			'https' => true,
311 1
			'mailto' => true,
312
			'ftp' => true,
313
			'nntp' => true,
314 48
			'news' => true,
315
			'tel' => true,
316 48
			'data' => true,
317 45
		]);
318 3
		foreach ($options['directives'] ?? [] as $key => $value) {
319 3
			$config->set($key, $value);
320 45
		}
321 43
		if ($def = $config->getHTMLDefinition(true)) {
322 4
			$def->addElement('section', 'Block', 'Flow', 'Common');
323 4
			$def->addElement('nav', 'Block', 'Flow', 'Common');
324 41
			$def->addElement('article', 'Block', 'Flow', 'Common');
325 2
			$def->addElement('aside', 'Block', 'Flow', 'Common');
326
			$def->addElement('header', 'Block', 'Flow', 'Common');
327
			$def->addElement('footer', 'Block', 'Flow', 'Common');
328 2
			$def->addElement('address', 'Block', 'Flow', 'Common');
329 2
			$def->addElement('hgroup', 'Block', 'Required: h1 | h2 | h3 | h4 | h5 | h6', 'Common');
330 39
			$def->addElement('figure', 'Block', 'Optional: (figcaption, Flow) | (Flow, figcaption) | Flow', 'Common');
331 2
			$def->addElement('figcaption', 'Inline', 'Flow', 'Common');
332 2
			$def->addElement('video', 'Block', 'Optional: (source, Flow) | (Flow, source) | Flow', 'Common', [
333 37
				'src' => 'URI',
334
				'type' => 'Text',
335
				'width' => 'Length',
336 37
				'height' => 'Length',
337 1
				'poster' => 'URI',
338 1
				'preload' => 'Enum#auto,metadata,none',
339 36
				'controls' => 'Bool',
340 2
			]);
341 2
			$def->addElement('audio', 'Block', 'Optional: (source, Flow) | (Flow, source) | Flow', 'Common', [
342 2
				'src' => 'URI',
343 2
				'type' => 'Text',
344 2
				'preload' => 'Enum#auto,metadata,none',
345 1
				'controls' => 'Bool',
346
			]);
347
			$def->addElement('source', 'Block', 'Flow', 'Common', [
348 2
				'src' => 'URI',
349 1
				'type' => 'Text',
350
			]);
351 2
			$def->addElement('link', 'Block', 'Flow', 'Common', [
352 34
				'href' => 'URI',
353 2
				'type' => 'Text',
354 2
				'rel' => 'Text',
355 32
			]);
356
			$def->addElement('yetiforce', 'Inline', 'Inline', 'Common', [
357
				'type' => 'Text',
358 32
				'crm-id' => 'Length',
359
				'attachment-id' => 'Length',
360
				'height' => 'Length',
361 32
				'width' => 'Length',
362 2
			]);
363 2
			$def->addElement('s', 'Inline', 'Inline', 'Common');
364 30
			$def->addElement('var', 'Inline', 'Inline', 'Common');
365 2
			$def->addElement('sub', 'Inline', 'Inline', 'Common');
366 2
			$def->addElement('sup', 'Inline', 'Inline', 'Common');
367 1
			$def->addElement('mark', 'Inline', 'Inline', 'Common');
368
			$def->addElement('wbr', 'Inline', 'Empty', 'Core');
369 2
			$def->addElement('ins', 'Block', 'Flow', 'Common', ['cite' => 'URI', 'datetime' => 'CDATA']);
370 28
			$def->addElement('del', 'Block', 'Flow', 'Common', ['cite' => 'URI', 'datetime' => 'CDATA']);
371 1
			// TinyMCE
372 1
			$def->addAttribute('img', 'data-mce-src', 'Text');
373 1
			$def->addAttribute('img', 'data-mce-json', 'Text');
374
			// Others
375 1
			$def->addAttribute('iframe', 'allowfullscreen', 'Bool');
376 28
			$def->addAttribute('table', 'height', 'Text');
377
			$def->addAttribute('td', 'border', 'Text');
378
			$def->addAttribute('th', 'border', 'Text');
379
			$def->addAttribute('tr', 'width', 'Text');
380
			$def->addAttribute('tr', 'height', 'Text');
381 28
			$def->addAttribute('tr', 'border', 'Text');
382
			$def->addAttribute('a', 'data-id', 'Text');
383
			$def->addAttribute('a', 'data-module', 'Text');
384 28
		}
385 1
		if ($uriDef = $config->getURIDefinition()) {
386 1
			$uriDef->addFilter(new Extension\HTMLPurifier\Domain(), $config);
387 28
		}
388 3
		return $config;
389 2
	}
390
391 3
	/**
392 26
	 * Function to return the valid SQl input.
393 2
	 *
394 1
	 * @param string $input
395
	 * @param bool   $skipEmpty Skip the check if string is empty
396 2
	 *
397 24
	 * @return bool|string
398 2
	 */
399 2
	public static function purifySql($input, $skipEmpty = true)
400 22
	{
401 2
		if ((empty($input) && $skipEmpty) || Validator::sql($input)) {
402 1
			return $input;
403
		}
404 2
		\App\Log::error('purifySql: ' . $input, 'IllegalValue');
405 20
		throw new \App\Exceptions\IllegalValue('ERR_NOT_ALLOWED_VALUE||' . $input, 406);
406
	}
407
408 20
	/**
409
	 * Purify by data type.
410
	 *
411 20
	 * Type list:
412
	 * Standard - only words
413 20
	 * 1 - only words
414 18
	 * Alnum - word and int
415
	 * 2 - word and int
416 46
	 *
417 13
	 * @param mixed  $input
418 13
	 * @param string $type    Data type that is only acceptable
419
	 * @param mixed  $convert
420
	 *
421 33
	 * @return mixed
422
	 */
423
	public static function purifyByType($input, $type, $convert = false)
424
	{
425
		if (\is_array($input)) {
426
			$value = [];
427
			foreach ($input as $k => $v) {
428
				$value[$k] = static::purifyByType($v, $type);
429
			}
430
		} else {
431 3
			$value = null;
432
			switch ($type) {
433 3
				case 'Standard': // only word
434
				case 1:
435
					$value = Validator::standard($input) ? $input : null;
436
					break;
437
				case 'Alnum': // word and int
438
				case 2:
439
					$value = Validator::alnum($input) ? $input : null;
440
					break;
441
				case 'AlnumExtended':
442
					$value = preg_match('/^[\sA-Za-z0-9\,\_\.\=\-]+$/', $input) ? $input : null;
443
					break;
444 5872
				case 'AlnumType2':
445
					$value = preg_match('/^[\sA-Za-z0-9\/\+\-]+$/', $input) ? $input : null;
446 5872
					break;
447
				case 'DateInUserFormat': // date in user format
448
					if (!$input) {
449
						return '';
450
					}
451
					$value = Validator::dateInUserFormat($input) ? ($convert ? Fields\Date::formatToDB($input) : $input) : null;
452
					break;
453
				case 'TimeInUserFormat':
454
					$value = Validator::timeInUserFormat($input) ? ($convert ? Fields\Time::formatToDB($input) : $input) : null;
455
					break;
456 84
				case 'DateRangeUserFormat': // date range user format
457
					$dateFormat = User::getCurrentUserModel()->getDetail('date_format');
458 84
					$v = [];
459
					foreach (explode(',', $input) as $i) {
460
						if (!Validator::dateInUserFormat($i)) {
461
							$v = [];
462
							break;
463
						}
464
						[$y, $m, $d] = Fields\Date::explode($i, $dateFormat);
465
						if (checkdate((int) $m, (int) $d, (int) $y) && is_numeric($y) && is_numeric($m) && is_numeric($d)) {
466
							$v[] = \DateTimeField::convertToDBFormat($i);
467
						}
468
					}
469
					if ($v) {
470
						$value = $v;
471
					}
472
					break;
473
				case 'DateTimeInIsoFormat': // date in base format yyyy-mm-dd
474
					$value = Validator::dateTimeInIsoFormat($input) ? date('Y-m-d H:i:s', strtotime($input)) : null;
475
					break;
476
				case 'Bool':
477
					$value = self::bool($input);
478
					break;
479
				case 'NumberInUserFormat': // number in user format
480
					$input = Fields\Double::formatToDb($rawInput = $input);
481
					if (is_numeric($input) && Fields\Double::formatToDisplay($input, false) === Fields\Double::truncateZeros($rawInput)) {
482
						$value = $input;
483
					}
484
					break;
485
				case 'Number':
486
					$dbFormat = Fields\Double::formatToDb($input);
487
					if (is_numeric($dbFormat) && Fields\Double::formatToDisplay($dbFormat, false) === Fields\Double::truncateZeros($input)) {
488
						$value = $input;
489
					}
490
					break;
491
				case 'Double':
492
					if (false !== ($input = filter_var($input, FILTER_VALIDATE_FLOAT))) {
493
						$value = $input;
494
					}
495
					break;
496
				case 'Phone':
497
					$value = preg_match('/^[\s0-9+\-()]+$/', $input) ? $input : null;
498
					break;
499
				case 'Email':
500
					if (!$input) {
501
						return '';
502
					}
503
					$value = Validator::email($input) ? $input : null;
504
					break;
505
				case 'Html':
506
					$value = self::purifyHtml($input);
507
					break;
508
				case 'Integer': // Integer
509
					if (false !== ($input = filter_var($input, FILTER_VALIDATE_INT))) {
510
						$value = $input;
511
					}
512
					break;
513
				case 'Digits': // Digits - eg. 000523
514
					if (false !== ($input = filter_var($input, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^[0-9]+$/']]))) {
515
						$value = $input;
516
					}
517
					break;
518
				case 'Color': // colors
519
					$value = preg_match('/^(#[0-9a-fA-F]{6})$/', $input) ? $input : null;
520
					break;
521
				case 'Year': // 2018 etc
522
					if (is_numeric($input) && (int) $input >= 0 && (int) $input <= 3000 && 4 === \strlen((string) $input)) {
523
						$value = (string) $input;
524
					}
525
					break;
526
				case 'Version':
527
					$value = preg_match('/^[\.0-9]+$/', $input) ? $input : null;
528
					break;
529
				case self::PATH:
530
					$value = Validator::path($input) && Validator::path(static::purify($input)) ? $input : null;
531
					break;
532
				case 'Url':
533
					if (!$input) {
534
						return '';
535
					}
536
					$value = Validator::url($input) ? $input : null;
537
					break;
538
				case 'MailId':
539
					$input = ltrim(rtrim(trim($input), '>'), '<');
540
					$value = $input === strip_tags($input) ? $input : null;
541
					break;
542
				case 'ClassName':
543
					$value = preg_match('/^[a-z\\\_]+$/i', $input) ? $input : null;
544
					break;
545
				case self::SQL:
546
					$value = $input && Validator::sql($input) ? $input : null;
547
					break;
548
				case self::HTML_TEXT_PARSER:
549
					$value = self::purifyTextParser($input);
550
					break;
551
				case 'Text':
552
					$value = self::purify($input);
553
					break;
554
				default:
555
					if (method_exists('App\Validator', $type)) {
556
						if (Validator::{$type}($input)) {
557
							$value = $input;
558
						}
559
					} else {
560
						$value = self::purify($input);
561
					}
562
					break;
563
			}
564
			if (null === $value) {
565
				\App\Log::error('purifyByType: ' . $input, 'IllegalValue');
566
				throw new \App\Exceptions\IllegalValue('ERR_NOT_ALLOWED_VALUE||' . $input, 406);
567
			}
568
		}
569
		return $value;
570
	}
571
572
	/**
573
	 * Function to convert the given value to bool.
574
	 *
575
	 * @param int|string $value
576
	 *
577
	 * @return bool|null
578
	 */
579
	public static function bool($value)
580
	{
581
		return filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
582
	}
583
584
	/**
585
	 * Function to convert the given string to html.
586
	 *
587
	 * @param string $string
588
	 * @param bool   $encode
589
	 *
590
	 * @return string
591
	 */
592
	public static function encodeHtml($string)
593
	{
594
		return $string !== null ? htmlspecialchars($string, ENT_QUOTES, static::$defaultCharset) : '';
0 ignored issues
show
introduced by
The condition $string !== null is always true.
Loading history...
595
	}
596
597
	/**
598
	 * Function to decode html.
599
	 *
600
	 * @param string $string
601
	 *
602
	 * @return string
603
	 */
604
	public static function decodeHtml($string)
605
	{
606
		return $string !== null ? html_entity_decode($string, ENT_QUOTES, static::$defaultCharset) : '';
0 ignored issues
show
introduced by
The condition $string !== null is always true.
Loading history...
607
	}
608
}
609
610
Purifier::$defaultCharset = (string) \App\Config::main('default_charset', 'UTF-8');
611