【问题标题】:Are you supposed to set up a 'pass by reference' variable beforehand?您是否应该事先设置“按引用传递”变量?
【发布时间】:2017-01-19 10:38:16
【问题描述】:

PHP 文档中的示例:

http://php.net/manual/en/function.openssl-public-encrypt.php

openssl_public_encrypt ( 
    string $data , string &$crypted , mixed $key [, int $padding =         
    OPENSSL_PKCS1_PADDING ] 
)

在这种情况下,它期望 $crypted 作为传递的引用。该功能无需预先设置 $crypted 即可工作。

在很多示例中,我看到 $crypted 在调用函数之前被初始化。

var $crypted = '';

这是真的需要(或者技术上更正确)还是即使它是“通过引用传递”变量也可以不设置变量?

【问题讨论】:

    标签: php pass-by-reference


    【解决方案1】:

    更多的是评论而不是答案 - 但对于评论框来说太长了。还有一个引人入胜的问题,让我问“为什么”。

    直接回答:

    • 不声明变量不会在任一版本中导致“通知” - 因此从技术上讲,它不是必需的。
    • 因此,声明是一种优先事项。像Alex 一样,我更喜欢声明可读性和清晰性。 (我也更喜欢避免使用像 extract 这样的函数,它们会从无处创建变量。)但我相信其他人会不同意。

    但这导致了为什么它会这样工作的问题?我很想知道。


    首先要注意的是,通过引用传递的变量和变量从 PHP 5 到 PHP 7 发生了很大变化。这里有一篇有趣的文章:https://nikic.github.io/2015/05/05/Internal-value-representation-in-PHP-7-part-1.html 解释了这些差异。

    但是在这两种 PHP 中,变量都存储在 zval 空间中,并带有一个引用计数器,用于计算变量被引用的次数。 (上面的那个文档是我在解释它时找到的最好的例子,但manual 中还有更多)

    当您扩展 PHP 并添加新函数时,您可以使用 zend_parse_parameters 从 Zend API 获取变量。这意味着传递给函数的变量也存储在 zval 容器中,并在 API 和扩展之间传递。 (Further reading - 这与 PHP5 有关,但与 PHP7 相同)。

    四个代码示例解释了我的理解(我很高兴得到纠正):

    1)

    function increment($i) {
        $i++;
    }
    
    increment(3);
    

    变量3 没有声明(很明显),但它需要在zval 容器中才能传递给API。这表明调用函数能够在 zval 容器中生成变量。 (因为它不是一个真正的变量,我认为它是“不可变的”,但这是一个猜测。)

    2)

    function increment($i) {
        $i++;
    }
    
    increment($y);
    

    此错误为 $y 未首先声明。 Notice: Undefined variable: y in test.php。所以在这里它无法生成$y 并放入本地范围。

    3)

    function increment($i) {
        $i++;
    }
    
    $y = 0;
    increment($y);
    

    变量被声明并放入 zval 容器中。调用函数时,会增加 refcount,以便将其传递给 API。按预期工作。

    4)

    function increment(&$i) {
        $i++;
    }
    
    increment(3);
    

    此错误Fatal error: Only variables can be passed by reference in test.php。这告诉我们变量3zend_parse_parameters 拒绝,因为它知道它是不可变的,或者调用函数知道该函数需要一个引用变量。

    5)

    function increment(&$i) {
        $i++;
    }
    
    $y = 0;
    increment($y);
    

    变量$y第一行放入当前作用域的zval容器中,生成$y的引用指针(增加两次refcount)。

    6)

    function increment(&$i) {
        $i++;
    }
    
    increment($y);
    

    这是梨形 IMO 的去处:虽然没有声明变量,但 $y == 1。这意味着该函数必须在当前范围内创建变量并放入zval容器中,并生成指向$y的引用指针(增加refcount)。

    要做到这一点,它必须知道该函数通过引用期待一个值 - 否则为什么行为与 2 不同?

    这些都不能解释为什么它不同。正因为它可以创建一个变量(如示例1所示),为什么示例2中没有,而示例6中却可以?

    【讨论】:

      【解决方案2】:

      虽然会创建变量,但出于多种原因,我更喜欢显式设置它:

      • 该变量可能在函数中用于从中获取数据
      • 该变量可能没有在函数中设置,所以我不需要在以后打扰isset
      • 变量可能已经在代码中的其他地方定义,因此如果函数不更改变量,则存在传递垃圾或接收垃圾的风险
      • 肯定有几个我忘了提,所以我们把它命名为直觉

      不确定它们中的任何一个是否特别适用于openssl_public_encrypt,但是由于您习惯于在函数调用之前设置变量,因此更改此行为需要付出比应有的更多努力和关注。

      【讨论】:

      • 在这种情况下,我知道该函数不会使用该变量从中获取数据。而且我知道它正确地返回了变量的值。它也包含在它自己的方法范围内。所以我觉得在这种情况下为了减少行数而把它排除在外是有道理的。不过,我想知道其他人对此有何看法。感谢您的回复。
      • 是的,我很想投票以基于意见的方式关闭它=)
      • 我不确定在事先省略变量初始化时可能没有意识到的任何技术后果。这就是为什么我决定提出这个问题。感谢您的帮助!
      猜你喜欢
      • 2010-09-05
      • 2014-12-06
      • 2021-07-11
      相关资源
      最近更新 更多