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

Я недавно читал эту тему , по некоторым из худших методов 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 ).

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

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

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

 <?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. Вам просто нужно использовать его осторожно. Санируйте эти массивы, прежде чем передавать их для извлечения () и убедитесь, что они не переопределяют ваши переменные. Не забывайте, что эта функция также принимает еще несколько аргументов! Используя второй и третий аргументы, вы можете контролировать поведение, если происходит столкновение. Вы можете переопределить, пропустить или добавить префикс. 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 ($ _ POST) не является хорошей идеей в любом случае, даже если вы знаете, что делаете. Тем не менее, он имеет свои применения, когда вы делаете такие вещи, как выставляете переменные шаблону вида или что-то подобное. В основном, используйте его только в том случае, если вы очень уверены в том, что у вас есть веские основания для этого, и понимаете, как использовать параметр типа extract, если у вас есть идея передать что-то сумасшедшее, как $ _POST.

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

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

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

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

 //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> с //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-кодом.

Риск такой же, как у register_globals. Вы разрешаете злоумышленнику устанавливать переменные в вашем скрипте, просто путем вмешательства в запрос.

Никогда не извлекайте ($ _ 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>' )); 

Как заметил кто-то в другом потоке, это более безопасный способ использовать экстракт , только позволяя ему извлекать переменные, которые вы указываете, а не все, что содержит массив.

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

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

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

Я использовал экстракт в моделях CodeIgniter с переключателем EXTR_IF_EXISTS и ограничил количество переменных, он работает очень хорошо.

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

 #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_EXISTS и EXTR_PREFIX_ALL .

Если вы используете его правильно, безопасно использовать

Еще одна веская причина, по которой больше не использовать extract (), заключается в том, что в PHP есть импульс для использования HHVM, который утверждает, что PHP примерно в 10 раз быстрее. Facebook (кто это сделал) использует его, Wikipedia на нем, и WordPress, по слухам, смотрит на него.

HHVM не позволяет извлекать ()

Это все еще вроде альфа, поэтому это не самая большая проблема