Posted By

section31 on 01/29/10


Tagged


Versions (?)

balance tags


 / Published in: PHP
 

  1. /**
  2.  * Balances tags of string using a modified stack.
  3.  *
  4.  * @since 2.0.4
  5.  *
  6.  * @author Leonard Lin <[email protected]>
  7.  * @license GPL v2.0
  8.  * @copyright November 4, 2001
  9.  * @version 1.1
  10.  * @todo Make better - change loop condition to $text in 1.2
  11.  * @internal Modified by Scott Reilly (coffee2code) 02 Aug 2004
  12.  * 1.1 Fixed handling of append/stack pop order of end text
  13.  * Added Cleaning Hooks
  14.  * 1.0 First Version
  15.  *
  16.  * @param string $text Text to be balanced.
  17.  * @return string Balanced text.
  18.  */
  19. function force_balance_tags( $text ) {
  20. $tagstack = array(); $stacksize = 0; $tagqueue = ''; $newtext = '';
  21. $single_tags = array('br', 'hr', 'img', 'input'); //Known single-entity/self-closing tags
  22. $nestable_tags = array('blockquote', 'div', 'span'); //Tags that can be immediately nested within themselves
  23.  
  24. # WP bug fix for comments - in case you REALLY meant to type '< !--'
  25. $text = str_replace('< !--', '< !--', $text);
  26. # WP bug fix for LOVE <3 (and other situations with '<' before a number)
  27. $text = preg_replace('#<([0-9]{1})#', '&lt;$1', $text);
  28.  
  29. while (preg_match("/<(\/?\w*)\s*([^>]*)>/",$text,$regex)) {
  30. $newtext .= $tagqueue;
  31.  
  32. $i = strpos($text,$regex[0]);
  33. $l = strlen($regex[0]);
  34.  
  35. // clear the shifter
  36. $tagqueue = '';
  37. // Pop or Push
  38. if ( isset($regex[1][0]) && '/' == $regex[1][0] ) { // End Tag
  39. $tag = strtolower(substr($regex[1],1));
  40. // if too many closing tags
  41. if($stacksize <= 0) {
  42. $tag = '';
  43. //or close to be safe $tag = '/' . $tag;
  44. }
  45. // if stacktop value = tag close value then pop
  46. else if ($tagstack[$stacksize - 1] == $tag) { // found closing tag
  47. $tag = '</' . $tag . '>'; // Close Tag
  48. // Pop
  49. array_pop ($tagstack);
  50. $stacksize--;
  51. } else { // closing tag not at top, search for it
  52. for ($j=$stacksize-1;$j>=0;$j--) {
  53. if ($tagstack[$j] == $tag) {
  54. // add tag to tagqueue
  55. for ($k=$stacksize-1;$k>=$j;$k--){
  56. $tagqueue .= '</' . array_pop ($tagstack) . '>';
  57. $stacksize--;
  58. }
  59. break;
  60. }
  61. }
  62. $tag = '';
  63. }
  64. } else { // Begin Tag
  65. $tag = strtolower($regex[1]);
  66.  
  67. // Tag Cleaning
  68.  
  69. // If self-closing or '', don't do anything.
  70. if((substr($regex[2],-1) == '/') || ($tag == '')) {
  71. }
  72. // ElseIf it's a known single-entity tag but it doesn't close itself, do so
  73. elseif ( in_array($tag, $single_tags) ) {
  74. $regex[2] .= '/';
  75. } else { // Push the tag onto the stack
  76. // If the top of the stack is the same as the tag we want to push, close previous tag
  77. if (($stacksize > 0) && !in_array($tag, $nestable_tags) && ($tagstack[$stacksize - 1] == $tag)) {
  78. $tagqueue = '</' . array_pop ($tagstack) . '>';
  79. $stacksize--;
  80. }
  81. $stacksize = array_push ($tagstack, $tag);
  82. }
  83.  
  84. // Attributes
  85. $attributes = $regex[2];
  86. if($attributes) {
  87. $attributes = ' '.$attributes;
  88. }
  89. $tag = '<'.$tag.$attributes.'>';
  90. //If already queuing a close tag, then put this tag on, too
  91. if ($tagqueue) {
  92. $tagqueue .= $tag;
  93. $tag = '';
  94. }
  95. }
  96. $newtext .= substr($text,0,$i) . $tag;
  97. $text = substr($text,$i+$l);
  98. }
  99.  
  100. // Clear Tag Queue
  101. $newtext .= $tagqueue;
  102.  
  103. // Add Remaining text
  104. $newtext .= $text;
  105.  
  106. // Empty Stack
  107. while($x = array_pop($tagstack)) {
  108. $newtext .= '</' . $x . '>'; // Add remaining tags to close
  109. }
  110.  
  111. // WP fix for the bug with HTML comments
  112. $newtext = str_replace("< !--","<!--",$newtext);
  113. $newtext = str_replace("< !--","< !--",$newtext);
  114.  
  115. return $newtext;
  116. }

Report this snippet  

You need to login to post a comment.