今天绝对是兴奋的一天,不仅仅是周五这个特殊的日子(周六日可以休息啦),也不是弄清了某wordpress插件的功能流程,更不是再次买到了想吃很久的手撕牛肉,而是真正解决了一直以来(嘿嘿,其实时间不长)对PHP中变量引用和函数传参的疑惑。
故事源于一个巧合,我在查PHP变量作用域的东西时看到的文章(后面再附上地址,无意侵权作者大大)。因为这篇只讲变量和函数传参的,所以其他的不涉及了(也无力涉及哈哈)。
看这么一句
$a = 'abc';
当我们定义一个变量时,PHP会为我们做两件事情
- 申请一个zval结构体,那什么是zval呢
这个就是zval的C语言定义,在PHP中变量都是用这个结构体来存储,其中成员zvalue_value是一个联合体,用以存储底层强类型变量,这种结构也是实现PHP弱类型的关键,在这里只需要清楚有这么个结构体就可以了。struct _zval_struct { /* Variable information */ zvalue_value value; /* value */ zend_uint refcount; zend_uchar type; /* active type */ zend_uchar is_ref; };
- zval结构体存实际内容(最最里面其实是那个union体),令变量名符号a指向这个zval,然后将a存在一个symbol_table中,这个symbol_table跟变量的作用域有关,这里不做讨论,只需知道一个变量是一个符号指向一个zval结构体就好。
$a = 12;
$b = $a;
执行到第二句时,会在symbol_table中新加入b,令b指向a所指向的那个zval结构体,这个zval的refcount加1, 到这根据以往的知识可能就有疑问了,b指向a指向的结构体不就是引用了嘛,再看下面代码:
$a = 12; $b = &$a;
那这算什么?执行第二句时,除了在symbol_table中加入b令其指向a指向的那个zval并使refcount加1外,还会改变该zval中is_ref值为1(默认为零应该),is_ref是个bool值,用来标识这个变量是否是属于引用集合(reference set),这样这个zval就与一开始不太一样了。
来看更复杂的:
$a = 12;
$b = $a;
$c = &$a;
前两行执行完与前面第一个例子是一样的,此时$a和$b指向的zval的refcount值为2,is_ref为0,表示有两个symbol指向这个zval, is_ref为0,表示zval不属于引用集合,当第三行执行时时,发现zval的refcount不为1且不是引用集合,那么便为$b复制一个zval结构体,同时$a的zval的refcount减1,又因为多了$c指向原来的zval,所以又refcount加1,还是2,同时$a也是$c的zval的is_ref置1,表示这个zval属于引用集合了,$b的zval则是新的zval(refcount=1,is_ref=0)。
第2、3行交换位置最后的结果相同,只不过zval的变化顺序不同,最终仍然是$a与$c指向同一个zval(refcount=2,is_ref=1),$b指向的zval(refcount=1,is_ref=0)。
再有$d、$e等原理一样。
当更改变量时也会发生zval的新建与变化,原理同上,不细讨论了,可参见:
- http://php.net/manual/zh/features.gc.refcounting-basics.php(复合类型的变量也有讲到)
- http://www.laruence.com/2008/09/19/520.html(L大大写的真的很棒)
当我们明白了这个机制的时候,函数传参就迎刃而解了:
$a = 12;
function change($k){
$k = 1;
}
change(&$a); //相当于$k = &$a,接下来就知道怎么办了吧,$a的值是可以改变的
change($a); //相当于$k = $a,你看,还用我说么?
好的,到这就差不多了,要是我写的有点乱不好懂(不是深奥,我有自知之明的),好好研究下上面两个网址。
恩,这篇写的好累,休息休息
来源:oschina
链接:https://my.oschina.net/u/1404256/blog/178411