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 matchedcreate_function[^\(]*\(.*?
: matchcreate_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 tocreate_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 acreate_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:$arg1
, if the argument was delimited using single quotes, an empty string if double quotes were used4
: The first argument, if it was delimited using double quotes, empty string if single quotes were used5
: The second argument without quotes, ie: the function body
More info:
The regex101 breakdown of this regex can be found here