WordPress.org

Make WordPress Core

Ticket #18312: 18312.diff

File 18312.diff, 8.1 KB (added by kurtpayne, 4 years ago)

New version of force_balance_tags function

  • wp-includes/formatting.php

     
    981981 * @since 2.0.4 
    982982 * 
    983983 * @author Leonard Lin <leonard@acm.org> 
     984 * @author Kurt Payne <kpayne@godaddy.com> 
    984985 * @license GPL 
    985986 * @copyright November 4, 2001 
    986987 * @version 1.1 
    987988 * @todo Make better - change loop condition to $text in 1.2 
    988989 * @internal Modified by Scott Reilly (coffee2code) 02 Aug 2004 
     990 *              1.2  Updated to fix bug 18312 - improper balancing of nested lists 
     991 *                       Added the ability to skip cleaning of certain tags       
    989992 *              1.1  Fixed handling of append/stack pop order of end text 
    990993 *                       Added Cleaning Hooks 
    991994 *              1.0  First Version 
     
    994997 * @return string Balanced text. 
    995998 */ 
    996999function force_balance_tags( $text ) { 
    997         $tagstack = array(); 
    998         $stacksize = 0; 
    999         $tagqueue = ''; 
    1000         $newtext = ''; 
    10011000        $single_tags = array('br', 'hr', 'img', 'input'); // Known single-entity/self-closing tags 
    1002         $nestable_tags = array('blockquote', 'div', 'span'); // Tags that can be immediately nested within themselves 
     1001        $skip_tags = array('script', 'style'); // No fixing should be done inside these tags 
    10031002 
    10041003        // WP bug fix for comments - in case you REALLY meant to type '< !--' 
    10051004        $text = str_replace('< !--', '<    !--', $text); 
    10061005        // WP bug fix for LOVE <3 (and other situations with '<' before a number) 
    10071006        $text = preg_replace('#<([0-9]{1})#', '&lt;$1', $text); 
    10081007 
    1009         while ( preg_match("/<(\/?[\w:]*)\s*([^>]*)>/", $text, $regex) ) { 
    1010                 $newtext .= $tagqueue; 
     1008        // Copy the old text to a new field 
     1009        $newtext = $text; 
    10111010 
    1012                 $i = strpos($text, $regex[0]); 
    1013                 $l = strlen($regex[0]); 
     1011        // Keeping track of the tags we've seen 
     1012        $stack = array(); 
    10141013 
    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; 
    10461016 
    1047                         // Tag Cleaning 
     1017        // Start off with a skip level of 0.  Skipping tags can be nested and we 
     1018        // have to roll / unroll each one every time we open / close a skip tag 
     1019        $skip_level = 0; 
    10481020 
    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) ) {         
    10641023 
    1065                         // Attributes 
    1066                         $attributes = $regex[2]; 
    1067                         if( !empty($attributes) ) 
    1068                                 $attributes = ' '.$attributes; 
     1024                /** 
     1025                 * Matches: 
     1026                 *  [0][0] = whole match (e.g. <div style="align:center;"> 
     1027                 *  [0][1] = numeric position in $text (e.g. 0) 
     1028                 *  [1][0] = just the tag (e.g. div or /div) 
     1029                 *  [1][1] = numeric position in $text (e.g. 1) 
     1030                 *  [2][0] = any attributes (e.g. style="align:center;") 
     1031                 *  [2][1] = numeric position in $text (e.g. 5) 
     1032                 */ 
    10691033 
    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; 
    10761047                } 
    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 { 
    10801087 
    1081         // Clear Tag Queue 
    1082         $newtext .= $tagqueue; 
     1088                        // If the stack is empty, the closing tag must be useless, skip it, move to the next match 
     1089                        if ( empty($stack) ) { 
     1090                                if ( $skip_level <= 0 ) 
     1091                                        $newtext = substr($newtext, 0, $newpos) . substr($newtext, $newpos + strlen($matches[0][0])); 
     1092                                $pos = $matches[0][1] + 1; 
     1093                        } else { 
     1094                                $lasttag = array_pop($stack); 
    10831095 
    1084         // Add Remaining text 
    1085         $newtext .= $text; 
     1096                                // If the closing tag matches the last tag on the stack, just move to the next match 
     1097                                if ( strtolower($lasttag[1][0]) == $tag ) { 
     1098                                        $pos = $matches[0][1] + 1; 
    10861099 
    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        } 
    10901119 
    10911120        // WP fix for the bug with HTML comments 
    1092         $newtext = str_replace("< !--","<!--",$newtext); 
    1093         $newtext = str_replace("<    !--","< !--",$newtext); 
     1121        $newtext = str_replace('< !--', '<!--', $newtext); 
     1122        $newtext = str_replace('<    !--', '< !--', $newtext); 
    10941123 
    10951124        return $newtext; 
    10961125}