最近我在阅读这个帖子,其中讨论了一些 PHP 最糟糕的实践。
在第二个回答中,有一个小讨论关于使用extract()
,我想知道所有的争议是关于什么的。
我个人使用它来分解给定的数组,如 $_GET
或 $_POST
,然后稍后再对变量进行清理,因为它们已经很方便地为我命名了。
这是不好的做法吗?这里面存在什么风险?您对使用extract()
的看法是什么?
我发现这样做只是不好的做法,因为它可能会导致许多变量,未来维护者(或几周后的自己)不知道它们来自哪里。考虑以下情况:
extract($someArray); // could be $_POST or anything
/* snip a dozen or more lines */
echo $someVariable;
$someVariable
变量是从哪里来的?有什么方法可以确定吗?
我认为从数组中访问变量并不是问题,所以你需要给出一个充分的理由去使用extract()
。如果你真的在意打一些额外的字符,那就这样做:
$a = $someLongNameOfTheVariableArrayIDidntWantToType;
$a['myVariable'];
我认为这里关于安全方面的评论有点夸张。该函数可以接受第二个参数,实际上可以对新创建的变量进行相当好的控制,包括不覆盖任何现有变量(EXTR_SKIP
),仅覆盖现有变量(因此您可以创建白名单)(EXTR_IF_EXISTS
),或向变量添加前缀(EXTR_PREFIX_ALL
)。
extract
函数也非常有用,您可以知道每个列应该包含什么内容,每一行/列作为值数组读入。但是,出于@nickf已经描述的原因,我通常避免使用它。简而言之:伴随着强大的功能就有伟大的责任。 - Sean the Bean现在来说,人们总是责怪工具而不是使用者。
这就像反对unlink()
因为你可以用它删除文件一样。extract()
就像任何其他函数一样,请明智和负责任地使用它。但是不要声称它本身就是不好的,那只是无知。
extract()
和 varvars(因为它们是相关的)很棘手。它们会在程序中神奇地引入难以跟踪的变量。如果你正在使用它们,为什么不直接使用关联数组呢?这样更加清晰;你只有一个入口点,而不是将局部作用域视为一个入口点。 - Dan Lugg$a
到 $zz
这样命名的变量的代码质量。我的意思是,传统继承下来的代码可能有点乱,但我宁愿用最合理的方式解决这个问题并彻底搞定(例如,将获取值的“捕获”封装到某个容器中,并通过一个明智的 API 公开它们)。神奇的可用变量只会带来麻烦,无论是你还是别人写的代码。就背景而言,我对超全局变量也有类似的看法。 - Dan Luggextract
的额外facet模式即可。不仅是最干净的,而且最便宜的(例如,每月2000美元分解为每小时10美元),并且最可靠。是否有比我上面提到的解决方案更干净或更便宜或更可靠的替代方案? - Pacerier风险在于:不要信任来自用户的数据,而将其提取到当前符号表中意味着用户提供的内容可能会覆盖您的变量。
<?php
$systemCall = 'ls -lh';
$i = 0;
extract($_GET);
system($systemCall);
do {
print_r($data[$i];
$i++;
} while ($i != 3);
?>
(一个毫无意义的例子)
但现在一个恶意用户猜测或知道代码并调用:
yourscript.php?i=10&systemCall=rm%20-rf
替代
yourscript.php?data[]=a&data[]=b&data[]=c
现在,$systemCall和$i被覆盖了,导致您的脚本首先删除数据,然后挂起。
这个函数本身没有问题,否则就不会被实现了。许多(MVC)框架在向视图(Views)传递(分配)变量时使用它。你只需要小心使用,对传递给extract()的数组进行清理并确保它不会覆盖你的变量。不要忘记该函数还接受几个参数! 使用第二个和第三个参数,您可以控制冲突发生时的行为。您可以覆盖、跳过或添加前缀。 http://www.php.net/extract
如果不小心使用,它会让你的同事感到困惑,请考虑以下几点:
<?php
$array = array('huh' => 'var_dump', 'whatThe' => 'It\'s tricky!', 'iDontGetIt' => 'This Extract Function');
extract($array);
$huh($whatThe, $iDontGetIt);
?>
产出:
string(12) "It's tricky!"
string(21) "This Extract Function"
这可以用于混淆代码。但是我遇到了“那个变量从哪里来?”的问题,无法解决。
人们对于提取(extract)的使用有些过于激动,因为它有被滥用的潜在风险。无论什么情况下,像 extract($_POST) 这样的做法都不是一个好主意,即使你知道自己在做什么。然而,在将变量暴露给视图模板或类似情况下,它确实有其用途。基本上,只有在非常确定你有充分理由这样做,并且了解如何使用提取类型参数时,才应该使用它,以防止传递像 $_POST 这样的疯狂数据。
我猜很多人不建议使用它的原因是因为抽取$_GET
和$_POST
(甚至包括$_REQUEST
)超全局变量会在全局命名空间中注册与这些数组中每个键相同名称的变量,这基本上是模拟了REGISTER_GLOBALS = 1。
//View.php
class View {
function render($filename = null) {
if ($filename !== null) {
$this->filename = $filename;
}
unset($filename);
extract($this->variables);
ob_start();
$this->returned = include($this->dir . $this->filename);
return ob_get_clean();
}
}
//test.php
$view = new View;
$view->filename = 'test.phtml';
$view->dir = './';
$view->variables = array('test' => 'tset');
echo $view->render('test.phtml');
var_dump($view->returned);
//test.phtml
<p><?php echo $test; ?></p>
通过一些替代目录,检查文件是否存在以及定义变量和方法,您几乎已经复制了Zend_View。
您还可以在包含后添加$this->outVariables = get_defined_vars();,以使用特定变量运行代码并获取这些变量的结果,以供旧的php代码使用。
不要在全局范围内使用extract($_GET)。除此之外,它还有其用途,比如调用一个可能有很多可选参数的函数。
这对WordPress开发人员应该看起来有点熟悉:
function widget (Array $args = NULL)
{
extract($args);
if($before_widget) echo $before_widget;
// do the widget stuff
if($after_widget) echo $after_widget;
}
widget(array(
'before_widget' => '<div class="widget">',
'after_widget' => '</div>'
));
register_globals
,因为它会导致安全问题,而extract()
函数则模拟了这种已经被弃用/移除的行为。(如果你有一些旧的代码库不想升级,那么了解这点还是很有好处的。) - TecBrat