在PHP中使用变量变量是一种不好的做法吗?

4
例如,一个简单的MVC类型的系统:
使用.htaccess/nginx.conf将/api/class/method重写为PHP变量,然后执行以下操作:
<?php

// Set up class + method variables
$className = some_class_filter($_GET['class']);
$method = some_method_filter($_GET['method']);

// Check if class exists and execute
if(file_exists(BASE . "/controllers/" . $className . ".class.php")) {
    require BASE . "/controllers/" . $className . ".class.php";
    $$className = new $className();

    // Execute the method
    $$className->$method();
} else {
    // Spit out some error based on the problem
}

?>

这是一种极不好的做法吗?如果这是不好的做法,有人能够解释一下为什么吗?如果是的话,那么有没有更好的方法来实现我的目标?
编辑:本质上,我使用可变变量是为了扩展核心系统变得更加简单 - 例如 - 添加一个新控制器非常容易。我完全理解允许任何函数或类被实例化存在的安全风险,因此应该加入某种过滤器。
“some_filter_here”可以是允许的控制器列表 - 白名单,正如一些人在这里提到的那样。

变量变量几乎总是一个坏主意。那么用数组怎么样?https://dev59.com/RUrSa4cB1Zd3GeqPXZGh#1817945 - Matt Ball
1
我认为你的意思是 $$className = new $className(); - Lightness Races in Orbit
@LightnessRacesinOrbit - 你是正确的,感谢指出这一点。 - Jonathan Coe
3个回答

6

是的,这是相当不好的实践。你需要一个变量变量来处理这种情况吗?换句话说,你需要在给定的请求中实例化多个类和方法吗?你的URI结构表明不需要。如果不需要,你可以使用:

$object = new $className();
$object->$method();

否则,您可能想要执行:
$objects = array();
$objects[$className] = new $className();
$objects[$className]->$method();

这可以避免使用变量变量污染作用域,这些变量更难以跟踪。
就给定目录中类的存在检查而言,这应该是一个足够的白名单(假设攻击者无法写入该目录)。 编辑:作为进一步的检查,您可能需要在调用方法之前考虑对对象进行 method_exists 检查。

我真的很喜欢这个,谢谢你的分享!我不知道为什么我最初没有想到。每个请求都会实例化多个对象,尽管只有一个“动态”的类,但可能会调用多个方法.. 但是多个方法更容易管理。谢谢! - Jonathan Coe

1

既然你正在编写“some_class_filter”和“some_method_filter”代码,我认为这是可以的。我还看到你有一个错误或默认处理程序,所以最终,我认为这是可以的。

我相信许多MVC框架都以类似的方式运作。


1

它们并不理想,但是按照你现在的方式使用它们也没问题。

不过,有一些要点需要注意:你的代码存在漏洞,攻击者可以通过像?class=../base这样的$_GET参数遍历你的目录。如果该文件存在,你的file_exists()调用将返回true,你的应用程序将尝试将其包含并实例化为一个类。

安全的做法是仅允许白名单中的参数为字母、数字和下划线(如果你使用下划线分隔单词,比如.php)。

此外,我更喜欢使用call_user_funccall_user_func_array的语法。在你的代码中使用这些函数,代码如下:

<?php
$class_name = $_GET['class'];
$method_name = $_GET['method'];

$parameters = $_GET;
unset($parameters['class'], $parameters['method']); // grabs any other $_GET parameters

if (file_exists(BASE.'/controllers/'.$class_name.'.class.php')) {
    require BASE.'/controllers/'.$class_name.'.class.php';
    $controller = new $class_name();
    $response = call_user_func_array(array($controller, $action_name), $parameters);
}
else {
    header('HTTP/1.1 404 Not Found');
    // ...and display an error message
}

谢谢您的输入!这很有道理,我以前没有使用过call_user_func/call_user_func_array,我会更深入地了解它。 - Jonathan Coe

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