How to Fix memory leak in WordPress?

Solution:

Basically, either manually edit the files, or write a script that replaces all create_function calls and turns them into closures. Something like this should be enough to do the job:

$string = file_get_contents('path/to/po.php');


if (preg_match_all('/(\$.+?)create_function[^\(]*\(.*?(\'([^\']+)\'|"([^"]+)")\s*,\s*[\'"](.+?)[\'"]\);$/m', $string, $matches)) {
    $createCalls = count($matches[0]);
    $createStrings = [];//array of matches
    $replaceStrings = [];//array of new bits of code
    for ($i=0;$i<$createCalls;++$i) {
        //iterate over matches, build replace strings
        $createStrings[] = $matches[0][$i];//the full match
        $args = $matches[3][$i] ?: $matches[4][$i];//I'll explain this later on
        $replaceStrings[] = sprintf(
            '%sfunction(%s) {' . PHP_EOL . '    %s' . PHP_EOL . '};',
            $matches[1][$i],
            $args,
            $matches[5][$i]
        );
    }
    $string = str_replace($createStrings, $replaceStrings, $string);
    file_put_contents('path/to/po.php', $string);//write new contents to file
}

Just to show that it works Demo here

On the regex:

  • (\$.+?): match and capture the assignment: a literal $ sign, followed by any character, non-greedy because of ?. The match will stop when the next part of the pattern is matched
  • create_function[^\(]*\(.*?: match create_function literally, then any non ( character zero or more times (in case there’s a space). Then match a literal (, and zero or more chars that aren’t defined further down in the regex (again: this is mainly for spaces)
  • (\'([^\']+)\'|"([^"]+)"): Either match a single quote, and anything in between that is a single quote, or a double quote, and anything in between that isn’t a double quote. The entire match is grouped twice: once with the quotes, once without the quotes. Lookaround assertions could’ve avoided the double grouping, but this is just a regex I put together quickly, tweaking it further is something you can do if you want
  • \s*,\s*[\'"]: zero or more spaces, a comma and zero or more spaces again, followed by a single or double quote. this matches the comma-separator between the two arguments passed to create_function, we don’t need this bit, hence no capturing
  • (.+?): match and capture anything, until the rest of the pattern is matched
  • [\'"]\);$: a single or double quote, closing bracket a semi-colon and an EOL. Put simply: the last 3 visible chars of a single line of code. The closing "); literal of a create_function call. No need to capture this either, so we don’t

Now we know what our $matches array will look like:

  • 0: the full line $var = create_function('$arg1', 'return $arg1;');
  • 1: The $var = bit (the actual assignment)
  • 2: The first argument, complete with quotes: '$arg1'
  • 3: The first argument, without the quotes: $arg1if the argument was delimited using single quotes, an empty string if double quotes were used
  • 4: The first argument, if it was delimited using double quotes, empty string if single quotes were used
  • 5: The second argument without quotes, ie: the function body

More info:

The regex101 breakdown of this regex can be found here