【问题标题】:Catch a fatal exception and continue捕获致命异常并继续
【发布时间】:2024-01-16 15:56:01
【问题描述】:

我知道,根据其定义,致命异常应该会终止执行,不应该被抑制,但问题就在这里。

我正在运行一个脚本,该脚本在数据库中抓取、解析和存储大约 10,000 个页面。这需要几个小时,并且在极少数情况下(千分之一)页面解析失败并引发致命异常。

目前,我正在这样做:

for ($i=0;$i<$count;$i++)   
        {
            $classObject = $classObjects[$i];           

            echo $i . "   :   " . memory_get_usage(true) . "\n";

            $classDOM = $scraper->scrapeClassInfo($classObject,$termMap,$subjectMap);           
            $class = $parser->parseClassInfo($classDOM);                    
            $dbmanager->storeClassInfo($class);         

            unset($classDOM,$class,$classObject);           
        }        

我可以做类似的事情

for ($i=0;$i<$count;$i++)   
{
   $classObject = $classObjects[$i];            
   echo $i . "   :   " . memory_get_usage(true) . "\n";

   try
   {
      $classDOM = $scraper->scrapeClassInfo($classObject,$termMap,$subjectMap);         
      $class = $parser->parseClassInfo($classDOM);                  
      $dbmanager->storeClassInfo($class);           
      unset($classDOM,$class,$classObject);         
    }
    catch (Exception $e)
    {
       //log the error here
       continue;
    }
}

上面的代码不适用于fatal exceptions

有没有可能做这样的事情: 如果我将主循环移动到一个方法中,然后从register_shutdown_function调用该方法?

像这样:

function do($start)
{
   for($i=$start;$i<$count;$i++)
   {
      //do stuff here
   }
}

register_shutdown_function('shutdown');

function shutdown()
{ 
   do();
}

这是执行停止时输出的消息:

Fatal error:  Call to a member function find() on a non-object in ...

当我使用的方法无法解析页面时,我希望看到上述消息。我可以跳过该页面并继续循环的下一次迭代。

【问题讨论】:

  • 什么是fatal exception?你的catch (Exception $e) 怎么不工作了?
  • 确实.....有人扔了不延伸Exception的东西吗?
  • ...在这种情况下,你不能只做} catch ($e) {吗?
  • 我认为他的意思是Fatal error。您收到的致命错误消息是什么?这将为您提供有关需要做什么的线索。可能一点也不例外。
  • @CharlesSprayberry:发布了得到输出的消息(见编辑)。我想我的措辞是错误的。它不是fatal exception,而是fatal error

标签: php exception exception-handling try-catch fatal-error


【解决方案1】:

我遇到过类似的问题,我发现在 find() 调用之前使用 is_object() 调用可以避免致命错误。

【讨论】:

    【解决方案2】:

    致命错误是致命的并终止执行。如果发生致命错误,则无法解决此问题。但是,您的错误:

    致命错误:在...中的非对象上调用成员函数 find()

    是完全可以预防的。只需添加检查以确保您拥有正确对象的实例,如果没有,则处理错误:

    if ($foo instanceof SomeObject) {
        $foo->find(...);
    } else {
        // something went wrong
    }
    

    【讨论】:

    • 是的,这似乎是最好的处理方式,而不是让它升级为致命错误
    • @mfonda 如何找到它是什么类型的?
    【解决方案3】:

    首先,exceptionserrors 之间存在明显区别。您遇到的是错误,而不是异常。根据您的消息和您发布的代码,问题在于您尚未提出问题。你想在什么变量上调用find()?好吧,那个变量不是一个对象。没有办法捕获致命错误并忽略它,您必须找到您在非对象上调用find() 的位置并修复它。

    【讨论】:

    • 我正在使用的 html dom 解析器库返回一个对象(页面的 DOM 结构),我使用该 object-&gt;find() 来查找某些元素。当页面不可解析时,它不会返回对象,因此object-&gt;find() 会引发致命错误。我想我可以在调用find()之前先检查is_object()
    • @xbonez 这将是您问题的答案,因为 DOM 解析器失败本身不会导致致命错误。您还可以按照下面@mfonda 的建议使用instanceof 运算符。
    • @xbonez 这是一种选择。或者您可以实现NullObject 设计模式。基本上,您设置了一个实现,当您调用 $object-&gt;find() 时,它会返回一个空对象,您仍然可以在该对象上调用方法。这将确保您始终有一个对象可以调用方法,而您不必进行这些对象检查。
    【解决方案4】:

    在我看来,“捕捉”致命错误的唯一可能方法是注册关闭功能。请记住将所有(或可能是组)查询添加到事务中,并在出现故障时回滚它们,以确保一致性。

    【讨论】:

    • 发生致命错误时,我不想优雅地退出(清理、回滚事务然后退出)。我正在寻找记录错误并继续。那可能吗?我想有可能不是。
    • 查看此链接php.net/manual/en/function.register-shutdown-function.php ,查看他们发的消息,其中一些与您的问题有关。但是你应该尽量避免代码中的错误