PHP: эквивалент включения использования eval
Я знаю, что они разные, потому что include работает нормально, и eval дает ошибку. Когда я изначально задал вопрос, я не был уверен, что он дал ошибку во всем коде или просто на моем (и потому, что код был eval , было очень сложно выяснить, что такое ошибка). Однако, после изучения ответа, выясняется, что независимо от того, получаете ли вы ошибку, не зависит от кода в файле external.php , но зависит от ваших настроек php (точнее, short_open_tag ).
Таким образом, правильный эквивалент включения:
eval('?>' . file_get_contents('external.php') . '
Кроме того, вы можете оставить открывающий тег вместе (как указано в комментариях ниже):
eval('?>' . file_get_contents('external.php'));
Моим первоначальным решением было добавить точку с запятой, которая также работает, но выглядит намного менее чистой, если вы спросите меня:
eval('?>' . file_get_contents('external.php') . ' ' . file_get_contents('external.php')); вариант – правильная замена для включения. ', '', 'some text', ); $tempFile = tempnam('/tmp', 'test_'); print "\r\n" . "Include:" . "\r\n"; foreach ($includes as $include) < file_put_contents($tempFile, $include); var_dump(include $tempFile); >unlink($tempFile); print "\r\n" . "Eval 1:" . "\r\n"; foreach ($includes as $include) var_dump(eval('?>' . $include . '' . $include)); print "\r\n" . "Eval 3:" . "\r\n"; foreach ($includes as $include) var_dump(eval('?>' . $include . ' ' и ' Тесты
Для начала, разными способами в цикле выведем простенькое представление 'view.php', содержащее, например: i=?=$i?>
for($i=0; $i'.file_get_contents('view.php').'
$code=' ?>'.file_get_contents('view.php').'
$i=0; include('view.php'); ---------- view.php ---------- html ?>
$i=0; $code=' ?>'.file_get_contents('view.php').''.file_get_contents('view.php').' ?>
$i=0; $code=' ?>'.file_get_contents('view.php').' ?>
Выводы
Первое, что бросается в глаза — варианты с eval не поддаются оптимизации и кешированию с помошью eAccelerator. Поэтому, если вы его используете и вам не нужны другие преимущества eval — лучше остановитесь на include.
Использование eval имеет смысл там, где представления вызываются многократно, но нет возможности использовать акселераторы. Или если представления хранятся в БД.
Немного отличается и обработка ошибок в eval. При возникновении ошибки разбора кода, например, выполнение скрипта не прерывается. Это может быть полезно в некоторых случаях. Но при этом, сообщение об ошибке выглядит по другому, что может немного сбить с толку:
Parse error: syntax error, unexpected T_ECHO, expecting ')' in /www/test/eval_vs_include/test.php(39) : eval()'d code on line 4
Здесь указанная ошибка возникла в 4 строке файла view.php, который был скормлен в eval, расположенный в 39 строке файла test.php. Конечно, нам ничего не мешает выводить и название подключаемого файла, если eval возвратил false. Учитывая, что работа над ошибками — это не штатный режим, и на рабочем проекте ошибки не выводятся, считаю указанный недостаток не существенным.
Eval
+ Быстрее чем include, если не используются акселераторы
+ Возможность кеширования кода представлений во фреймворке или шаблонизаторе
+ Возможность хранения представлений в БД
+ Выполнение скрипта не прерывается при возникновении ошибок
— Не оптимизируется и не кешируется внешними акселераторами
— Вывод ошибок отличаться от привычного
— На некоторых хостингах может быть запрещено выполнение eval
Include
+ Работает везде
+ Внешние акселераторы на много увеличивают производительность
— Низкая скорость без акселераторов
— Нет возможности кешировать подключаемый из файла код в своём движке
— Подключаемый код может находиться только в файле
Следует отметить, что во фреймворке CodeIgniter eval тоже используется для вывода представлений, но только в том случае, если в настройках указана необходимость замены коротких '
PHP: Equivalent of include using eval
Alternatively, you can leave the opening tag out all together (as noted in the comments below):
eval('?>' . file_get_contents('external.php'));
My original solution was to add a semicolon, which also works, but looks a lot less clean if you ask me:
eval('?>' . file_get_contents('external.php') . '
Solution 2
AFAIK you can't take advantage of php accelerators if you use eval().
Solution 3
If you are using a webserver on which you have installed an opcode cache, like APC, eval will not be the "best solution" : eval'd code is not store in the opcode cache, if I remember correctly (and another answer said the same thing, btw).
A solution you could use, at least if the code is not often changed, is get a mix of code stored in database and included code :
- when necessary, fetch the code from DB, and store it in a file on disk
- include that file
- as the code is now in a file, on disk, opcode cache will be able to cache it -- which is better for performances
- and you will not need to make a request to the DB each time you have to execute the code.
I've worked with software that uses this solution (the on-disk file being no more than a cache of the code stored in DB), and I worked not too bad -- way better that doing loads of DB requests of each page, anyway.
Some not so good things, as a consequence :
- you have to fetch the code from the DB to put it in the file "when necessary"
- this could mean re-generating the temporary file once every hour, or deleting it when the entry in DB is modified ? Do you have a way to identify when this happens ?
- if you have several places to modifiy, this could mean some work
BTW : would I dare saying something like "eval is evil" ?
Solution 4
As noted by @bwoebi in this answer to my question, the eval substitution does not respect the file path context of the included file. As a test case:
' . file_get_contents('Baz.php', FILE_USE_INCLUDE_PATH)) . "\n"; echo (include 'Baz.php') . "\n";
Result of executing php Foo.php :
$ php Foo.php /path/to/file/Foo.php(2) : eval()'d code /path/to/file/Baz.php
I don't know of any way to change the __FILE__ constant and friends at runtime, so I do not think there is any general way to define include in terms of eval .
Solution 5
This lets you include a file assuming file wrappers for includes is on in PHP:
function stringToTempFileName($str) < if (version_compare(PHP_VERSION, '5.1.0', '>=') && strlen($str < (1024 * 512))) < $file = 'data://text/plain;base64,' . base64_encode($str); >else < $file = Utils::tempFileName(); file_put_contents($file, $str); >return $file; >
. Then include that 'file.' Yes, this will also disable opcode caches, but it makes this 'eval' the same as an include with respect to behavior.