| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 |
- <?php
- namespace Snipe\BanBuilder;
- class CensorWords
- {
- public $badwords;
- /*
- * When the dictionary is loaded, a ton of regular expression strings are generated
- * These regular expressions are used to perform the profanity checks.
- * Store them here so when we call censorString we don't need to regenerate them on every call
- */
- private $censorChecks = null;
- /**
- * @var array
- */
- private $whiteList = [];
- /**
- * @var string
- */
- private $whiteListPlaceHolder = ' {whiteList[i]} ';
- public function __construct()
- {
- $this->badwords = array();
- $this->replacer = '*';
- $this->setDictionary('en-us');
- }
- /**
- * Sets the dictionar(y|ies) to use
- * This can accept a string to a language file path,
- * or an array of strings to multiple paths
- *
- * @param string|array
- * @throws \RuntimeException if a dictionary file is not found
- */
- public function setDictionary($dictionary)
- {
- $this->badwords = $this->readBadWords($dictionary);
- }
- /**
- * Add more the dictionar(y|ies) to current bad words list
- * This can accept a string to a language file path,
- * or an array of strings to multiple paths
- *
- * @param string|array
- * @throws \RuntimeException if a dictionary file is not found
- */
- public function addDictionary($dictionary)
- {
- $this->badwords = array_merge($this->badwords, $this->readBadWords($dictionary));
- }
- /**
- * Adds more words to current bad words list from an array of words.
- *
- * @param array
- */
- public function addFromArray($words)
- {
- $badwords = array_merge($this->badwords, $words);
- $this->badwords = array_keys(array_count_values($badwords));
- }
- /**
- * Read bad words list from dictionar(y|ies) and return it
- *
- * @param string|array a language identifier or path for a dictionary (or an array of identifiers/paths)
- *
- * @throws \RuntimeException if a dictionary file is not found
- *
- * @return array de-duplicated array of bad words
- */
- private function readBadWords($dictionary)
- {
- $badwords = array();
- $baseDictPath = __DIR__ . DIRECTORY_SEPARATOR . 'dict' . DIRECTORY_SEPARATOR;
- if (is_array($dictionary)) {
- foreach ($dictionary as $dictionary_file) {
- $badwords = array_merge($badwords, $this->readBadWords($dictionary_file));
- }
- }
- // just a single string, not an array
- if (is_string($dictionary)) {
- if (file_exists($baseDictPath . $dictionary . '.php')) {
- include $baseDictPath . $dictionary . '.php';
- } elseif (file_exists($dictionary)) {
- include $dictionary;
- } else {
- throw new \RuntimeException('Dictionary file not found: ' . $dictionary);
- }
- }
- // counting values and then only returning the keys is said
- // to be more efficient than array_values(array_unique())
- return array_keys(array_count_values($badwords));
- }
- /**
- * List of word to add which will be overridden
- *
- * @param array $list
- */
- public function addWhiteList(array $list)
- {
- foreach ($list as $value) {
- if (is_string($value) && !empty($value)) {
- $this->whiteList[]['word'] = $value;
- }
- }
- }
- /**
- * Replace white listed words with placeholders and inversely
- *
- * @param $string
- * @param bool $reverse
- *
- * @return mixed
- */
- private function replaceWhiteListed($string, $reverse = false)
- {
- foreach ($this->whiteList as $key => $list) {
- if ($reverse && !empty($this->whiteList[$key]['placeHolder'])) {
- $placeHolder = $this->whiteList[$key]['placeHolder'];
- $string = str_replace($placeHolder, $list['word'], $string);
- } else {
- $placeHolder = str_replace('[i]', $key, $this->whiteListPlaceHolder);
- $this->whiteList[$key]['placeHolder'] = $placeHolder;
- $string = str_replace($list['word'], $placeHolder, $string);
- }
- }
- return $string;
- }
- /**
- * Sets the replacement character to use
- *
- * @param string $replacer Character to use.
- */
- public function setReplaceChar($replacer)
- {
- $this->replacer = $replacer;
- }
- /**
- * Generates a random string.
- *
- * @param string $chars Chars that can be used.
- * @param int $len Length of the output string.
- *
- *
- * @return string
- */
- public function randCensor($chars, $len)
- {
- return str_shuffle(
- str_repeat($chars, (int)($len / strlen($chars))) .
- substr($chars, 0, $len % strlen($chars))
- );
- }
- /**
- * Generates the regular expressions that are going to be used to check for profanity
- *
- * @param boolean $fullWords Option to generate regular expressions used for full words instead. Default is
- * false
- */
- private function generateCensorChecks($fullWords = false)
- {
- $badwords = $this->badwords;
- // generate censor checks as soon as we load the dictionary
- // utilize leet equivalents as well
- $leet_replace = array();
- $leet_replace['a'] = '(a|a\.|a\-|4|@|Á|á|À|Â|à|Â|â|Ä|ä|Ã|ã|Å|å|α|Δ|Λ|λ)';
- $leet_replace['b'] = '(b|b\.|b\-|8|\|3|ß|Β|β)';
- $leet_replace['c'] = '(c|c\.|c\-|Ç|ç|¢|€|<|\(|{|©)';
- $leet_replace['d'] = '(d|d\.|d\-|∂|\|\)|Þ|þ|Ð|ð)';
- $leet_replace['e'] = '(e|e\.|e\-|3|€|È|è|É|é|Ê|ê|∑)';
- $leet_replace['f'] = '(f|f\.|f\-|ƒ)';
- $leet_replace['g'] = '(g|g\.|g\-|6|9)';
- $leet_replace['h'] = '(h|h\.|h\-|Η)';
- $leet_replace['i'] = '(i|i\.|i\-|!|\||\]\[|]|1|∫|Ì|Í|Î|Ï|ì|í|î|ï)';
- $leet_replace['j'] = '(j|j\.|j\-)';
- $leet_replace['k'] = '(k|k\.|k\-|Κ|κ)';
- $leet_replace['l'] = '(l|1\.|l\-|!|\||\]\[|]|£|∫|Ì|Í|Î|Ï)';
- $leet_replace['m'] = '(m|m\.|m\-)';
- $leet_replace['n'] = '(n|n\.|n\-|η|Ν|Π)';
- $leet_replace['o'] = '(o|o\.|o\-|0|Ο|ο|Φ|¤|°|ø)';
- $leet_replace['p'] = '(p|p\.|p\-|ρ|Ρ|¶|þ)';
- $leet_replace['q'] = '(q|q\.|q\-)';
- $leet_replace['r'] = '(r|r\.|r\-|®)';
- $leet_replace['s'] = '(s|s\.|s\-|5|\$|§)';
- $leet_replace['t'] = '(t|t\.|t\-|Τ|τ|7)';
- $leet_replace['u'] = '(u|u\.|u\-|υ|µ)';
- $leet_replace['v'] = '(v|v\.|v\-|υ|ν)';
- $leet_replace['w'] = '(w|w\.|w\-|ω|ψ|Ψ)';
- $leet_replace['x'] = '(x|x\.|x\-|Χ|χ)';
- $leet_replace['y'] = '(y|y\.|y\-|¥|γ|ÿ|ý|Ÿ|Ý)';
- $leet_replace['z'] = '(z|z\.|z\-|Ζ)';
- $censorChecks = array();
- for ($x = 0, $xMax = count($badwords); $x < $xMax; $x++) {
- $censorChecks[$x] = $fullWords
- ? '/\b' . str_ireplace(array_keys($leet_replace), array_values($leet_replace), $badwords[$x]) . '\b/iu'
- : '/' . str_ireplace(array_keys($leet_replace), array_values($leet_replace), $badwords[$x]) . '/i';
- }
-
- $this->censorChecks = $censorChecks;
- }
- /**
- * Apply censorship to $string, replacing $badwords with $censorChar.
- *
- * @param string $string String to be censored.
- * @param bool $fullWords Option to censor by word only.
- *
- * @return array
- */
- public function censorString($string, $fullWords = false)
- {
- // generate our censor checks if they are not defined yet
- if (!$this->censorChecks) {
- $this->generateCensorChecks($fullWords);
- }
- $anThis = &$this;
- $counter = 0;
- $match = array();
- $newstring = array();
- $newstring['orig'] = html_entity_decode($string);
- $original = $this->replaceWhiteListed($newstring['orig']);
- // $anThis for <= PHP5.3
- $newstring['clean'] = preg_replace_callback(
- $this->censorChecks,
- function ($matches) use (&$anThis, &$counter, &$match) {
- $match[$counter++] = $matches[0];
- // is $anThis->replacer a single char?
- return (strlen($anThis->replacer) === 1)
- ? str_repeat($anThis->replacer, strlen($matches[0]))
- : $anThis->randCensor($anThis->replacer, strlen($matches[0]));
- },
- $original
- );
- $newstring['clean'] = $this->replaceWhiteListed($newstring['clean'], true);
- $newstring['matched'] = $match;
- return $newstring;
- }
- }
|