redwall_hp
redwall_hp

Reputation: 1067

PHP Replace, But Alternate Replace String

Okay, here's what I'm trying to do: I'm trying to use PHP to develop what's essentially a tiny subset of a markdown implementation, not worth using a full markdown class.

I need essentially do a str_replace, but alternate the replace string for every occurrence of the needle, so as to handle the opening and closing HTML tags.

For example, italics are a pair of asterisks like *this*, and code blocks are surrounded by backticks like `this`.

I need to replace the first occurrence of a pair of the characters with the opening HTML tag corresponding, and the second with the closing tag.

Any ideas on how to do this? I figured some sort of regular expression would be involved...

Upvotes: 3

Views: 2319

Answers (3)

TRT 1968
TRT 1968

Reputation: 482

I created an alternative to str_replace, because the PHP manual for str_replace says that:

If search and replace are arrays, then str_replace() takes a value from each array and uses them to search and replace on subject.

If replace has fewer values than search, then an empty string is used for the rest of replacement values.

If search is an array and replace is a string, then this replacement string is used for every value of search. The converse would not make sense, though.

But the converse DOES make sense if the same needle appears several times in your haystack, such as '?' in a prepared statement (e.g. PHP's MySQLi extension), and you need to write a log or diagnostic report of what's going on as it runs through the parameters, substituting the parameters in the query string to make a 'cut and paste' version of the query for testing elsewhere.

Occurrences of needle are replaced left-to-right with the values in the replace array. If there are more occurrences of needle that there are replacements, it resets the replace array pointer. This means that for the OP's use, the needle would be "*", and the replacement would be an array with two values, "<I>" and "</I>".

function str_replace_seriatim(string $needle, array $replace, string $haystack) {
    $occurrences = substr_count($haystack, $needle);
    for ($i = 0; $i <= $occurrences; $i++) {
        $substitute = current($replace);
        $pos = strpos($haystack, $needle);
        if ($pos !== FALSE) $haystack = substr_replace($haystack, $substitute, $pos, strlen($needle));
        if ((next($replace) === FALSE)) reset($replace);
    }
return $haystack;
}

To do the whole lot in one function call, I suppose that one could expand on this a little, taking an array ($pincushion) of needles and a multidimensional array as the replacement, but I'm not sure if that isn't more work than just multiple function calls.

Upvotes: 1

Peter Bailey
Peter Bailey

Reputation: 105914

What you're looking for is more commonly handled by a state machine or lexer/parser.

This is ugly but it works. Catch: only for one pattern type at a time.

$input = "Here's some \\italic\\ text and even \\some more\\ wheee";

$output = preg_replace_callback( "/\\\/", 'replacer', $input );

echo $output;

function replacer( $matches )
{
    static $toggle = 0;
    if ( $toggle )
    {
        $toggle = 0;
        return "</em>";
    }
    $toggle = 1;
    return "<em>";
}

Upvotes: 4

Sasha Chedygov
Sasha Chedygov

Reputation: 130967

Personally, I'd loop through each occurrence of * or \ with a counter, and replace the character with the appropriate HTML tag based on the count (for example, if the count is even and you hit an asterisk, replace it with <em>, if it's odd then replace it with </em>, etc).

But if you're sure that you only need to support a couple simple kinds of markup, then a regular expression for each might be the easiest solution. Something like this for asterisks, for example (untested):

preg_replace('/\*([^*]+)\*/', '<em>\\1</em>', $text);

And something similar for backslashes.

Upvotes: 6

Related Questions