[ Index ] |
PHP Cross Reference of phpBB-3.3.14-deutsch |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * @package s9e\TextFormatter 5 * @copyright Copyright (c) 2010-2022 The s9e authors 6 * @license http://www.opensource.org/licenses/mit-license.php The MIT License 7 */ 8 namespace s9e\TextFormatter\Plugins\Censor; 9 10 use ArrayAccess; 11 use Countable; 12 use Iterator; 13 use s9e\TextFormatter\Configurator\Collections\NormalizedCollection; 14 use s9e\TextFormatter\Configurator\Helpers\ConfigHelper; 15 use s9e\TextFormatter\Configurator\Helpers\RegexpBuilder; 16 use s9e\TextFormatter\Configurator\Items\Regexp; 17 use s9e\TextFormatter\Configurator\JavaScript\Code; 18 use s9e\TextFormatter\Configurator\JavaScript\RegexpConvertor; 19 use s9e\TextFormatter\Configurator\Traits\CollectionProxy; 20 use s9e\TextFormatter\Plugins\ConfiguratorBase; 21 22 /** 23 * @method mixed add(string $key, mixed $value) Add an item to this collection 24 * @method array asConfig() 25 * @method void clear() Empty this collection 26 * @method bool contains(mixed $value) Test whether a given value is present in this collection 27 * @method integer count() 28 * @method mixed current() 29 * @method void delete(string $key) Delete an item from this collection 30 * @method bool exists(string $key) Test whether an item of given key exists 31 * @method mixed get(string $key) Return a value from this collection 32 * @method mixed indexOf(mixed $value) Find the index of a given value 33 * @method integer|string key() 34 * @method mixed next() 35 * @method string normalizeKey(string $key) Normalize an item's key 36 * @method mixed normalizeValue(mixed $value) Normalize a value for storage 37 * @method bool offsetExists(string|integer $offset) 38 * @method mixed offsetGet(string|integer $offset) 39 * @method void offsetSet(string|integer $offset, mixed $value) 40 * @method void offsetUnset(string|integer $offset) 41 * @method string onDuplicate(string|null $action) Query and set the action to take when add() is called with a key that already exists 42 * @method void rewind() 43 * @method mixed set(string $key, mixed $value) Set and overwrite a value in this collection 44 * @method bool valid() 45 */ 46 class Configurator extends ConfiguratorBase implements ArrayAccess, Countable, Iterator 47 { 48 use CollectionProxy; 49 50 /** 51 * @var array List of whitelisted words as [word => true] 52 */ 53 protected $allowed = []; 54 55 /** 56 * @var string Name of attribute used for the replacement 57 */ 58 protected $attrName = 'with'; 59 60 /** 61 * @var NormalizedCollection List of [word => replacement] 62 */ 63 protected $collection; 64 65 /** 66 * @var string Default string used to replace censored words 67 */ 68 protected $defaultReplacement = '****'; 69 70 /** 71 * @var array Options passed to the RegexpBuilder 72 */ 73 protected $regexpOptions = [ 74 'caseInsensitive' => true, 75 'specialChars' => [ 76 '*' => '[\\pL\\pN]*', 77 '?' => '.', 78 ' ' => '\\s*' 79 ] 80 ]; 81 82 /** 83 * @var string Name of the tag used to mark censored words 84 */ 85 protected $tagName = 'CENSOR'; 86 87 /** 88 * Plugin's setup 89 * 90 * Will initialize its collection and create the plugin's tag if it does not exist 91 */ 92 protected function setUp() 93 { 94 $this->collection = new NormalizedCollection; 95 $this->collection->onDuplicate('replace'); 96 97 if (isset($this->configurator->tags[$this->tagName])) 98 { 99 return; 100 } 101 102 // Create a tag 103 $tag = $this->configurator->tags->add($this->tagName); 104 105 // Create the attribute and make it optional 106 $tag->attributes->add($this->attrName)->required = false; 107 108 // Ensure that censored content can't ever be used by other tags 109 $tag->rules->ignoreTags(); 110 111 // Create a template that renders censored words either as their custom replacement or as 112 // the default replacement 113 $tag->template = 114 '<xsl:choose> 115 <xsl:when test="@' . $this->attrName . '"> 116 <xsl:value-of select="@' . htmlspecialchars($this->attrName) . '"/> 117 </xsl:when> 118 <xsl:otherwise>' . htmlspecialchars($this->defaultReplacement, ENT_COMPAT) . '</xsl:otherwise> 119 </xsl:choose>'; 120 } 121 122 /** 123 * Add a word to the list of uncensored words 124 * 125 * @param string $word Word to exclude from the censored list 126 * @return void 127 */ 128 public function allow($word) 129 { 130 $this->allowed[$word] = true; 131 } 132 133 /** 134 * Return an instance of s9e\TextFormatter\Plugins\Censor\Helper 135 * 136 * @return Helper 137 */ 138 public function getHelper() 139 { 140 $config = $this->asConfig(); 141 if (isset($config)) 142 { 143 $config = ConfigHelper::filterConfig($config, 'PHP'); 144 } 145 else 146 { 147 // Use a dummy config with a regexp that doesn't match anything 148 $config = [ 149 'attrName' => $this->attrName, 150 'regexp' => '/(?!)/', 151 'tagName' => $this->tagName 152 ]; 153 } 154 155 return new Helper($config); 156 } 157 158 /** 159 * {@inheritdoc} 160 */ 161 public function asConfig() 162 { 163 $words = $this->getWords(); 164 165 if (empty($words)) 166 { 167 return; 168 } 169 170 // Create the config 171 $config = [ 172 'attrName' => $this->attrName, 173 'regexp' => $this->getWordsRegexp(array_keys($words)), 174 'regexpHtml' => $this->getWordsRegexp(array_map('htmlspecialchars', array_keys($words))), 175 'tagName' => $this->tagName 176 ]; 177 178 // Add custom replacements 179 $replacementWords = []; 180 foreach ($words as $word => $replacement) 181 { 182 if (isset($replacement) && $replacement !== $this->defaultReplacement) 183 { 184 $replacementWords[$replacement][] = $word; 185 } 186 } 187 188 foreach ($replacementWords as $replacement => $words) 189 { 190 $wordsRegexp = '/^' . RegexpBuilder::fromList($words, $this->regexpOptions) . '$/Diu'; 191 192 $regexp = new Regexp($wordsRegexp); 193 $regexp->setJS(RegexpConvertor::toJS(str_replace('[\\pL\\pN]', '[^\\s!-\\/:-?]', $wordsRegexp))); 194 195 $config['replacements'][] = [$regexp, $replacement]; 196 } 197 198 // Add the whitelist 199 if (!empty($this->allowed)) 200 { 201 $config['allowed'] = $this->getWordsRegexp(array_keys($this->allowed)); 202 } 203 204 return $config; 205 } 206 207 /** 208 * {@inheritdoc} 209 */ 210 public function getJSHints() 211 { 212 $hints = [ 213 'CENSOR_HAS_ALLOWED' => !empty($this->allowed), 214 'CENSOR_HAS_REPLACEMENTS' => false 215 ]; 216 foreach ($this->getWords() as $replacement) 217 { 218 if (isset($replacement) && $replacement !== $this->defaultReplacement) 219 { 220 $hints['CENSOR_HAS_REPLACEMENTS'] = true; 221 break; 222 } 223 } 224 225 return $hints; 226 } 227 228 /** 229 * Return a list of censored words 230 * 231 * @return string[] 232 */ 233 protected function getWords() 234 { 235 return array_diff_key(iterator_to_array($this->collection), $this->allowed); 236 } 237 238 /** 239 * Generate a regexp that matches the given list of words 240 * 241 * @param array $words List of words 242 * @return Regexp Regexp instance with a Unicode-free JS variant 243 */ 244 protected function getWordsRegexp(array $words) 245 { 246 $expr = RegexpBuilder::fromList($words, $this->regexpOptions); 247 $regexp = new Regexp('/(?<![\\pL\\pN])' . $expr . '(?![\\pL\\pN])/Siu'); 248 249 // JavaScript regexps don't support Unicode properties, so instead of Unicode letters 250 // we'll accept any non-whitespace, non-common punctuation 251 $expr = str_replace('[\\pL\\pN]', '[^\\s!-\\/:-?]', $expr); 252 $expr = str_replace('(?>', '(?:', $expr); 253 $regexp->setJS('/(?:^|\\W)' . $expr . '(?!\\w)/gi'); 254 255 return $regexp; 256 } 257 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Mon Nov 25 19:05:08 2024 | Cross-referenced by PHPXref 0.7.1 |