【问题标题】:PHP: Equivalent of include using evalPHP:相当于使用 eval 的包含
【发布时间】:2018-09-21 21:51:57
【问题描述】:

如果代码相同,则似乎存在以下差异:

include 'external.php';

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

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


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

【问题讨论】:

标签: php include eval


【解决方案1】:

经过更多研究,我自己发现了问题所在。问题在于<?php 是一个“短开始标签”,因此只有在short_open_tag 设置为1(在php.ini 中或具有相同效果的东西中)时才会起作用。正确的完整标签是<?php,在第二个p之后有一个空格。

因此,包含的适当等价物是:

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

或者,您可以将开始标记放在一起(如下面的 cmets 中所述):

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

我最初的解决方案是添加一个分号,这也可以,但如果你问我,它看起来不太干净:

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

【讨论】:

  • 不是 eval("?>".file_get_contents('external.php'));与包含“external.php”相同; ??
  • YuriKolovsky,你是对的,省略了 php-opening 标记是一个替代分号的替代方法。
  • 请问你为什么要附加 '?>' 和 '
  • @kapitanluffy: include 处理文件,这意味着它以“文本模式”开始(与“php 模式”相反,在 php 模式下执行代码,在“文本模式”下执行文本被简单地复制到输出中),而 eval 以“php 模式”开始。我在 eval 案例中退出了 php 模式,因此它的作用与 include 相同。
【解决方案2】:

AFAIK 如果您使用 eval(),您将无法利用 php 加速器。

【讨论】:

  • AFAIK 你必须在文件系统上有一个真实的文件。
  • ...除非您遇到性能问题,否则您不应该担心它。
  • 我的意思是 AFAIK php 加速器只适用于文件系统上的真实文件。为 php 加速器构建很可能会使您的代码复杂化(您必须检查文件是否可写等),并且可能不会有任何明显的改进。如果它们只是模板文件,我猜它不会改变任何东西。
  • @niteria 只是想指出你说的 AFAIK 太多了。
  • 我决定删除(我的一部分)在这里占用太多空间的讨论。这个答案回答了问题的原始措辞,尽管我不相信它回答了它的本质(保持不变)。讨论还涉及不在问题中的事情(stream wrappers)。最后,关于无法使用 eval 的操作码缓存(并且由于 php 5.5 有一个内置的 php)与 eval 保持警惕的观点是一个很好的观点,尽管它可能更多的是评论而不是当前措辞的答案.
【解决方案3】:

如果您使用的是安装了操作码缓存的网络服务器,例如APCeval 将不是“最佳解决方案”:eval 的代码不会存储在操作码缓存,如果我没记错的话(另一个答案说同样的话,顺便说一句)

至少在代码不经常更改的情况下,您可以使用的解决方案是将存储在数据库中的代码和包含的代码混合在一起:

  • 必要时,从 DB 中获取代码,并将其存储在磁盘上的文件中
  • 包含该文件
  • 由于代码现在在文件中,在磁盘上,操作码缓存将能够缓存它——这对性能更好
  • 您无需在每次必须执行代码时都向数据库发出请求。

我使用过使用此解决方案的软件(磁盘上的文件只不过是存储在 DB 中的代码的缓存),而且我的工作还不错——比执行大量 DB 请求要好得多反正每一页……

结果有些不太好:

  • 您必须在“必要时”从数据库中获取代码以将其放入文件中
    • 这可能意味着每小时重新生成一次临时文件,或者在数据库中的条目被修改时将其删除?您是否有办法确定何时发生这种情况?
  • 您还必须更改代码、使用临时文件或在必要时重新生成它
    • 如果您有几个地方要修改,这可能意味着需要一些工作

顺便说一句:我敢说“eval 是邪恶的”吗?

【讨论】:

  • 很抱歉,您的建议完全无关紧要。我正在编写一些软件,并且我制作它以便它只使用文件。我想通过使用数据库来提供一种替代方案,以便在无法写入文件时使用,因此我最终得到了这个。但是,我们只讨论一个 db 查询和一个 eval 语句。而且速度也不是那么糟糕:1:2 用于从文件中包含:从 db 中包含,10:8 用于从 db 中包含:从 db 中包含 eval。无论如何,我确实想知道来自 db 的包含是否会被缓存......哦,我来这里的原因是缺少分号让我发疯
  • 好的,然后;对不起^^
  • 如果该文件可以在磁盘上更改并且您有多个可以同时访问该文件的进程,您将如何处理动态生成的代码?当另一个进程正在更新文件时,您必须以某种方式保证该文件不会被另一个进程读取...您认为flock 可以解决问题吗?那么还有什么,在这种情况下使用eval 似乎“更安全”......你觉得呢?
【解决方案4】:

正如@bwoebi 在this answer to my question 中所指出的,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__ 常量和朋友,所以我认为没有任何通用方法可以根据eval 来定义include

【讨论】:

    【解决方案5】:

    这允许你包含一个文件,假设包含的文件包装器在 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' 在行为方面与包含相同。

    【讨论】:

      【解决方案6】:

      只有 eval('?&gt;' . file_get_contents('external.php')); 变体是包含的正确替换。

      查看测试:

      <?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
      

      【讨论】:

        【解决方案7】:

        关于上述解决方案的一些想法:

        临时文件

        不要。这对性能非常不利,请不要这样做。它不仅使您的操作码缓存完全疯狂(缓存命中永远不会发生+它每次都会尝试再次缓存它)而且还会让您在高(甚至中等)负载下文件系统锁定令人头疼,因为您必须编写文件和Apache/PHP 必须阅读它。

        简单的 eval()

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

        内存中 eval()

        实际上,当你的脚本已经在字符串中时,eval 非常快;大多数时候是磁盘操作将其拉回,现在这当然取决于您在脚本中所做的事情,但在我的非常小的脚本案例中,它快了约 400 倍。 (你有 memcached 吗?只是想大声点)所以 include() 不能做的是 在没有文件操作的情况下对同一事物进行两次评估,这非常重要。如果你将它用于不断变化的、小的、内存生成的字符串,显然它是 eval 可供选择 - 一次又一次地加载一次 + eval 比迭代的 include() 快很多倍。

        TL;DR

        • 相同的代码,每个请求一次:include
        • 相同的代码,每个请求多次调用:eval
        • 不同的代码:eval

        【讨论】:

          【解决方案8】:

          这是我的方法。

          它创建临时 php 文件并包含它。

          但是如果你想在这个函数上运行的代码有错误,程序在删除临时文件之前退出

          所以我在函数中创建了一个自动清洁程序。这样,每次函数运行时,它都会通过超时清除旧的临时文件。您可以在功能开始时从选项中设置超时或禁用它

          我还添加了忽略错误选项来解决未删除的临时文件。如果忽略错误,程序将继续并删除临时文件。

          还有一些项目必须禁用自动清理,因为它每次运行时都会扫描整个目录。它可能会损害磁盘性能。

          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;  
          
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2010-11-14
            • 2012-12-03
            • 2020-09-21
            • 1970-01-01
            • 1970-01-01
            • 2014-05-30
            • 1970-01-01
            相关资源
            最近更新 更多