extract()有什么问题?

我最近正在阅读这个主题 ,讨论一些最糟糕的PHP实践。 在第二个答案有一个关于使用extract()的迷你讨论,而我只是想知道什么是所有的怒气。

我个人用它来砍掉一个给定的数组,例如$_GET$_POST ,之后我将这些variables清理干净,因为它们已经方便地命名了。

这是不好的做法? 这里有什么风险? 你对extract()的使用有什么想法?

我发现,这只是一个不好的做法,它可能会导致一些变数,未来的维护者(或者你几个星期后)不知道他们来自哪里。 考虑这种情况:

 extract($someArray); // could be $_POST or anything /* snip a dozen or more lines */ echo $someVariable; 

$someVariable从哪里来? 谁能说出来?

我没有看到从他们开始的数组内访问variables的问题,所以你真的需要提出一个很好的情况下使用extract()给我认为这是值得的。 如果你真的关心input一些额外的字符,那么只要这样做:

 $a = $someLongNameOfTheVariableArrayIDidntWantToType; $a['myVariable']; 

我认为这里关于安全方面的评论有些夸张。 该函数可以接受第二个参数,它实际上可以很好地控制新创build的variables,包括不覆盖任何现有variables( EXTR_SKIP ),只覆盖现有variables(因此您可以创build白名单)( EXTR_IF_EXISTS ),或者添加前缀variables( EXTR_PREFIX_ALL )。

来吧。 人们责怪工具,而不是用户。

这就像谈论unlink()因为你可以用它删除文件。 extract()是一个像其他任何函数,明智和负责任地使用它。 但是不要说它本身就是坏的,那只是无知而已。

风险是:不信任用户的数据,并且提取到当前的符号表意味着,您的variables可能被用户提供的东西覆盖。

 <?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)框架在将variables传递给Views时使用它。 你只需要小心使用它。 在传递给extract()之前清理这些数组,并确保它不覆盖你的variables。 不要忘记,这个函数也接受一些更多的参数! 使用第二个和第三个参数可以控制发生碰撞时的行为。 您可以覆盖,跳过或添加前缀。 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" 

在混淆中使用会很有用。 但是我无法克服“这个问题从哪里来?” 我遇到的问题。

人们对于提取物有所了解,因为它有可能被滥用。 不pipe怎样,做一些类似extract($ _ POST)的事情都不是一个好主意,即使你知道自己在做什么。 但是,当你将variables暴露给视图模板或类似的东西时,它确实有用。 基本上,只有当你非常确定你有足够的理由这样做的时候才使用它,并且理解如何使用提取types参数,如果你想把一些像$ _POST这样疯狂的东西传递给它。

我猜很多人不推荐使用它的原因是,提取$_GET$_POST (甚至$_REQUEST )超级全局variables在全局名称空间中注册与这些数组中的每个键名称相同的variables,这基本上是模拟REGISTER_GLOBALS = 1。

我将让PHP手册为我说话。

背景: extract($_REQUEST)与php.ini中的register_globals = On相同

如果你在一个函数中提取,variables将只在该范围内可用。 这通常用在视图中。 简单的例子:

 //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> 

使用一些替代目录,检查文件是否存在以及定义variables和方法 – 几乎复制了Zend_View。

你也可以添加$ this-> outVariables = get_defined_vars(); 在包含运行特定variables的代码之后,得到这些用于旧php代码的结果。

风险与register_globals相同。 您只需通过篡改请求就可以让攻击者在您的脚本中设置variables。

切勿在全局范围内提取($ _ 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>' )); 

正如有人在不同的线程中指出的那样, 这里使用提取是一种更安全的方式 ,只允许它提取指定的variables,而不是数组包含的所有内容。

这有两个目的,logging哪些variables是从它出来的,所以跟踪一个variables不会那么困难。

每种方法的使用可能会导致一些条件,可能是应用程序的失败点。 我个人觉得extract()不应该被用于用户input(这是不可预测的)和未被清理的数据。

即使CodeIgniter的核心代码也使用提取,所以如果数据被消毒和处理的话,使用这个方法不会有什么坏处。

我已经使用EXTR_IF_EXISTS开关在CodeIgniter模型中提取了限制variables的数量,它工作得很好。

只要您以安全的方式使用,提取物就是安全的。 你想要做的是过滤数组的键只有你打算使用的,也许检查所有这些键存在,如果你的scheme需要他们的存在。

 #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']) )); 

请注意,如果您正在处理用户数据(如请求的结果),则extract()不安全,所以最好使用带有标志EXTR_IF_EXISTSEXTR_PREFIX_ALL此函数。

如果你使用正确的话,可以安全使用

不再使用extract()的一个额外的好理由是,在PHP中使用HHVM有一个势头,声称使PHP快10倍。 脸书(谁做的)正在使用它,维基百科是在上面,WordPress传闻是在看它。

HHVM不允许extract()

这仍然是一个阿尔法,所以它不是最大的关注