如果代码相同,则:
之间似乎存在差异 include 'external.php';
和
eval('?>' . file_get_contents('external.php') . '<?php');
有什么区别?有人知道吗?
我知道这两个是不同的,因为include
工作正常而eval
给出错误。当我最初提出这个问题时,我不确定它是否会对所有代码或我的代码都有错误(并且因为代码是eval
,所以很难找出错误的含义)。但是,在研究了答案之后,事实证明你是否得到错误并不依赖于external.php
中的代码,而是取决于你的php设置(short_open_tag
是准确的)
答案 0 :(得分:13)
经过一些研究,我发现自己出了什么问题。问题在于<?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;');
答案 1 :(得分:6)
如果使用eval(),AFAIK就无法利用php加速器。
答案 2 :(得分:6)
如果您使用的是已安装操作码缓存的网络服务器,例如APC,则eval
将不是“最佳解决方案”:评估代码不存储在操作码缓存中,如果我没记错(并且另一个答案说同样的话,顺便说一下)。
您可以使用的解决方案,至少在代码不经常更改的情况下,可以获得存储在数据库中的混合代码和包含的代码:
我使用的是使用此解决方案的软件(磁盘文件只不过是存储在DB中的代码的缓存),而且我的工作并不太糟糕 - 比执行大量数据库请求更好每页,无论如何...
有些不太好的事情,因此:
BTW:我敢说“eval是邪恶的”吗?
答案 3 :(得分:2)
正如this answer to my question中的@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
{{1} }}
答案 4 :(得分:1)
只有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
答案 5 :(得分:0)
这允许您包含一个文件,假设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'与包含行为的'eval'相同。
答案 6 :(得分:0)
这是我的方法。
它创建临时php文件并包含它。
但是这样,如果你想在这个函数上运行的代码在删除临时文件
之前有错误程序退出所以我在功能上做了一个autoclean程序。这样它每次运行时都会通过超时清除旧的临时文件。你可以设置超时或从功能开始时的选项中禁用它
我还添加了忽略错误选项来解决未删除的临时文件。如果忽略错误,程序将继续并删除临时文件。
还有一些项目必须禁用autoclean,因为它每次运行时都会扫描整个目录。它可能会损害磁盘性能。
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
$s=@include($n);
}else{
$s=include($n);
}
return $s;
}
答案 7 :(得分:0)
关于上述解决方案的一些想法:
不要。这对性能非常不利,只是不要这样做。它不仅使您的操作码高速缓存完全疯狂(高速缓存命中永远不会发生,而且每次都会尝试再次对其进行高速缓存),而且还给您带来了文件系统在高(甚至中等)负载下锁定的麻烦,因为您必须写入文件并Apache / PHP必须阅读它。
在少数情况下可以接受;不要经常这样做。确实,它没有被缓存(糟糕的操作码缓存只是不知道它和以前的字符串是一样的)。同时,如果您的代码每次都在更改,则eval比include()好得多,这主要是因为include()会在每次调用时填充操作码缓存。就像临时文件一样。太可怕了(慢了约4倍)。
实际上,当您的脚本已经在字符串中时,eval就会非常快。在大多数情况下,是磁盘操作将其拉回原位,现在可以肯定,这取决于您在脚本中执行的操作,但在我的脚本非常小的情况下,速度要快约400倍。 (您是否拥有memcached?只是大声思考)所以include()不能做的是在没有文件操作的情况下两次评估同一件事,这非常重要。如果将它用于不断变化的,较小的,由内存生成的字符串,那么显然应该选择-与一次迭代的include()相比,一次加载一次+评估要快很多倍。