目录
谈谈你对Zend SAPIs(Zend SAPI Internals)的理解,sapissapi
首页 php教程 php手册 谈谈你对Zend SAPIs(Zend SAPI Internals)的理解,sapissapi

谈谈你对Zend SAPIs(Zend SAPI Internals)的理解,sapissapi

Jun 13, 2016 am 08:51 AM
zend

谈谈你对Zend SAPIs(Zend SAPI Internals)的理解,sapissapi

SAPI: Server abstraction API,研究过PHP架构的同学应该知道这个东东的重要性,它提供了一个接口,使得PHP可以和其他应用进行交互数据。 本文不会详细介绍每个PHP的SAPI,只是针对最简单的CGI SAPI,来说明SAPI的机制。

首先,我们来看看PHP的架构图:

图1 PHP Architecture

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",     /* pretty name */
#else
 "cgi",       /* name */
 "CGI",       /* pretty name */
#endif
 
 php_cgi_startup,    /* startup */
 php_module_shutdown_wrapper, /* shutdown */
 
 NULL,       /* activate */
 sapi_cgi_deactivate,   /* deactivate */
 
 sapi_cgibin_ub_write,   /* unbuffered write */
 sapi_cgibin_flush,    /* flush */
 NULL,       /* get uid */
 sapi_cgibin_getenv,    /* getenv */
 
 php_error,      /* error handler */
 
 NULL,       /* header handler */
 sapi_cgi_send_headers,   /* send headers handler */
 NULL,       /* send header handler */
 
 sapi_cgi_read_post,    /* read POST data */
 sapi_cgi_read_cookies,   /* read Cookies */
 
 sapi_cgi_register_variables, /* register server variables */
 sapi_cgi_log_message,   /* Log message */
 NULL,       /* Get request time */
 
 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;
 }
 return SUCCESS;
}
登录后复制

2. php_module_shutdown_wrapper , 一个对PHP关闭函数的简单包装。只是简单的调用php_module_shutdown;

3. PHP会在每个request的时候,处理一些初始化,资源分配的事务。这部分就是activate字段要定义的,从上面的结构我们可以看出,对于CGI来说,它并没有提供初始化处理句柄。对于mod_php来说,那就不同了,他要在apache的pool中注册资源析构函数, 申请空间, 初始化环境变量,等等等等。

4. sapi_cgi_deactivate, 这个是对应与activate的函数,顾名思义,它会提供一个handler, 用来处理收尾工作,对于CGI来说,他只是简单的刷新缓冲区,用以保证用户在Zend关闭前得到所有的输出数据:

 static int sapi_cgi_deactivate(TSRMLS_D)
{
 /* flush only when SAPI was started. The reasons are:
  1. SAPI Deactivate is called from two places: module init and request shutdown
  2. When the first call occurs and the request is not set up, flush fails on
   FastCGI.
 */
 if (SG(sapi_started)) {
  sapi_cgibin_flush(SG(server_context));
 }
 return SUCCESS;
}
登录后复制

5. sapi_cgibin_ub_write, 这个hanlder告诉了Zend,如何输出数据,对于mod_php来说,这个函数提供了一个向response数据写的接口,而对于CGI来说,只是简单的写到stdout:

static inline size_t sapi_cgibin_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) {
   return 0;
  }
  return ret;
 }
#endif
#ifdef PHP_WRITE_STDOUT
 ret = write(STDOUT_FILENO, str, str_length);
 if (ret <= 0) return 0;
 return ret;
#else
 ret = fwrite(str, 1, MIN(str_length, 16384), stdout);
 return ret;
#endif
}
static int sapi_cgibin_ub_write(const char *str, uint str_length TSRMLS_DC)
{
 const char *ptr = str;
 uint remaining = str_length;
 size_t ret;
 while (remaining > 0) {
  ret = sapi_cgibin_single_write(ptr, remaining TSRMLS_CC);
  if (!ret) {
   php_handle_aborted_connection();
   return str_length - remaining;
  }
  ptr += ret;
  remaining -= ret;
 }
 return str_length;
}
登录后复制

把真正的写的逻辑剥离出来,就是为了简单实现兼容fastcgi的写方式。

6. sapi_cgibin_flush, 这个是提供给zend的刷新缓存的函数句柄,对于CGI来说,只是简单的调用系统提供的fflush;

7.NULL, 这部分用来让Zend可以验证一个要执行脚本文件的state,从而判断文件是否据有执行权限等等,CGI没有提供。

8. sapi_cgibin_getenv, 为Zend提供了一个根据name来查找环境变量的接口,对于mod_php5来说,当我们在脚本中调用getenv的时候,就会间接的调用这个句柄。而对于CGI来说,因为他的运行机制和CLI很类似,直接调用父级是Shell, 所以,只是简单的调用了系统提供的genenv:

static char *sapi_cgibin_getenv(char *name, size_t name_len TSRMLS_DC)
{
#if PHP_FASTCGI
 /* when php is started by mod_fastcgi, no regular environment
  is provided to PHP. It is always sent to PHP at the start
  of a request. So we have to do our own lookup to get env
  vars. This could probably be faster somehow. */
 if (fcgi_is_fastcgi()) {
  fcgi_request *request = (fcgi_request*) SG(server_context);
  return fcgi_getenv(request, name, name_len);
 }
#endif
 /* if cgi, or fastcgi and not found in fcgi env
  check the regular environment */
 return getenv(name);
}
登录后复制

9. php_error, 错误处理函数, 到这里,说几句题外话,上次看到php maillist 提到的使得PHP的错误处理机制完全OO化, 也就是,改写这个函数句柄,使得每当有错误发生的时候,都throw一个异常。而CGI只是简单的调用了PHP提供的错误处理函数。

10. 这个函数会在我们调用PHP的header()函数的时候被调用,对于CGI来说,不提供。

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

static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
{
 char buf[SAPI_CGI_MAX_HEADER_LENGTH];
 sapi_header_struct *h;
 zend_llist_position pos;
 if (SG(request_info).no_headers == 1) {
  return SAPI_HEADER_SENT_SUCCESSFULLY;
 }
 if (cgi_nph || SG(sapi_headers).http_response_code != 200)
 {
  int len;
  if (rfc2616_headers && SG(sapi_headers).http_status_line) {
   len = snprintf(buf, SAPI_CGI_MAX_HEADER_LENGTH,
       "%s\r\n", SG(sapi_headers).http_status_line);
   if (len > SAPI_CGI_MAX_HEADER_LENGTH) {
    len = SAPI_CGI_MAX_HEADER_LENGTH;
   }
  } else {
   len = sprintf(buf, "Status: %d\r\n", SG(sapi_headers).http_response_code);
  }
  PHPWRITE_H(buf, len);
 }
 h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
 while (h) {
  /* prevent CRLFCRLF */
  if (h->header_len) {
   PHPWRITE_H(h->header, h->header_len);
   PHPWRITE_H("\r\n", 2);
  }
  h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
 }
 PHPWRITE_H("\r\n", 2);
 return SAPI_HEADER_SENT_SUCCESSFULLY;
 }
登录后复制

 12. NULL, 这个用来单独发送每一个header, CGI没有提供

13. sapi_cgi_read_post, 这个句柄指明了如何获取POST的数据,如果做过CGI编程的话,我们就知道CGI是从stdin中读取POST DATA的,

static int sapi_cgi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
{
 uint read_bytes=0, tmp_read_bytes;
#if 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(request, pos, count_bytes - read_bytes);
   pos += tmp_read_bytes;
  } else {
   tmp_read_bytes = read(0, buffer + read_bytes, count_bytes - read_bytes);
  }
#else
  tmp_read_bytes = read(0, buffer + read_bytes, count_bytes - read_bytes);
#endif
  if (tmp_read_bytes <= 0) {
   break;
  }
  read_bytes += tmp_read_bytes;
 }
 return read_bytes;
}
登录后复制

14. sapi_cgi_read_cookies, 这个和上面的函数一样,只不过是去获取cookie值:

static char *sapi_cgi_read_cookies(TSRMLS_D)
{
 return sapi_cgibin_getenv((char *) "HTTP_COOKIE", sizeof("HTTP_COOKIE")-1 TSRMLS_CC);
}

登录后复制

15. sapi_cgi_register_variables, 这个函数给了一个接口,用以给$_SERVER变量中添加变量,对于CGI来说,注册了一个PHP_SELF,这样我们就可以在脚本中访问$_SERVER['PHP_SELF']来获取

本次的request_uri:

static void sapi_cgi_register_variables(zval *track_vars_array TSRMLS_DC)
{
 /* In CGI mode, we consider the environment to be a part of the server
  * variables
  */
 php_import_environment_variables(track_vars_array TSRMLS_CC);
 /* Build the special-case PHP_SELF variable for the CGI version */
 php_register_variable("PHP_SELF", (SG(request_info).request_uri &#63; SG(request_info).request_uri : ""), track_vars_array TSRMLS_CC);
}
登录后复制

16. sapi_cgi_log_message ,用来输出错误信息,对于CGI来说,只是简单的输出到stderr:

static 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(request, FCGI_STDERR, buf, len+1);
   free(buf);
  } else {
   fprintf(stderr, "%s\n", message);
  }
  /* ignore return code */
 } else
#endif /* PHP_FASTCGI */
 fprintf(stderr, "%s\n", message);
}
登录后复制

经过分析,我们已经了解了一个SAPI是如何实现的了, 分析过CGI以后,我们也就可以想象mod_php, embed等SAPI的实现机制。 :)

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

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

PHP实现框架:Zend Framework入门教程 PHP实现框架:Zend Framework入门教程 Jun 19, 2023 am 08:09 AM

PHP实现框架:ZendFramework入门教程ZendFramework是PHP开发的一种开源网站框架,目前由ZendTechnologies维护,ZendFramework采用了MVC设计模式,提供了一系列可重用的代码库,服务于实现Web2.0应用程序和Web服务。ZendFramework深受PHP开发者的欢迎和推崇,拥有广泛

如何在Zend框架中使用ACL(Access Control List)进行权限控制 如何在Zend框架中使用ACL(Access Control List)进行权限控制 Jul 29, 2023 am 09:24 AM

如何在Zend框架中使用ACL(AccessControlList)进行权限控制导言:在一个Web应用程序中,权限控制是至关重要的一项功能。它可以确保用户只能访问其有权访问的页面和功能,并防止未经授权的访问。Zend框架提供了一种方便的方法来实现权限控制,即使用ACL(AccessControlList)组件。本文将介绍如何在Zend框架中使用ACL

PHP无法识别ZendOptimizer,如何解决? PHP无法识别ZendOptimizer,如何解决? Mar 19, 2024 pm 01:09 PM

PHP无法识别ZendOptimizer,如何解决?在PHP开发中,有时可能会遇到PHP无法识别ZendOptimizer的情况,这会导致部分PHP代码无法正常运行。在这种情况下,我们需要采取一些措施来解决这个问题。下面将介绍一些可能的解决方法,并附上具体的代码示例。1.确认ZendOptimizer是否正确安装:首先,我们需要确认ZendOptimize

Window2003 IIS+MySQL+PHP+Zend环境如何配置 Window2003 IIS+MySQL+PHP+Zend环境如何配置 Jun 02, 2023 pm 09:56 PM

  Windows2003安装包中包含了Zend,PHP5.2.17,PHPWind8.7和PHPMyadmin3.5.2,您可以直接下载安装包,节约搜索资源的时间。  但是,由于MySQL超出了上传限制,您需要另行前往MySQL官网下载。然后解压拷贝到D盘,如下图:  MySQLinDdisk  安装与配置WindowsIIS+FTP  单击开始>控制面板>添加或删除程序。  AddingordeletingaPG  单击添加/删除Windows组件(A)。  Addingorde

如何使用PHP框架Zend开发一个高效的ERP管理平台 如何使用PHP框架Zend开发一个高效的ERP管理平台 Jun 26, 2023 pm 11:00 PM

随着信息技术的飞速发展,越来越多的企业开始意识到信息化管理的必要性。ERP(企业资源计划)管理平台是现代企业管理的重要工具,可以帮助企业实现资源的规划、协同、控制、优化和管理。其中,PHP框架Zend作为一款优秀的开发工具,可以帮助开发者快速高效地实现ERP系统的开发。本文将介绍如何使用Zend开发一个高效的ERP管理平台。一、确定需求分析在开始开发过程之前

使用PHP框架Zend开发一个高性能的搜索引擎 使用PHP框架Zend开发一个高性能的搜索引擎 Jun 27, 2023 am 08:36 AM

随着互联网信息的爆炸式增长,搜索引擎已经成为人们获取信息的首选方式之一。而现在,随着网站数量的不断增加,搜索引擎的快速响应和准确性变得越来越重要,而这就要求搜索引擎必须具备高性能。在这篇文章中,我将介绍如何使用PHP框架Zend来开发一个高性能的搜索引擎。一、为什么使用Zend框架Zend框架是一个高性能的PHP框架,它在性能和可扩展性方面都有非常出色的表现

Symfony 3 vs Zend Framework 3:哪个PHP框架更容易上手? Symfony 3 vs Zend Framework 3:哪个PHP框架更容易上手? Jun 19, 2023 am 09:46 AM

PHP是一种广泛使用的动态Web编程语言。开发者可以利用不同的框架来简化其Web开发工作。Symfony和ZendFramework是PHP中最受欢迎的两个框架之一。在Symfony3和ZendFramework3之间进行选择时,初学者经常会困惑。这里我们将比较这两个框架,看看哪个更容易上手。Symfony3Symfony是一个基于MVC模式的PH

Laravel vs Zend:哪个框架更适合开发大型应用? Laravel vs Zend:哪个框架更适合开发大型应用? Jun 19, 2023 am 08:52 AM

随着互联网应用的不断发展,大型应用的开发需求也不断增加。在这样的背景下,选择适合自己的开发框架显得尤为重要。Laravel和Zend是两个广泛使用的PHP框架,他们各有优势,但哪一个更适合开发大型应用呢?Laravel是一个广受欢迎的开发框架,已经成为PHP开发人员的首选框架之一。它采用了现代化的设计理念,内置多种强大的功能和工具,例如EloquentOR

See all articles