参考文献:https://blog.csdn.net/zhuocr/article/details/60328967 、《PHP7内核剖析》秦鹏/著
SAPI:Server Application Programming Interface 服务器端应用编程端口。它就是PHP与其它应用交互的接口,PHP脚本要执行有很多种方式,通过Web服务器,或者直接在命令行下,也可以嵌入在其他程序中。
SAPI提供了一个和外部通信的接口,常见的SAPI有:cgi 、fast-cgi、cli、isapi、apache 模块的 DLL
目录
1、常见的SAPI介绍
CGI
CGI即通用网关接口(Common Gateway Interface),一段把网页和web服务器中的执行程序连接起来的程序。它把HTML接收的指令传递给服务器执行程序,再把服务器执行程序的结果返还给HTML页。CGI的跨平台性也极强,几乎能在任何操作系统上运行。
CGI在遇到用户连接请求时要先创建CGI的子进程,然后处理请求,处理完后结束这个子进程。这就是fork-and-execute模式。不过有多少连接请求就有多少CGI子进程,会造成资源的拥挤。
fast-cgi
CGI的升级版本,常住型的CGI,前面介绍过。PHP的Fpm就是FastCGI Process Manager,全称为PHP FastCGI进程管理器。FastCGI的工作原理:
(1)Web Server启动时载入FastCGI进程管理器(IIS ISAPI或Apache Module)
(2)FastCGI进程管理器自身初始化,启动多个CGI解释器进程(可见多个php-cgi)并等待来自Web Server的连接。
(3)当客户端请求到达Web Server时,FastCGI进程管理器选择并连接到一个CGI解释器。Web server将CGI环境变量和标准输入发送到FastCGI子进程php-cgi。
(4)FastCGI子进程完成处理后将标准输出和错误信息从同一连接返回Web Server。当FastCGI子进程关闭连接时,请求便告处理完成。FastCGI子进程接着等待并处理来自FastCGI进程管理器(运行在Web Server中)的下一个连接。 在CGI模式中,php-cgi在此便退出了。
在上述情况中,你可以想象CGI通常有多慢。每一个Web 请求PHP都必须重新解析php.ini、重新载入全部扩展并重初始化全部数据结构。使用FastCGI,所有这些都只在进程启动时发生一次。一个额外的 好处是,持续数据库连接(Persistent database connection)可以工作。
APACHE2HANDLER
PHP作为Apache模块,Apache服务器在系统启动后,预先生成多个进程副本驻留在内存中,一旦有请求出现,就立即使用这些空余的子进程进行处理,这样就不存在生成子进程造成的延迟了。这些服务器副本在处理完一次HTTP请求之后并不立即退出,而是停留在计 算机中等待下次请求。对于客户浏览器的请求反应更快,性能较高。
apache模块的DLL:
该运行模式是我们以前在windows环境下使用apache服务器经常使用的,而在模块化(DLL)中,PHP是与Web服务器一起启动并运行的。(是apache在CGI的基础上进行的一种扩展,加快PHP的运行效率)
ISAPI:
ISAPI即Internet Server Application Program Interface,是微软提供的一套面向Internet服务的API接口.一个ISAPI的DLL,可以在被用户请求激活后长驻内存,等待用户的另一个请求,还可以在一个DLL里设置多个用户请求处理函数,此外,ISAPI的DLL应用程序和WWW服务器处于同一个进程中,效率要显著高于CGI。
cli:
cli是php的命令行运行模式,大家经常会使用它,但是可能并没有注意到(例如:我们在linux下经常使用 “php -m”查找PHP安装了那些扩展就是PHP命令行运行模式;
在windows下使用cli模式从命令行进入www目录,然后运行。
Embed
如果我们自己的第三方程序想使用PHP,那么我们就需要这类SAPI,它在编译后就是普通的库文件(可以选择编译为静态库、共享库),我们可以在其它C/C++应用中调用PHP提供的API,甚至可以提供给其它语言处理。
2、Embed
编译PHP时通过 --enable-embed=[shared|static]指定库类型,默认是共享库。编译之后可以在PHP安装位置的/lib目录下看到生成的库文件,同时在/include/php/sapi目录下会生成一个存放Embed头文件的目录。
Embed的实现逻辑非常简单,只是把PHP生命周期的几个处理函数进行了封装,它对外提供了两个API。
2.1 php_embed_init()
这个接口主要进行PHP框架的初始化操作,比如TSRM、初始化SAPI、初始化信号处理,另外它还完成了非常重要的两个操作,那就是php_module_startup()、php_request_startup()。
EMBED_SAPI_API int php_embed_init(int argc, char **argv)
{
zend_llist global_vars;
#ifdef ZTS
//初始化SAPI
tsrm_startup(1, 1, 0, NULL);
(void)ts_resource(0);
ZEND_TSRMLS_CACHE_UPDATE();
#endif
zend_signal_startup();
//初始化SAPI
sapi_startup(&php_embed_module);
...
//进入module startup阶段
if (php_embed_module.startup(&php_embed_module)==FAILURE) {
return FAILURE;
}
...
//请求request startup阶段
if (php_request_startup()==FAILURE) {
php_module_shutdown();
return FAILURE;
}
...
}
在第三方应用中嵌入PHP时首先需要调用这个接口,然后就可以使用PHP/Zend提供的API完成PHP脚本的执行了。
2.2 php_embed_shutdown()
此接口与php_embed_init()对应,主要完成PHP框架的关闭收尾工作,包括request shutdown、module shutdown两个阶段的操作。
EMBED_SAPI_API void php_embed_shutdown(void)
{
//关闭请求,request shutdown阶段
php_request_shutdown((void *) 0);
//关闭模块
php_module_shutdown();
sapi_shutdown();
#ifdef ZTS
tsrm_shutdown();
TSRMLS_CACHE_RESET();
#endif
if (php_embed_module.ini_entries) {
free(php_embed_module.ini_entries);
php_embed_module.ini_entries = NULL;
}
}