宝哥软件园

谈谈你对Zend SAPIs(Zend SAPI内部)的理解

编辑:宝哥软件园 来源:互联网 时间:2021-09-22

SAPI: Server抽象API,学过PHP架构的同学应该知道这个东西的重要性,它提供了一个接口,让PHP可以和其他应用交换数据。本文不会详细介绍每一个PHP的SAPI,只针对最简单的CGI SAPI解释SAPI的机制。

首先,让我们看一下PHP的架构图:

图PHP架构。

SAPI为外部交流提供了一个接口。对于PHP5.2,默认提供多种SAPI,比如apache的mod_php5和CGI,IIS的ISAPI,Shell的CLI。本文从CGI SAPI开始,介绍了SAPI的机制。CGI虽然简单,但不用担心,它包含了大部分内容,足以让你深刻理解SAPI的工作原理。

要定义一个SAPI,首先定义一个sapi_module_struct,并检查PHP-src/SAPI/CGI/CGI _ main . c :

*/static SAPI _ module _ struct CGI _ SAPI _ module={ # if PHP _ FASTCGI ' CGI-fcgi ',/* name */'CGI/FastCGI ',/*俏丽name */#else 'cgi ',/* name */'CGI ',/*俏丽name */#endif php_cgi_startup,/* startup */PHP _ module _ shut down _ wrapper,/* shutdown */NULL,/* activate */SAPI _ CGI _ deactive,/* deactive /*读取POST数据*/sapi_cgi_read_cookies,/*读取Cookies */SAPI _ CGI _ register _ variables,/*注册服务器变量*/sapi_cgi_log_message,/*日志消息*/NULL,/*获取请求时间*/STANDARD _ SAPI _ MODULE _ PROPERTIES }; 这个结构包含一些常量,比如name,当我们调用php_info()时会用到它。一些初始化和整理函数,以及一些函数指针用来告诉Zend如何获取和输出数据。

1.php_cgi_startup,当一个应用程序调用php时,会调用这个函数。对于CGI,它只是调用PHP的初始化函数:

static int PHP _ CGI _ startup(SAPI _ module _ struct * SAPI _ module){ if(PHP _ module _ startup(SAPI _ module,NULL,0)==FAILURE){ return FAILURE;}返回SUCCESS}2.php_module_shutdown_wrapper,一个简单的PHP shutdown函数的包装器。只需调用php _ module _ shutdown

3.PHP将在每个请求中处理一些初始化和资源分配。该零件由激活字段定义。从上面的结构中,我们可以看到CGI没有提供初始化句柄。对于mod_php,这是不同的。他必须在apache的池中注册资源析构函数,申请空间,初始化环境变量,等等。

4.sapi_cgi_deactivate,这是一个对应于activate的函数。顾名思义,它将提供一个处理程序来处理收尾工作。对于CGI,只需刷新缓冲区,确保用户在Zend关闭前获得所有输出数据:

静态int sapi _ CGI _ deactivate(TSRMLS _ D){/*仅在SAPI启动时刷新。原因是: 1。从两个地方调用SAPI停用3360模块初始化和请求关闭2。当第一次调用发生且请求未设置时,刷新会在FastCGI上失败。*/if(SG(SAPI _ started)){ SAPI _ CGI bin _ flush(SG(server _ context));}返回SUCCESS}5.sapi_cgibin_ub_write,这个hanlder告诉Zend如何输出数据。对于mod_php,这个函数提供了一个接口来写入响应数据,而对于CGI,它只是被写入stdout:

静态内联size _ t SAPI _ CGI bin _ single _ WRITE(const char * str,uint str _ length TSRMLS _ DC){ # ifdef PHP _ WRITE _ STDOUT long ret;# else size _ t ret # endif # if PHP _ FASTCGI if(fcgi _ is _ FASTCGI()){ fcgi _ request * request=(fcgi _ request *)SG(server _ context);long ret=fcgi_write(request,FCGI_STDOUT,str,str _ length);if(ret=0){ 0返回0;}返回ret } # endif # ifdef PHP _ WRITE _ STDOUT ret=WRITE(STDOUT _ FILENO,str,str _ length);if (ret=0)返回0;返回ret#else ret=fwrite(str,1,MIN(str_length,16384),stdout);返回ret # endif } static int SAPI _ CGI bin _ ub _ write(const char * str,uint str _ length TSRMLS _ DC){ const char * ptr=str;uint剩余=str _ lengthsize _ t retwhile(剩余0){ ret=SAPI _ CGI _ bin _ single _ write(ptr,剩余TSRMLS _ CC);if(!ret){ PHP _ handle _ abort _ connection();返回字符串长度-剩余;} ptr=ret剩余-=ret;}返回字符串长度}把真正的写的逻辑剥离出来,就是为了简单实现兼容fastcgi的写方式。

6.sapi_cgibin_flush,这个是提供给古波斯语的刷新缓存的函数句柄,对于公共网关接口来说,只是简单的调用系统提供的弗鲁什;

7.空值,这部分用来让阿维斯陀经注解可以验证一个要执行脚本文件的国家,从而判断文件是否据有执行权限等等,CGI没有提供。

8.sapi_cgibin_getenv,为阿维斯陀经注解提供了一个根据名字来查找环境变量的接口,对于mod_php5来说,当我们在脚本中调用getenv的时候,就会间接的调用这个句柄。而对于公共网关接口来说,因为他的运行机制和硬币指示器(硬币水平指示器的缩写)命令行界面(批处理脚本的命令行界面)很类似,直接调用父级是贝壳,所以,只是简单的调用了系统提供的genenv:

静态char * SAPI _ CGI bin _ getenv(char * name,size _ t name _ len TSRMLS _ DC){ # if PHP _ FASTCGI/*当服务器端编程语言(Professional Hypertext Preprocessor的缩写)由mod_fastcgi启动时,没有为服务器端编程语言(Professional Hypertext Preprocessor的缩写)提供常规环境。它总是在请求开始时被发送到PHP .所以我们必须自己查找才能得到环境变量。这可能会更快*/if(fcgi _ is _ fastcgi()){ fcgi _ request * request=(fcgi _ request *)SG(server _ context);返回fcgi_getenv(请求、名称、名称_ len);}#endif /*如果在fcgi环境中找不到计算机生成图像或fastcgi,请检查常规环境*/返回getenv(名称);}9.php_error,错误处理函数,到这里,说几句题外话,上次看到服务器端编程语言(Professional Hypertext Preprocessor的缩写)邮件列表提到的使得服务器端编程语言(专业超文本预处理器的缩写)的错误处理机制完全面向对象的(=面向对象)化,也就是,改写这个函数句柄,使得每当有错误发生的时候,都扔一个异常。而公共网关接口只是简单的调用了服务器端编程语言(专业超文本预处理器的缩写)提供的错误处理函数。

10.这个函数会在我们调用服务器端编程语言(专业超文本预处理器的缩写)的标题()函数的时候被调用,对于公共网关接口来说,不提供。

11.sapi_cgi_send_headers,这个函数会在要真正发送页眉的时候被调用,一般来说,就是当有任何的输出要发送之前:

静态int SAPI _ CGI _ send _ headers(SAPI _ headers _ struct * SAPI _ headers TSRMLS _ DC){ char buf[SAPI _ CGI _ MAX _ HEADER _ LENGTH];SAPI _表头_结构* h;Zend _ llist _ positionif(SG(request _ info).no _ HEADERS==1){ return SAPI _ HEADER _ SENT _ SUCCESS;} if (cgi_nph || SG(sapi_headers).http_response_code!=200){ int len;中频(RFC 2616 _报头)SG(SAPI _报头).http _ status _ line){ len=snprintf(buf,SAPI_CGI_MAX_HEADER_LENGTH,' %srn ',SG(SAPI _ HEADER).http _ status _ line);if(len SAPI _ CGI _ MAX _ HEADER _ LENGTH){ len=SAPI _ CGI _ MAX _ HEADER _ LENGTH;} } else { len=sprintf(buf,' status 3360% d r n ',SG(sapi_headers).http _ response _ code);} PHPWRITE_H(buf,len);} h=(SAPI _ header _ struct *)Zend _ llist _ get _ first _ ex(SAPI _ header-header,pos);而(h) { /*防止CRLFCRLF */if(H-header _ len){ PHPWRITE _ H(H-header,H-header _ len);PHPWRITE_H('rn ',2);} h=(SAPI _ header _ struct *)Zend _ llist _ get _ next _ ex(SAPI _ header-header,pos);} PHPWRITE_H('rn ',2);返回已发送_报头_发送_成功;}12.空值,这个用来单独发送每一个标题,CGI没有提供

13.sapi_cgi_read_post,这个句柄指明了如何获取邮政的数据,如果做过公共网关接口编程的话,我们就知道公共网关接口是从标准输入中读取后数据的,

静态int SAPI _ CGI _ read _ post(char * buffer,uint count _ bytes TSRMLS _ DC){ uint read _ bytes=0,tmp _ read _ bytes#如果PHP _ FASTCGI char * pos=buffer # endif count _ bytes=MIN(count _ bytes),(uint) SG(request_info).content _ length-SG(read _ post _ bytes));while(read _ bytes count _ bytes){ # if PHP _ FASTCGI if(fcgi _ is _ FASTCGI()){ fcgi _ request * request=(fcgi _ request *)SG(server _ context);tmp_read_bytes=fcgi_read(请求、pos、count _ bytes-read _ bytes);pos=tmp _ read _ bytes } else { tmp _ read _ bytes=read(0,缓冲区read_bytes,count _ bytes-read _ bytes);}#else tmp_read_bytes=read(0,缓冲区read_bytes,count _ bytes-read _ bytes);# endif if(tmp _ read _ bytes=0){ break;} read _ bytes=tmp _ read _ bytes}返回read _ bytes } 14 . SAPI _ CGI _ read _ cookies,这个和上面的函数一样,只不过是去获取甜饼干值:

静态char * SAPI _ CGI _ read _ cookies(TSRMLS _ D){ return SAPI _ CGI bin _ getenv((char *)' HTTP _ COOKIE ',sizeof(' HTTP _ COOKIE ')-1 TSRMLS _ CC);} 15 . SAPI _ CGI _ register _ variables,这个函数给了一个接口,用以给$_SERVER变量中添加变量,对于公共网关接口来说,注册了一个PHP_SELF,这样我们就可以在脚本中访问$_SERVER['PHP_SELF']来获取

本次的请求uri:

静态void SAPI _ CGI _ register _ variables(zval * track _ vars _ array TSRMLS _ DC){/*在公共网关接口模式下,我们认为环境是服务器*变量*/PHP _ import _ environment _ vars变量(track _ vars _ array TSRMLS _ CC)的一部分;/*为公共网关接口版本*/PHP _ register _ variable(' PHP _ SELF ',(SG(request_info))构建特殊情况的PHP _ SELF变量。请求尤里。SG(请求信息)。request_uri : ' '),track _ vars _ array TSRMLS _ CC);}16.sapi_cgi_log_message,用来输出错误信息,对于公共网关接口来说,只是简单的输出到stderr:

静态void SAPI _ CGI _ log _ message(char * message){ # if PHP _ FASTCGI if(fcgi _ is _ FASTCGI)(fcgi _ logging){ fcgi _ request * request;TSRMLS _ FETCH();request=(fcgi _ request *)SG(server _ context);if(request){ int len=strlen(message);char * buf=malloc(len 2);memcpy(buf,message,len);memcpy(buf len,' n ',sizeof(' n ');fcgi_write(请求,FCGI_STDERR,buf,len 1);免费(buf);} else { fprintf(stderr,' %sn ',消息);} /*忽略返回代码*/} else # endif/* PHP _ FATSCGI */fprintf(stderr,' %sn ',消息);}经过分析,我们已经了解了一个SAPI是如何实现的了,分析过公共网关接口以后,我们也就可以想象mod_php,嵌入等SAPI的实现机制。)

怎么样,本文介绍的是不是非常详细,希望大家喜欢。

更多资讯
游戏推荐
更多+