Merge "Cap 'days' param in RC/Watchlist at $wgRCMaxAge"
[lhc/web/wiklou.git] / includes / libs / StringUtils.php
index 6b10c09..9638706 100644 (file)
@@ -54,6 +54,59 @@ class StringUtils {
                        ( $newPHP || preg_match( "/\xf4[\x90-\xbf]|[\xf5-\xff]/S", $value ) === 0 );
        }
 
+       /**
+        * Explode a string, but ignore any instances of the separator inside
+        * the given start and end delimiters, which may optionally nest.
+        * The delimiters are literal strings, not regular expressions.
+        * @param string $startDelim Start delimiter
+        * @param string $endDelim End delimiter
+        * @param string $separator Separator string for the explode.
+        * @param string $subject Subject string to explode.
+        * @param bool $nested True iff the delimiters are allowed to nest.
+        * @return ArrayIterator
+        */
+       static function delimiterExplode( $startDelim, $endDelim, $separator,
+               $subject, $nested = false ) {
+               $inputPos = 0;
+               $lastPos = 0;
+               $depth = 0;
+               $encStart = preg_quote( $startDelim, '!' );
+               $encEnd = preg_quote( $endDelim, '!' );
+               $encSep = preg_quote( $separator, '!' );
+               $len = strlen( $subject );
+               $m = [];
+               $exploded = [];
+               while (
+                       $inputPos < $len &&
+                       preg_match(
+                               "!$encStart|$encEnd|$encSep!S", $subject, $m,
+                               PREG_OFFSET_CAPTURE, $inputPos
+                       )
+               ) {
+                       $match = $m[0][0];
+                       $matchPos = $m[0][1];
+                       $inputPos = $matchPos + strlen( $match );
+                       if ( $match === $separator ) {
+                               if ( $depth === 0 ) {
+                                       $exploded[] = substr(
+                                               $subject, $lastPos, $matchPos - $lastPos
+                                       );
+                                       $lastPos = $inputPos;
+                               }
+                       } elseif ( $match === $startDelim ) {
+                               if ( $depth === 0 || $nested ) {
+                                       $depth++;
+                               }
+                       } else {
+                               $depth--;
+                       }
+               }
+               $exploded[] = substr( $subject, $lastPos );
+               // This method could be rewritten in the future to avoid creating an
+               // intermediate array, since the return type is just an iterator.
+               return new ArrayIterator( $exploded );
+       }
+
        /**
         * Perform an operation equivalent to `preg_replace()`
         *
@@ -115,6 +168,7 @@ class StringUtils {
        ) {
                $inputPos = 0;
                $outputPos = 0;
+               $contentPos = 0;
                $output = '';
                $foundStart = false;
                $encStart = preg_quote( $startDelim, '!' );
@@ -222,7 +276,7 @@ class StringUtils {
 
                // Replace instances of the separator inside HTML-like tags with the placeholder
                $replacer = new DoubleReplacer( $separator, $placeholder );
-               $cleaned = StringUtils::delimiterReplaceCallback( '<', '>', $replacer->cb(), $text );
+               $cleaned = self::delimiterReplaceCallback( '<', '>', $replacer->cb(), $text );
 
                // Explode, then put the replaced separators back in
                $items = explode( $separator, $cleaned );
@@ -249,7 +303,7 @@ class StringUtils {
 
                // Replace instances of the separator inside HTML-like tags with the placeholder
                $replacer = new DoubleReplacer( $search, $placeholder );
-               $cleaned = StringUtils::delimiterReplaceCallback( '<', '>', $replacer->cb(), $text );
+               $cleaned = self::delimiterReplaceCallback( '<', '>', $replacer->cb(), $text );
 
                // Explode, then put the replaced separators back in
                $cleaned = str_replace( $search, $replace, $cleaned );