【问题标题】:In PHP, why does "or die()" work, but "or return" doesn't?在 PHP 中,为什么“or die()”有效,而“or return”无效?
【发布时间】:2013-01-01 19:46:30
【问题描述】:

在PHP中,当遇到某些错误时,您可以通过调用or die退出来处理错误,如下所示:

$handle = fopen($location, "r") or die("Couldn't get handle");

使用die() 不是处理错误的好方法。我宁愿返回一个错误代码,以便父函数可以决定要做什么,而不是仅仅不优雅地结束脚本并向用户显示错误。

但是,当我尝试将or die 替换为or return 时,PHP 显示错误,如下所示:

$handle = fopen($location, "r") or return 0;

为什么or die() 有效,而or return 0 无效?

【问题讨论】:

    标签: php error-handling return internals parse-error


    【解决方案1】:

    感谢您提出这个问题,因为我不知道您不能在 PHP 中执行 or return。当我测试它时,我和你一样感到惊讶。这个问题给了我一个很好的借口,让我在 PHP 的内部进行一些研究和玩耍,这实际上很有趣。不过我不是PHP内部的专家,所以下面是外行对PHP内部的看法,虽然我觉得还算准确。


    or return 不起作用,因为return 不被语言解析器视为“表达式”——就这么简单。

    关键字 or 在 PHP 语言中被定义为一个名为 T_LOGICAL_OR 的标记,而似乎定义它的唯一表达式看起来像 this

     expr T_LOGICAL_OR { zend_do_boolean_or_begin(&$1, &$2 TSRMLS_CC); } expr { zend_do_boolean_or_end(&$$, &$1, &$4, &$2 TSRMLS_CC); }
    

    不要担心大括号中的位——它只是定义了实际的“或”逻辑是如何处理的。你剩下的是expr T_LOGICAL_OR expr,它只是说有一个表达式是一个有效的表达式,然后是T_LOGICAL_OR 标记,然后是另一个表达式。

    如您所料,解析器也定义了expr。它可以是r_variable,这意味着它是一个允许您读取的变量,也可以是expr_without_variable,这是一种可以用其他表达式组成表达式的奇特方式。

    您可以使用or die(),因为语言构造die(不是函数!)及其别名exit 都由标记T_EXIT 表示,并且T_EXIT 被认为是有效的expr_without_variable,而 return 语句 - 令牌 T_RETURN - 不是。

    现在,为什么 T_EXIT 被认为是一个表达式,而 T_RETURN 不是?老实说,我不知道。也许这只是为了允许您所询问的 or die() 构造而做出的设计选择。它曾经被广泛使用的事实 - 至少在教程之类的东西中,因为我无法处理大量生产代码 - 似乎暗示这可能是一个有意的选择。您必须询问语言开发人员才能确定。


    说了这么多,这应该没关系。虽然几年前or die() 结构在教程(见上文)中似乎无处不在,但实际上并不推荐它,因为它是“聪明代码”的一个例子。 or die() 不是它自己的构造,而是一个使用 - 有些人可能会说滥用 - or 运算符的两个副作用的技巧:

    • 它在运算符优先级列表中非常低,这意味着几乎所有其他表达式都会在它被评估之前被评估
    • 它是一个短路运算符,这意味着如果第一个操作数返回TRUE,则不会执行第二个操作数(or 之后的位),因为如果一个操作数在@987654353 中是TRUE @ 表达式,那么它们都是。

    有些人认为这种诡计是不利的,因为程序员更难阅读,但只在源代码中节省了几个字符的空间。由于程序员的时间很昂贵,而磁盘空间又很便宜,你可以理解为什么人们不喜欢这样。

    相反,您应该通过将代码扩展为完整的if 语句来明确您的意图:

    $handle = fopen($location, "r");
    if ($handle) {
      // process the file
    } else {
      return 0;
    }
    

    您甚至可以在if 语句中进行变量赋值。有些人仍然觉得这不可读,但大多数人(包括我自己)不同意:

    if ($handle = fopen($location, "r")) {
      // process the file
    } else {
      return 0;
    }
    

    最后一件事:按照惯例,将0 作为状态码返回表示成功,因此您可能希望返回一个不同的值来表示您无法打开文件。

    【讨论】:

    • 这就是它的有趣之处...Return 是一种语言结构,不能以 OP 所需的方式使用。而die 也是一种语言结构,但仍可用于上述所需方法。而 die 是 exit 的别名,可以使用另一种语言结构。 Return 是这一组中的奇数。
    • @MarkTomlin 我不认为return 是一个奇怪的人。 return 是一个语句,并且与ifforeach 等其他语句一起定义为这样(特别是unticked_statement)。它显然适合那个群体。我不确定为什么exit 会被视为表达式,因为将其分配给变量会调用函数并终止脚本。 exit 也是一个 void 函数,因此您只需将 NULL 分配给变量,这没有帮助。我真的认为它只是一个允许or die() 构造工作的表达式。很奇怪。
    【解决方案2】:

    Return 相当特殊 - 它不能像函数一样,因为它是退出函数的工具。想象一下:

    if(1==1) return();  // say what??
    

    如果是这样,return 必须是一个执行“双重退出”的函数,不仅留下自己的作用域,还留下调用者的作用域。因此 return 与表达式完全不同,它根本不能那样工作。

    现在理论上,return 可以是一个计算结果为(比如说)false 并且 然后 退出函数的表达式;也许以后的 php 版本会实现这个。

    同样的事情也适用于 goto 这将是一个作为后备工作的魅力;是的,回退是必要的,并且通常使代码可读,所以如果有人抱怨“聪明的代码”(这当然是一个好点),也许 php 应该有一些“官方”的方式来做这样的事情:

    connectMyDB() fallback return false;
    

    类似于 try...catch,更重要的是。就个人而言,我对“或”做这项工作会更满意,因为它与英语语法配合得很好:“连接或报告失败”。

    TLDR:您说的完全正确:return、goto、break - 它们都不起作用。原因很容易理解,但仍然很烦人。

    【讨论】:

      【解决方案3】:

      我也偶然发现过一次。我能找到的只有这个:

      https://bugs.php.net/bug.php?id=40712

      看看下面的评论:

      这不是错误

      我在文档中进行了搜索,我认为这是因为return 0 是一个语句,而die() 本质上是一个表达式。您无法运行 $handle = return 0;,但 $handle = fun(); 是有效代码。

      关于错误处理,我建议使用自定义代码或使用自定义处理程序和触发器。后者例如是described here

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-12-16
        • 2019-01-03
        • 2020-03-09
        • 2011-07-05
        • 2012-01-15
        • 2013-12-02
        • 1970-01-01
        相关资源
        最近更新 更多