PHP说简单,可是要精通也不是一件简单的事。咱们除了会使用以外,还得知道它底层的工做原理。程序员
PHP是一种适用于web开发的动态语言。具体点说,就是一个用C语言实现包含大量组件的软件框架。更狭义点看,能够把它认为是一个强大的UI框架。web
了解PHP底层实现的目的是什么?动态语言要像用好首先得了解它,内存管理、框架模型值得咱们借鉴,经过扩展开发实现更多更强大的功能,优化咱们程序的性能。apache
多进程模型:因为PHP是多进程模型,不一样请求间互不干涉,这样保证了一个请求挂掉不会对全盘服务形成影响,固然,随着时代发展,PHP也早已支持多线程模型。编程
弱类型语言:和C/C++、Java、C#等语言不一样,PHP是一门弱类型语言。一个变量的类型并非一开始就肯定不变,运行中才会肯定并可能发生隐式或显式的类型转换,这种机制的灵活性在web开发中很是方便、高效,具体会在后面PHP变量中详述。api
引擎(Zend)+组件(ext)的模式下降内部耦合。数组
中间层(sapi)隔绝web server和PHP。性能优化
语法简单灵活,没有太多规范。缺点致使风格混杂,但再差的程序员也不会写出太离谱危害全局的程序。数据结构
PHP的核心架构以下图:多线程
从图上能够看出,PHP从下到上是一个4层体系:架构
Zend引擎:Zend总体用纯C实现,是PHP的内核部分,它将PHP代码翻译(词法、语法解析等一系列编译过程)为可执行opcode的 处理并实现相应的处理方法、实现了基本的数据结构(如hashtable、oo)、内存分配及管理、提供了相应的api方法供外部调用,是一切的核心,所 有的外围功能均围绕Zend实现。
Extensions:围绕着Zend引擎,extensions经过组件式的方式提供各类基础服务,咱们常见的各类内置函数(如array 系列)、标准库等都是经过extension来实现,用户也能够根据须要实现本身的extension以达到功能扩展、性能优化等目的(如贴吧正在使用的 PHP中间层、富文本解析就是extension的典型应用)。
Sapi:Sapi全称是Server Application Programming Interface,也就是服务端应用编程接口,Sapi经过一系列钩子函数,使得PHP能够和外围交互数据,这是PHP很是优雅和成功的一个设计,经过 sapi成功的将PHP自己和上层应用解耦隔离,PHP能够再也不考虑如何针对不一样应用进行兼容,而应用自己也能够针对本身的特色实现不一样的处理方式。
上层应用:这就是咱们平时编写的PHP程序,经过不一样的sapi方式获得各类各样的应用模式,如经过webserver实现web应用、在命令行下以脚本方式运行等等。
若是PHP是一辆车,那么车的框架就是PHP自己,Zend是车的引擎(发动机),Ext下面的各类组件就是车的轮子,Sapi能够看作是公路, 车能够跑在不一样类型的公路上,而一次PHP程序的执行就是汽车跑在公路上。所以,咱们须要:性能优异的引擎+合适的车轮+正确的跑道。
如前所述,Sapi经过经过一系列的接口,使得外部应用能够和PHP交换数据并能够根据不一样应用特色实现特定的处理方法,咱们常见的一些sapi有:
apache2handler:这是以apache做为webserver,采用mod_PHP模式运行时候的处理方式,也是如今应用最普遍的一种。
cgi:这是webserver和PHP直接的另外一种交互方式,也就是大名鼎鼎的fastcgi协议,在最近今年fastcgi+PHP获得愈来愈多的应用,也是异步webserver所惟一支持的方式。
cli:命令行调用的应用模式
咱们先来看看PHP代码的执行所通过的流程。
从图上能够看到,PHP实现了一个典型的动态语言执行过程:拿到一段代码后,通过词法解析、语法解析等阶段后,源程序会被翻译成一个个指令 (opcodes),而后ZEND虚拟机顺次执行这些指令完成操做。PHP自己是用C实现的,所以最终调用的也都是C的函数,实际上,咱们能够把PHP看 作是一个C开发的软件。
PHP的执行的核心是翻译出来的一条一条指令,也即opcode。
Opcode是PHP程序执行的最基本单位。一个opcode由两个参数(op1,op2)、返回值和处理函数组成。PHP程序最终被翻译为一组opcode处理函数的顺序执行。
常见的几个处理函数:
1 |
ZEND_ASSIGN_SPEC_CV_CV_HANDLER : 变量分配 ( $a = $b ) |
2 |
ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER:函数调用 |
3 |
ZEND_CONCAT_SPEC_CV_CV_HANDLER:字符串拼接 $a . $b |
4 |
ZEND_ADD_SPEC_CV_CONST_HANDLER: 加法运算 $a +2 |
5 |
ZEND_IS_EQUAL_SPEC_CV_CONST:判断相等 $a ==1 |
6 |
ZEND_IS_IDENTICAL_SPEC_CV_CONST:判断相等 $a ===1 |
HashTable是zend的核心数据结构,在PHP里面几乎并用来实现全部常见功能,咱们知道的PHP数组便是其典型应用,此外,在zend内部,如函数符号表、全局变量等也都是基于hash table来实现。
PHP的hash table具备以下特色:
支持典型的key->value查询
能够当作数组使用
添加、删除节点是O(1)复杂度
key支持混合类型:同时存在关联数组合索引数组
Value支持混合类型:array (“string”,2332)
支持线性遍历:如foreach
Zend hash table实现了典型的hash表散列结构,同时经过附加一个双向链表,提供了正向、反向遍历数组的功能。其结构以下图:
能够看到,在hash table中既有key->value形式的散列结构,也有双向链表模式,使得它可以很是方便的支持快速查找和线性遍历。
散列结构:Zend的散列结构是典型的hash表模型,经过链表的方式来解决冲突。须要注意的是zend的hash table是一个自增加的数据结构,当hash表数目满了以后,其自己会动态以2倍的方式扩容并从新元素位置。初始大小均为8。另外,在进行 key->value快速查找时候,zend自己还作了一些优化,经过空间换时间的方式加快速度。好比在每一个元素中都会用一个变量 nKeyLength标识key的长度以做快速断定。
双向链表:Zend hash table经过一个链表结构,实现了元素的线性遍历。理论上,作遍历使用单向链表就够了,之因此使用双向链表,主要目的是为了快速删除,避免遍历。 Zend hash table是一种复合型的结构,做为数组使用时,即支持常见的关联数组也可以做为顺序索引数字来使用,甚至容许2者的混合。
PHP关联数组:关联数组是典型的hash_table应用。一次查询过程通过以下几步(从代码能够看出,这是一个常见的hash查询过程并增长一些快速断定加速查找。):
01 |
getKeyHashValue h; |
02 |
index = n & nTableMask; |
03 |
Bucket *p = arBucket[index]; |
04 |
while (p) { |
05 |
if ((p->h == h) && (p->nKeyLength == nKeyLength)) { |
06 |
RETURN p->data; |
07 |
} |
08 |
p=p->next; |
09 |
} |
10 |
RETURN FALTURE; |
PHP索引数组:索引数组就是咱们常见的数组,经过下标访问。例如 $arr[0],Zend HashTable内部进行了归一化处理,对于index类型key一样分配了hash值和nKeyLength(为0)。内部成员变量 nNextFreeElement就是当前分配到的最大id,每次push后自动加一。正是这种归一化处理,PHP才可以实现关联和非关联的混合。因为 push操做的特殊性,索引key在PHP数组中前后顺序并非经过下标大小来决定,而是由push的前后决定。例如 $arr[1] = 2; $arr[2] = 3;对于double类型的key,Zend HashTable会将他当作索引key处理