PHP底层实现
来源:互联网 发布:阿里云代码托管 收费 编辑:程序博客网 时间:2024/05/21 05:59
PHP代码的执行过程:
-知其然知其所以然
PHP虽然是脚本语言,但是不是靠解释器解释的,而是靠zend虚拟机
PHP代码编译成了opcode,由zend虚拟机老执行opcode
但是opcode在PHP脚本执行结束后就会被自动清楚(跟java不同,java中的.class文件会存留下来)
- 思考:
opcode能否缓存?
PHP本身不支持但是其余加速器工具支持,能实现这一功能。
PHP底层是用C语言来写的,但是C语言是强类型语言,PHP是弱类型语言,如何实现的呢?
解压PHP的源码包会看到相应文件夹:
最核心的—Zend目录, 这是zend虚拟的实现. 包括栈,数据类型,编译器等,都在这实现.
最主要的main –PHP的一些内建函数,最主要函数都在这里放着.
最大的一个目录 ext – PHP的扩展.
Zend对变量的表示:
每个变量由变量名和变量值组成;
变量名存放在一个符号表中,符号表是一张哈希表,存放着变量的名和变量值的地址映射,类似于关联数组,形成一一对应的关系。
变量值存放在一个zval结构体中
{value: [联合体] ,联合体的内容可能是C语言中的long,double,hashtable...type: 变量类型 , IS_NULL,IS_BOOL,IS_STRING...... IS_RESOURCErefcount_gcis_ref_gc }
如:$a = 3;{value : [long lval = 3]type: IS_LONG}$a = 3.5{value: [double dval = 3.5]type:IS_DOUBLE}
typedef union _zvalue_value{ long lval; /*long型值*/ double dval; /*double型值*/ struct{ char *val; int len; }str; HashTable *ht; /*hash table 值*/ zend)object_value obj;}zvalue_value;
- 疑问:
PHP中有8中数据类型(bool int string float , obj array ,resource null),为什么zval->value联合体中只有5种?
1:NULL,直接看zval->type=IS_UNLL,则跳过,不必设置value的值;
2:bool型,zval->type=IS_BOOL,再设置值为1或0;
3:Resources型,资源型,往往是在服务器上打开一个接口,如果 文件读取接口
zval->type = IS_RESOURCE, zval->tyoe.lval = 服务器上打开的接口的编号
- 发现:
PHP中,字符串类型,长度是已经缓存的,调用strlen时,系统可以直接返回其长度,不必计算。
变量的赋值与引用
在赋值时,值传递,并没有再次产生结构体,而是2个变量公用1个结构体,此时两个变量地址指向一个结构体,结构体中refcount_gc值为2。
- 思考:
a,b指向同一个结构体,那么,修改a或者b对方会不会受干扰?
不会。
因为2者有一方修改时就会造成结构体的分裂,这种特点称为cow(copy on write),写时复制。
当变量引用赋值时,双方共用一个结构体,此时结构体中is_ref_gc值自增1,初始值为0。
引用赋值时不会出现写时复制,而是两个变量共享一个结构体,都有操作它的权利。
引用时的一些怪现象
<?php$a = array(2,3,4,5);$tem = $a;$x = &$a[1];$a[1] = 999;echo $tem[1]; //3
<?php$a = array(2,3,4,5);$x = &$a[1];$tem = $a;$a[1] = 999;echo $tem[1]; //999
循环数组时的怪现象
循环遍历数组时,遍历的不是数组本身,而是在内存中复制了一份arraycopy,
如果遍历时有写操作,则会发生写时复制,指针重新指向分离出的数组顶端。
<?php$arr = array('a','b','c','d');foreach ($arr as &$v){ //空操作}//相当于$v = &$arr[3];foreach ($arr as $v){ //arrcopy的每一个值取出来赋给$v print_r($arr);}//分别是arrcopy的[0],[1],[2],[3]赋给了$v,//arrcopy[0]是a,a赋给了$v,而$v是引用的arr[3];所以arr[3]值收到了影响,-->abca,依次循环
- 注意
数组使用时,慎用引用;
foreach使用后,不会把数组的内部指针重置,使用数组时,不要假象内部指针正好指向头部。
也可以在foreach后reset一下数组,重置指针。
函数执行时的栈变化
函数在执行时,编译过后会根据函数的参数,局部变量等,生成一个执行环境的结构体,结构体入栈,
_zend_execute_data {}结构体
这个结构体中,还有2个重要的信息:
{
*op_array ——>是函数的执行步骤
*hash_table—->symbol_table 这个函数对应的符号表
}
函数编译后的opcode称为op_array(执行逻辑),开始执行——以入栈的环境结构体为环境来执行。
注意
一个函数可能调用多次,栈中可能有某函数的多个执行环境入栈,但这个函数的op_array只有一个。思考
一个函数,递归调用自己三次,在栈上肯定有三个execute_data生成,但是这三个execute_data对用几个*op_array?
函数编译完了生成一份*op_array,因为函数的执行逻辑是固定的。
生成了几个几个symbol_table?
三个符号表。
函数中的静态变量是如何形成的
一个函数编译出来值存在一份op_array,静态变量存在于执行过程op_array,并不存在于结构体入栈的符号表中。
不管调用多少次静态变量,调用的只是一个共同的op_array中的变量。
常量的实现
define一个常量时其全局符号表就一个。
变量的符号表可以有多个,但是常量的符号表就一个。
对象的实现
当每new一个对象的时候,对象统一放到了一个hash表(对象池)里面
typedef struct _zend_object_value{ zend_object_handle handle; zend_object_handlers *handlers;}zend_object_value;
对象定义的就两个值,handle是一个指向对象池里的地址,相当于一个对象的指针,
定义一个对象其实生成了一个指针,这个指针再次指向对象。
$obj = new human();value:{handle:23851}//handle指向对象地址type:IS_OBJECTrefcount_gc:1is_ref_gc:0
对应的对象池里的存储:
23851{age:24name:lisi...}
- 实例
<?phpclass Dog{ public $leg = 4;}$dog = new Dog();$d2 = $dog;$d2->leg = 5;echo $dog->leg,$d2->leg;//5,5
问:$dog
是一个对象吗? $dog
的内容:
{
handle —指向—->[hash表{leg:4}]
}
new的$dog
指向一个handle,handle再次指向一个对象池里的Dog,
当又有了$d2
,也指向这个zval(同一个handle),$d2
并没有改变这个zval的值,
而是通过zval的指引改变了这个对象池里对象的值,所以$dog
的值也被改变。
- 注意
这并不是说$d2
与$dog
是引用传递的关系,如果$d2=false
的话并不会使$dog
为空,
而是$d2
自己的指向了false,$dog
还是指向之前的handle。
只是他们共同通过一个handle去指向一个对象池里的对象。
内存管理与垃圾回收
注意
PHP封装了对系统内存的请求,不要直接用malloc直接请求内存。
PHP的hashtable太强大!
- PHP底层实现
- PHP 底层实现
- Php变量底层实现
- PHP的底层实现
- php底层变量的实现
- php底层变量的实现
- PHP的数组底层实现
- php底层HashTable的实现
- PHP的数组底层实现
- include|require(_once)? 在php底层实现
- PHP - String字符串的底层实现原理
- PHP底层之PHP底层工作原理
- php底层深度探索
- 探究php底层运行机制
- 探究PHP底层运行机制
- 探究PHP底层运行机制
- PHP底层工作原理
- PHP底层运行机制
- bootstrap在线设计,快速定制自己的网站!
- java 前台页面传到后台 乱码解决方案
- shopex虚拟分类后台不更新前台能更新问题
- An error was encountered while running(Domain=LaunchSerivcesError, Code=0)
- (最大化平均值)poj2976,Dropping tests
- PHP底层实现
- 转载大神IOS开发系列【16】--通讯录、蓝牙、内购、GameCenter、iCloud、Passbook
- 数据库变量绑定
- 汉化软件Radialix 3的使用
- Linux 升级内核步骤和方法
- Coin Change(UVA 674)
- ORA-12638: 身份证明检索失败 的解决办法
- poj 2001 Shortest Prefixes
- iOS 用类别给类加属性以及原理