00001 <?php
00002
00003
00004
00005
00006
00007
00008 class HtmlString {
00009 const USE_STRING_FUNCTIONS = 0;
00010 const USE_PHP_FUNCTIONS = 1;
00011
00012 private $html_texts = array();
00013 private $html_tags = array();
00014 private $policy = 0;
00015
00016 public function __construct($text = '', $policy = self::USE_STRING_FUNCTIONS) {
00017 $this->set_text($text);
00018 $this->policy = $policy;
00019 }
00020
00021 public function set_text($text) {
00022 $tag = '#<[^>]*>#';
00023 $text = ' ' . $text;
00024
00025
00026 $this->html_texts = String::preg_split($tag, $text, -1);
00027
00028
00029 $html_tags = '';
00030 String::preg_match_all($tag, $text, $html_tags);
00031 $this->html_tags = $html_tags[0];
00032
00033
00034
00035
00036 }
00037
00038
00039
00040
00041 public function build() {
00042 $count_texts = count($this->html_texts);
00043 $count_tags = count($this->html_tags);
00044
00045 $ret = '';
00046
00047 for($index = 0; $index < $count_texts; $index ++) {
00048 $ret .= $this->html_texts[$index];
00049
00050 if ($index < $count_tags) {
00051 $ret .= $this->html_tags[$index];
00052 }
00053 }
00054
00055 return trim($ret);
00056 }
00057
00058
00059
00060
00061 protected function rebuild() {
00062 $this->set_text($this->build());
00063 }
00064
00065
00066
00067
00068
00069
00070 public function preg_replace($regex, $replace, $max = -1, $tags_to_skip = '') {
00071 if ($max == 0) {
00072 return 0;
00073 }
00074
00075 $count_texts = count($this->html_texts);
00076 if (!is_array($tags_to_skip)) {
00077 $tags_to_skip = empty($tags_to_skip) ? array() : explode(' ', $tags_to_skip);
00078 }
00079
00080 $num_matches = 0;
00081
00082 for($index = 0; $index < $count_texts; $index ++) {
00083 if (!$this->is_within_tags($tags_to_skip, $index)) {
00084 $count_matches = 0;
00085 if ($this->policy == self::USE_STRING_FUNCTIONS) {
00086 $this->html_texts[$index] = String::preg_replace($regex, $replace, $this->html_texts[$index], $max, $count_matches);
00087 }
00088 else {
00089 $this->html_texts[$index] = preg_replace($regex, $replace, $this->html_texts[$index], $max, $count_matches);
00090 }
00091 $num_matches += $count_matches;
00092 }
00093
00094 if ($max > 0 && $num_matches >= $max ) {
00095 break;
00096 }
00097 }
00098
00099 if ($num_matches > 0) {
00100
00101 $this->rebuild();
00102 }
00103 return $num_matches;
00104 }
00105
00106
00107
00108
00109 protected function is_within_tags($arr_tags, $text_index) {
00110 if (count($arr_tags) == 0) {
00111 return false;
00112 }
00113 $ret = false;
00114 $stack = array();
00115
00116 for($i = $text_index - 1; $i >= 0; $i--) {
00117 $tag = $this->html_tags[$i];
00118
00119 if (substr($tag, -2, 2) == '/>') {
00120
00121 continue;
00122 }
00123 else if (substr($tag, 0, 2) == '</') {
00124
00125 $tag = $this->get_plain_tagname($tag);
00126 array_unshift($stack, $tag);
00127 }
00128 else {
00129
00130 $tag = $this->get_plain_tagname($tag);
00131 if (count($stack) > 0 && $stack[0] == $tag) {
00132
00133 array_shift($stack);
00134 continue;
00135 }
00136 else {
00137
00138 if (in_array($tag, $arr_tags)) {
00139
00140 $ret = true;
00141 break;
00142 }
00143 }
00144 }
00145 }
00146 return $ret;
00147 }
00148
00149
00150
00151
00152 public function insert($text, $pos, $tags_to_skip = '') {
00153 $count_texts = count($this->html_texts);
00154 if (!is_array($tags_to_skip)) {
00155 $tags_to_skip = empty($tags_to_skip) ? array() : explode(' ', $tags_to_skip);
00156 }
00157
00158 $arr_startpos = array();
00159 $arr_endpos = array();
00160 $pos_total = 0;
00161
00162 for($index = 0; $index < $count_texts; $index ++) {
00163 $arr_startpos[] = $pos_total;
00164 $pos_total += String::length($this->html_texts[$index]);
00165 $arr_endpos[] = $pos_total;
00166
00167 if ($pos_total >= $pos) {
00168 $index++;
00169 break;
00170 }
00171 }
00172
00173 $index--;
00174
00175 $matching_index = 0;
00176 for (; $index >= 0; $index--) {
00177 if (!$this->is_within_tags($tags_to_skip, $index)) {
00178 $matching_index = $index;
00179 break;
00180 }
00181 }
00182
00183 $block_text = $this->html_texts[$matching_index];
00184 if ($arr_endpos[$matching_index] < $pos) {
00185
00186 $block_text .= $text;
00187 }
00188 else {
00189
00190 $tmp = String::substr_word($block_text, 0, $pos - $arr_startpos[$matching_index]);
00191 $block_text = $tmp . $text . String::substr($block_text, String::length($tmp));
00192 }
00193 $this->html_texts[$matching_index] = $block_text;
00194 $this->rebuild();
00195 }
00196
00197
00198
00199
00200 protected function get_plain_tagname($tag) {
00201 $ret = str_replace(array('<', '>', '/'), '', $tag);
00202 $ret = trim($ret);
00203 $ret = String::extract_before($ret, ' ');
00204 return $ret;
00205 }
00206 }