extract()有什么问题?

时间:2009-05-06 12:51:14

标签: php arrays extract

我最近阅读了this thread,介绍了一些最糟糕的PHP实践。 在第二个答案中,对extract()的使用进行了小型讨论,我只是想知道所有的愤怒是什么。

我个人用它来剪切一个给定的数组,例如$_GET$_POST,然后我在那里清理变量,因为它们已经为我方便地命名了。

这是不好的做法吗?这有什么风险?您对extract()的使用有何看法?

18 个答案:

答案 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)

我会让PHP manual为我说话。

背景:extract($_REQUEST)与在php.ini

中设置register_globals = On相同

答案 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_EXISTSEXTR_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正在研究它。

HHVM doesn't allow extract()

它仍然是一种阿尔法,所以它不是最大的问题