00001 <?php
00005 class StringUtils {
00017 static function hungryDelimiterReplace( $startDelim, $endDelim, $replace, $subject ) {
00018 $segments = explode( $startDelim, $subject );
00019 $output = array_shift( $segments );
00020 foreach ( $segments as $s ) {
00021 $endDelimPos = strpos( $s, $endDelim );
00022 if ( $endDelimPos === false ) {
00023 $output .= $startDelim . $s;
00024 } else {
00025 $output .= $replace . substr( $s, $endDelimPos + strlen( $endDelim ) );
00026 }
00027 }
00028 return $output;
00029 }
00030
00041 # If the start delimiter ends with an initial substring of the end delimiter,
00042 # e.g. in the case of C-style comments, the behaviour differs from the model
00043 # regex. In this implementation, the end must share no characters with the
00044 # start, so e.g.
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126 static function delimiterReplace( $startDelim, $endDelim, $replace, $subject, $flags = '' ) {
00127 $replacer = new RegexlikeReplacer( $replace );
00128 return self::delimiterReplaceCallback( $startDelim, $endDelim,
00129 $replacer->cb(), $subject, $flags );
00130 }
00131
00139 static function explodeMarkup( $separator, $text ) {
00140 $placeholder = "\x00";
00141
00142
00143 $text = str_replace( $placeholder, '', $text );
00144
00145
00146 $replacer = new DoubleReplacer( $separator, $placeholder );
00147 $cleaned = StringUtils::delimiterReplaceCallback( '<', '>', $replacer->cb(), $text );
00148
00149
00150 $items = explode( $separator, $cleaned );
00151 foreach( $items as $i => $str ) {
00152 $items[$i] = str_replace( $placeholder, $separator, $str );
00153 }
00154
00155 return $items;
00156 }
00157
00165 static function escapeRegexReplacement( $string ) {
00166 $string = str_replace( '\\', '\\\\', $string );
00167 $string = str_replace( '$', '\\$', $string );
00168 return $string;
00169 }
00170
00175 static function explode( $separator, $subject ) {
00176 if ( substr_count( $subject, $separator ) > 1000 ) {
00177 return new ExplodeIterator( $separator, $subject );
00178 } else {
00179 return new ArrayIterator( explode( $separator, $subject ) );
00180 }
00181 }
00182 }
00183
00188 class Replacer {
00189 function cb() {
00190 return array( &$this, 'replace' );
00191 }
00192 }
00193
00197 class RegexlikeReplacer extends Replacer {
00198 var $r;
00199 function __construct( $r ) {
00200 $this->r = $r;
00201 }
00202
00203 function replace( $matches ) {
00204 $pairs = array();
00205 foreach ( $matches as $i => $match ) {
00206 $pairs["\$$i"] = $match;
00207 }
00208 return strtr( $this->r, $pairs );
00209 }
00210
00211 }
00212
00216 class DoubleReplacer extends Replacer {
00217 function __construct( $from, $to, $index = 0 ) {
00218 $this->from = $from;
00219 $this->to = $to;
00220 $this->index = $index;
00221 }
00222
00223 function replace( $matches ) {
00224 return str_replace( $this->from, $this->to, $matches[$this->index] );
00225 }
00226 }
00227
00231 class HashtableReplacer extends Replacer {
00232 var $table, $index;
00233
00234 function __construct( $table, $index = 0 ) {
00235 $this->table = $table;
00236 $this->index = $index;
00237 }
00238
00239 function replace( $matches ) {
00240 return $this->table[$matches[$this->index]];
00241 }
00242 }
00243
00248 class ReplacementArray {
00249 var $data = false;
00250 var $fss = false;
00251
00256 function __construct( $data = array() ) {
00257 $this->data = $data;
00258 }
00259
00260 function __sleep() {
00261 return array( 'data' );
00262 }
00263
00264 function __wakeup() {
00265 $this->fss = false;
00266 }
00267
00271 function setArray( $data ) {
00272 $this->data = $data;
00273 $this->fss = false;
00274 }
00275
00276 function getArray() {
00277 return $this->data;
00278 }
00279
00283 function setPair( $from, $to ) {
00284 $this->data[$from] = $to;
00285 $this->fss = false;
00286 }
00287
00288 function mergeArray( $data ) {
00289 $this->data = array_merge( $this->data, $data );
00290 $this->fss = false;
00291 }
00292
00293 function merge( $other ) {
00294 $this->data = array_merge( $this->data, $other->data );
00295 $this->fss = false;
00296 }
00297
00298 function removePair( $from ) {
00299 unset($this->data[$from]);
00300 $this->fss = false;
00301 }
00302
00303 function removeArray( $data ) {
00304 foreach( $data as $from => $to )
00305 $this->removePair( $from );
00306 $this->fss = false;
00307 }
00308
00309 function replace( $subject ) {
00310 if ( function_exists( 'fss_prep_replace' ) ) {
00311 wfProfileIn( __METHOD__.'-fss' );
00312 if ( $this->fss === false ) {
00313 $this->fss = fss_prep_replace( $this->data );
00314 }
00315 $result = fss_exec_replace( $this->fss, $subject );
00316 wfProfileOut( __METHOD__.'-fss' );
00317 } else {
00318 wfProfileIn( __METHOD__.'-strtr' );
00319 $result = strtr( $subject, $this->data );
00320 wfProfileOut( __METHOD__.'-strtr' );
00321 }
00322 return $result;
00323 }
00324 }
00325
00335 class ExplodeIterator implements Iterator {
00336
00337 var $subject, $subjectLength;
00338
00339
00340 var $delim, $delimLength;
00341
00342
00343 var $curPos;
00344
00345
00346 var $endPos;
00347
00348
00349 var $current;
00350
00354 function __construct( $delim, $s ) {
00355 $this->subject = $s;
00356 $this->delim = $delim;
00357
00358
00359 $this->subjectLength = strlen( $s );
00360 $this->delimLength = strlen( $delim );
00361
00362 $this->rewind();
00363 }
00364
00365 function rewind() {
00366 $this->curPos = 0;
00367 $this->endPos = strpos( $this->subject, $this->delim );
00368 $this->refreshCurrent();
00369 }
00370
00371
00372 function refreshCurrent() {
00373 if ( $this->curPos === false ) {
00374 $this->current = false;
00375 } elseif ( $this->curPos >= $this->subjectLength ) {
00376 $this->current = '';
00377 } elseif ( $this->endPos === false ) {
00378 $this->current = substr( $this->subject, $this->curPos );
00379 } else {
00380 $this->current = substr( $this->subject, $this->curPos, $this->endPos - $this->curPos );
00381 }
00382 }
00383
00384 function current() {
00385 return $this->current;
00386 }
00387
00388 function key() {
00389 return $this->curPos;
00390 }
00391
00392 function next() {
00393 if ( $this->endPos === false ) {
00394 $this->curPos = false;
00395 } else {
00396 $this->curPos = $this->endPos + $this->delimLength;
00397 if ( $this->curPos >= $this->subjectLength ) {
00398 $this->endPos = false;
00399 } else {
00400 $this->endPos = strpos( $this->subject, $this->delim, $this->curPos );
00401 }
00402 }
00403 $this->refreshCurrent();
00404 return $this->current;
00405 }
00406
00407 function valid() {
00408 return $this->curPos !== false;
00409 }
00410 }
00411