TYPO3 CMS  TYPO3_6-2
HtmlParser.php
Go to the documentation of this file.
1 <?php
3 
18 
25 class HtmlParser {
26 
27  protected $caseShift_cache = array();
28 
29  // Void elements that do not have closing tags, as defined by HTML5, except link element
30  const VOID_ELEMENTS = 'area|base|br|col|command|embed|hr|img|input|keygen|meta|param|source|track|wbr';
39  static public function getSubpart($content, $marker) {
40  $start = strpos($content, $marker);
41  if ($start === FALSE) {
42  return '';
43  }
44  $start += strlen($marker);
45  $stop = strpos($content, $marker, $start);
46  // Q: What shall get returned if no stop marker is given
47  // Everything till the end or nothing?
48  if ($stop === FALSE) {
49  return '';
50  }
51  $content = substr($content, $start, $stop - $start);
52  $matches = array();
53  if (preg_match('/^([^\\<]*\\-\\-\\>)(.*)(\\<\\!\\-\\-[^\\>]*)$/s', $content, $matches) === 1) {
54  return $matches[2];
55  }
56  // Resetting $matches
57  $matches = array();
58  if (preg_match('/(.*)(\\<\\!\\-\\-[^\\>]*)$/s', $content, $matches) === 1) {
59  return $matches[1];
60  }
61  // Resetting $matches
62  $matches = array();
63  if (preg_match('/^([^\\<]*\\-\\-\\>)(.*)$/s', $content, $matches) === 1) {
64  return $matches[2];
65  }
66  return $content;
67  }
68 
79  static public function substituteSubpart($content, $marker, $subpartContent, $recursive = TRUE, $keepMarker = FALSE) {
80  $start = strpos($content, $marker);
81  if ($start === FALSE) {
82  return $content;
83  }
84  $startAM = $start + strlen($marker);
85  $stop = strpos($content, $marker, $startAM);
86  if ($stop === FALSE) {
87  return $content;
88  }
89  $stopAM = $stop + strlen($marker);
90  $before = substr($content, 0, $start);
91  $after = substr($content, $stopAM);
92  $between = substr($content, $startAM, $stop - $startAM);
93  if ($recursive) {
94  $after = self::substituteSubpart($after, $marker, $subpartContent, $recursive, $keepMarker);
95  }
96  if ($keepMarker) {
97  $matches = array();
98  if (preg_match('/^([^\\<]*\\-\\-\\>)(.*)(\\<\\!\\-\\-[^\\>]*)$/s', $between, $matches) === 1) {
99  $before .= $marker . $matches[1];
100  $between = $matches[2];
101  $after = $matches[3] . $marker . $after;
102  } elseif (preg_match('/^(.*)(\\<\\!\\-\\-[^\\>]*)$/s', $between, $matches) === 1) {
103  $before .= $marker;
104  $between = $matches[1];
105  $after = $matches[2] . $marker . $after;
106  } elseif (preg_match('/^([^\\<]*\\-\\-\\>)(.*)$/s', $between, $matches) === 1) {
107  $before .= $marker . $matches[1];
108  $between = $matches[2];
109  $after = $marker . $after;
110  } else {
111  $before .= $marker;
112  $after = $marker . $after;
113  }
114  } else {
115  $matches = array();
116  if (preg_match('/^(.*)\\<\\!\\-\\-[^\\>]*$/s', $before, $matches) === 1) {
117  $before = $matches[1];
118  }
119  if (is_array($subpartContent)) {
120  $matches = array();
121  if (preg_match('/^([^\\<]*\\-\\-\\>)(.*)(\\<\\!\\-\\-[^\\>]*)$/s', $between, $matches) === 1) {
122  $between = $matches[2];
123  } elseif (preg_match('/^(.*)(\\<\\!\\-\\-[^\\>]*)$/s', $between, $matches) === 1) {
124  $between = $matches[1];
125  } elseif (preg_match('/^([^\\<]*\\-\\-\\>)(.*)$/s', $between, $matches) === 1) {
126  $between = $matches[2];
127  }
128  }
129  $matches = array();
130  // resetting $matches
131  if (preg_match('/^[^\\<]*\\-\\-\\>(.*)$/s', $after, $matches) === 1) {
132  $after = $matches[1];
133  }
134  }
135  if (is_array($subpartContent)) {
136  $between = $subpartContent[0] . $between . $subpartContent[1];
137  } else {
138  $between = $subpartContent;
139  }
140  return $before . $between . $after;
141  }
142 
150  static public function substituteSubpartArray($content, array $subpartsContent) {
151  foreach ($subpartsContent as $subpartMarker => $subpartContent) {
152  $content = self::substituteSubpart($content, $subpartMarker, $subpartContent);
153  }
154  return $content;
155  }
156 
167  static public function substituteMarker($content, $marker, $markContent) {
168  return str_replace($marker, $markContent, $content);
169  }
170 
189  static public function substituteMarkerArray($content, $markContentArray, $wrap = '', $uppercase = FALSE, $deleteUnused = FALSE) {
190  if (is_array($markContentArray)) {
191  $wrapArr = GeneralUtility::trimExplode('|', $wrap);
192  $search = array();
193  $replace = array();
194  foreach ($markContentArray as $marker => $markContent) {
195  if ($uppercase) {
196  // use strtr instead of strtoupper to avoid locale problems with Turkish
197  $marker = strtr($marker, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
198  }
199  if (count($wrapArr) > 0) {
200  $marker = $wrapArr[0] . $marker . $wrapArr[1];
201  }
202  $search[] = $marker;
203  $replace[] = $markContent;
204  }
205  $content = str_replace($search, $replace, $content);
206  unset($search, $replace);
207  if ($deleteUnused) {
208  if (empty($wrap)) {
209  $wrapArr = array('###', '###');
210  }
211  $content = preg_replace('/' . preg_quote($wrapArr[0], '/') . '([A-Z0-9_|\\-]*)' . preg_quote($wrapArr[1], '/') . '/is', '', $content);
212  }
213  }
214  return $content;
215  }
216 
252  static public function substituteMarkerAndSubpartArrayRecursive($content, array $markersAndSubparts, $wrap = '', $uppercase = FALSE, $deleteUnused = FALSE) {
253  $wraps = GeneralUtility::trimExplode('|', $wrap);
254  $singleItems = array();
255  $compoundItems = array();
256  // Split markers and subparts into separate arrays
257  foreach ($markersAndSubparts as $markerName => $markerContent) {
258  if (is_array($markerContent)) {
259  $compoundItems[] = $markerName;
260  } else {
261  $singleItems[$markerName] = $markerContent;
262  }
263  }
264  $subTemplates = array();
265  $subpartSubstitutes = array();
266  // Build a cache for the sub template
267  foreach ($compoundItems as $subpartMarker) {
268  if ($uppercase) {
269  // Use strtr instead of strtoupper to avoid locale problems with Turkish
270  $subpartMarker = strtr($subpartMarker, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
271  }
272  if (count($wraps) > 0) {
273  $subpartMarker = $wraps[0] . $subpartMarker . $wraps[1];
274  }
275  $subTemplates[$subpartMarker] = self::getSubpart($content, $subpartMarker);
276  }
277  // Replace the subpart contents recursively
278  foreach ($compoundItems as $subpartMarker) {
279  $completeMarker = $subpartMarker;
280  if ($uppercase) {
281  // use strtr instead of strtoupper to avoid locale problems with Turkish
282  $completeMarker = strtr($completeMarker, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
283  }
284  if (count($wraps) > 0) {
285  $completeMarker = $wraps[0] . $completeMarker . $wraps[1];
286  }
287  if (count($markersAndSubparts[$subpartMarker]) > 0) {
288  foreach ($markersAndSubparts[$subpartMarker] as $partialMarkersAndSubparts) {
289  $subpartSubstitutes[$completeMarker] .= self::substituteMarkerAndSubpartArrayRecursive($subTemplates[$completeMarker],
290  $partialMarkersAndSubparts, $wrap, $uppercase, $deleteUnused);
291  }
292  } else {
293  $subpartSubstitutes[$completeMarker] = '';
294  }
295  }
296  // Substitute the single markers and subparts
297  $result = self::substituteSubpartArray($content, $subpartSubstitutes);
298  $result = self::substituteMarkerArray($result, $singleItems, $wrap, $uppercase, $deleteUnused);
299  return $result;
300  }
301 
302  /************************************
303  *
304  * Parsing HTML code
305  *
306  ************************************/
319  public function splitIntoBlock($tag, $content, $eliminateExtraEndTags = FALSE) {
320  $tags = array_unique(GeneralUtility::trimExplode(',', $tag, TRUE));
321  foreach ($tags as &$tag) {
322  $tag = preg_quote($tag, '/');
323  }
324  $regexStr = '/\\<\\/?(' . implode('|', $tags) . ')(\\s*\\>|\\s[^\\>]*\\>)/si';
325  $parts = preg_split($regexStr, $content);
326  $newParts = array();
327  $pointer = strlen($parts[0]);
328  $buffer = $parts[0];
329  $nested = 0;
330  reset($parts);
331  next($parts);
332  while (list($k, $v) = each($parts)) {
333  $isEndTag = substr($content, $pointer, 2) == '</' ? 1 : 0;
334  $tagLen = strcspn(substr($content, $pointer), '>') + 1;
335  // We meet a start-tag:
336  if (!$isEndTag) {
337  // Ground level:
338  if (!$nested) {
339  // Previous buffer stored
340  $newParts[] = $buffer;
341  $buffer = '';
342  }
343  // We are inside now!
344  $nested++;
345  // New buffer set and pointer increased
346  $mbuffer = substr($content, $pointer, strlen($v) + $tagLen);
347  $pointer += strlen($mbuffer);
348  $buffer .= $mbuffer;
349  } else {
350  // If we meet an endtag:
351  // Decrease nested-level
352  $nested--;
353  $eliminated = 0;
354  if ($eliminateExtraEndTags && $nested < 0) {
355  $nested = 0;
356  $eliminated = 1;
357  } else {
358  // In any case, add the endtag to current buffer and increase pointer
359  $buffer .= substr($content, $pointer, $tagLen);
360  }
361  $pointer += $tagLen;
362  // if we're back on ground level, (and not by eliminating tags...
363  if (!$nested && !$eliminated) {
364  $newParts[] = $buffer;
365  $buffer = '';
366  }
367  // New buffer set and pointer increased
368  $mbuffer = substr($content, $pointer, strlen($v));
369  $pointer += strlen($mbuffer);
370  $buffer .= $mbuffer;
371  }
372  }
373  $newParts[] = $buffer;
374  return $newParts;
375  }
376 
390  public function splitIntoBlockRecursiveProc($tag, $content, &$procObj, $callBackContent, $callBackTags, $level = 0) {
391  $parts = $this->splitIntoBlock($tag, $content, TRUE);
392  foreach ($parts as $k => $v) {
393  if ($k % 2) {
394  $firstTagName = $this->getFirstTagName($v, TRUE);
395  $tagsArray = array();
396  $tagsArray['tag_start'] = $this->getFirstTag($v);
397  $tagsArray['tag_end'] = '</' . $firstTagName . '>';
398  $tagsArray['tag_name'] = strtolower($firstTagName);
399  $tagsArray['add_level'] = 1;
400  $tagsArray['content'] = $this->splitIntoBlockRecursiveProc($tag, $this->removeFirstAndLastTag($v), $procObj, $callBackContent, $callBackTags, $level + $tagsArray['add_level']);
401  if ($callBackTags) {
402  $tagsArray = $procObj->{$callBackTags}($tagsArray, $level);
403  }
404  $parts[$k] = $tagsArray['tag_start'] . $tagsArray['content'] . $tagsArray['tag_end'];
405  } else {
406  if ($callBackContent) {
407  $parts[$k] = $procObj->{$callBackContent}($parts[$k], $level);
408  }
409  }
410  }
411  return implode('', $parts);
412  }
413 
425  public function splitTags($tag, $content) {
426  $tags = GeneralUtility::trimExplode(',', $tag, TRUE);
427  foreach ($tags as &$tag) {
428  $tag = preg_quote($tag, '/');
429  }
430  $regexStr = '/\\<(' . implode('|', $tags) . ')(\\s[^>]*)?\\/?>/si';
431  $parts = preg_split($regexStr, $content);
432  $pointer = strlen($parts[0]);
433  $newParts = array();
434  $newParts[] = $parts[0];
435  reset($parts);
436  next($parts);
437  while (list($k, $v) = each($parts)) {
438  $tagLen = strcspn(substr($content, $pointer), '>') + 1;
439  // Set tag:
440  // New buffer set and pointer increased
441  $tag = substr($content, $pointer, $tagLen);
442  $newParts[] = $tag;
443  $pointer += strlen($tag);
444  // Set content:
445  $newParts[] = $v;
446  $pointer += strlen($v);
447  }
448  return $newParts;
449  }
450 
461  public function getAllParts($parts, $tag_parts = TRUE, $include_tag = TRUE) {
462  $newParts = array();
463  foreach ($parts as $k => $v) {
464  if (($k + ($tag_parts ? 0 : 1)) % 2) {
465  if (!$include_tag) {
466  $v = $this->removeFirstAndLastTag($v);
467  }
468  $newParts[] = $v;
469  }
470  }
471  return $newParts;
472  }
473 
482  public function removeFirstAndLastTag($str) {
483  // End of first tag:
484  $start = strpos($str, '>');
485  // Begin of last tag:
486  $end = strrpos($str, '<');
487  // Return
488  return substr($str, $start + 1, $end - $start - 1);
489  }
490 
499  public function getFirstTag($str) {
500  // First:
501  $endLen = strpos($str, '>') + 1;
502  return substr($str, 0, $endLen);
503  }
504 
514  public function getFirstTagName($str, $preserveCase = FALSE) {
515  $matches = array();
516  if (preg_match('/^\\s*\\<([^\\s\\>]+)(\\s|\\>)/', $str, $matches) === 1) {
517  if (!$preserveCase) {
518  return strtoupper($matches[1]);
519  }
520  return $matches[1];
521  }
522  return '';
523  }
524 
534  public function get_tag_attributes($tag, $deHSC = 0) {
535  list($components, $metaC) = $this->split_tag_attributes($tag);
536  // Attribute name is stored here
537  $name = '';
538  $valuemode = FALSE;
539  $attributes = array();
540  $attributesMeta = array();
541  if (is_array($components)) {
542  foreach ($components as $key => $val) {
543  // Only if $name is set (if there is an attribute, that waits for a value), that valuemode is enabled. This ensures that the attribute is assigned it's value
544  if ($val != '=') {
545  if ($valuemode) {
546  if ($name) {
547  $attributes[$name] = $deHSC ? htmlspecialchars_decode($val) : $val;
548  $attributesMeta[$name]['dashType'] = $metaC[$key];
549  $name = '';
550  }
551  } else {
552  if ($namekey = preg_replace('/[^[:alnum:]_\\:\\-]/', '', $val)) {
553  $name = strtolower($namekey);
554  $attributesMeta[$name] = array();
555  $attributesMeta[$name]['origTag'] = $namekey;
556  $attributes[$name] = '';
557  }
558  }
559  $valuemode = FALSE;
560  } else {
561  $valuemode = TRUE;
562  }
563  }
564  return array($attributes, $attributesMeta);
565  }
566  }
567 
578  public function split_tag_attributes($tag) {
579  $matches = array();
580  if (preg_match('/(\\<[^\\s]+\\s+)?(.*?)\\s*(\\>)?$/s', $tag, $matches) !== 1) {
581  return array(array(), array());
582  }
583  $tag_tmp = $matches[2];
584  $metaValue = array();
585  $value = array();
586  $matches = array();
587  if (preg_match_all('/("[^"]*"|\'[^\']*\'|[^\\s"\'\\=]+|\\=)/s', $tag_tmp, $matches) > 0) {
588  foreach ($matches[1] as $part) {
589  $firstChar = $part[0];
590  if ($firstChar == '"' || $firstChar == '\'') {
591  $metaValue[] = $firstChar;
592  $value[] = substr($part, 1, -1);
593  } else {
594  $metaValue[] = '';
595  $value[] = $part;
596  }
597  }
598  }
599  return array($value, $metaValue);
600  }
601 
616  public function checkTagTypeCounts($content, $blockTags = 'a,b,blockquote,body,div,em,font,form,h1,h2,h3,h4,h5,h6,i,li,map,ol,option,p,pre,select,span,strong,table,td,textarea,tr,u,ul', $soloTags = 'br,hr,img,input,area') {
617  $content = strtolower($content);
618  $analyzedOutput = array();
619  // Counts appearances of start-tags
620  $analyzedOutput['counts'] = array();
621  // Lists ERRORS
622  $analyzedOutput['errors'] = array();
623  // Lists warnings.
624  $analyzedOutput['warnings'] = array();
625  // Lists stats for block-tags
626  $analyzedOutput['blocks'] = array();
627  // Lists stats for solo-tags
628  $analyzedOutput['solo'] = array();
629  // Block tags, must have endings...
630  $blockTags = explode(',', $blockTags);
631  foreach ($blockTags as $tagName) {
632  $countBegin = count(preg_split(('/\\<' . preg_quote($tagName, '/') . '(\\s|\\>)/s'), $content)) - 1;
633  $countEnd = count(preg_split(('/\\<\\/' . preg_quote($tagName, '/') . '(\\s|\\>)/s'), $content)) - 1;
634  $analyzedOutput['blocks'][$tagName] = array($countBegin, $countEnd, $countBegin - $countEnd);
635  if ($countBegin) {
636  $analyzedOutput['counts'][$tagName] = $countBegin;
637  }
638  if ($countBegin - $countEnd) {
639  if ($countBegin - $countEnd > 0) {
640  $analyzedOutput['errors'][$tagName] = 'There were more start-tags (' . $countBegin . ') than end-tags (' . $countEnd . ') for the element "' . $tagName . '". There should be an equal amount!';
641  } else {
642  $analyzedOutput['warnings'][$tagName] = 'There were more end-tags (' . $countEnd . ') than start-tags (' . $countBegin . ') for the element "' . $tagName . '". There should be an equal amount! However the problem is not fatal.';
643  }
644  }
645  }
646  // Solo tags, must NOT have endings...
647  $soloTags = explode(',', $soloTags);
648  foreach ($soloTags as $tagName) {
649  $countBegin = count(preg_split(('/\\<' . preg_quote($tagName, '/') . '(\\s|\\>)/s'), $content)) - 1;
650  $countEnd = count(preg_split(('/\\<\\/' . preg_quote($tagName, '/') . '(\\s|\\>)/s'), $content)) - 1;
651  $analyzedOutput['solo'][$tagName] = array($countBegin, $countEnd);
652  if ($countBegin) {
653  $analyzedOutput['counts'][$tagName] = $countBegin;
654  }
655  if ($countEnd) {
656  $analyzedOutput['warnings'][$tagName] = 'There were end-tags found (' . $countEnd . ') for the element "' . $tagName . '". This was not expected (although XHTML technically allows it).';
657  }
658  }
659  return $analyzedOutput;
660  }
661 
662  /*********************************
663  *
664  * Clean HTML code
665  *
666  *********************************/
704  public function HTMLcleaner($content, $tags = array(), $keepAll = 0, $hSC = 0, $addConfig = array()) {
705  $newContent = array();
706  $tokArr = explode('<', $content);
707  $newContent[] = $this->processContent(current($tokArr), $hSC, $addConfig);
708  next($tokArr);
709  $c = 1;
710  $tagRegister = array();
711  $tagStack = array();
712  $inComment = FALSE;
713  $inCdata = FALSE;
714  $skipTag = FALSE;
715  while (list(, $tok) = each($tokArr)) {
716  if ($inComment) {
717  if (($eocPos = strpos($tok, '-->')) === FALSE) {
718  // End of comment is not found in the token. Go further until end of comment is found in other tokens.
719  $newContent[$c++] = '<' . $tok;
720  continue;
721  }
722  // Comment ends in the middle of the token: add comment and proceed with rest of the token
723  $newContent[$c++] = '<' . substr($tok, 0, ($eocPos + 3));
724  $tok = substr($tok, $eocPos + 3);
725  $inComment = FALSE;
726  $skipTag = TRUE;
727  } elseif ($inCdata) {
728  if (($eocPos = strpos($tok, '/*]]>*/')) === FALSE) {
729  // End of comment is not found in the token. Go futher until end of comment is found in other tokens.
730  $newContent[$c++] = '<' . $tok;
731  continue;
732  }
733  // Comment ends in the middle of the token: add comment and proceed with rest of the token
734  $newContent[$c++] = '<' . substr($tok, 0, $eocPos + 10);
735  $tok = substr($tok, $eocPos + 10);
736  $inCdata = FALSE;
737  $skipTag = TRUE;
738  } elseif (substr($tok, 0, 3) == '!--') {
739  if (($eocPos = strpos($tok, '-->')) === FALSE) {
740  // Comment started in this token but it does end in the same token. Set a flag to skip till the end of comment
741  $newContent[$c++] = '<' . $tok;
742  $inComment = TRUE;
743  continue;
744  }
745  // Start and end of comment are both in the current token. Add comment and proceed with rest of the token
746  $newContent[$c++] = '<' . substr($tok, 0, ($eocPos + 3));
747  $tok = substr($tok, $eocPos + 3);
748  $skipTag = TRUE;
749  } elseif (substr($tok, 0, 10) === '![CDATA[*/') {
750  if (($eocPos = strpos($tok, '/*]]>*/')) === FALSE) {
751  // Comment started in this token but it does end in the same token. Set a flag to skip till the end of comment
752  $newContent[$c++] = '<' . $tok;
753  $inCdata = TRUE;
754  continue;
755  }
756  // Start and end of comment are both in the current token. Add comment and proceed with rest of the token
757  $newContent[$c++] = '<' . substr($tok, 0, $eocPos + 10);
758  $tok = substr($tok, $eocPos + 10);
759  $skipTag = TRUE;
760  }
761  $firstChar = $tok[0];
762  // It is a tag... (first char is a-z0-9 or /) (fixed 19/01 2004). This also avoids triggering on <?xml..> and <!DOCTYPE..>
763  if (!$skipTag && preg_match('/[[:alnum:]\\/]/', $firstChar) == 1) {
764  $tagEnd = strpos($tok, '>');
765  // If there is and end-bracket... tagEnd can't be 0 as the first character can't be a >
766  if ($tagEnd) {
767  $endTag = $firstChar == '/' ? 1 : 0;
768  $tagContent = substr($tok, $endTag, $tagEnd - $endTag);
769  $tagParts = preg_split('/\\s+/s', $tagContent, 2);
770  $tagName = strtolower($tagParts[0]);
771  $emptyTag = 0;
772  if (isset($tags[$tagName])) {
773  // If there is processing to do for the tag:
774  if (is_array($tags[$tagName])) {
775  if (preg_match('/^(' . self::VOID_ELEMENTS . ' )$/i', $tagName)) {
776  $emptyTag = 1;
777  }
778  // If NOT an endtag, do attribute processing (added dec. 2003)
779  if (!$endTag) {
780  // Override attributes
781  if ((string)$tags[$tagName]['overrideAttribs'] !== '') {
782  $tagParts[1] = $tags[$tagName]['overrideAttribs'];
783  }
784  // Allowed tags
785  if ((string)$tags[$tagName]['allowedAttribs'] !== '') {
786  // No attribs allowed
787  if ((string)$tags[$tagName]['allowedAttribs'] === '0') {
788  $tagParts[1] = '';
789  } elseif (trim($tagParts[1])) {
790  $tagAttrib = $this->get_tag_attributes($tagParts[1]);
791  $tagParts[1] = '';
792  $newTagAttrib = array();
793  if (!($tList = $tags[$tagName]['_allowedAttribs'])) {
794  // Just explode attribts for tag once
795  $tList = ($tags[$tagName]['_allowedAttribs'] = GeneralUtility::trimExplode(',', strtolower($tags[$tagName]['allowedAttribs']), TRUE));
796  }
797  foreach ($tList as $allowTag) {
798  if (isset($tagAttrib[0][$allowTag])) {
799  $newTagAttrib[$allowTag] = $tagAttrib[0][$allowTag];
800  }
801  }
802  $tagParts[1] = $this->compileTagAttribs($newTagAttrib, $tagAttrib[1]);
803  }
804  }
805  // Fixed attrib values
806  if (is_array($tags[$tagName]['fixAttrib'])) {
807  $tagAttrib = $this->get_tag_attributes($tagParts[1]);
808  $tagParts[1] = '';
809  foreach ($tags[$tagName]['fixAttrib'] as $attr => $params) {
810  if (strlen($params['set'])) {
811  $tagAttrib[0][$attr] = $params['set'];
812  }
813  if (isset($params['unset']) && !empty($params['unset'])) {
814  unset($tagAttrib[0][$attr]);
815  }
816  if (!isset($tagAttrib[0][$attr]) && (string)$params['default'] !== '') {
817  $tagAttrib[0][$attr] = $params['default'];
818  }
819  if ($params['always'] || isset($tagAttrib[0][$attr])) {
820  if ($params['trim']) {
821  $tagAttrib[0][$attr] = trim($tagAttrib[0][$attr]);
822  }
823  if ($params['intval']) {
824  $tagAttrib[0][$attr] = (int)$tagAttrib[0][$attr];
825  }
826  if ($params['lower']) {
827  $tagAttrib[0][$attr] = strtolower($tagAttrib[0][$attr]);
828  }
829  if ($params['upper']) {
830  $tagAttrib[0][$attr] = strtoupper($tagAttrib[0][$attr]);
831  }
832  if ($params['range']) {
833  if (isset($params['range'][1])) {
834  $tagAttrib[0][$attr] = \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange($tagAttrib[0][$attr], (int)$params['range'][0], (int)$params['range'][1]);
835  } else {
836  $tagAttrib[0][$attr] = \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange($tagAttrib[0][$attr], (int)$params['range'][0]);
837  }
838  }
839  if (is_array($params['list'])) {
840  // For the class attribute, remove from the attribute value any class not in the list
841  // Classes are case sensitive
842  if ($attr == 'class') {
843  $newClasses = array();
844  $classes = GeneralUtility::trimExplode(' ', $tagAttrib[0][$attr], TRUE);
845  foreach ($classes as $class) {
846  if (in_array($class, $params['list'])) {
847  $newClasses[] = $class;
848  }
849  }
850  if (count($newClasses)) {
851  $tagAttrib[0][$attr] = implode(' ', $newClasses);
852  } else {
853  $tagAttrib[0][$attr] = '';
854  }
855  } else {
856  if (!in_array($this->caseShift($tagAttrib[0][$attr], $params['casesensitiveComp']), $this->caseShift($params['list'], $params['casesensitiveComp'], $tagName))) {
857  $tagAttrib[0][$attr] = $params['list'][0];
858  }
859  }
860  }
861  if ($params['removeIfFalse'] && $params['removeIfFalse'] != 'blank' && !$tagAttrib[0][$attr] || $params['removeIfFalse'] == 'blank' && (string)$tagAttrib[0][$attr] === '') {
862  unset($tagAttrib[0][$attr]);
863  }
864  if ((string)$params['removeIfEquals'] !== '' && $this->caseShift($tagAttrib[0][$attr], $params['casesensitiveComp']) === $this->caseShift($params['removeIfEquals'], $params['casesensitiveComp'])) {
865  unset($tagAttrib[0][$attr]);
866  }
867  if ($params['prefixLocalAnchors']) {
868  if ($tagAttrib[0][$attr][0] === '#') {
869  if ($params['prefixLocalAnchors'] == 2) {
871  $contentObjectRenderer = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\ContentObject\\ContentObjectRenderer');
872  $prefix = $contentObjectRenderer->getUrlToCurrentLocation();
873  } else {
874  $prefix = GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL');
875  }
876  $tagAttrib[0][$attr] = $prefix . $tagAttrib[0][$attr];
877  }
878  }
879  if ($params['prefixRelPathWith']) {
880  $urlParts = parse_url($tagAttrib[0][$attr]);
881  if (!$urlParts['scheme'] && $urlParts['path'][0] !== '/') {
882  // If it is NOT an absolute URL (by http: or starting "/")
883  $tagAttrib[0][$attr] = $params['prefixRelPathWith'] . $tagAttrib[0][$attr];
884  }
885  }
886  if ($params['userFunc']) {
887  $tagAttrib[0][$attr] = GeneralUtility::callUserFunction($params['userFunc'], $tagAttrib[0][$attr], $this);
888  }
889  }
890  }
891  $tagParts[1] = $this->compileTagAttribs($tagAttrib[0], $tagAttrib[1]);
892  }
893  } else {
894  // If endTag, remove any possible attributes:
895  $tagParts[1] = '';
896  }
897  // Protecting the tag by converting < and > to &lt; and &gt; ??
898  if ($tags[$tagName]['protect']) {
899  $lt = '&lt;';
900  $gt = '&gt;';
901  } else {
902  $lt = '<';
903  $gt = '>';
904  }
905  // Remapping tag name?
906  if ($tags[$tagName]['remap']) {
907  $tagParts[0] = $tags[$tagName]['remap'];
908  }
909  // rmTagIfNoAttrib
910  if ($endTag || trim($tagParts[1]) || !$tags[$tagName]['rmTagIfNoAttrib']) {
911  $setTag = 1;
912  // Remove this closing tag if $tagName was among $TSconfig['removeTags']
913  if ($endTag && $tags[$tagName]['allowedAttribs'] === 0 && $tags[$tagName]['rmTagIfNoAttrib'] === 1) {
914  $setTag = 0;
915  }
916  if ($tags[$tagName]['nesting']) {
917  if (!is_array($tagRegister[$tagName])) {
918  $tagRegister[$tagName] = array();
919  }
920  if ($endTag) {
921  $correctTag = 1;
922  if ($tags[$tagName]['nesting'] == 'global') {
923  $lastEl = end($tagStack);
924  if ($tagName !== $lastEl) {
925  if (in_array($tagName, $tagStack)) {
926  while (count($tagStack) && $tagName !== $lastEl) {
927  $elPos = end($tagRegister[$lastEl]);
928  unset($newContent[$elPos]);
929  array_pop($tagRegister[$lastEl]);
930  array_pop($tagStack);
931  $lastEl = end($tagStack);
932  }
933  } else {
934  // In this case the
935  $correctTag = 0;
936  }
937  }
938  }
939  if (!count($tagRegister[$tagName]) || !$correctTag) {
940  $setTag = 0;
941  } else {
942  array_pop($tagRegister[$tagName]);
943  if ($tags[$tagName]['nesting'] == 'global') {
944  array_pop($tagStack);
945  }
946  }
947  } else {
948  array_push($tagRegister[$tagName], $c);
949  if ($tags[$tagName]['nesting'] == 'global') {
950  array_push($tagStack, $tagName);
951  }
952  }
953  }
954  if ($setTag) {
955  // Setting the tag
956  $newContent[$c++] = $this->processTag($lt . ($endTag ? '/' : '') . trim(($tagParts[0] . ' ' . $tagParts[1])) . ($emptyTag ? ' /' : '') . $gt, $addConfig, $endTag, $lt == '&lt;');
957  }
958  }
959  } else {
960  $newContent[$c++] = $this->processTag('<' . ($endTag ? '/' : '') . $tagContent . '>', $addConfig, $endTag);
961  }
962  } elseif ($keepAll) {
963  // This is if the tag was not defined in the array for processing:
964  if ($keepAll === 'protect') {
965  $lt = '&lt;';
966  $gt = '&gt;';
967  } else {
968  $lt = '<';
969  $gt = '>';
970  }
971  $newContent[$c++] = $this->processTag($lt . ($endTag ? '/' : '') . $tagContent . $gt, $addConfig, $endTag, $lt == '&lt;');
972  }
973  $newContent[$c++] = $this->processContent(substr($tok, $tagEnd + 1), $hSC, $addConfig);
974  } else {
975  $newContent[$c++] = $this->processContent('<' . $tok, $hSC, $addConfig);
976  }
977  } else {
978  $newContent[$c++] = $this->processContent(($skipTag ? '' : '<') . $tok, $hSC, $addConfig);
979  // It was not a tag anyways
980  $skipTag = FALSE;
981  }
982  }
983  // Unsetting tags:
984  foreach ($tagRegister as $tag => $positions) {
985  foreach ($positions as $pKey) {
986  unset($newContent[$pKey]);
987  }
988  }
989  return implode('', $newContent);
990  }
991 
1000  public function bidir_htmlspecialchars($value, $dir) {
1001  $dir = (int)$dir;
1002  if ($dir === 1) {
1003  $value = htmlspecialchars($value);
1004  } elseif ($dir === 2) {
1005  $value = htmlspecialchars($value, ENT_COMPAT, 'UTF-8', FALSE);
1006  } elseif ($dir === -1) {
1007  $value = htmlspecialchars_decode($value);
1008  }
1009  return $value;
1010  }
1011 
1022  public function prefixResourcePath($main_prefix, $content, $alternatives = array(), $suffix = '') {
1023  $parts = $this->splitTags('embed,td,table,body,img,input,form,link,script,a,param', $content);
1024  foreach ($parts as $k => $v) {
1025  if ($k % 2) {
1026  $params = $this->get_tag_attributes($v);
1027  // Detect tag-ending so that it is re-applied correctly.
1028  $tagEnd = substr($v, -2) == '/>' ? ' />' : '>';
1029  // The 'name' of the first tag
1030  $firstTagName = $this->getFirstTagName($v);
1031  $somethingDone = 0;
1032  $prefix = isset($alternatives[strtoupper($firstTagName)]) ? $alternatives[strtoupper($firstTagName)] : $main_prefix;
1033  switch (strtolower($firstTagName)) {
1034  case 'td':
1035 
1036  case 'body':
1037 
1038  case 'table':
1039  $src = $params[0]['background'];
1040  if ($src) {
1041  $params[0]['background'] = $this->prefixRelPath($prefix, $params[0]['background'], $suffix);
1042  $somethingDone = 1;
1043  }
1044  break;
1045  case 'img':
1046 
1047  case 'input':
1048 
1049  case 'script':
1050 
1051  case 'embed':
1052  $src = $params[0]['src'];
1053  if ($src) {
1054  $params[0]['src'] = $this->prefixRelPath($prefix, $params[0]['src'], $suffix);
1055  $somethingDone = 1;
1056  }
1057  break;
1058  case 'link':
1059 
1060  case 'a':
1061  $src = $params[0]['href'];
1062  if ($src) {
1063  $params[0]['href'] = $this->prefixRelPath($prefix, $params[0]['href'], $suffix);
1064  $somethingDone = 1;
1065  }
1066  break;
1067  case 'form':
1068  $src = $params[0]['action'];
1069  if ($src) {
1070  $params[0]['action'] = $this->prefixRelPath($prefix, $params[0]['action'], $suffix);
1071  $somethingDone = 1;
1072  }
1073  break;
1074  case 'param':
1075  $test = $params[0]['name'];
1076  if ($test && $test === 'movie') {
1077  if ($params[0]['value']) {
1078  $params[0]['value'] = $this->prefixRelPath($prefix, $params[0]['value'], $suffix);
1079  $somethingDone = 1;
1080  }
1081  }
1082  break;
1083  }
1084  if ($somethingDone) {
1085  $tagParts = preg_split('/\\s+/s', $v, 2);
1086  $tagParts[1] = $this->compileTagAttribs($params[0], $params[1]);
1087  $parts[$k] = '<' . trim((strtolower($firstTagName) . ' ' . $tagParts[1])) . $tagEnd;
1088  }
1089  }
1090  }
1091  $content = implode('', $parts);
1092  // Fix <style> section:
1093  $prefix = isset($alternatives['style']) ? $alternatives['style'] : $main_prefix;
1094  if (strlen($prefix)) {
1095  $parts = $this->splitIntoBlock('style', $content);
1096  foreach ($parts as $k => &$part) {
1097  if ($k % 2) {
1098  $part = preg_replace('/(url[[:space:]]*\\([[:space:]]*["\']?)([^"\')]*)(["\']?[[:space:]]*\\))/i', '\\1' . $prefix . '\\2' . $suffix . '\\3', $part);
1099  }
1100  }
1101  unset($part);
1102  $content = implode('', $parts);
1103  }
1104  return $content;
1105  }
1106 
1117  public function prefixRelPath($prefix, $srcVal, $suffix = '') {
1118  // Only prefix if it's not an absolute URL or
1119  // only a link to a section within the page.
1120  if ($srcVal[0] !== '/' && $srcVal[0] !== '#') {
1121  $urlParts = parse_url($srcVal);
1122  // Only prefix URLs without a scheme
1123  if (!$urlParts['scheme']) {
1124  $srcVal = $prefix . $srcVal . $suffix;
1125  }
1126  }
1127  return $srcVal;
1128  }
1129 
1141  public function cleanFontTags($value, $keepFace = 0, $keepSize = 0, $keepColor = 0) {
1142  // ,1 ?? - could probably be more stable if splitTags() was used since this depends on end-tags being properly set!
1143  $fontSplit = $this->splitIntoBlock('font', $value);
1144  foreach ($fontSplit as $k => $v) {
1145  // Font
1146  if ($k % 2) {
1147  $attribArray = $this->get_tag_attributes_classic($this->getFirstTag($v));
1148  $newAttribs = array();
1149  if ($keepFace && $attribArray['face']) {
1150  $newAttribs[] = 'face="' . $attribArray['face'] . '"';
1151  }
1152  if ($keepSize && $attribArray['size']) {
1153  $newAttribs[] = 'size="' . $attribArray['size'] . '"';
1154  }
1155  if ($keepColor && $attribArray['color']) {
1156  $newAttribs[] = 'color="' . $attribArray['color'] . '"';
1157  }
1158  $innerContent = $this->cleanFontTags($this->removeFirstAndLastTag($v), $keepFace, $keepSize, $keepColor);
1159  if (count($newAttribs)) {
1160  $fontSplit[$k] = '<font ' . implode(' ', $newAttribs) . '>' . $innerContent . '</font>';
1161  } else {
1162  $fontSplit[$k] = $innerContent;
1163  }
1164  }
1165  }
1166  return implode('', $fontSplit);
1167  }
1168 
1179  public function mapTags($value, $tags = array(), $ltChar = '<', $ltChar2 = '<') {
1180  foreach ($tags as $from => $to) {
1181  $value = preg_replace('/' . preg_quote($ltChar, '/') . '(\\/)?' . $from . '\\s([^\\>])*(\\/)?\\>/', $ltChar2 . '$1' . $to . ' $2$3>', $value);
1182  }
1183  return $value;
1184  }
1185 
1194  public function unprotectTags($content, $tagList = '') {
1195  $tagsArray = GeneralUtility::trimExplode(',', $tagList, TRUE);
1196  $contentParts = explode('&lt;', $content);
1197  next($contentParts);
1198  // bypass the first
1199  while (list($k, $tok) = each($contentParts)) {
1200  $firstChar = $tok[0];
1201  if (trim($firstChar) !== '') {
1202  $subparts = explode('&gt;', $tok, 2);
1203  $tagEnd = strlen($subparts[0]);
1204  if (strlen($tok) != $tagEnd) {
1205  $endTag = $firstChar == '/' ? 1 : 0;
1206  $tagContent = substr($tok, $endTag, $tagEnd - $endTag);
1207  $tagParts = preg_split('/\\s+/s', $tagContent, 2);
1208  $tagName = strtolower($tagParts[0]);
1209  if ((string)$tagList === '' || in_array($tagName, $tagsArray)) {
1210  $contentParts[$k] = '<' . $subparts[0] . '>' . $subparts[1];
1211  } else {
1212  $contentParts[$k] = '&lt;' . $tok;
1213  }
1214  } else {
1215  $contentParts[$k] = '&lt;' . $tok;
1216  }
1217  } else {
1218  $contentParts[$k] = '&lt;' . $tok;
1219  }
1220  }
1221  return implode('', $contentParts);
1222  }
1223 
1234  public function caseShift($str, $flag, $cacheKey = '') {
1235  $cacheKey .= $flag ? 1 : 0;
1236  if (is_array($str)) {
1237  if (!$cacheKey || !isset($this->caseShift_cache[$cacheKey])) {
1238  foreach ($str as &$v) {
1239  if (!$flag) {
1240  $v = strtoupper($v);
1241  }
1242  }
1243  unset($v);
1244  if ($cacheKey) {
1245  $this->caseShift_cache[$cacheKey] = $str;
1246  }
1247  } else {
1248  $str = $this->caseShift_cache[$cacheKey];
1249  }
1250  } elseif (!$flag) {
1251  $str = strtoupper($str);
1252  }
1253  return $str;
1254  }
1255 
1266  public function compileTagAttribs($tagAttrib, $meta = array(), $xhtmlClean = 0) {
1267  $accu = array();
1268  foreach ($tagAttrib as $k => $v) {
1269  if ($xhtmlClean) {
1270  $attr = strtolower($k);
1271  if ((string)$v !== '' || isset($meta[$k]['dashType'])) {
1272  $attr .= '="' . htmlspecialchars($v) . '"';
1273  }
1274  } else {
1275  $attr = $meta[$k]['origTag'] ?: $k;
1276  if (strcmp($v, '') || isset($meta[$k]['dashType'])) {
1277  $dash = $meta[$k]['dashType'] ?: (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($v) ? '' : '"');
1278  $attr .= '=' . $dash . $v . $dash;
1279  }
1280  }
1281  $accu[] = $attr;
1282  }
1283  return implode(' ', $accu);
1284  }
1285 
1295  public function get_tag_attributes_classic($tag, $deHSC = 0) {
1296  $attr = $this->get_tag_attributes($tag, $deHSC);
1297  return is_array($attr[0]) ? $attr[0] : array();
1298  }
1299 
1309  public function indentLines($content, $number = 1, $indentChar = TAB) {
1310  $preTab = str_pad('', $number * strlen($indentChar), $indentChar);
1311  $lines = explode(LF, str_replace(CR, '', $content));
1312  foreach ($lines as &$line) {
1313  $line = $preTab . $line;
1314  }
1315  unset($line);
1316  return implode(LF, $lines);
1317  }
1318 
1328  public function HTMLparserConfig($TSconfig, $keepTags = array()) {
1329  // Allow tags (base list, merged with incoming array)
1330  $alTags = array_flip(GeneralUtility::trimExplode(',', strtolower($TSconfig['allowTags']), TRUE));
1331  $keepTags = array_merge($alTags, $keepTags);
1332  // Set config properties.
1333  if (is_array($TSconfig['tags.'])) {
1334  foreach ($TSconfig['tags.'] as $key => $tagC) {
1335  if (!is_array($tagC) && $key == strtolower($key)) {
1336  if ((string)$tagC === '0') {
1337  unset($keepTags[$key]);
1338  }
1339  if ((string)$tagC === '1' && !isset($keepTags[$key])) {
1340  $keepTags[$key] = 1;
1341  }
1342  }
1343  }
1344  foreach ($TSconfig['tags.'] as $key => $tagC) {
1345  if (is_array($tagC) && $key == strtolower($key)) {
1346  $key = substr($key, 0, -1);
1347  if (!is_array($keepTags[$key])) {
1348  $keepTags[$key] = array();
1349  }
1350  if (is_array($tagC['fixAttrib.'])) {
1351  foreach ($tagC['fixAttrib.'] as $atName => $atConfig) {
1352  if (is_array($atConfig)) {
1353  $atName = substr($atName, 0, -1);
1354  if (!is_array($keepTags[$key]['fixAttrib'][$atName])) {
1355  $keepTags[$key]['fixAttrib'][$atName] = array();
1356  }
1357  $keepTags[$key]['fixAttrib'][$atName] = array_merge($keepTags[$key]['fixAttrib'][$atName], $atConfig);
1358  // Candidate for \TYPO3\CMS\Core\Utility\GeneralUtility::array_merge() if integer-keys will some day make trouble...
1359  if ((string)$keepTags[$key]['fixAttrib'][$atName]['range'] !== '') {
1360  $keepTags[$key]['fixAttrib'][$atName]['range'] = GeneralUtility::trimExplode(',', $keepTags[$key]['fixAttrib'][$atName]['range']);
1361  }
1362  if ((string)$keepTags[$key]['fixAttrib'][$atName]['list'] !== '') {
1363  $keepTags[$key]['fixAttrib'][$atName]['list'] = GeneralUtility::trimExplode(',', $keepTags[$key]['fixAttrib'][$atName]['list']);
1364  }
1365  }
1366  }
1367  }
1368  unset($tagC['fixAttrib.']);
1369  unset($tagC['fixAttrib']);
1370  if (isset($tagC['rmTagIfNoAttrib']) && $tagC['rmTagIfNoAttrib'] && empty($tagC['nesting'])) {
1371  $tagC['nesting'] = 1;
1372  }
1373  // Candidate for \TYPO3\CMS\Core\Utility\GeneralUtility::array_merge() if integer-keys will some day make trouble...
1374  $keepTags[$key] = array_merge($keepTags[$key], $tagC);
1375  }
1376  }
1377  }
1378  // LocalNesting
1379  if ($TSconfig['localNesting']) {
1380  $lN = GeneralUtility::trimExplode(',', strtolower($TSconfig['localNesting']), TRUE);
1381  foreach ($lN as $tn) {
1382  if (isset($keepTags[$tn])) {
1383  if (!is_array($keepTags[$tn])) {
1384  $keepTags[$tn] = array();
1385  }
1386  $keepTags[$tn]['nesting'] = 1;
1387  }
1388  }
1389  }
1390  if ($TSconfig['globalNesting']) {
1391  $lN = GeneralUtility::trimExplode(',', strtolower($TSconfig['globalNesting']), TRUE);
1392  foreach ($lN as $tn) {
1393  if (isset($keepTags[$tn])) {
1394  if (!is_array($keepTags[$tn])) {
1395  $keepTags[$tn] = array();
1396  }
1397  $keepTags[$tn]['nesting'] = 'global';
1398  }
1399  }
1400  }
1401  if ($TSconfig['rmTagIfNoAttrib']) {
1402  $lN = GeneralUtility::trimExplode(',', strtolower($TSconfig['rmTagIfNoAttrib']), TRUE);
1403  foreach ($lN as $tn) {
1404  if (isset($keepTags[$tn])) {
1405  if (!is_array($keepTags[$tn])) {
1406  $keepTags[$tn] = array();
1407  }
1408  $keepTags[$tn]['rmTagIfNoAttrib'] = 1;
1409  if (empty($keepTags[$tn]['nesting'])) {
1410  $keepTags[$tn]['nesting'] = 1;
1411  }
1412  }
1413  }
1414  }
1415  if ($TSconfig['noAttrib']) {
1416  $lN = GeneralUtility::trimExplode(',', strtolower($TSconfig['noAttrib']), TRUE);
1417  foreach ($lN as $tn) {
1418  if (isset($keepTags[$tn])) {
1419  if (!is_array($keepTags[$tn])) {
1420  $keepTags[$tn] = array();
1421  }
1422  $keepTags[$tn]['allowedAttribs'] = 0;
1423  }
1424  }
1425  }
1426  if ($TSconfig['removeTags']) {
1427  $lN = GeneralUtility::trimExplode(',', strtolower($TSconfig['removeTags']), TRUE);
1428  foreach ($lN as $tn) {
1429  $keepTags[$tn] = array();
1430  $keepTags[$tn]['allowedAttribs'] = 0;
1431  $keepTags[$tn]['rmTagIfNoAttrib'] = 1;
1432  }
1433  }
1434  // Create additional configuration:
1435  $addConfig = array();
1436  if ($TSconfig['xhtml_cleaning']) {
1437  $addConfig['xhtml'] = 1;
1438  }
1439  return array(
1440  $keepTags,
1441  '' . $TSconfig['keepNonMatchedTags'],
1442  (int)$TSconfig['htmlSpecialChars'],
1443  $addConfig
1444  );
1445  }
1446 
1473  public function XHTML_clean($content) {
1474  $content = $this->HTMLcleaner($content, array(), 1, 0, array('xhtml' => 1));
1475  return $content;
1476  }
1477 
1490  public function processTag($value, $conf, $endTag, $protected = 0) {
1491  // Return immediately if protected or no parameters
1492  if ($protected || !count($conf)) {
1493  return $value;
1494  }
1495  // OK then, begin processing for XHTML output:
1496  // STILL VERY EXPERIMENTAL!!
1497  if ($conf['xhtml']) {
1498  // Endtags are just set lowercase right away
1499  if ($endTag) {
1500  $value = strtolower($value);
1501  } elseif (substr($value, 0, 4) != '<!--') {
1502  // ... and comments are ignored.
1503  // Finding inner value with out < >
1504  $inValue = substr($value, 1, substr($value, -2) == '/>' ? -2 : -1);
1505  // Separate attributes and tagname
1506  list($tagName, $tagP) = preg_split('/\\s+/s', $inValue, 2);
1507  $tagName = strtolower($tagName);
1508  // Process attributes
1509  $tagAttrib = $this->get_tag_attributes($tagP);
1510  if ($tagName === 'img' && !isset($tagAttrib[0]['alt'])) {
1511  $tagAttrib[0]['alt'] = '';
1512  }
1513  // Set alt attribute for all images (not XHTML though...)
1514  if ($tagName === 'script' && !isset($tagAttrib[0]['type'])) {
1515  $tagAttrib[0]['type'] = 'text/javascript';
1516  }
1517  // Set type attribute for all script-tags
1518  $outA = array();
1519  foreach ($tagAttrib[0] as $attrib_name => $attrib_value) {
1520  // Set attributes: lowercase, always in quotes, with htmlspecialchars converted.
1521  $outA[] = $attrib_name . '="' . $this->bidir_htmlspecialchars($attrib_value, 2) . '"';
1522  }
1523  $newTag = '<' . trim(($tagName . ' ' . implode(' ', $outA)));
1524  // All tags that are standalone (not wrapping, not having endtags) should be ended with '/>'
1525  if (GeneralUtility::inList('img,br,hr,meta,link,base,area,input,param,col', $tagName) || substr($value, -2) == '/>') {
1526  $newTag .= ' />';
1527  } else {
1528  $newTag .= '>';
1529  }
1530  $value = $newTag;
1531  }
1532  }
1533  return $value;
1534  }
1535 
1546  public function processContent($value, $dir, $conf) {
1547  if ($dir != 0) {
1548  $value = $this->bidir_htmlspecialchars($value, $dir);
1549  }
1550  return $value;
1551  }
1552 
1553 }
static substituteSubpartArray($content, array $subpartsContent)
Definition: HtmlParser.php:150
getFirstTagName($str, $preserveCase=FALSE)
Definition: HtmlParser.php:514
splitIntoBlock($tag, $content, $eliminateExtraEndTags=FALSE)
Definition: HtmlParser.php:319
checkTagTypeCounts($content, $blockTags='a, b, blockquote, body, div, em, font, form, h1, h2, h3, h4, h5, h6, i, li, map, ol, option, p, pre, select, span, strong, table, td, textarea, tr, u, ul', $soloTags='br, hr, img, input, area')
Definition: HtmlParser.php:616
static forceIntegerInRange($theInt, $min, $max=2000000000, $defaultValue=0)
Definition: MathUtility.php:32
mapTags($value, $tags=array(), $ltChar='<', $ltChar2='<')
HTMLparserConfig($TSconfig, $keepTags=array())
getAllParts($parts, $tag_parts=TRUE, $include_tag=TRUE)
Definition: HtmlParser.php:461
indentLines($content, $number=1, $indentChar=TAB)
prefixRelPath($prefix, $srcVal, $suffix='')
static trimExplode($delim, $string, $removeEmptyValues=FALSE, $limit=0)
static substituteMarkerAndSubpartArrayRecursive($content, array $markersAndSubparts, $wrap='', $uppercase=FALSE, $deleteUnused=FALSE)
Definition: HtmlParser.php:252
compileTagAttribs($tagAttrib, $meta=array(), $xhtmlClean=0)
static callUserFunction($funcName, &$params, &$ref, $checkPrefix='', $errorMode=0)
get_tag_attributes($tag, $deHSC=0)
Definition: HtmlParser.php:534
caseShift($str, $flag, $cacheKey='')
splitIntoBlockRecursiveProc($tag, $content, &$procObj, $callBackContent, $callBackTags, $level=0)
Definition: HtmlParser.php:390
static substituteMarker($content, $marker, $markContent)
Definition: HtmlParser.php:167
if($list_of_literals) if(!empty($literals)) if(!empty($literals)) $result
Analyse literals to prepend the N char to them if their contents aren&#39;t numeric.
processContent($value, $dir, $conf)
static getSubpart($content, $marker)
Definition: HtmlParser.php:39
prefixResourcePath($main_prefix, $content, $alternatives=array(), $suffix='')
static substituteMarkerArray($content, $markContentArray, $wrap='', $uppercase=FALSE, $deleteUnused=FALSE)
Definition: HtmlParser.php:189
static substituteSubpart($content, $marker, $subpartContent, $recursive=TRUE, $keepMarker=FALSE)
Definition: HtmlParser.php:79
bidir_htmlspecialchars($value, $dir)
cleanFontTags($value, $keepFace=0, $keepSize=0, $keepColor=0)
processTag($value, $conf, $endTag, $protected=0)
get_tag_attributes_classic($tag, $deHSC=0)
unprotectTags($content, $tagList='')