-
引擎(Zend)+组件(ext)的模式降低内部耦合
-
中间层(sapi)隔绝web server和php
-
Sapi主要有apache2handler,cgi,cli等接口
-
PHP类型
php的变量都保存在一个结构中
其中真正的值都保存在zvalue_value中
typedef struct _zval_struct { zvalue_value value; zend_uint refcount; zend_uchar type; zend_uchar is_ref; } zval;
refcount为引用计数,zval.type为Is_Long或者Is_Bool时,则去取zval.value.lval或dval,字符串取str,资源也会去lval,像是对应资源链表的偏移值。zvalue_value的数据结构为union
typedef union _zvalue_value { long lval; double dval; struct { char *val; int len; } str; HashTable *ht; zend_object_value obj; } zvalue_value;
-
PHP变量
给变量赋值时,PHP会分配一个zval来存值,而zval的结构中是没有变量名的。
然后变量名和指向这个zval的指针会填入一个数组hashtable中。
不同的变量会保存在不同的定义域的活动符号表中(活动符号表和全局符号表)(symbol_table和active_symbol_table)(在函数中,我们可以通过显式申明global来使用全局变量。在active_symbol_table中创建symbol_table中同名变量的引用,如果symbol_table中没有同名变量则会先创建) $var=”a”;
var(全局符号表)=>*zval指(针)
在函数b中的变量,则保存在b符号表中,而函数的参数$var也是保存在a符号表中,不过会把其指针指向一份全局变量$var的copy的zval
$var1=$var;
复制给另一个变量,其实只是添加了同一个指针,引用计数refcount+1
$var=1;
这样会修改一个变量,执行copy on write机制,如果refcount>1,则会复制一个新的zval,将原有refcount-1,并修改符号表指针。
上面这个是复制,而当引用时:
$a=”v”; $b=&$a; $b=1;
这时修改一个is_ref=1的zval时,会执行change on write,当is_ref=1是,不会复制新的zval
因为is_ref是bool型,所以当其zval存在is_ref时,就表示所有指向其的符号表都是引用,复制的要分离出去。还有一种情况,同时又引用和赋值的情况,这个时候会分离出复制新的zval,来确保is_ref=1时不会同时存在非引用的情况。
可以看这儿,(那如果是混合了引用(assign-by-reference)和普通赋值(assign-by-value)的脚本,又是什么情况呢?)
-
unset只会清除符号表,而不会去管zval,zval被GC只看refcount是否为0.
-
引用传递
function do_zval_test(&$s){ $s = "after"; return $s; } $a = "before"; $b = do_zval_test($a);
在函数调用结束之后 $a的is_ref恢复成0
-
引用返回
“引用返回用在当想用函数找到引用应该被绑定在哪一个变量上面时”
function &find_node($key,&$tree){ $item = &$tree[$key]; return $item; } $tree = array(1=>'one',2=>'two',3=>'three'); $node =& find_node(3,$tree); $node ='new';
为了使用引用返回,必须在函数定义和函数调用的地方都显式的使用&符号
-
global关键字
- 函数中声明global,会在函数内部生成一个局部的变量,并与全局的变量建立引用。
- 函数中对global变量的任何更改操作都会间接更改全局变量的值。
- 函数unset局部变量不会影响global,而只是解除与全局变量的绑定。unset只是删除函数内部符号表中var符号,而不是删除全局的。同时,更新原zval的refcount值和is_ref引用标志(引用解绑)
-
CGI和FastCGI两个协议
CGI是webserver与后台语言交互的协议,有了这个协议,开发者可以使用任何语言处理webserve发来的请求。但是它处理每个请求的时候都会重新fork一个进程,请求结束后就结束进程,性能低下。
所以有了FastCGI,在一个进程里处理多个请求。
FPM是FastCGI的实现,任何实现了FastCGI协议的webserver都能与之通信。FPM是一个PHP 进程管理器,包含master和worker进程两种,master进程只有一个,负责监听端口,接受来自webserver的请求,而worker进程一般有多个,每个进程内部都有php解释器,可以执行PHP代码。
master进程接受到请求。
master根据配置指派worker进程处理请求,如果没有可用进程,返回502.
worker进程处理请求,如果超时,返回504.
请求处理结束,返回结果。
Nginx是如何发送请求给fpm的master进程呢?
Nginx不仅仅是web服务器,也是一个proxy服务器,除了http请求的代理,也可以进行其他协议的代理,包括fpm相关的fastcgi协议。
Nginx提供了fastcgi模块来将http请求映射为对应的fastcgi请求。
Nginx的fastcgi模块提供了fastcgi_param指令来处理这些映射关系,主要是将Nginx里面的变量翻译成PHP中能理解的变量。
此外还有个fastcgi_pass指令,用于指定fpm进程监听的地址,Nginx会把所有的php请求翻译成fastcgi请求后再发送到这个地址。
这儿的配置可以看到,我们新建了虚拟主机,通过location指令,将所有php结尾的请求都交给了fastcgi模块处理,从而把所有php请求都交给了fpm处理,从而完成Nginx到fpm的闭环。