【问题标题】:PHP robust include to handle errors?PHP 健壮的包含来处理错误?
【发布时间】:2010-12-04 09:12:44
【问题描述】:

我有一个 PHP 应用程序“index.php”,由于各种原因,它需要通过在其他脚本上使用 include_once 来运行其他 PHP 脚本。那个其他脚本不是很稳定,所以有什么方法可以做一个安全的include_once 不会停止调用者吗?

即:

<?php

safely_include_once('badfile.php'); // MAY throw syntax error, parse error, other badness
echo "I can continue execution after error";

?>

(我知道这可能是个坏主意,等等,请放心,这不是生产环境的东西。)

【问题讨论】:

    标签: php exception include


    【解决方案1】:

    这些答案都不起作用。如果包含的文件中存在解析错误,则对 @include() 表示的最高票数仍将终止第一个脚本。

    你不能 eval() 它,你不能尝试/捕捉它,并且调用 include 或 require 的每一种方式都会终止脚本中的 all 执行。

    这个问题仍然悬而未决。

    http://bugs.php.net/bug.php?id=41810

    这是 PHP 中的一个错误,至少从 2006 年开始就缺少此功能。他们将此错误归类为“虚假”,因为他们声称,includes() 和 requires() 发生在编译时。

    如果您在运行时为 include() 和/或 require() 生成字符串参数,或者在包含运行 include() 的代码的字符串上执行 eval(),这将消失。

    【讨论】:

    • 必须有办法,Wordpress 能够做到这一点。如果您激活有语法错误或生成警告/致命错误的插件,它会告诉您并继续执行脚本(禁用插件并显示错误消息)。例如:“插件无法激活,因为它触发了致命错误。”不过,我相信它是通过 iframe 诡计做到这一点的,这并不理想。但我不确定。
    • Graben 的答案,使用带有 -f 标志的解释器命令行,将起作用。它很慢,看起来像个 hack,但它确实完成了工作。
    • bugs.php.net/bug.php?id=41810#1349470363 是对上述错误报告值得一读的评论。
    【解决方案2】:

    唯一真正的解决方案,认为不是最优雅但有效的解决方案是调用另一个解析和执行脚本的 php 解释器,除了检查它是否产生解析错误或未检测到错误消息来过滤错误消息像 PHP Parse Error blablabla。就像这段代码一样:

    <?php
    function ChkInc($file){
       return file_exists($file) && (substr(exec("php -l $file"), 0, 28) == "No syntax errors detected in");
    }
    ?>
    

    这个想法来自 PHP 文档中的 gillis' commentinclude 函数手册页。

    【讨论】:

    • 这是目前唯一的方法。
    • 使用if返回真假?这听起来不是很疯狂吗?将您的解决方案转换为单线有什么问题:return (substr(exec("php -l $file"), 0, 28) == "No syntax errors detected in");
    • ChkInc("/tmp ; rm -rf /");
    • 添加了 file_exists 检查偷偷评论,但在一个体面的应用程序中达到此功能时应该已经完成​​用户输入验证。
    【解决方案3】:

    你可以

    @include "fileWithBadSyntax.php";
    

    根据我的快速测试,这适用于解析错误或 trigger_error() 引发的错误。

    编辑:这是完全错误的。看看偷偷摸摸的答案,这是正确的,如果有点无益的话。

    【讨论】:

    • 这项工作,是否可以使用这种方法捕获错误详细信息?
    • 这种方法将消除所有错误。请参阅 KyleFarris 的回答,了解一种可以捕获错误并可以选择保持安静的方法(IE,自定义错误处理)。
    • 确实,凯尔的回答更有力
    • 有人刚刚投了赞成票,我不敢相信我写了它。如果不是公认的答案,我会删除它。
    【解决方案4】:

    是否有可能改变你的架构并将“badfile.php”变成一个网络服务?您可以通过网络调用它并解析或包含其输出,而不是将其直接包含到您的代码库中。这将帮助您避免解析错误,如果您对 badfile.php 的环境进行了适当的限制(使用安全模式,或以有限的权限运行单独的 Web 服务器进程),您还可以避免潜在的恶意代码。

    【讨论】:

    • @pix0r:这当然可以,但根据他的项目范围,它可能有点矫枉过正和不雅。
    【解决方案5】:

    你可以做一个 try/catch 块

    <?php
    try {
        include("someFile.php");
    } catch (Exception $e) {
        // Should probably write it to a log file, but... for brevity's sake:
        echo 'Caught exception: ',  $e->getMessage(), "\n";
    }
    ?>
    

    现在,它只会在没有错误的情况下包含文件。如果出现错误,它将跳过这些内容并写入异常(例如,在 cmets 中,最好写入日志文件或其他内容)。

    更多信息: http://us2.php.net/manual/en/language.exceptions.php

    【讨论】:

    • 解析错误会在包含文件的中间停止 PHP,并且不会返回到 catch 块。
    【解决方案6】:

    include-file-not-found ....的优雅解决方法

    功能安全要求($incfile,$showfn=1){ $a = explode(":", get_include_path()) ; $a[] = ""; $b = 0; foreach( $a 作为 $p ) { if( !empty( $p )) $p .= "/"; $f = $p.$incfile; 如果(文件存在($f)){ $b = 1; 休息; } } 如果(!$b) exit("无法继续,需要文件" . (($showfn) ? $incfile : "") 。 " 不可用或服务器已超时。" ); 要求一次(“$f”); }

    【讨论】:

    • 相当不错,但仍然失败。想象一下,在您验证文件存在后该文件被删除......
    【解决方案7】:

    看第二个答案,@sneak 是对的,你无法捕捉需求/包含。

    我的自动加载 require 解决方案是使用 error_get_last,所以只需将其添加到基础文件中(然后这将处理来自诸如自动加载/子类等内容的 requires/include 的解析错误:

    <?php 
        register_shutdown_function( "fatal_handler" );
        function fatal_handler() 
        {
            $error = error_get_last();  
            if($error !== NULL) 
            {
                if(isset($error['type']) && ($error['type'] === E_ERROR  || $error['type'] === 1 || $error['type'] === 2 || $error['type'] === 3 || $error['type'] === 4))
                {
                    // do what you need to do here to make safe, I just log and use that log (to make sure other tests dont run in my case)
                    Some_Class::logErrorToFile('fatal_error:' . json_encode($error));
                    file_put_contents('somp_file_path_that_all_other_classes_check', 'fatal_error:' . json_encode($error));
                }
                else
                {
                    Some_Class::logErrorToFile('error:' . json_encode($error)); 
                }
            }
            return;
        }
    
    spl_autoload_register(function ($classname) {
        $classPath = str_replace("_", "/", $classname);
        $path = dirname(__FILE__) . '/../'.$classPath.'.php';
        require_once($path);  // fatal errors are logged for this runtime require, will not actually stop execution for this case, but we can handle how we deal with that now      
    });
    

    【讨论】:

      【解决方案8】:

      在 badfile.php 中你可以让它返回一个值:

      <?php
      //inestable instructions.
      
      
      return true; ?>
      

      然后在主文件上你可以这样做:

       <?php
      
       if (require($badFile)) {
           //if it was true continue with normal execution
       } else {
           echo "Error: ". error_get_last();
       }
      

      【讨论】:

        【解决方案9】:

        这是我能找到的唯一真正的解决方案:

        function safe_include($fn) {
                $fc = file_get_contents($fn);
                if(!eval($fc)) {
                        return 0;
                }
                return 1;
        }
        

        请注意,上面的代码意味着您必须从要包含的文件中取出开头语句,或者执行以下操作:

        eval("?>" . $fc)
        

        问题是您不能在任何时候调用 require() 或 include() 或它们的 _once() 变体,并期望它们不会终止一切,包括任何错误处理程序。当遇到解析错误时,PHP 将完全停止处理所有内容。

        唯一的例外是 eval() 中的字符串。但问题是,你不能真正做到这一点:

        eval('require($fn);'); 
        

        ...因为 eval'd 字符串中的 require() 将 仍然 停止。您必须阅读内容,祈祷有问题的文件不再包含()或要求(),然后评估()它。

        真正的解决方案?跳过 PHP。 :/

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-09-22
          • 1970-01-01
          • 2011-04-26
          • 2023-02-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多