БлогNot. PHP: динамическая callback-функция?

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 [1408 просмотров]


теги: программирование учебное php безопасность

К этой статье пока нет комментариев, Ваш будет первым