eval函数的替代方案是什么?

8
我在当前项目中使用eval(),用法如下:

if (class_exists($class_name)) //$class_name depends on user input
    eval($class_name.'::MyStaticMethod()');

eval() 仅在存在名为 $class_name 的类时才被执行,因此它相对安全,但我仍然认为这不是最佳解决方案。

如果没有使用 eval(),我能否实现与上述代码相同的功能?

6个回答

18

我最近回答了这个问题。我在我的答案的最后一部分完美地回答了这个问题,并且对于未来的读者来说比这里提供的答案更有用。这就是为什么我要回答自己的问题。

PHP具有避免在大多数情况下使用eval的功能:

  1. PHP是一种非常动态的语言。它能够对字符串执行以下操作:

    • 定义和/或获取变量(从PHP 4.3开始支持)。例如:

      $variableName = 'MyVariable';
      // Create new variable with the name defined in variable $variableName
      ${$variableName} = 'MyValue';
      //Outputs: string(7) "MyValue"
      var_dump($MyVariable);
      //Outputs: string(7) "MyValue"
      var_dump(${'MyVariable'});
      

      演示

    • 调用函数(从PHP 4.3开始支持)。例如:

    • // Create function with the name defined in variable $functionName
      function MyFunction($argument) {
          return 'Argument passed is: '.$argument;
      }
      
      $functionName = 'MyFunction';
      
      // Outputs:
      // string(48) "Argument passed is: Calling MyFunction directly."
      var_dump(MyFunction('Calling MyFunction directly.'));
      // Outputs:
      // string(51) "Argument passed is: Calling MyFunction with string."
      var_dump($functionName('Calling MyFunction with string.'));
      

      演示

    • 创建类的实例(自 PHP 5.0 开始支持)。例如:

    • class MyClass {
          public function __construct() {
              echo 'Constructing MyClass'."\n";
          }
      }
      
      $className = 'MyClass';
      
      $objFromString = new $className();
      // Outputs: object(MyClass)#1 (0) {}
      var_dump($objFromString);
      

      演示

    • 调用静态方法(从PHP 5.0开始支持)。例如:

    • class MyClass {
          public static function staticMethod() {
              return 'MyClass::staticMethod called';
          }
      }
      
      $staticMethodName = 'staticMethod';
      // Outputs: string(28) "MyClass::staticMethod called"
      var_dump(MyClass::$staticMethodName());
      

      演示

      从 PHP 5.3 开始,类名也可以通过字符串定义。例如:

      class MyClass {
          public static function staticMethod() {
          return 'MyClass::staticMethod called';
          }
      }
      
      $className = 'MyClass';
      $staticMethodName = 'staticMethod';
      
      var_dump($className::$staticMethodName());
      var_dump($className::staticMethod());
      

      演示

    • 调用对象的实例方法(从 PHP 5.0 开始支持)。例如:

    • class MyClass {
          public function instanceMethod() {
              return 'MyClass::instanceMethod called';
          }
      }
      
      $methodName = 'instanceMethod';
      
      $obj = new MyClass();
      // Outputs: string(30) "MyClass::instanceMethod called"
      var_dump($obj->$methodName());
      

      演示

    • 访问对象的静态和实例属性(自 PHP 5.0 起已支持)。例如:

    • class MyClass {
          public static $myStaticProperty;
          public $myInstanceProperty;
      }
      
      $staticPropertyName = 'myStaticProperty';
      $instancePropertyName = 'myInstanceProperty';
      
      MyClass::${$staticPropertyName} = 'my static value';
      $obj = new MyClass();
      $obj->{$instancePropertyName} = 'my instance value';
      
      var_dump(MyClass::${$staticPropertyName});
      var_dump($obj->{$instancePropertyName});
      

      演示

  2. PHP有两个函数:call_user_funccall_user_func_array,用于动态调用函数/方法。这两个函数都有完善的文档,所以我这里不再详细介绍。
  3. 即使以上内容仍然不足够,PHP 5也提供了强大的Reflection API。不幸的是,文档中很少有示例,但反射是一个相当大的主题,在这里难以详细介绍。基本上,阅读一下它的工作原理后,使用反射并不是什么大问题。

2
所有这些信息(动态访问变量/函数和实例/静态成员)都是相当常见的知识,并且是 PHP 文档的一部分。将它们聚合在这里作为答案并没有什么特别有用,除非将问题本身从“eval 的替代方案是什么”更改为“PHP 提供了哪些动态访问方式”。 - Mihai Stancu

6

我建议使用call_user_func

call_user_func()的替代方法是像这样调用它:

$class_and_method = 'Class::MyStaticMethod()';
$class_and_method();

5

是的:

call_user_func(array($class_name, 'MyStaticMethod'));

3

从PHP 5.3+版本开始,

$class_name::MyStaticMethod();

1

警告:

用户输入 + eval = 安全漏洞;

而且,eval 是一项昂贵的操作,需要将字符串解析为可执行格式(解析树、抽象语法树等),并执行新发现的逻辑。

你不想对每个小代码片段进行 eval。如果有东西可以让它处理,那么使用 eval,或者将该逻辑放在可重用和参数化的地方,例如函数中。

另外,从 PHP 5.4 开始

$method = array('class_name', 'method_name');
$method(); // calls class_name::method_name()

1
为了在加载后评估一些PHP代码。
我们可以使用proc_open并将一些PHP代码输入到STDIN中。
为了避免与shell和Unicode字符相关的任何问题,最好传入一个以base64编码的字符串。
请注意,PHP取决于php-cli的.ini设置。
function run($php){
  $php = base64_encode($php);
  $proc = proc_open("echo '$php' | base64 --decode | php",
    [["pipe","r"],["pipe","w"],["pipe","w"]],
    $pipes
  );
  print stream_get_contents($pipes[1]);
}

run("Hi <?php echo getenv()['USER'];?>! \n");
// Hi NVRM! 

https://www.php.net/manual/en/function.proc-open.php


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接