Code前端首页关于Code前端联系我们

PHP7内核学习和理解的参考

terry 2年前 (2023-09-25) 阅读数 50 #后端开发

参考(REFERENT)是PHP5中的一个标志,但在PHP7之后我们将其转换为新类型:IS_REFERNCE。然而,参考是一个非常普遍的应用。所以这个改变带来了很多的改变,同时也因为我们有时候忽略了处理这种事情而导致了我们开发PHP7的时候出现了很多的bug。

最简单的情况是当您处理不同类型时。从现在开始,我们需要对这种新类型进行更多的思考。例如,在 PHP7 中,这种代码形式已经变得很常见:

try_again:
swtich (Z_TYPE_P(zv)) {
     case IS_TRING:
     break;
     case IS_ARRAY:
     break;
    ...
     case IS_REFERENCE:
     zv = Z_REFVAL_P(zv); //解引用
     goto try_again;
     break;
}
复制代码

如果你自己写扩展,如果你忘记考虑这个新类型,就会出现问题。

为什么?

所以自从有了这个新的类型之后,类型会带来这么多问题,为什么要用引用来给出一个关于类型的情况呢?为什么不使用标志位?
总之,我们必须这样做。 -_#
之前说过,Hashtable直接存储zval,那么在符号表中,两个zval如何共享相同的值呢?对于字符串这样的复杂类型,似乎我们可以在zend_refcounted结构体中添加一个标志位来表明它是一个引用来解决这个问题。不过,这样也会遇到Change On Write问题。复制,但是我们知道,在PHP7中,有些类型是直接存储在zval中的,比如IS_LONG,但是引用类型需要引用计数,那么如何表达一个IS_LONG和IS_REFERNCE的zval呢?
为此我们创建了这个新类型: PHP7内核学习理解之Reference

如图所示,reference 是一个新类型:zend_reference。对于 IS_REFERNCE 类型的 zval,zval.value.ref 是一个指向 zend_reference 的指针,包含引用号和一个 zval,具体的 zval 值存储在 zval.value.ref->val 中。
所以对于IS_LONG的引用,使用一个IS_REFERNCE类型的Zval,它指向一个zend_reference,而这个zend_reference->val是一个IS_LONG类型的Zval。

编写时更改

PHP 使用引用计数来轻松进行垃圾回收。考虑以下代码:

<?php
1\. $val = "laruence";
2\. $ref = &$val;
3\. $copy = $val;
?>
复制代码

ref 和 ref 以及 ref 和 val 是指向相同 Zval 的引用。在PHP5中我们用引用计数为2,引用标志为1来表示这种情况。当val被复制到val时,val被复制到val,复制到copy(第3行),当时我们发现$val是一个引用的数字大于1,所以写的时候需要改变,即分离。所以我们必须复制这个Zval。

在 PHP7 中,情况变得容易得多。首先,当引用赋值给 $ref 时(第 2 行),会生成一个 IS_REFERNCE 类型,然后由于此时有两个变量引用,所以 zend_reference 结构体的引用号 zval 为 .value.ref->gc 。 refcount 为 2。

如果随后将该值赋值给 copy(line3),则当找到 copy(line3) 时,会找到 val 作为引用。所以让$copy指向zval.value.ref->val,即字符串值为laruence的zval,然后将zval的引用计数加1,即zval.value.ref->val.value.str 。 gc.refcount 为 2。不生成任何副本。

这有效解决了上一章提到的PHP5的经典问题。比如我们在 PHP7 下运行上一章的问题,得到的结果是:

$ php-7.0/sapi/cli/php /tmp/1.php
Used 0.00021380008539
Used 0.00020173048281
复制代码

可以看出,没有发生复制,所以不存在性能问题。

作者:PHP开源社区

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门