我最近阅读了this thread,介绍了一些最糟糕的PHP实践。
在第二个答案中,对extract()
的使用进行了小型讨论,我只是想知道所有的愤怒是什么。
我个人用它来剪切一个给定的数组,例如$_GET
或$_POST
,然后我在那里清理变量,因为它们已经为我方便地命名了。
这是不好的做法吗?这有什么风险?您对extract()
的使用有何看法?
答案 0 :(得分:60)
我发现这只是一种不好的做法,因为它可能导致许多变量,未来的维护者(或者你自己在几周内)不知道他们来自哪里。请考虑以下情况:
extract($someArray); // could be $_POST or anything
/* snip a dozen or more lines */
echo $someVariable;
$someVariable
来自哪里?谁能说出来?
我没有看到从他们开始的数组中访问变量的问题,所以你真的需要使用extract()
为我提供一个好的案例 for 认为这是值得的。如果您真的担心输入一些额外的字符,那么就这样做:
$a = $someLongNameOfTheVariableArrayIDidntWantToType;
$a['myVariable'];
我认为这里关于安全方面的评论有些过分夸大了。该函数可以采用第二个参数,实际上可以很好地控制新创建的变量,包括不覆盖任何现有变量(EXTR_SKIP
),只覆盖现有变量(因此可以创建白名单)({{1 }},或为变量添加前缀(EXTR_IF_EXISTS
)。
答案 1 :(得分:36)
现在来吧。人们责怪工具而不是用户。
这就像与unlink()
交谈,因为你可以用它删除文件。 extract()
是一个与其他功能一样的功能,明智且负责任地使用它。但不要声称它本身不好,那只是无知。
答案 2 :(得分:17)
风险在于:不信任来自用户的数据,并提取到当前符号表意味着,您的变量可能会被用户提供的内容覆盖。
<?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被覆盖,导致您的脚本首先删除您的数据然后挂起。
答案 3 :(得分:9)
它没有任何问题。否则就不会实施。当您向视图传递(赋值)变量时,许多(MVC)框架都会使用它。你只需要仔细使用它。在将这些数组传递给extract()之前清理它们并确保它不会覆盖您的变量。别忘了这个函数还接受了一些参数! 如果发生碰撞,使用第二个和第三个参数可以控制行为。您可以覆盖,跳过或添加前缀。 http://www.php.net/extract
答案 4 :(得分:5)
人们对提取物全面了解,因为它会滥用潜力。做任何类似提取($ _ POST)的事情在任何情况下都不是一个好主意,即使你知道你在做什么。但是,当您执行诸如将变量暴露给视图模板或类似的东西时,它确实具有它的用途。基本上,只有当你非常确定你有充分理由这样做时才使用它,并且如果你想要把像$ _POST这样疯狂的东西传递给它,那么就要理解如何使用extract类型参数。
答案 5 :(得分:5)
如果不小心使用,可能会使你工作的其他人感到困惑:
<?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"
在混淆中使用会很有用。但我无法克服“变量来自哪里?”我遇到的问题。
答案 6 :(得分:4)
我猜很多人不建议使用它的原因是提取$_GET
和$_POST
(偶数$_REQUEST
)superglobals在全局命名空间中注册变量同名作为这些数组中的每个键,基本上模拟REGISTER_GLOBALS = 1。
答案 7 :(得分:3)
如果在函数中提取,则变量仅在该范围内可用。这通常用在视图中。简单的例子:
//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。
您还可以在include之后添加 $ this-&gt; outVariables = get_defined_vars(); 以运行具有特定变量的代码,并获取这些变量以使用旧的PHP代码。
答案 8 :(得分:3)
答案 9 :(得分:2)
只要您以安全的方式使用,提取物就是安全的。你想要做的是将数组的键过滤到你想要使用的键,如果你的场景需要它们,可以检查所有这些键是否存在。
#Extract only the specified keys.
$extract=array_intersect_key(
get_data()
,$keys=array_flip(['key1','key2','key3','key4','key5'])
);
#Make sure all the keys exist.
if ($missing=array_keys(array_diff_key($keys,$extract))) {
throw new Exception('Missing variables: '.implode(', ',$missing));
}
#Everything is good to go, you may proceed.
extract($extract);
或
#If you don't care to check that all keys exist, you could just do this.
extract(array_intersect_key(
get_data()
,array_flip(['key1','key2','key3','key4','key5'])
));
答案 10 :(得分:1)
永远不要在全局范围内提取($ _ 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>'
));
答案 11 :(得分:1)
风险与register_globals相同。您可以让攻击者在脚本中设置变量,只需篡改请求即可。
答案 12 :(得分:1)
有人在另一个帖子here is a safer way to use extract中注明,只允许它提取您指定的变量,而不是数组包含的所有内容。
这有双重目的,即记录变量的来源,因此跟踪变量不会那么困难。
答案 13 :(得分:1)
每种方法的使用都可能导致某些情况,它可能成为应用程序的失败点。 我个人认为extract()不应该用于用户输入(这是不可预测的)和未经过清理的数据。
即使CodeIgniter核心代码使用提取,因此如果数据被清理并处理得当,使用该方法一定不会有任何损害。
我使用了带有EXTR_IF_EXISTS开关的CodeIgniter模型中的extract并限制了变量的数量,它运行得很好。
答案 14 :(得分:0)
请注意,如果您正在处理用户数据(如请求结果),则extract()
不安全,因此最好将此函数与标记EXTR_IF_EXISTS
和EXTR_PREFIX_ALL
一起使用。
如果使用正确,可以安全使用
答案 15 :(得分:0)
仅对先前的答案进行一些说明... extract()没什么问题,只要您正确过滤输入(如其他人所述)即可;否则,您可能会遇到诸如此类的巨大安全问题:
<?php
// http://foobar.doo?isLoggedIn=1
$isLoggedIn = (new AdminLogin())->isLoggedIn(); // Let's assume this returns FALSE
extract($_GET);
if ($isLoggedIn) {
echo "Okay, Houston, we've had a problem here.";
} else {
echo "This is Houston. Say again, please.";
}
答案 16 :(得分:0)
添加到@user10306 我想出了一个简单的函数来安全地加载 $_POST 变量。它只提取指定的变量
function diffArr(array $input, array $allowed) {
foreach ($input as $key => $v)
if(!in_array($key, $allowed)) unset($input[$key]);
return $input;
}
extract(diffArr($_POST, ['title', 'tagline', 'content', 'user_id']));
答案 17 :(得分:-1)
不再使用extract()的另一个好理由是PHP中有动力使用HHVM,它声称PHP的速度提高了大约10倍。 Facebook(制作它)正在使用它,维基百科正在使用它,而传言WordPress正在研究它。
它仍然是一种阿尔法,所以它不是最大的问题