| 1015 | | // clear the shifter |
| 1016 | | $tagqueue = ''; |
| 1017 | | // Pop or Push |
| 1018 | | if ( isset($regex[1][0]) && '/' == $regex[1][0] ) { // End Tag |
| 1019 | | $tag = strtolower(substr($regex[1],1)); |
| 1020 | | // if too many closing tags |
| 1021 | | if( $stacksize <= 0 ) { |
| 1022 | | $tag = ''; |
| 1023 | | // or close to be safe $tag = '/' . $tag; |
| 1024 | | } |
| 1025 | | // if stacktop value = tag close value then pop |
| 1026 | | else if ( $tagstack[$stacksize - 1] == $tag ) { // found closing tag |
| 1027 | | $tag = '</' . $tag . '>'; // Close Tag |
| 1028 | | // Pop |
| 1029 | | array_pop( $tagstack ); |
| 1030 | | $stacksize--; |
| 1031 | | } else { // closing tag not at top, search for it |
| 1032 | | for ( $j = $stacksize-1; $j >= 0; $j-- ) { |
| 1033 | | if ( $tagstack[$j] == $tag ) { |
| 1034 | | // add tag to tagqueue |
| 1035 | | for ( $k = $stacksize-1; $k >= $j; $k--) { |
| 1036 | | $tagqueue .= '</' . array_pop( $tagstack ) . '>'; |
| 1037 | | $stacksize--; |
| 1038 | | } |
| 1039 | | break; |
| 1040 | | } |
| 1041 | | } |
| 1042 | | $tag = ''; |
| 1043 | | } |
| 1044 | | } else { // Begin Tag |
| 1045 | | $tag = strtolower($regex[1]); |
| | 1014 | // Start the position tracking for the new text at 0 |
| | 1015 | $newpos = 0; |
| 1049 | | // If self-closing or '', don't do anything. |
| 1050 | | if ( substr($regex[2],-1) == '/' || $tag == '' ) { |
| 1051 | | // do nothing |
| 1052 | | } |
| 1053 | | // ElseIf it's a known single-entity tag but it doesn't close itself, do so |
| 1054 | | elseif ( in_array($tag, $single_tags) ) { |
| 1055 | | $regex[2] .= '/'; |
| 1056 | | } else { // Push the tag onto the stack |
| 1057 | | // If the top of the stack is the same as the tag we want to push, close previous tag |
| 1058 | | if ( $stacksize > 0 && !in_array($tag, $nestable_tags) && $tagstack[$stacksize - 1] == $tag ) { |
| 1059 | | $tagqueue = '</' . array_pop ($tagstack) . '>'; |
| 1060 | | $stacksize--; |
| 1061 | | } |
| 1062 | | $stacksize = array_push ($tagstack, $tag); |
| 1063 | | } |
| | 1021 | // Look for matches |
| | 1022 | while ( preg_match('/<(\/?[\w:]*)\s*([^>]*)>/S', $text, $matches, PREG_OFFSET_CAPTURE, $pos) ) { |
| 1070 | | $tag = '<' . $tag . $attributes . '>'; |
| 1071 | | //If already queuing a close tag, then put this tag on, too |
| 1072 | | if ( !empty($tagqueue) ) { |
| 1073 | | $tagqueue .= $tag; |
| 1074 | | $tag = ''; |
| 1075 | | } |
| | 1034 | // Is it a closing tag or not? |
| | 1035 | $closing = ($matches[1][0][0] == '/'); |
| | 1036 | |
| | 1037 | // Just the tag, no brackets, slashes, or attributes |
| | 1038 | $tag = ( $closing ? strtolower(substr($matches[1][0], 1) ) : strtolower($matches[1][0])); |
| | 1039 | |
| | 1040 | // Find the position of this |
| | 1041 | $newpos = strpos($newtext, $matches[0][0], $newpos); |
| | 1042 | |
| | 1043 | // Skip any empty tags (most likely comments or malformed html tags ... |
| | 1044 | // we could make the doc worse by adding a closing tag to a bad html tag) |
| | 1045 | if ( empty($matches[1][0]) ) { |
| | 1046 | $pos = $matches[0][1] + 1; |
| 1077 | | $newtext .= substr($text, 0, $i) . $tag; |
| 1078 | | $text = substr($text, $i + $l); |
| 1079 | | } |
| | 1048 | |
| | 1049 | // If it's a known single tag and it's a closing tag, discard it |
| | 1050 | elseif ( in_array($tag, $single_tags) && $closing ) { |
| | 1051 | if ( $skip_level <= 0 ) |
| | 1052 | $newtext = substr($newtext, 0, $newpos) . substr($newtext, $newpos + strlen($matches[0][0])); |
| | 1053 | |
| | 1054 | // Move to the next match |
| | 1055 | $pos = $matches[0][1] + 1; |
| | 1056 | } |
| | 1057 | |
| | 1058 | // If it's a known single tag and it's not properly closed, close it |
| | 1059 | elseif ( in_array($tag, $single_tags) && '/>' != substr($matches[0][0], -2) ) { |
| | 1060 | |
| | 1061 | // Add the properly closed tag to the new text |
| | 1062 | if ( $skip_level <= 0 ) |
| | 1063 | $newtext = substr($newtext, 0, $newpos) . substr($matches[0][0], 0, -1) . '/>' . substr($newtext, $newpos + strlen($matches[0][0])); |
| | 1064 | |
| | 1065 | // Move to the next match |
| | 1066 | $pos = $matches[0][1] + 1; |
| | 1067 | } |
| | 1068 | |
| | 1069 | // If it's an opening tag, add it to the stack |
| | 1070 | elseif ( !$closing ) { |
| | 1071 | |
| | 1072 | // If it's a self closing tag (regardless of whether we have it in our list) we don't |
| | 1073 | // want it in the stack, as it's legitimate XML, but it will mess up our balancing act |
| | 1074 | if ( '/>' != substr($matches[0][0], -2) && ($skip_level <= 0 || in_array($tag, $skip_tags)) ) |
| | 1075 | array_push($stack, $matches); |
| | 1076 | |
| | 1077 | // Are we in skip mode? |
| | 1078 | if ( in_array($tag, $skip_tags) ) |
| | 1079 | $skip_level++; |
| | 1080 | |
| | 1081 | // Move to the next match |
| | 1082 | $pos = $matches[0][1] + 1; |
| | 1083 | } |
| | 1084 | |
| | 1085 | // If we're here, it must be a closing tag |
| | 1086 | else { |
| 1087 | | // Empty Stack |
| 1088 | | while( $x = array_pop($tagstack) ) |
| 1089 | | $newtext .= '</' . $x . '>'; // Add remaining tags to close |
| | 1100 | // Can we stop skipping? |
| | 1101 | if ( $skip_level > 0 && in_array($tag, $skip_tags) ) |
| | 1102 | $skip_level--; |
| | 1103 | |
| | 1104 | // If it doesn't match, add in a closing tag, but don't remove it from the original text. This will force the tag to run through the |
| | 1105 | // loop again and continue to go up a level in the stack until it finds its match or until the stack is empty and the tag is discarded |
| | 1106 | } else { |
| | 1107 | if ( $skip_level <= 0 ) |
| | 1108 | $newtext = substr($newtext, 0, $newpos) . '</' . $lasttag[1][0] . '>' . substr($newtext, $newpos); |
| | 1109 | } |
| | 1110 | } |
| | 1111 | } |
| | 1112 | } |
| | 1113 | |
| | 1114 | // If there are still elements on the stack, add the closing tags at the bottom of the text |
| | 1115 | while ( !empty($stack) ) { |
| | 1116 | $tag = array_pop($stack); |
| | 1117 | $newtext .= '</' . $tag[1][0] . '>'; |
| | 1118 | } |