Replace preg_replace() e modifier with preg_replace_callback
In a regular expression, you can "capture" parts of the matched string with (brackets)
; in this case, you are capturing the (^|_)
and ([a-z])
parts of the match. These are numbered starting at 1, so you have back-references 1 and 2. Match 0 is the whole matched string.
The /e
modifier takes a replacement string, and substitutes backslash followed by a number (e.g. \1
) with the appropriate back-reference - but because you're inside a string, you need to escape the backslash, so you get '\\1'
. It then (effectively) runs eval
to run the resulting string as though it was PHP code (which is why it's being deprecated, because it's easy to use eval
in an insecure way).
The preg_replace_callback
function instead takes a callback function and passes it an array containing the matched back-references. So where you would have written '\\1'
, you instead access element 1 of that parameter - e.g. if you have an anonymous function of the form function($matches) { ... }
, the first back-reference is $matches[1]
inside that function.
So a /e
argument of
'do_stuff(\\1) . "and" . do_stuff(\\2)'
could become a callback of
function($m) { return do_stuff($m[1]) . "and" . do_stuff($m[2]); }
Or in your case
'strtoupper("\\2")'
could become
function($m) { return strtoupper($m[2]); }
Note that $m
and $matches
are not magic names, they're just the parameter name I gave when declaring my callback functions. Also, you don't have to pass an anonymous function, it could be a function name as a string, or something of the form array($object, $method)
, as with any callback in PHP, e.g.
function stuffy_callback($things) {
return do_stuff($things[1]) . "and" . do_stuff($things[2]);
}
$foo = preg_replace_callback('/([a-z]+) and ([a-z]+)/', 'stuffy_callback', 'fish and chips');
As with any function, you can't access variables outside your callback (from the surrounding scope) by default. When using an anonymous function, you can use the use
keyword to import the variables you need to access, as discussed in the PHP manual. e.g. if the old argument was
'do_stuff(\\1, $foo)'
then the new callback might look like
function($m) use ($foo) { return do_stuff($m[1], $foo); }
Gotchas
- Use of
preg_replace_callback
is instead of the/e
modifier on the regex, so you need to remove that flag from your "pattern" argument. So a pattern like/blah(.*)blah/mei
would become/blah(.*)blah/mi
. - The
/e
modifier used a variant ofaddslashes()
internally on the arguments, so some replacements usedstripslashes()
to remove it; in most cases, you probably want to remove the call tostripslashes
from your new callback.
Replace preg_replace through preg_replace_callback
You need to use $m[1]
to access the second element in the $m
array:
preg_replace_callback("/=([0-9A-F][0-9A-F])/", function($m) { return
chr(hexdec($m[1])); }, $line
);
See PHP demo.
Replacing preg_replace e modifier
Easily.
$str = preg_replace_callback('/(\d+);/', function($m) use ($lo) {
return code2utf($m[1],$lo);
}, $str);
The important thing here is the use ($lo)
, as it allows you to "import" the $lo
variable into your callback.
I've also cleaned up your regex - way too many backslashes ;)
Can't convert preg_replace() with modifier /e to preg_replace_callback()
Your problem is as you already know, that your variables are out of scope in your anonymous functions and since you don't know which one you will replace you can't pass them to the function, so you have to use the global
keyword, e.g.
$uri = "module/page/{#page}";
$page = 3;
$uri_to_call = $uri_rule = preg_replace_callback("/\{\#([A-Za-z_]+)\}/", function($m){
global ${$m[1]};
return ${$m[1]};
});
PHP7 - The /e modifier is no longer supported, use preg_replace_callback instead
The error message is telling you to remove the e
modifier that you've included in your new code.
/ (?<=^|[\x09\x20\x2D]). / e
^ ^------Pattern-------^ ^ ^ ---- Modifiers
| |
-------Delimiters-------
You need to remove the modifier, so preg_replace_callback('/(?<=^|[\x09\x20\x2D])./e', ...)
should be preg_replace_callback('/(?<=^|[\x09\x20\x2D])./' , ...)
.
As an aside, you aren't benefiting from using a foreach
loop in your new code. The match will always be in the second item in the array. Here's an example without using a loop:
$inputString = 'foobazbar';
$result = preg_replace_callback('/^foo(.*)bar$/', function ($matches) {
// $matches[0]: "foobazbar"
// $matches[1]: "baz"
return "foo" . strtoupper($matches[1]) . "bar";
}, $inputString);
// "fooBAZbar"
var_dump($result);
The /e modifier is deprecated, use preg_replace_callback instead of preg_replace()
You can't put the replacement function in fetch_full_ameinfo()
, because it needs to refer to the $param1
and $param2
variables, which are local to this function.
This means it needs to use eval()
in the current function (this is essentially what preg_replace()
does internally when it processes the /e
flag).
You need to change the replacement string that fetch_full_ameinfo()
creates so that it uses a variable instead of \1
, \2
, etc. to refer to the capture groups, because the callback function receives the captured matches as an array. So replace the block beginning with if (!$findonly)
with this:
if (!$findonly)
{
$ameinfo['find'][] = "~($result[findcode])~i";
$ameinfo['replace'][] = 'ame_match_bbcode($param1, $param2, \'' . $result['ameid'] . '\', \'' . ame_slasher($result['title']) . '\', ' . $result['container'] . ', \'' . ame_slasher($result['replacecode']) . '\', \'$match[1]\', \'$match[2]\', \'$match[3]\', \'$match[4]\', \'$match[5]\', \'$match[6]\')';
}
else
{
$ameinfo['find'][] = "~(\[url\]$result[findcode]\[/url\])~i";
$ameinfo['find'][] = "~(\[url=\"?$result[findcode]\"?\](.*?)\[/url\])~i";
$ameinfo['replace'][] = 'ame_match("$match[1]", "", ' . intval($result['extraction']) .', "' . ($result['embedregexp'] ? "~" . ame_slasher($result['embedregexp']) . "~sim" : "") . '", "' . ($result['validation'] ? "~" . ame_slasher($result['validation']) . "~sim" : "") . '",$ameinfo)';
$ameinfo['replace'][] = 'ame_match("$match[1]", "$match[2]", ' . intval($result['extraction']) .', "' . ($result['embedregexp'] ? "~" . ame_slasher($result['embedregexp']) . "~sim" : "") . '", "' . ($result['validation'] ? "~" . ame_slasher($result['validation']) . "~sim" : "") . '", $ameinfo)';
}
Then change your code to:
$text = preg_replace_callback($ameinfo['find'], function($match) use (&$param1, &$param2, &$ameinfo) {
return eval($ameinfo['replace']);
}, ($param2 ? $param2 : $param1), 1);
Replace deprecated preg_replace /e with preg_replace_callback
You can use an anonymous function to pass the matches to your function:
$result = preg_replace_callback(
"/\{([<>])([a-zA-Z0-9_]*)(\?{0,1})([a-zA-Z0-9_]*)\}(.*)\{\\1\/\\2\}/isU",
function($m) { return CallFunction($m[1], $m[2], $m[3], $m[4], $m[5]); },
$result
);
Apart from being faster, this will also properly handle double quotes in your string. Your current code using /e
would convert a double quote "
into \"
.
Related Topics
Converting Soap Xml Response to a PHP Object or Array
Passing PHP Variable in Onclick Function
Alert Show Up When I Refresh Page
Update Data on a Page Without Refreshing
Cron Job to Delete Files Created Before a Specific Time
General Error: 1364 Field 'User_Id' Doesn't Have a Default Value
Regex for No More Than 5 Digits or Contain String
How to Count Columns With the Same Value in a Specific Row in MySQL
How to Echo Selected Value of Dropdown in PHP
Php Warning: Mysqli_Connect(): (Hy000/2002): Connection Refused
How to Add a Space Between Every Sequence of Four Characters (Like a Credit Card Number)
How Should a Model Be Structured in MVC
Change the Maximum Upload File Size
MySQLi or Die, Does It Have to Die
How to Flatten a Multidimensional Array