Когда (если вообще когда-либо) не является злом?

Я слышал много мест, что функция eval PHP часто не является ответом . В свете LSB PHP 5.3 и закрытий у нас заканчиваются причины, зависящие от eval или create_function .

Существуют ли какие- либо мыслимые случаи, когда eval является лучшим (только?) Ответом в PHP 5.3?

Этот вопрос заключается не в том, является ли eval злом вообще, как это, очевидно, нет.

Резюме ответов:

  • Оценка числовых выражений (или других «безопасных» подмножеств PHP)
  • Единичное тестирование
  • Интерактивная PHP-оболочка
  • Дезертификация доверенного var_export
  • Некоторые языки шаблонов
  • Создание backdoor для администраторов и / или хакеров
  • Совместимость с <PHP 5.3
  • Проверка синтаксиса (возможно, небезопасная)

Эрик Липперт рассчитывает на три сообщения в блоге. Это очень интересное чтение.

Насколько мне известно, следующие лишь некоторые из причин, по которым используется eval.

Например, когда вы создаете сложные математические выражения, основанные на пользовательском вводе, или когда вы сериализуете состояние объекта в строку, чтобы его можно было сохранить или передать, а затем восстановить позже.

Если вы пишете вредоносное ПО, и вы хотите усложнить жизнь системному администратору, который пытается очистить вас. Это, пожалуй, самый распространенный случай использования в моем опыте.

Основная проблема с eval – это шлюз для вредоносного кода. Таким образом, вы никогда не должны использовать его в контексте, где его можно использовать извне, например, пользовательский ввод.

Один действительный UseCase будет в Mocking Frameworks.

Пример из PHPUnit_Framework_TestCase::getMock()

 // ... some code before $mock = PHPUnit_Framework_MockObject_Generator::generate( $originalClassName, $methods, $mockClassName, $callOriginalClone, $callAutoload ); if (!class_exists($mock['mockClassName'], FALSE)) { eval($mock['code']); } // ... some code after 

В методе генерации происходит много всего. В простых условиях: PHPUnit будет принимать аргументы для generate и создания шаблона класса из него. Затем он будет eval этот шаблон класса, чтобы сделать его доступным для создания экземпляра. Дело в том, что TestDoubles будет издеваться над зависимостями в UnitTests, конечно.

Вы можете использовать eval для создания ad-hoc-классов:

 function myAutoLoad($sClassName){ # classic part if (file_exists($sClassName.'.php'){ require $sClassName.'.php'; } else { eval(" class $sClassName{ public function __call($sMethod,$aArgs){ return 'No such class: ' . $sClassName; } }"); } } 

Хотя, конечно, использование довольно ограничено (некоторые контейнеры API или, возможно, DI, платформы тестирования, ORM, которые должны иметь дело с базами данных с динамической структурой, игровыми площадками кодов)

Если вы пишете сайт, который интерпретирует и выполняет PHP-код, как интерактивная оболочка.

Я системный парень, вот и все, что у меня есть.

eval – это конструкция, которая может использоваться для проверки ошибок синтаксиса.

Скажем, у вас есть два сценария PHP:

script1.php

 <?php // This is a valid syntax $a = 1; 

script2.php

 <?php // This is an invalid syntax $a = abcdef 

Вы можете проверить наличие синтаксических ошибок с помощью eval :

 $code1 = 'return true; ?>'.file_get_contents('script1.php'); $code2 = 'return true; ?>'.file_get_contents('script2.php'); echo eval($code1) ? 'script1 has valid syntax' : 'script1 has syntax errors'; echo eval($code2) ? 'script2 has valid syntax' : 'script2 has syntax errors'; 

В отличие от php_check_syntax (который в любом случае устарел и удален) код не будет выполнен.

РЕДАКТИРОВАТЬ:

Другая (предпочтительная) альтернатива – php -l . Вы можете использовать указанное выше решение, если у вас нет доступа к командам системы () или командам оболочки.

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

Что касается темы OP: Когда (если когда-либо) eval NOT evil? eval просто не злой. Программисты злы для использования eval без причины. eval может сократить ваш код (оценка математического выражения, например).

Я обнаружил, что бывают случаи, когда большинство особенностей языка полезны. В конце концов, даже у GOTO были его сторонники . Eval используется в нескольких структурах и хорошо используется. Например, CodeIgniter использует eval для различения иерархии классов PHP 4 и PHP 5. Плагины блога, которые позволяют выполнять PHP-код, определенно нуждаются в нем (и это функция, доступная в Expression Engine, WordPress и т. Д.). Я также использовал его для одного веб-сайта, где серия представлений практически идентична, но для каждого из них нужен индивидуальный код, и создание какого-то безумного правила было намного сложнее и медленнее.

Хотя я знаю, что это не PHP, я обнаружил, что eval Python делает реализацию базового калькулятора намного проще.

В принципе, вот вопрос:

  1. Легко ли читать eval? Одна из наших главных целей – общаться с другими программистами, что происходит через нашу голову, когда мы написали это. В примере CodeIgniter очень ясно, что они пытались выполнить.
  2. Есть ли другой способ? Скорее всего, если вы используете eval (или переменные переменные или любую другую форму синтаксиса строкового поиска или отражения), есть и другой способ сделать это. Вы исчерпали свои другие варианты? У вас есть достаточно ограниченный набор входных данных? Можно ли использовать оператор switch?

Другие соображения:

  1. Можно ли это сделать безопасным? Есть ли способ, чтобы блуждающий фрагмент кода мог работать в описании eval?
  2. Можно ли это сделать согласованным? Можете ли вы, учитывая ввод, всегда и последовательно производить тот же результат?

Соответствующим случаем (учитывая отсутствие простых альтернатив) было бы, когда доверенные данные были сериализованы с помощью var_export и необходимо его неэтериализовать. Конечно, он никогда не должен был сериализоваться таким образом, но иногда ошибка уже выполнена.

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

Вы можете использовать eval для создания установки для добавления кода после установки системы. Обычно, если вы хотите изменить код на сервере, вам придется добавлять / изменять существующие файлы PHP. Альтернативой этому будет сохранение кода в базе данных и использование eval для его выполнения. Вы должны быть уверены, что добавленный код безопасен.

Подумайте об этом как о плагине, просто о том, что можно что-то сделать …

Вы могли бы подумать о сайте, который позволил бы людям вносить фрагменты кода, которые пользователи могли бы динамически добавлять на свои веб-страницы, – без их фактического сохранения кода в файловой системе webservers. Вам понадобится процесс утверждения, хотя …

Совместимость . Очень часто можно предоставить резервные копии PHP4. Но также возможно желание подражать функциональности PHP5.4 в 5.3, как пример SplString . Хотя просто предоставление двух включенных вариантов (include.php4 vs. include.php5) является частым, иногда более эффективным или читаемым является обращение к eval ():

  $IMPL_AA = PHP_VERSION >= 5 ? "implements ArrayAccess" : ""; eval(<<<END class BaseFeature $IMPL_AA { 

Где в этом случае код будет работать на PHP4, но выставить более хороший API / синтаксис только на PHP5. Обратите внимание, что пример является вымышленным.

Я использовал eval, когда у меня был бот с php-engined, который общался со мной, и я мог сказать, что он делает команды с помощью команд EVAL: php commands here . Все еще зло, но если ваш код не знает, чего ожидать (если вы вытаскиваете кусок кода PHP из базы данных), eval – единственное решение.

Таким образом, это должно быть справедливо для всех языков с eval :

В принципе, за немногими исключениями, если вы строите значение, переданное на eval или получающее его из источника, не имеющего права наследования, вы делаете что-то неправильно. То же самое верно, если вы вызываете eval на статическую строку.

Помимо проблем с производительностью при инициализации парсера во время выполнения и проблем безопасности, вы, как правило, обходитесь с системой типов.

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

По моему опыту, я только нашел законные виды использования, которые попадают в категории плагинов и привилегированных пользователей (например, администратор веб-сайта, а не пользователь таких) расширений. В основном вещи, которые действуют как код, поступающий из надежных источников.

Не прямое использование, но модификатор / e для preg_replace использует eval и может быть весьма удобным. См. Пример № 4 на http://php.net/preg_replace .

Независимо от того, является ли это злом / плохим субъективным и полностью зависит от того, что вы считаете «хорошим» в конкретном контексте. При работе с ненадежными входами он обычно считается плохим. Однако в других ситуациях это может быть полезно. Представьте себе, что вы пишете одноразовый сценарий преобразования данных под предельным предельным давлением. В этой ситуации, если eval работает и упрощает, мне будет сложно называть его злым.

Эта дискуссия по eval – фактически одно большое недоразумение в контексте php. Люди мыслят о том, что eval являются злыми, но обычно у них нет проблем с использованием include , хотя включение – это, по сути, одно и то же. Включить foo так же, как eval file_get_contents foo, поэтому каждый раз, когда вы включаете то, что вы совершаете, смертный грех eval.