以下列方式在php中使用变量变量是不好的做法吗?

时间:2012-01-03 17:10:39

标签: php class variables variable-variables

例如,一个简单的mvc类型系统:

使用.htaccess / nginx.conf将

/ api / class /方法重写为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'可以是允许的控制器列表 - 白名单,正如这里提到的那样。

3 个答案:

答案 0 :(得分:6)

是的,这是一种相当不好的做法。您是否需要该实例的变量变量?换句话说,你需要不止一个课程吗?要在给定请求中实例化的方法?你的URI结构不是。如果没有,你可以使用:

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

否则,您可能想要这样做:

$objects = array();
$objects[$className] = new $className();
$objects[$className]->$method();

这可以避免使用变量变量来控制范围,这些变量很难跟踪。

只要在给定目录中存在对您的类的检查,这应该是一个足够的白名单(假设攻击者无法写入该目录)。

编辑:作为进一步检查,您可能需要在调用方法之前考虑检查对象上的method_exists

答案 1 :(得分:1)

由于您正在编写“some_class_filter”和“some_method_filter”代码,我会说没关系。您也看到了错误或默认处理程序,所以最后我说它没问题。

我相信很多MVC框架无论如何都以类似的方式运作。

答案 2 :(得分:1)

他们不可取,但你可以如何使用它们。

但需要指出几点:您的代码确实存在一个漏洞,攻击者可以使用$_GET参数遍历您的目录,例如?class=../base。如果该文件存在,您的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
}