TYPO3 CMS  TYPO3_7-6
SpellCheckingController.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
20 
25 {
29  protected $csConvObj;
30 
31  // The extension key
35  public $extKey = 'rtehtmlarea';
36 
40  public $siteUrl;
41 
45  public $charset = 'utf-8';
46 
50  public $parserCharset = 'utf-8';
51 
55  public $defaultAspellEncoding = 'utf-8';
56 
61 
65  public $result;
66 
70  public $text;
71 
75  public $misspelled = [];
76 
81 
85  public $wordCount = 0;
86 
90  public $suggestionCount = 0;
91 
95  public $suggestedWordCount = 0;
96 
100  public $pspell_link;
101 
105  public $pspellMode = 'normal';
106 
110  public $dictionary;
111 
116 
121 
125  public $forceCommandMode = 0;
126 
130  public $filePrefix = 'rtehtmlarea_';
131 
132  // Pre-FAL backward compatibility
133  protected $uploadFolder = 'uploads/tx_rtehtmlarea/';
134 
135  // Path to main dictionary
137 
138  // Path to personal dictionary
140 
144  public $xmlCharacterData = '';
145 
154  public function main(ServerRequestInterface $request, ResponseInterface $response)
155  {
156  return $this->processRequest($request, $response);
157  }
158 
168  public function processRequest(ServerRequestInterface $request, ResponseInterface $response)
169  {
170  $this->csConvObj = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Charset\CharsetConverter::class);
171  // Setting start time
172  $time_start = microtime(true);
173  $this->pspell_is_available = in_array('pspell', get_loaded_extensions());
174  $this->AspellDirectory = trim($GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->extKey]['plugins']['SpellChecker']['AspellDirectory']) ?: '/usr/bin/aspell';
175  // Setting command mode if requested and available
176  $this->forceCommandMode = trim($GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->extKey]['plugins']['SpellChecker']['forceCommandMode']) ?: 0;
177  if (!$this->pspell_is_available || $this->forceCommandMode) {
178  $AspellVersionString = explode('Aspell', shell_exec($this->AspellDirectory . ' -v'));
179  $AspellVersion = substr($AspellVersionString[1], 0, 4);
180  if (floatval($AspellVersion) < floatval('0.5') && (!$this->pspell_is_available || $this->forceCommandMode)) {
181  echo 'Configuration problem: Aspell version ' . $AspellVersion . ' too old. Spell checking cannot be performed in command mode.';
182  }
183  $this->defaultAspellEncoding = trim(shell_exec($this->AspellDirectory . ' config encoding'));
184  }
185  // Setting the list of dictionaries
186  $dictionaryList = shell_exec($this->AspellDirectory . ' dump dicts');
187  $dictionaryList = implode(',', GeneralUtility::trimExplode(LF, $dictionaryList, true));
188  $dictionaryArray = GeneralUtility::trimExplode(',', $dictionaryList, true);
189  $restrictToDictionaries = GeneralUtility::_POST('restrictToDictionaries');
190  if ($restrictToDictionaries) {
191  $dictionaryArray = array_intersect($dictionaryArray, GeneralUtility::trimExplode(',', $restrictToDictionaries, 1));
192  }
193  if (empty($dictionaryArray)) {
194  $dictionaryArray[] = 'en';
195  }
196  $this->dictionary = GeneralUtility::_POST('dictionary');
197  $defaultDictionary = $this->dictionary;
198  if (!$defaultDictionary || !in_array($defaultDictionary, $dictionaryArray)) {
199  $defaultDictionary = 'en';
200  }
201  uasort($dictionaryArray, 'strcoll');
202  $dictionaryList = implode(',', $dictionaryArray);
203  // Setting the dictionary
204  if (empty($this->dictionary) || !in_array($this->dictionary, $dictionaryArray)) {
205  $this->dictionary = 'en';
206  }
207  // Setting the pspell suggestion mode
208  $this->pspellMode = GeneralUtility::_POST('pspell_mode') ? GeneralUtility::_POST('pspell_mode') : $this->pspellMode;
209  switch ($this->pspellMode) {
210  case 'ultra':
211 
212  case 'fast':
213  $pspellModeFlag = PSPELL_FAST;
214  break;
215  case 'bad-spellers':
216  $pspellModeFlag = PSPELL_BAD_SPELLERS;
217  break;
218  case 'normal':
219 
220  default:
221  $pspellModeFlag = PSPELL_NORMAL;
222  // sanitize $this->pspellMode
223  $this->pspellMode = 'normal';
224  }
225  // Setting the charset
226  if (GeneralUtility::_POST('pspell_charset')) {
227  $this->charset = trim(GeneralUtility::_POST('pspell_charset'));
228  }
229  if (strtolower($this->charset) == 'iso-8859-1') {
230  $this->parserCharset = strtolower($this->charset);
231  }
232  // In some configurations, Aspell uses 'iso8859-1' instead of 'iso-8859-1'
233  $this->aspellEncoding = $this->parserCharset;
234  if ($this->parserCharset == 'iso-8859-1' && strstr($this->defaultAspellEncoding, '8859-1')) {
235  $this->aspellEncoding = $this->defaultAspellEncoding;
236  }
237  // However, we are going to work only in the parser charset
238  if ($this->pspell_is_available && !$this->forceCommandMode) {
239  $this->pspell_link = pspell_new($this->dictionary, '', '', $this->parserCharset, $pspellModeFlag);
240  }
241  // Setting the path to main dictionary
242  $this->setMainDictionaryPath();
243  // Setting the path to user personal dictionary, if any
244  $this->setPersonalDictionaryPath();
246  $cmd = GeneralUtility::_POST('cmd');
247  if ($cmd == 'learn') {
248  // Only availble for BE_USERS, die silently if someone has gotten here by accident
249  if (TYPO3_MODE !== 'BE' || !is_object($GLOBALS['BE_USER'])) {
250  die('');
251  }
252  // Updating the personal word list
253  $to_p_dict = GeneralUtility::_POST('to_p_dict');
254  $to_p_dict = $to_p_dict ? $to_p_dict : [];
255  $to_r_list = GeneralUtility::_POST('to_r_list');
256  $to_r_list = $to_r_list ? $to_r_list : [];
257  header('Content-Type: text/plain; charset=' . strtoupper($this->parserCharset));
258  header('Pragma: no-cache');
259  if ($to_p_dict || $to_r_list) {
260  $tmpFileName = GeneralUtility::tempnam($this->filePrefix);
261  $filehandle = fopen($tmpFileName, 'wb');
262  if ($filehandle) {
263  // Get the character set of the main dictionary
264  // We need to convert the input into the character set of the main dictionary
265  $mainDictionaryCharacterSet = $this->getMainDictionaryCharacterSet();
266  // Write the personal words addition commands to the temporary file
267  foreach ($to_p_dict as $personal_word) {
268  $cmd = '&' . $this->csConvObj->conv($personal_word, $this->parserCharset, $mainDictionaryCharacterSet) . LF;
269  fwrite($filehandle, $cmd, strlen($cmd));
270  }
271  // Write the replacent pairs addition commands to the temporary file
272  foreach ($to_r_list as $replace_pair) {
273  $cmd = '$$ra ' . $this->csConvObj->conv($replace_pair[0], $this->parserCharset, $mainDictionaryCharacterSet) . ' , ' . $this->csConvObj->conv($replace_pair[1], $this->parserCharset, $mainDictionaryCharacterSet) . LF;
274  fwrite($filehandle, $cmd, strlen($cmd));
275  }
276  $cmd = '#' . LF;
277  $result = fwrite($filehandle, $cmd, strlen($cmd));
278  if ($result === false) {
279  GeneralUtility::sysLog('SpellChecker tempfile write error: ' . $tmpFileName, $this->extKey, GeneralUtility::SYSLOG_SEVERITY_ERROR);
280  } else {
281  // Assemble the Aspell command
282  $aspellCommand = ((TYPO3_OS === 'WIN') ? 'type ' : 'cat ') . escapeshellarg($tmpFileName) . ' | '
283  . $this->AspellDirectory
284  . ' -a --mode=none'
285  . ($this->personalDictionaryPath ? ' --home-dir=' . escapeshellarg($this->personalDictionaryPath) : '')
286  . ' --lang=' . escapeshellarg($this->dictionary)
287  . ' --encoding=' . escapeshellarg($mainDictionaryCharacterSet)
288  . ' 2>&1';
289  $aspellResult = shell_exec($aspellCommand);
290  // Close and delete the temporary file
291  fclose($filehandle);
292  GeneralUtility::unlink_tempfile($tmpFileName);
293  }
294  } else {
295  GeneralUtility::sysLog('SpellChecker tempfile open error: ' . $tmpFileName, $this->extKey, GeneralUtility::SYSLOG_SEVERITY_ERROR);
296  }
297  }
298  flush();
299  die;
300  } else {
301  // Check spelling content
302  // Initialize output
303  $this->result = '<?xml version="1.0" encoding="' . $this->parserCharset . '"?>
304 <!DOCTYPE html
305  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
306  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
307 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="' . substr($this->dictionary, 0, 2) . '" lang="' . substr($this->dictionary, 0, 2) . '">
308 <head>
309 <meta http-equiv="Content-Type" content="text/html; charset=' . $this->parserCharset . '" />
310 <link rel="stylesheet" type="text/css" media="all" href="' . (TYPO3_MODE == 'BE' ? '../' : '') . \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::siteRelPath($this->extKey) . '/Resources/Public/Css/Skin/Plugins/spell-checker-iframe.css" />
311 <script type="text/javascript">
312 /*<![CDATA[*/
313 <!--
314 ';
315  // Getting the input content
316  $content = GeneralUtility::_POST('content');
317  // Parsing the input HTML
318  $parser = xml_parser_create(strtoupper($this->parserCharset));
319  // Disables the functionality to allow external entities to be loaded when parsing the XML, must be kept
320  $previousValueOfEntityLoader = libxml_disable_entity_loader(true);
321  xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
322  xml_set_object($parser, $this);
323  if (!xml_set_element_handler($parser, 'startHandler', 'endHandler')) {
324  echo 'Bad xml handler setting';
325  }
326  if (!xml_set_character_data_handler($parser, 'collectDataHandler')) {
327  echo 'Bad xml handler setting';
328  }
329  if (!xml_set_default_handler($parser, 'defaultHandler')) {
330  echo 'Bad xml handler setting';
331  }
332  if (!xml_parse($parser, ('<?xml version="1.0" encoding="' . $this->parserCharset . '"?><spellchecker> ' . preg_replace(('/&nbsp;/' . ($this->parserCharset == 'utf-8' ? 'u' : '')), ' ', $content) . ' </spellchecker>'))) {
333  echo 'Bad parsing';
334  }
335  if (xml_get_error_code($parser)) {
336  throw new \UnexpectedValueException('Line ' . xml_get_current_line_number($parser) . ': ' . xml_error_string(xml_get_error_code($parser)), 1294585788);
337  }
338  libxml_disable_entity_loader($previousValueOfEntityLoader);
339  xml_parser_free($parser);
340  if ($this->pspell_is_available && !$this->forceCommandMode) {
341  pspell_clear_session($this->pspell_link);
342  }
343  $this->result .= 'var suggestedWords = {' . $this->suggestedWords . '};
344 var dictionaries = "' . $dictionaryList . '";
345 var selectedDictionary = "' . $this->dictionary . '";
346 ';
347  // Calculating parsing and spell checkting time
348  $time = number_format(microtime(true) - $time_start, 2, ',', ' ');
349  // Insert spellcheck info
350  $this->result .= 'var spellcheckInfo = { "Total words":"' . $this->wordCount . '","Misspelled words":"' . count($this->misspelled) . '","Total suggestions":"' . $this->suggestionCount . '","Total words suggested":"' . $this->suggestedWordCount . '","Spelling checked in":"' . $time . '" };
351 // -->
352 /*]]>*/
353 </script>
354 </head>
355 ';
356  $this->result .= '<body onload="window.parent.RTEarea[' . GeneralUtility::quoteJSvalue(GeneralUtility::_POST('editorId')) . '].editor.getPlugin(\'SpellChecker\').spellCheckComplete();">';
357  $this->result .= preg_replace('/' . preg_quote('<?xml') . '.*' . preg_quote('?>') . '[' . preg_quote((LF . CR . chr(32))) . ']*/' . ($this->parserCharset == 'utf-8' ? 'u' : ''), '', $this->text);
358  $this->result .= '<div style="display: none;">' . $dictionaries . '</div>';
359  // Closing
360  $this->result .= '
361 </body></html>';
362  // Outputting
363  $response = $response->withHeader('Content-Type', 'text/html; charset=' . strtoupper($this->parserCharset));
364  $response->getBody()->write($this->result);
365  return $response;
366  }
367  }
368 
374  protected function setMainDictionaryPath()
375  {
376  $this->mainDictionaryPath = '';
377  $aspellCommand = $this->AspellDirectory . ' config dict-dir';
378  $aspellResult = shell_exec($aspellCommand);
379  if ($aspellResult) {
380  $this->mainDictionaryPath = trim($aspellResult);
381  }
382  if (!$aspellResult || !$this->mainDictionaryPath) {
383  GeneralUtility::sysLog('SpellChecker main dictionary path retrieval error: ' . $aspellCommand, $this->extKey, GeneralUtility::SYSLOG_SEVERITY_ERROR);
384  }
386  }
387 
393  protected function getMainDictionaryCharacterSet()
394  {
395  $characterSet = '';
396  if ($this->mainDictionaryPath) {
397  // Keep only the first part of the dictionary name
398  $mainDictionary = preg_split('/[-_]/', $this->dictionary, 2);
399  // Read the options of the dictionary
400  $dictionaryFileName = $this->mainDictionaryPath . '/' . $mainDictionary[0] . '.dat';
401  $dictionaryHandle = fopen($dictionaryFileName, 'rb');
402  if (!$dictionaryHandle) {
403  GeneralUtility::sysLog('SpellChecker main dictionary open error: ' . $dictionaryFileName, $this->extKey, GeneralUtility::SYSLOG_SEVERITY_ERROR);
404  } else {
405  $dictionaryContent = fread($dictionaryHandle, 500);
406  if ($dictionaryContent === false) {
407  GeneralUtility::sysLog('SpellChecker main dictionary read error: ' . $dictionaryFileName, $this->extKey, GeneralUtility::SYSLOG_SEVERITY_ERROR);
408  } else {
409  fclose($dictionaryHandle);
410  // Get the line that contains the character set option
411  $dictionaryContent = preg_split('/charset\s*/', $dictionaryContent, 2);
412  if ($dictionaryContent[1]) {
413  // Isolate the character set
414  $dictionaryContent = GeneralUtility::trimExplode(LF, $dictionaryContent[1]);
415  // Fix Aspell character set oddity (i.e. iso8859-1)
416  $characterSet = str_replace(
417  ['iso', '--'],
418  ['iso-', '-'],
419  $dictionaryContent[0]
420  );
421  }
422  if (!$characterSet) {
423  GeneralUtility::sysLog('SpellChecker main dictionary character set retrieval error: ' . $dictionaryContent[1], $this->extKey, GeneralUtility::SYSLOG_SEVERITY_ERROR);
424  }
425  }
426  }
427  }
428  return $characterSet;
429  }
430 
436  protected function setPersonalDictionaryPath()
437  {
438  $this->personalDictionaryPath = '';
439  if (GeneralUtility::_POST('enablePersonalDicts') == 'true' && TYPO3_MODE == 'BE' && is_object($GLOBALS['BE_USER'])) {
440  if ($GLOBALS['BE_USER']->user['uid']) {
441  $personalDictionaryFolderName = 'BE_' . $GLOBALS['BE_USER']->user['uid'];
442  // Check for pre-FAL personal dictionary folder
443  try {
444  $personalDictionaryFolder = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance()->getFolderObjectFromCombinedIdentifier(PATH_site . $this->uploadFolder . $personalDictionaryFolderName);
445  } catch (\Exception $e) {
446  $personalDictionaryFolder = false;
447  }
448  // The personal dictionary folder is created in the user's default upload folder and named BE_(uid)_personaldictionary
449  if (!$personalDictionaryFolder) {
450  $personalDictionaryFolderName .= '_personaldictionary';
451  $backendUserDefaultFolder = $GLOBALS['BE_USER']->getDefaultUploadFolder();
452  if ($backendUserDefaultFolder->hasFolder($personalDictionaryFolderName)) {
453  $personalDictionaryFolder = $backendUserDefaultFolder->getSubfolder($personalDictionaryFolderName);
454  } else {
455  $personalDictionaryFolder = $backendUserDefaultFolder->createFolder($personalDictionaryFolderName);
456  }
457  }
458  $this->personalDictionaryPath = PATH_site . rtrim($personalDictionaryFolder->getPublicUrl(), '/');
459  }
460  }
462  }
463 
470  {
471  if ($this->personalDictionaryPath) {
472  // Fix the options of the personl word list and of the replacement pairs files
473  // Aspell creates such files only for the main dictionary
474  $fileNames = [];
475  $mainDictionary = preg_split('/[-_]/', $this->dictionary, 2);
476  $fileNames[0] = $this->personalDictionaryPath . '/' . '.aspell.' . $mainDictionary[0] . '.pws';
477  $fileNames[1] = $this->personalDictionaryPath . '/' . '.aspell.' . $mainDictionary[0] . '.prepl';
478  foreach ($fileNames as $fileName) {
479  if (file_exists($fileName)) {
480  $fileContent = file_get_contents($fileName);
481  if ($fileContent === false) {
482  GeneralUtility::sysLog('SpellChecker personal word list read error: ' . $fileName, $this->extKey, GeneralUtility::SYSLOG_SEVERITY_ERROR);
483  } else {
484  $fileContent = explode(LF, $fileContent);
485  if (strpos($fileContent[0], 'utf-8') === false) {
486  $fileContent[0] .= ' utf-8';
487  $fileContent = implode(LF, $fileContent);
488  $result = file_put_contents($fileName, $fileContent);
489  if ($result === false) {
490  GeneralUtility::sysLog('SpellChecker personal word list write error: ' . $fileName, $this->extKey, GeneralUtility::SYSLOG_SEVERITY_ERROR);
491  }
492  }
493  }
494  }
495  }
496  }
497  }
498 
502  public function startHandler($xml_parser, $tag, $attributes)
503  {
504  if ((string)$this->xmlCharacterData !== '') {
505  $this->spellCheckHandler($xml_parser, $this->xmlCharacterData);
506  $this->xmlCharacterData = '';
507  }
508  switch ($tag) {
509  case 'spellchecker':
510  break;
511  case 'br':
512 
513  case 'BR':
514 
515  case 'img':
516 
517  case 'IMG':
518 
519  case 'hr':
520 
521  case 'HR':
522 
523  case 'area':
524 
525  case 'AREA':
526  $this->text .= '<' . $this->csConvObj->conv_case($this->parserCharset, $tag, 'toLower') . ' ';
527  foreach ($attributes as $key => $val) {
528  $this->text .= $key . '="' . $val . '" ';
529  }
530  $this->text .= ' />';
531  break;
532  default:
533  $this->text .= '<' . $this->csConvObj->conv_case($this->parserCharset, $tag, 'toLower') . ' ';
534  foreach ($attributes as $key => $val) {
535  $this->text .= $key . '="' . $val . '" ';
536  }
537  $this->text .= '>';
538  }
539  }
540 
544  public function endHandler($xml_parser, $tag)
545  {
546  if ((string)$this->xmlCharacterData !== '') {
547  $this->spellCheckHandler($xml_parser, $this->xmlCharacterData);
548  $this->xmlCharacterData = '';
549  }
550  switch ($tag) {
551  case 'spellchecker':
552 
553  case 'br':
554 
555  case 'BR':
556 
557  case 'img':
558 
559  case 'IMG':
560 
561  case 'hr':
562 
563  case 'HR':
564 
565  case 'input':
566 
567  case 'INPUT':
568 
569  case 'area':
570 
571  case 'AREA':
572  break;
573  default:
574  $this->text .= '</' . $tag . '>';
575  }
576  }
577 
581  public function spellCheckHandler($xml_parser, $string)
582  {
583  $incurrent = [];
584  $stringText = $string;
585  $words = preg_split($this->parserCharset == 'utf-8' ? '/\\P{L}+/u' : '/\\W+/', $stringText);
586  foreach ($words as $word) {
587  $word = preg_replace('/ /' . ($this->parserCharset == 'utf-8' ? 'u' : ''), '', $word);
588  if ($word && !is_numeric($word)) {
589  if ($this->pspell_is_available && !$this->forceCommandMode) {
590  if (!pspell_check($this->pspell_link, $word)) {
591  if (!in_array($word, $this->misspelled)) {
592  if (count($this->misspelled) != 0) {
593  $this->suggestedWords .= ',';
594  }
595  $suggest = [];
596  $suggest = pspell_suggest($this->pspell_link, $word);
597  if (count($suggest) != 0) {
598  $this->suggestionCount++;
599  $this->suggestedWordCount += count($suggest);
600  }
601  $this->suggestedWords .= '"' . $word . '":"' . implode(',', $suggest) . '"';
602  $this->misspelled[] = $word;
603  unset($suggest);
604  }
605  if (!in_array($word, $incurrent)) {
606  $stringText = preg_replace('/\\b' . $word . '\\b/' . ($this->parserCharset == 'utf-8' ? 'u' : ''), '<span class="htmlarea-spellcheck-error">' . $word . '</span>', $stringText);
607  $incurrent[] = $word;
608  }
609  }
610  } else {
611  $tmpFileName = GeneralUtility::tempnam($this->filePrefix);
612  if (!($filehandle = fopen($tmpFileName, 'wb'))) {
613  echo 'SpellChecker tempfile open error';
614  }
615  if (!fwrite($filehandle, $word)) {
616  echo 'SpellChecker tempfile write error';
617  }
618  if (!fclose($filehandle)) {
619  echo 'SpellChecker tempfile close error';
620  }
621  $catCommand = TYPO3_OS === 'WIN' ? 'type' : 'cat';
622  $AspellCommand = $catCommand . ' ' . escapeshellarg($tmpFileName) . ' | '
623  . $this->AspellDirectory
624  . ' -a check'
625  . ' --mode=none'
626  . ' --sug-mode=' . escapeshellarg($this->pspellMode)
627  . ($this->personalDictionaryPath ? ' --home-dir=' . escapeshellarg($this->personalDictionaryPath) : '')
628  . ' --lang=' . escapeshellarg($this->dictionary)
629  . ' --encoding=' . escapeshellarg($this->aspellEncoding)
630  . ' 2>&1';
631  $AspellAnswer = shell_exec($AspellCommand);
632  $AspellResultLines = [];
633  $AspellResultLines = GeneralUtility::trimExplode(LF, $AspellAnswer, true);
634  if (substr($AspellResultLines[0], 0, 6) == 'Error:') {
635  echo '{' . $AspellAnswer . '}';
636  }
637  GeneralUtility::unlink_tempfile($tmpFileName);
638  if ($AspellResultLines['1'][0] !== '*') {
639  if (!in_array($word, $this->misspelled)) {
640  if (count($this->misspelled) != 0) {
641  $this->suggestedWords .= ',';
642  }
643  $suggest = [];
644  $suggestions = [];
645  if ($AspellResultLines['1'][0] === '&') {
646  $suggestions = GeneralUtility::trimExplode(':', $AspellResultLines['1'], true);
647  $suggest = GeneralUtility::trimExplode(',', $suggestions['1'], true);
648  }
649  if (count($suggest) != 0) {
650  $this->suggestionCount++;
651  $this->suggestedWordCount += count($suggest);
652  }
653  $this->suggestedWords .= '"' . $word . '":"' . implode(',', $suggest) . '"';
654  $this->misspelled[] = $word;
655  unset($suggest);
656  unset($suggestions);
657  }
658  if (!in_array($word, $incurrent)) {
659  $stringText = preg_replace('/\\b' . $word . '\\b/' . ($this->parserCharset == 'utf-8' ? 'u' : ''), '<span class="htmlarea-spellcheck-error">' . $word . '</span>', $stringText);
660  $incurrent[] = $word;
661  }
662  }
663  unset($AspellResultLines);
664  }
665  $this->wordCount++;
666  }
667  }
668  $this->text .= $stringText;
669  unset($incurrent);
670  }
671 
675  public function collectDataHandler($xml_parser, $string)
676  {
677  $this->xmlCharacterData .= $string;
678  }
679 
683  public function defaultHandler($xml_parser, $string)
684  {
685  $this->text .= $string;
686  }
687 }
main(ServerRequestInterface $request, ResponseInterface $response)
static unlink_tempfile($uploadedTempFileName)
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
static tempnam($filePrefix, $fileSuffix='')
processRequest(ServerRequestInterface $request, ResponseInterface $response)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']