SAPI: Server Application Programming Interface 服务器端应用编程端口。先看一张php模块图。

从图中可以看出,各种应用都是通过对应的SAPI与php进行交互的,SAPI相当于一个接口,使得php的核心实现不用关心各个应用交互的细节。虽然通过Web服务器和命令行程序执行脚本看起来很不一样,实际上它们的工作流程是一样的。


在php的源代码sapi目录下有多种sapi的具体实现,比如cgi、cli、apache、fpm等。SAPI中最重要的一个数据结构就是_sapi_module_struct,定义在/main/SAPI.h中。

struct _sapi_module_struct {
    char *name;       //应用层的名称,比如cgi,apache等
    char *pretty_name;     //应用层更易读的名字
    int (*startup)(struct _sapi_module_struct *sapi_module);    //startup 函数指针, 当一个应用要调用PHP的时候,这个函数会被调用
    int (*shutdown)(struct _sapi_module_struct *sapi_module);     // shutdown 函数指针,
    int (*activate)(TSRMLS_D);    //active 函数指针,PHP会在每个request的时候,处理一些初始化,资源分配的事务。这部分就是activate字段要定义的
    int (*deactivate)(TSRMLS_D);    //deactivate函数指针,这个是对应与activate的函数,顾名思义,它会提供一个handler, 用来处理收尾工作
    int (*ub_write)(const char *str, unsigned int str_length TSRMLS_DC);    //这个hanlder告诉了php如何输出数据,比如cgi和fpm模式下输出数据方式肯定不一样
    void (*flush)(void *server_context);    //这个是提供给php的刷新缓存的函数指针
    struct stat *(*get_stat)(TSRMLS_D);    //这部分用来让php可以验证一个要执行脚本文件的state,从而判断文件是否据有执行权限等等
    char *(*getenv)(char *name, size_t name_len TSRMLS_DC);     //为Zend提供了一个根据name来查找环境变量的接口
    void (*sapi_error)(int type, const char *error_msg, ...);    //错误处理函数指针
    int (*header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC);    // 这个函数会在我们调用PHP的header()函数的时候被调用
    int (*send_headers)(sapi_headers_struct *sapi_headers TSRMLS_DC);  //这个函数会在要真正发送header的时候被调用,一般来说,就是当有任何的输出要发送之前
    void (*send_header)(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC);  //单独发送每一个header的函数指针
    int (*read_post)(char *buffer, uint count_bytes TSRMLS_DC);   //这个句函数指针明了如何获取POST的数据
    char *(*read_cookies)(TSRMLS_D);  //这个句函数指针明了如何获取COOKIE的数据
    void (*register_server_variables)(zval *track_vars_array TSRMLS_DC);    //这个函数给了一个接口,用以给$_SERVER变量中添加变量 
    void (*log_message)(char *message TSRMLS_DC);    //用来输出错误信息的函数指针
    double (*get_request_time)(TSRMLS_D);   //获得请求时间的函数指针
    void (*terminate_process)(TSRMLS_D); 
    char *php_ini_path_override;
    void (*block_interruptions)(void);
    void (*unblock_interruptions)(void);
    void (*default_post_reader)(TSRMLS_D);
    void (*treat_data)(int arg, char *str, zval *destArray TSRMLS_DC);
    char *executable_location;
    int php_ini_ignore;
    int php_ini_ignore_cwd; /* don't look for php.ini in the current directory */
    int (*get_fd)(int *fd TSRMLS_DC);
    int (*force_http_10)(TSRMLS_D);
    int (*get_target_uid)(uid_t * TSRMLS_DC);
    int (*get_target_gid)(gid_t * TSRMLS_DC);
    unsigned int (*input_filter)(int arg, char *var, char **val, unsigned int val_len, unsigned int *new_val_len TSRMLS_DC);
   void (*ini_defaults)(HashTable *configuration_hash);
   int phpinfo_as_text;
   char *ini_entries;
   const zend_function_entry *additional_functions;
   unsigned int (*input_filter_init)(TSRMLS_D);
};

不同的SAPI就是用不同的参数实例化_sapi_module_struct来实习的,下面我们分别简单分析一些cgi SAPI和cli SAPI的源代码,力求对SAPI有些更深入的理解。


1.cgi模式

cgi模式下,_sapi_module_struct的实例定义在cgi_main.c中。

static sapi_module_struct cgi_sapi_module = {
    "cgi-fcgi",                     /* name */
    "CGI/FastCGI",                  /* pretty name */
    php_cgi_startup,                /* startup */
    php_module_shutdown_wrapper,    /* shutdown */
    sapi_cgi_activate,              /* activate */
    sapi_cgi_deactivate,            /* deactivate */
    sapi_cgi_ub_write,              /* unbuffered write */
    sapi_cgi_flush,                 /* flush */
    NULL,                           /* get uid */
    sapi_cgi_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 */
    NULL,                           /* Child terminate */
    STANDARD_SAPI_MODULE_PROPERTIES
};

下面分析 char *(*read_cookies)(TSRMLS_D) 在cgi模式下的实现:sapi_cgi_read_cookies。其中sapi_cgi_read_cookies的源码片段如下:

static char *sapi_cgi_read_cookies(TSRMLS_D)
{
    return getenv("HTTP_COOKIE");
}

可以看到,cgi模式下的char *(*read_cookies)(TSRMLS_D)最终为从环境变量中读取HTTP_COOKIE。

2.cli 模式

cgi模式下,_sapi_module_struct的实例定义在php_cli.c

static sapi_module_struct cli_sapi_module = {
    "cli",                          /* name */
    "Command Line Interface",       /* pretty name */
    php_cli_startup,                /* startup */
    php_module_shutdown_wrapper,    /* shutdown */
    NULL,                           /* activate */
    sapi_cli_deactivate,            /* deactivate */
    sapi_cli_ub_write,              /* unbuffered write */
    sapi_cli_flush,                 /* flush */
    NULL,                           /* get uid */
    NULL,                           /* getenv */
    php_error,                      /* error handler */
    sapi_cli_header_handler,        /* header handler */
    sapi_cli_send_headers,          /* send headers handler */
    sapi_cli_send_header,           /* send header handler */
    NULL,                           /* read POST data */
    sapi_cli_read_cookies,          /* read Cookies */
    sapi_cli_register_variables,    /* register server variables */
    sapi_cli_log_message,           /* Log message */
    NULL,                           /* Get request time */
    NULL,                           /* Child terminate */
    STANDARD_SAPI_MODULE_PROPERTIES
};

下面分析 char *(*read_cookies)(TSRMLS_D) 在cli模式下的实现:sapi_cli_read_cookies。其中sapi_cli_read_cookies的源码片段如下:

static char* sapi_cli_read_cookies(TSRMLS_D) 
{
    return NULL;
}

可以看到,cgi模式下的char *(*read_cookies)(TSRMLS_D)最终为直接返回NULL,因为cli模式下不存在用户cookies信息。


通过上面的cgi和cli模式下read_cookies的不同实现,可以看出sapi确实对下层php屏蔽了交互细节,当下层php核心要读取用户cookies时,只需要通过sapi_module_struct->read_cookies,而不需要关注上层应用的交互细节。

that right! 这就是SAPI的作用。

全面理解React,实现自己的React

通过实现一个简单的React, 来理解React的原理 Continue reading

同构渲染的常见风险

Published on October 01, 2017

React16升级避坑指南

Published on September 10, 2017