PHP: эквивалент включения использования eval
Если код одинаков, то существует разница между:
include 'external.php';
и
eval('?>' . file_get_contents('external.php') . '<?php');
В чем разница? Кто-нибудь знает?
Я знаю, что они разные, потому что include
отлично работает, а eval
дает ошибку. Когда я изначально задал вопрос, я не был уверен, что он дал ошибку во всем коде или просто на моем (а потому, что код был eval
ed, было очень сложно выяснить, что означала ошибка). Однако, после исследования ответа, выясняется, что независимо от того, получаете ли вы ошибку, не зависит от кода в external.php
, но зависит от ваших настроек php (short_open_tag
, если быть точным).
Ответы
Ответ 1
После нескольких исследований я выяснил, что было не так. Проблема заключается в том, что <?php
является "коротким открывающим тегом" и поэтому будет работать, только если short_open_tag
установлено в 1 (в php.ini или что-то подобное). Правильный полный тег <?php
, который имеет пробел после второго p.
Таким образом, правильный эквивалент включения:
eval('?>' . file_get_contents('external.php') . '<?php ');
В качестве альтернативы вы можете оставить открывающий тег вместе (как указано в комментариях ниже):
eval('?>' . file_get_contents('external.php'));
Моим первоначальным решением было добавить точку с запятой, которая также работает, но выглядит намного менее чистой, если вы спросите меня:
eval('?>' . file_get_contents('external.php') . '<?php;');
Ответ 2
AFAIK вы не можете использовать ускорители php, если используете eval().
Ответ 3
Если вы используете веб-сервер, на котором установлен кеш-код операции, например APC, eval
не будет "лучшее решение": код eval'd не хранится в кэше операций, если я правильно помню (и другой ответ сказал то же самое, кстати).
Решение, которое вы могли бы использовать, по крайней мере, если код не часто изменяется, получает соединение кода, хранящегося в базе данных, и включает код:
- при необходимости извлеките код из БД и сохраните его в файле на диске
- включить этот файл
- поскольку код теперь находится в файле, на диске, кеш-код операции может кэшировать его - что лучше для выполнения
- и вам не нужно будет делать запрос к БД каждый раз, когда вам нужно выполнить код.
Я работал с программным обеспечением, использующим это решение (файл на диске был не более чем кешем кода, хранящимся в БД), и я работал не так уж плохо - лучше, чем при загрузке запросов БД каждая страница, во всяком случае...
Некоторые не очень хорошие вещи, как следствие:
- вам нужно извлечь код из БД, чтобы поместить его в файл "при необходимости",
- Это может означать повторное создание временного файла один раз в час или удаление его при изменении записи в БД? У вас есть способ определить, когда это произойдет?
- вам также необходимо изменить свой код, использовать временный файл или при необходимости сгенерировать его
- Если у вас есть несколько мест для модификации, это может означать некоторую работу.
Кстати: я бы смел сказать что-то вроде "eval is evil"?
Ответ 4
Как отмечено @bwoebi в этом ответе на мой вопрос, подстановка eval
не учитывает контекст пути файла к включенному файлу. В качестве тестового примера:
Baz.php
:
<?php return __FILE__;
Foo.php
:
<?php
echo eval('?>' . file_get_contents('Baz.php', FILE_USE_INCLUDE_PATH)) . "\n";
echo (include 'Baz.php') . "\n";
Результат выполнения php Foo.php
:
$ php Foo.php
/path/to/file/Foo.php(2) : eval()'d code
/path/to/file/Baz.php
Я не знаю, как изменить константу __FILE__
и друзей во время выполнения, поэтому я не думаю, что существует общий способ определения include
в терминах eval
.
Ответ 5
Только вариант eval('?>' . file_get_contents('external.php'));
- это правильная замена для include.
См. тесты:
<?php
$includes = array(
'some text',
'<?php print "some text"; ?>',
'<?php print "some text";',
'some text<?php',
'some text<?php ',
'some text<?php;',
'some text<?php ?>',
'<?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 . '<?php '));
print "\r\n" . "Eval 2:" . "\r\n";
foreach ($includes as $include)
var_dump(eval('?>' . $include));
print "\r\n" . "Eval 3:" . "\r\n";
foreach ($includes as $include)
var_dump(eval('?>' . $include . '<?php;'));
Вывод:
Include:
some textint(1)
some textint(1)
some textint(1)
some text<?phpint(1)
some textint(1)
some text<?php;int(1)
some textint(1)
some textint(1)
Eval 1:
some textNULL
some textNULL
bool(false)
some text<?phpNULL
bool(false)
some text<?php;NULL
some textNULL
some textNULL
Eval 2:
some textNULL
some textNULL
some textNULL
some text<?phpNULL
some textNULL
some text<?php;NULL
some textNULL
some textNULL
Eval 3:
some text<?php;NULL
some text<?php;NULL
bool(false)
some text<?php<?php;NULL
bool(false)
some text<?php;<?php;NULL
some text<?php;NULL
some text<?php;NULL
Ответ 6
Это позволяет включать файл, предполагающий, что в 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;
}
... Затем включите этот файл. Да, это также отключит кеши кода операции, но делает это "eval" тем же, что и include в отношении поведения.
Ответ 7
вот мой подход.
он создает временный файл php и включает его.
но таким образом, если код, который вы хотите запустить в этой функции, имеет ошибки выхода программ перед удалением временного файла
поэтому я делаю процедуру автоклавирования в функции. таким образом он очищает старые временные файлы таймаутом каждый раз, когда запускается функция. вы можете установить тайм-аут или отключить его из параметров при запуске функции
i также добавлена опция игнорирования ошибки для решения не удаленных временных файлов. если ошибки игнорируются, программа продолжит работу и удалит временный файл.
также некоторые проекты должны отключить автозабор, потому что он сканирует всю директорию каждый раз, когда она запускается. это может повредить производительность диска.
function eval2($c) {
$auto_clean_old_temporary_files=false; //checks old temporary eval2 files for this spesific temporary file names generated by settings below
$ignore_all_errors=true; //if you ignore errors you can remove temporary files even there is an error
$tempfiledirectory=''; //temporary file directory
$tempfileheader='eval2_'; // temporary file header
$tempfiletimeseperator='__'; // temporary file seperator for time
$tempfileremovetimeout=200; // temp file cleaning time in seconds
if ($auto_clean_old_temporary_files===true) {
$sd=scandir('.'); //scaning for old temporary files
foreach ($sd as $sf) {
if (strlen($sf)>(32+strlen($tempfileheader)+strlen($tempfiletimeseperator)+3)) { // if filename long enough
$t1=substr($sf,(32+strlen($tempfileheader)),strlen($tempfiletimeseperator)); //searching time seperator
$t2=substr($sf,0,strlen($tempfileheader)); //searching file header
if ($t1==$tempfiletimeseperator && $t2==$tempfileheader) { //checking for timeseperator and file name header
$ef=explode('.',$sf);
unset($ef[count($ef)]);//removing file extension
$nsf=implode('.',$ef);//joining file name without extension
$ef=explode($tempfiletimeseperator,$nsf);
$tm=(int)end($ef); //getting time from filename
$tmf=time()-$tm;
if ($tmf>$tempfileremovetimeout && $tmf<123456 && $tmf>0) { // if time passed more then timeout and difference with real time is logical
unlink($sf); // finally removing temporary file
}
}
}
}
}
$n=$tempfiledirectory.$tempfileheader . md5(microtime().rand(0,5000)). $tempfiletimeseperator . time() .'.php'; //creating spesific temporary file name
$c='<?php' . PHP_EOL . $c . PHP_EOL; //generating php content
file_put_contents($n,$c); //creating temporary file
if ($ignore_all_errors===true) { // including temporary file by your choise
[email protected]($n);
}else{
$s=include($n);
}
return $s;
}