Что не так с extract ()? - PullRequest
       29

Что не так с extract ()?

32 голосов
/ 06 мая 2009

Я недавно читал эту ветку о некоторых из худших практик PHP. Во втором ответе есть мини-дискуссия об использовании extract(), и я просто задаюсь вопросом, о чем весь гул.

Лично я использую его для разбиения заданного массива, такого как $_GET или $_POST, где я затем очищаю переменные позже, так как они были удобно названы для меня.

Это плохая практика? Какой здесь риск? Что вы думаете об использовании extract()?

Ответы [ 17 ]

60 голосов
/ 06 мая 2009

Я считаю, что это плохая практика, так как она может привести к ряду переменных, которые будущие сопровождающие (или вы сами через несколько недель) не поймете, откуда они берутся. Рассмотрим этот сценарий:

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).

36 голосов
/ 06 мая 2009

Давай сейчас. Люди обвиняют инструмент вместо пользователя.

Это все равно что говорить против unlink(), потому что вы можете удалять файлы с ним. extract() - это функция, как и любая другая, используйте ее с умом и ответственно. Но не говорите, что это плохо само по себе, это просто невежественно.

17 голосов
/ 06 мая 2009

риск заключается в следующем: не доверяйте данным от пользователей, а извлечение в текущую таблицу символов означает, что ваши переменные могут быть перезаписаны тем, что предоставляет пользователь.

<?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 перезаписываются, в результате чего ваш скрипт сначала удаляет ваши данные, а затем зависает.

9 голосов
/ 06 мая 2009

В этом нет ничего плохого. В противном случае это не будет реализовано. Многие (MVC) фреймворки используют его при передаче (назначении) переменных в Views. Вам просто нужно использовать это осторожно. Очистите эти массивы перед передачей его в extract () и убедитесь, что он не переопределяет ваши переменные. Не забывайте, что эта функция также принимает еще несколько аргументов! Используя второй и третий аргументы, вы можете контролировать поведение в случае столкновения. Вы можете переопределить, пропустить или добавить префикс. http://www.php.net/extract

5 голосов
/ 07 мая 2009

Люди спорят об экстракте, потому что он имеет потенциал , который может быть использован не по назначению. Делать что-то вроде извлечения ($ _ POST) в любом случае не очень хорошая идея, даже если вы знаете, что делаете. Тем не менее, он действительно используется, когда вы делаете такие вещи, как предоставление переменных в шаблон представления или что-то подобное. По сути, используйте его только тогда, когда вы уверены, что у вас есть для этого веские основания, и понимаете, как использовать параметр типа экстракта, если у вас возникает идея передать ему что-то сумасшедшее, например $ _POST.

5 голосов
/ 07 мая 2009

Если его не использовать осторожно, это может запутать других людей, с которыми вы работаете:

<?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"

Было бы полезно использовать при запутывании. Но я не могу пройти через "Откуда взялся этот вар?" проблема, с которой я сталкиваюсь.

4 голосов
/ 06 мая 2009

Я думаю, причина, по которой многие люди не рекомендуют его использовать, заключается в том, что при извлечении суперглобальных переменных $_GET и $_POST (даже $_REQUEST) в глобальном пространстве имен регистрируются переменные с тем же именем, что и каждый ключ в этих массивах. , который в основном эмулирует REGISTER_GLOBALS = 1.

3 голосов
/ 06 мая 2009

Если вы извлекаете функцию, переменные будут доступны только в этой области. Это часто используется в представлениях. Простой пример:

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

3 голосов
/ 06 мая 2009

Я позволю руководству по PHP говорить за меня.

Фон: extract($_REQUEST) совпадает с настройкой register_globals = On в php.ini

2 голосов
/ 29 марта 2016

Экстракт безопасен, если вы используете его безопасным образом. То, что вы хотите сделать, это отфильтровать ключи массива только к тем, которые вы собираетесь использовать, и, возможно, проверить, существуют ли все эти ключи, если ваш сценарий требует их существования.

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