【问题标题】:PHP: How to catch Fatal error: Cannot declare class [myclassname], because the name is already in usePHP:如何捕获致命错误:无法声明类 [myclassname],因为该名称已在使用中
【发布时间】:2020-07-16 10:45:29
【问题描述】:

我正在为客户端创建的自定义插件实现插件控制器类。 由于每个插件的类名称很可能最终与已安装的其他插件发生冲突,我想确保当新插件出现时我们不会出现致命错误。 我想向用户报告新插件与已安装的插件发生冲突。

所以基本上 atm 我用两个包含完全相同代码的文件进行测试并且得到:

Fatal error: Cannot declare class [myclassname], because the name is already in use

我尝试使用以下方法捕获此问题,但没有成功:

try {
    include_once $file; 
} catch (ClosedGeneratorException|DOMException|ErrorException|IntlException|LogicException|BadFunctionCallException|BadMethodCallException|DomainException|InvalidArgumentException|LengthException|OutOfRangeException|PharException|ReflectionException|RuntimeException|OutOfBoundsException|OverflowException|PDOException|RangeException|UnderflowException|UnexpectedValueException|SodiumException $ex) {
    echo "Unable to load file.";
} 

所有这些都是我从 https://www.php.net/manual/en/class.exception.php 7.2.0 的 Lists of Throwable 和 Exception 树中获得的

手册规定:

抛出的对象必须是 Exception 类的实例或 异常的子类。试图扔一个不是意志的物体 导致 PHP 致命错误。

这可能不是 Exception 类的实例/子类的对象吗? 我错过了什么?

【问题讨论】:

  • “我错过了什么?” - 任何地方都没有人首先抛出任何异常。你试图捕捉一些不存在的东西。
  • 如何知道致命错误不是“可捕获”的?
  • 任何可捕获的内容都会有一条错误消息,类似于“致命错误:未捕获的错误...”。注意 Error 中的大写 E - 它专门指 Error 类,它是 Throwable 的子类型
  • 您的评论是基于经验还是您在某处读过此内容?链接?
  • 两者兼而有之 - Throwable 层次结构在文档here 中进行了解释,但我不知道有任何好的文档说明哪些内部错误在新层次结构下,哪些不是。

标签: php exception


【解决方案1】:

简短的回答:不,你没有错过任何东西。声明重复的类名不能在任何版本的 PHP(包括 8.0.0 至 alpha 版本)中被捕获。见https://3v4l.org/2TLA3

对于一些额外的背景知识,PHP 有时会在 Throwable 层次结构下移动此类错误,以便可以在运行时检测到它们。 PHP 7 添加了对捕获实例化缺失类的尝试的支持,并且 7.3 增加了对捕获扩展缺失类的尝试的支持。请参阅 https://3v4l.org/BDnm9 了解这些的简短演示。

【讨论】:

  • 抓不到...能绕开吗?可以避免吗?我不能弹出致命错误。最糟糕的情况是我将不得不解析文件......
  • 鼓励编写插件的人至少将它们放在自定义命名空间中。如果您正在运行用户提供的代码,那么这始终是一种风险——您只有真正的约定(或审查过程)来防止它。
  • 以前没有使用过命名空间,所以我不得不问...如果他们使用与类名相同的命名空间怎么办?那么会有命名空间冲突吗?然后回到第一格?
  • 命名空间听起来绝对是一个不错的解决方案。如果完整的命名空间 + 类名与另一个重叠,您只会遇到冲突。它们确实旨在解决此类问题(以及帮助提高可读性)。我建议阅读它们并找出它们如何适合您的工作流程
  • 开发者应该使用他们独有的命名空间——这可能是他们的名字、用户名等等。类名作为命名空间没有多大意义——如果我个人编写两个插件,它们应该在同一个命名空间中
【解决方案2】:

最后,为了避免这个致命错误,我决定使用如下标记解析文件以获取第一个可用的类声明:

public function getClassInFile($filenpath) {
    $src = $this->uncommentFile($filenpath);
    $class ='';
    if (preg_match('/class[\s\n]+([a-zA-Z0-9_]+)[\s\na-zA-Z0-9_]+\{/', $src, $matches)) {
        $class = $matches[1];
    }
    return $class;
}

public function uncommentFile($filenpath) {
    $fileStr = file_get_contents($filenpath);
    $newStr  = '';

    $commentTokens = array(T_COMMENT);

    if (defined('T_DOC_COMMENT'))
    $commentTokens[] = T_DOC_COMMENT; // PHP 5
    if (defined('T_ML_COMMENT'))
    $commentTokens[] = T_ML_COMMENT;  // PHP 4

    $tokens = token_get_all($fileStr);

    foreach ($tokens as $token) {    
        if (is_array($token)) {
            if (in_array($token[0], $commentTokens))
            continue;

            $token = $token[1];
        }

        $newStr .= $token;
    }

    return $newStr; 
}

通过这种方式,我可以检查我期望找到的类是否在那里,或者那里的类是否已经存在,从而在这些情况下完全避免 include_once。 问题解决了。

【讨论】:

    猜你喜欢
    • 2023-03-10
    • 2016-11-03
    • 2020-03-20
    • 2020-10-10
    • 2019-12-25
    • 2019-04-15
    • 2019-08-23
    • 2021-08-17
    • 2020-06-21
    相关资源
    最近更新 更多