Выполнение кода
int eval(string $code)
Эта функция делает довольно интересную вещь: она берет параметр $st и, рассматривая его как код программы на PHP, запускает. Если этот код возвратил какое-то значение оператором return (как, например, это обычно делают функции), eval()
также вернет эту величину.
Параметр $st представляет собой обычную строку, содержащую участок PHP-программы. То есть в ней может быть все, что допустимо в сценариях:
r
ввод-вывод, в том числе закрытие и открытие тэгов <? и ?>;
r управляющие инструкции: циклы, условные операторы и т. д.;
r объявления и вызовы функций;
r вложенные вызовы функции eval().
Тем не менее, нужно помнить несколько важных правил[E127] [DK128] .
r Код в $st
будет использовать те же самые глобальные переменные, что и вызвавшая программа. Таким образом, переменные не локализуются
внутри eval().
r Любая критическая ошибка (например, вызов неопределенной функции) в коде строки $st
приведет к завершению работы всего сценария (разумеется, сообщение об ошибке также напечатается в браузер). Это значит, что мы не можем перехватить все ошибки в коде, вставив его в eval().
Последний факт вызывает довольно удручающие мысли. К сожалению, разработчики PHP опять не задумались о том, как было бы удобно, если бы eval() при ошибке в вызванном ей коде просто возвращала значение false, помещая сообщение об ошибке в какую-нибудь переменную (как это сделано, например, в Perl).
r Тем не менее, синтаксические ошибки и предупреждения, возникающие при трансляции кода в $st, не приводят к завершению работы сценария, а всего лишь вызывают возврат из eval()значения ложь. Что ж, хоть кое-что.
Не забывайте, что переменные в строках, заключенных в двойные кавычки, в PHP интерполируются (то есть заменяются на соответствующие значения). Это значит, что, если мы хотим реже использовать обратные слэши для защиты символов-кавычек, нужно стараться применять строки в апострофах для параметра, передаваемого eval(). Например:
eval("$a=$b;"); // Íåâåðíî!
// Âû, âèäèìî, õîòåëè íàïèñàòü ñëåäóþùåå:
eval("\$a=\$b");
// íî êîðî÷å áóäåò òàê:
eval('$a=$b');
Возможно, вы спросите: зачем нам использовать eval(), если она занимается лишь выполнением кода, который мы и так можем написать прямо в нужном месте программы? Например, следующий фрагмент
eval('for($i=0; $i<10; $i++) echo $i; ');
эквивалентен такому коду:
for($i=0; $i<10; $i++) echo $i;
Почему бы всегда не пользоваться последним фрагментом? Да, конечно, в нашем примере лучше было бы так и поступить. Однако сила eval() заключается прежде всего в том, что параметр $st
может являться (и чаще всего является) не статической строковой константой, а сгенерированной переменной. Вот, например, как мы можем создать 100 функций с именами Func1()...Func100(), которые будут печатать квадраты первых 100 чисел:
Листинг 24.1. Генерация семейства функций
for($i=1; $i<=100; $i++)
eval("function Func$i() { return $i*$i; }");
Попробуйте-ка сделать это, не прибегая к услугам eval()!
Я уже говорил, что в случае ошибки (например, синтаксической) в коде, обрабатываемом eval(), сценарий завершает свою работу и выводит сообщение об ошибке в браузер. Как обычно, сообщение сопровождается указанием того, в какой строке произошла ошибка, однако вместе с именем файла выдается уведомление, что программа оборвалась в функции eval(). Вот как, например, может выглядеть такое сообщение:
Parse error: parse error in eval.php(4) : eval()'d code on line 1
Как видим, в круглых скобках после имени файла PHP печатает номер строки, в которой была вызвана сама функция eval(), а после "on line" — номер строки в параметре eval() $st. Впрочем, мы никак не можем перехватить эту ошибку, поэтому последнее нам не особенно-то интересно.
Давайте теперь в качестве тренировки напишем код, являющийся аналогом инструкции include. Пусть нам нужно включить файл, имя которого хранится в $fname. Вот как это будет выглядеть:
$code=join("",File($fname));
eval("?>$code<?");
Всего две строчки, но какие...… Рассмотрим их подробнее.
Что делает первая строка — совершенно ясно: она сначала считывает все содержимое файла $fname по строкам в список, а затем образует одну большую строку путем "склеивания"
всех элементов этого списка. Заметьте, как получилось лаконично: нам не нужно ни открывать файл, ни использовать функцию fread()
или fgets().
Вторая строка, собственно, запускает тот код, который мы только что считали. Но çr÷le îír ddläârd?lnn? nceâîërec ?> c çrerí÷cârlnn? <? — nýarec înedunc? c çredunc? eîär PHP? Наверное, вы уже догадались: суть в том, что функция eval() воспринимает свой параметр именно как код, а не как документ со вставками PHP-кода. В то же время, считанный нами файл представляет собой обычный PHP-сценарий, т. е. документ со "вставками" PHP. Иными словами, настоящая инструкция include воспринимает файл в контексте документа, а функция eval() — в контексте кода. Поэтому-то мы и используем ?> — переводим текущий контекст в режим восприятия документа, чтобы eval() "осознала"
статический текст верно. Мы еще неоднократно столкнемся с этим приемом в будущем.