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
00127
00128
00129
00130 static function delimiterReplace( $startDelim, $endDelim, $replace, $subject, $flags = '' ) {
00131 $replacer = new RegexlikeReplacer( $replace );
00132 return self::delimiterReplaceCallback( $startDelim, $endDelim,
00133 $replacer->cb(), $subject, $flags );
00134 }
00135
00143 static function explodeMarkup( $separator, $text ) {
00144 $placeholder = "\x00";
00145
00146
00147 $text = str_replace( $placeholder, '', $text );
00148
00149
00150 $replacer = new DoubleReplacer( $separator, $placeholder );
00151 $cleaned = StringUtils::delimiterReplaceCallback( '<', '>', $replacer->cb(), $text );
00152
00153
00154 $items = explode( $separator, $cleaned );
00155 foreach( $items as $i => $str ) {
00156 $items[$i] = str_replace( $placeholder, $separator, $str );
00157 }
00158
00159 return $items;
00160 }
00161
00169 static function escapeRegexReplacement( $string ) {
00170 $string = str_replace( '\\', '\\\\', $string );
00171 $string = str_replace( '$', '\\$', $string );
00172 return $string;
00173 }
00174
00179 static function explode( $separator, $subject ) {
00180 if ( substr_count( $subject, $separator ) > 1000 ) {
00181 return new ExplodeIterator( $separator, $subject );
00182 } else {
00183 return new ArrayIterator( explode( $separator, $subject ) );
00184 }
00185 }
00186 }
00187
00192 class Replacer {
00193 function cb() {
00194 return array( &$this, 'replace' );
00195 }
00196 }
00197
00201 class RegexlikeReplacer extends Replacer {
00202 var $r;
00203 function __construct( $r ) {
00204 $this->r = $r;
00205 }
00206
00207 function replace( $matches ) {
00208 $pairs = array();
00209 foreach ( $matches as $i => $match ) {
00210 $pairs["\$$i"] = $match;
00211 }
00212 return strtr( $this->r, $pairs );
00213 }
00214
00215 }
00216
00220 class DoubleReplacer extends Replacer {
00221 function __construct( $from, $to, $index = 0 ) {
00222 $this->from = $from;
00223 $this->to = $to;
00224 $this->index = $index;
00225 }
00226
00227 function replace( $matches ) {
00228 return str_replace( $this->from, $this->to, $matches[$this->index] );
00229 }
00230 }
00231
00235 class HashtableReplacer extends Replacer {
00236 var $table, $index;
00237
00238 function __construct( $table, $index = 0 ) {
00239 $this->table = $table;
00240 $this->index = $index;
00241 }
00242
00243 function replace( $matches ) {
00244 return $this->table[$matches[$this->index]];
00245 }
00246 }
00247
00252 class ReplacementArray {
00253 var $data = false;
00254 var $fss = false;
00255
00260 function __construct( $data = array() ) {
00261 $this->data = $data;
00262 }
00263
00264 function __sleep() {
00265 return array( 'data' );
00266 }
00267
00268 function __wakeup() {
00269 $this->fss = false;
00270 }
00271
00275 function setArray( $data ) {
00276 $this->data = $data;
00277 $this->fss = false;
00278 }
00279
00280 function getArray() {
00281 return $this->data;
00282 }
00283
00287 function setPair( $from, $to ) {
00288 $this->data[$from] = $to;
00289 $this->fss = false;
00290 }
00291
00292 function mergeArray( $data ) {
00293 $this->data = array_merge( $this->data, $data );
00294 $this->fss = false;
00295 }
00296
00297 function merge( $other ) {
00298 $this->data = array_merge( $this->data, $other->data );
00299 $this->fss = false;
00300 }
00301
00302 function removePair( $from ) {
00303 unset($this->data[$from]);
00304 $this->fss = false;
00305 }
00306
00307 function removeArray( $data ) {
00308 foreach( $data as $from => $to )
00309 $this->removePair( $from );
00310 $this->fss = false;
00311 }
00312
00313 function replace( $subject ) {
00314 if ( function_exists( 'fss_prep_replace' ) ) {
00315 wfProfileIn( __METHOD__.'-fss' );
00316 if ( $this->fss === false ) {
00317 $this->fss = fss_prep_replace( $this->data );
00318 }
00319 $result = fss_exec_replace( $this->fss, $subject );
00320 wfProfileOut( __METHOD__.'-fss' );
00321 } else {
00322 wfProfileIn( __METHOD__.'-strtr' );
00323 $result = strtr( $subject, $this->data );
00324 wfProfileOut( __METHOD__.'-strtr' );
00325 }
00326 return $result;
00327 }
00328 }
00329
00339 class ExplodeIterator implements Iterator {
00340
00341 var $subject, $subjectLength;
00342
00343
00344 var $delim, $delimLength;
00345
00346
00347 var $curPos;
00348
00349
00350 var $endPos;
00351
00352
00353 var $current;
00354
00358 function __construct( $delim, $s ) {
00359 $this->subject = $s;
00360 $this->delim = $delim;
00361
00362
00363 $this->subjectLength = strlen( $s );
00364 $this->delimLength = strlen( $delim );
00365
00366 $this->rewind();
00367 }
00368
00369 function rewind() {
00370 $this->curPos = 0;
00371 $this->endPos = strpos( $this->subject, $this->delim );
00372 $this->refreshCurrent();
00373 }
00374
00375
00376 function refreshCurrent() {
00377 if ( $this->curPos === false ) {
00378 $this->current = false;
00379 } elseif ( $this->curPos >= $this->subjectLength ) {
00380 $this->current = '';
00381 } elseif ( $this->endPos === false ) {
00382 $this->current = substr( $this->subject, $this->curPos );
00383 } else {
00384 $this->current = substr( $this->subject, $this->curPos, $this->endPos - $this->curPos );
00385 }
00386 }
00387
00388 function current() {
00389 return $this->current;
00390 }
00391
00392 function key() {
00393 return $this->curPos;
00394 }
00395
00396 function next() {
00397 if ( $this->endPos === false ) {
00398 $this->curPos = false;
00399 } else {
00400 $this->curPos = $this->endPos + $this->delimLength;
00401 if ( $this->curPos >= $this->subjectLength ) {
00402 $this->endPos = false;
00403 } else {
00404 $this->endPos = strpos( $this->subject, $this->delim, $this->curPos );
00405 }
00406 }
00407 $this->refreshCurrent();
00408 return $this->current;
00409 }
00410
00411 function valid() {
00412 return $this->curPos !== false;
00413 }
00414 }
00415