前段时间用PHP开发了一个QQGame的后台发布系统(http://km.oa.com/post/167989),觉得使用PHP开发一些组内的工具是非常方便的。后来就打算把PHP了解的深入一点,要知道,C/C++程序员总是喜欢探究某语言底层的实现,难道是没被各类内存问题虐够?
想深入了解PHP的实现原理,推荐一本书:《TIPI:深入理解PHP内核》;
看完之后,针对变量在PHP中如何存放,做了些总结和研究,主要内容包括:PHP变量的内存布局,PHP弱类型的实现原理,PHP变量的类型和作用域等;
1. PHP变量的内存布局
下图是变量在内存中的布局:
1. PHP若类型的实现原理
PHP是一个弱类型、动态的脚本语言。弱类型是说PHP并不严格验证变量类型,在申明变量的时候不需要显示指明它保存的数据的类型。
动态脚本语言是说PHP的语言结构在运行期是可变的,例如我们在运行期require一个函数定义文件,会导致语言的函数表动态的改变。
大家都知道C/C++是强类型语言,即所有的变量在它被声明到最终销毁,都只能保存一种类型的数据。 那PHP是如何在Zend引擎的基础上实现弱类型的呢?
在PHP中,所有的变量都是用一个结构_zval_struct来保存的,在Zend/zend.h中可以看到_zval_struct的定义:
其中zvalue_value是保存数据的关键部分,是个union体,定义如下:
PHP根据_zval_struct中的type字段来储存一个变量的真正类型,然后根据type来选择如何获取zvalue_value的值。其中,type支持的数据类型如下:
对于数组和对象,type分别对应IS_ARRAY, IS_OBJECT, 相对应的则分别取_zval_struct.value.ht和_zval_struct .value.obj;
在PHP中,资源是个很特别的变量,任何不属于PHP内建的变量类型的变量,都会被看作成资源来进行保存,比如,数据库句柄,打开的文件句柄等等。
对于资源,Zend引擎会去取_zval_struct l.value.lval,此时的lval是个整型的指示器,然后PHP会再根据这个指示器在PHP内建的一个资源列表中查询相对应的资源。
借用这样的机制,PHP就实现了弱类型,因为对于Zend引擎来说,它所面对的永远都是同一种类型,即_zval_struct。
2. PHP变量的类型、作用域
变量的作用域:变量所起作用的范围;
大部分的 PHP 变量只有一个单独的范围。这个单独的范围跨度同样包含了 include 和 require 引入的文件。
例如: 变量 $a 将会在包含文件 file2.inc.php 中生效,打印出100;
a) 局部变量:
i. 多数PHP变量都是局部变量,在其作用域外无法引用,并且超出其作用域时自动销毁。
ii. 在当前文件主程序中定义的变量,其作用域限于当前文件的主程序,不能在其他文件或当前文件的局部函数中起作用。
iii. 在局部函数中定义的变量仅限于当前函数,当前文件中主程序、其他函数、其他文件中无法引用。(这点和C/C++一致)
b) 全局变量:
1. C/C++语言中,全局变量在函数中自动生效,除非被局部变量覆盖。
2. PHP 中,在函数中使用全局变量时必须使用“global”、 $globals["变量名称"]关键字申明为全局,否则视为局部变量。
ii. 在php程序执行时,系统会在内存中保留一块全局变量的区域。(这点和C/C++一致)
iii. 使用“global”关键字来申明全局数据的缺点:
全局数据把代码中原本单独的代码段都联系在一起了,这样的后果就是如果改变其中某一部分代码,可能就会导致其它部分出错。
1. 代码重用几乎是不可能的
如果一个函数依赖于全局变量,那么想在不同的环境中使用这个函数几乎是不可能的。另外一个问题就是你不能提取出这个函数,然后在其他的代码中使用。
2. 调试并解决问题是非常困难的
跟踪一个全局变量比跟踪一个非全局变量困难的多。
3. 理解这些代码将是非常难的事情
很难弄清楚一个全局变量是从哪里来,以及是用来做什么的。
c) 静态变量:
i. 静态变量仅在局部函数域中存在,但当程序执行离开此作用域时,其值并不丢失。
ii. 静态作用域对递归函数很有用。