How to preg_replace_all in the large amount of data

Serhii Matrunchyk picture Serhii Matrunchyk · Nov 2, 2014 · Viewed 14.4k times · Source

I just making an upgrade script for replacing all the MODX snippet tags to a newer format:

Old one: [~123~]

New one: [[~123]]

(of course, 123 - it is an example — there are a lot of different numbers)

Also I wanted to replace all the custom old snippets to a newer ones:

Old snippet format:

[!MySnippetName? &param1=`value1` &param2=`value2` !]

New one:

[[!MySnippetName? &param1=`value1` &param2=`value2` ]]

(of course, &param1=value1 &param2=value2 is just an example and it is different in real snippets)

How to use a preg_replace function to make a global replacements? I supposed to create like this:

$doc[ 'content' ] = $this->preg_replace_all(
        array(
                '/\[~([0-9]+)~\]/',
                '\[!LatestUpdates(.*)!\]',
                '\[!ArticleSummary(.*)!\]',
            ), array(
                '[[~\1]]',
                '[[!LatestUpdates\1]]',
                '[[!ArticleSummary\1]]',
            ),
            $doc[ 'content' ]
        );

preg_replace_all function:

private function preg_replace_all( $find, $replacement, $s )
{
    while(preg_match($find, $s)) {
        $s = preg_replace($find, $replacement, $s);
    }
    return $s;
}

But it doesn't work. Please help to figure out the problem. Thanks in advance.

Answer

Casimir et Hippolyte picture Casimir et Hippolyte · Nov 2, 2014

The preg_replace function performs already a global replacement and since only matching substrings are replaced, you don't need to test the existence of these substrings with preg_match.

You can reduce the three patterns to only one pattern:

$pattern = '/\[(?|(!LatestUpdates|!ArticleSummary)(.*?)!]|(~)([0-9]+)~])/';
$replacement = '[[$1$2]]';
$doc['content'] = preg_replace($pattern, $replacement, $doc['content']);

This pattern use the branch reset feature that allows capture groups to have the same number in each alternative.

If it is possible (if it doesn't change tags you want to preserve), you can do the same with strtr:

$doc['content'] = strtr($doc['content'], array('[!'=>'[[!', '[~'=>'[[~', '~]'=>']]', '!]'=>']]'));

Note that if you can use it, don't hesitate since this way is much faster.