PHP: динамическая callback-функция?
Сделать обычный "универсальный табулятор функций" на PHP так же легко, как и в Mathcad - просто передавая нужную функцию в качестве аргумента функции табулирования:
<?php function tab ($a, $b, $dx, $f, $prec = 3) { echo "<pre>\n"; for ($x = $a; $x <= $b; $x+=$dx) echo number_format($x,$prec).' '.number_format($f($x),$prec)."<br>\n"; echo "</pre>\n"; } tab (0, pi(), pi()/10, function ($x) { return sin($x); } ); ?>
Как быть, если выражение для callback-функции, которую мы табулируем, должно быть не задано в коде, а передано в скрипт откуда-то извне, пусть даже прочитано из файла или введено юзером?
Возможно, например, такое решение:
<?php function tab ($a, $b, $dx, $f, $prec = 3) { echo "<pre>\n"; for ($x = $a; $x <= $b; $x+=$dx) echo number_format($x,$prec).' '.number_format($f($x),$prec)."<br>\n"; echo "</pre>\n"; } $expr = 'max(exp(x),pow(x,2))'; //на самом деле, выражение получено откуда-то извне $srch = array ( 'X', 'x', 'e$xp', 'ma$x'); //списки можно пополнить именами $repl = array ('$x', '$x', 'exp', 'max'); //других стандартных функций с 'x' $GLOBALS['expr'] = str_replace($srch, $repl, $expr); tab (0, 1, 0.1, function ($x) { $f = preg_replace( '/^\((\+|\-)?(\d+\.?|\.\d+|\d+\.\d+)([eE](\+|\-)?\d+)?\)$/', '('.$x.')', $GLOBALS['expr']); $r = eval('return '.$f.';'); return $r; }); ?>
Предполагается, что единственный аргумент функции называется x
, а в остальном выражение записано по правилам PHP, например, max(exp(x),pow(x,2))
или sin(x*x+1)-abs(x)
Я не стал составлять километровую регулярку для того, чтобы заменить x
на $x
более тщательно - ведь он может быть указан не только в круглых скобках как аргумент функции, но и быть вторым или третьим в списке после запятой, иметь перед собой или после себя лишние разделители и т.п. Проще сделать замену везде а потом отменить обратно в тех лексемах, где x
является частью имени функции (в примере такими лексемами указаны только exp
и max
).
На практике всегда нужно помнить про крайнюю опасность применения eval, особенно к данным, полученным от пользователя и неидеально отфильтрованным (а идеальной фильтрации не бывает).
Все листинги проверены в текущем XAMPP под Windows 10.
27.11.2019, 07:09 [1460 просмотров]