PHP:包含使用eval的等价物

时间:2009-07-26 13:42:28

标签: php include eval

如果代码相同,则:

之间似乎存在差异

include 'external.php';

eval('?>' . file_get_contents('external.php') . '<?php');

有什么区别?有人知道吗?


我知道这两个是不同的,因为include工作正常而eval给出错误。当我最初提出这个问题时,我不确定它是否会对所有代码或我的代码都有错误(并且因为代码是eval,所以很难找出错误的含义)。但是,在研究了答案之后,事实证明你是否得到错误并不依赖于external.php中的代码,而是取决于你的php设置(short_open_tag是准确的)

8 个答案:

答案 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获取代码,并将其存储在磁盘上的文件中
  • 包含该文件
  • 由于代码现在位于文件中,在磁盘上,操作码缓存将能够缓存它 - 这对性能更好
  • 并且每次必须执行代码时都不需要向DB发出请求。

我使用的是使用此解决方案的软件(磁盘文件只不过是存储在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()

在少数情况下可以接受;不要经常这样做。确实,它没有被缓存(糟糕的操作码缓存只是不知道它和以前的字符串是一样的)。同时,如果您的代码每次都在更改,则eval比include()好得多,这主要是因为include()会在每次调用时填充操作码缓存。就像临时文件一样。太可怕了(慢了约4倍)。

内存中的eval()

实际上,当您的脚本已经在字符串中时,eval就会非常快。在大多数情况下,是磁盘操作将其拉回原位,现在可以肯定,这取决于您在脚本中执行的操作,但在我的脚本非常小的情况下,速度要快约400倍。 (您是否拥有memcached?只是大声思考)所以include()不能做的是在没有文件操作的情况下两次评估同一件事,这非常重要。如果将它用于不断变化的,较小的,由内存生成的字符串,那么显然应该选择-与一次迭代的include()相比,一次加载一次+评估要快很多倍。

TL; DR

  • 相同的代码,每个请求一次:包括
  • 相同的代码,每个请求几次调用:评估
  • 更改代码:评估