PHP头条
热点:

PHP常说的SAPI是什么,PHP常说SAPI


什么是SAPI

SAPI全称是Server Application Programming Interface,即服务器应用编程接口,实质上就是定义了一个统一的接口,它的核心就是一个结构体sapi_module_struct。SAPI提供给了外部应用跟php通信的管道,这个外部应用包括不限于Apache,httpd,liunx终端等,sapi通俗的讲就是php-cgi,php-cli,mod_php等,php就是php内核。PHP架构图如下:



从源码理解SAPI

首先看一下他的工作原理:

无论是cgi还是apache的mod_php、cli都是按sapi_module_struct结构实现的,这种做法屏蔽了php的不同实现,于是外部程序通过调用sapi接口的sapi_module_struct接口就可以无差别的调用不同的底层php程序。sapi_module_struct结构如下:

struct _sapi_module_struct {  // SAPI模块结构
    char *name; // 应用层名称,比如cli,cgi等
    char *pretty_name; // 应用层更易读的名字,比如cli对应的就是Command Line Interface

    int (*startup)(struct _sapi_module_struct *sapi_module); // 当一个应用要调用php的时候,这个模块启动的时候会调用的函数
    int (*shutdown)(struct _sapi_module_struct *sapi_module); // 当一个应用要调用php的时候,这个模块结束的时候会调用的函数

    int (*activate)(void); // 在处理每个request的时候,激活需要调用的函数
    int (*deactivate)(void); // 在处理完每个request的时候,收尾时候要调用的函数

    size_t (*ub_write)(const char *str, size_t str_length); // 这个函数告诉php如何输出数据
    void (*flush)(void *server_context); // 提供给php的刷新缓存的函数指针
    zend_stat_t *(*get_stat)(void); // 用来判断要执行文件的权限,来判断是否有执行权限
    char *(*getenv)(char *name, size_t name_len); // 获取环境变量的方法

    void (*sapi_error)(int type, const char *error_msg, ...) ZEND_ATTRIBUTE_FORMAT(printf, 2, 3); // 错误处理方法

    int (*header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers); // 这个函数会在我们调用header()的时候被调用
    int (*send_headers)(sapi_headers_struct *sapi_headers); // 发送所有的header
    void (*send_header)(sapi_header_struct *sapi_header, void *server_context); // 单独发送某一个header

    size_t (*read_post)(char *buffer, size_t count_bytes); // 如何获取HTTP POST中的数据
    char *(*read_cookies)(void);  // 如何获取cookie中的数据

    void (*register_server_variables)(zval *track_vars_array); // 这个函数可以给$_SERVER中获取变量
    void (*log_message)(char *message, int syslog_type_int); // 输出错误信息函数
    double (*get_request_time)(void); // 获取请求时间的函数
    void (*terminate_process)(void);  // TODO: 调用exit的时候调用的方法

    char *php_ini_path_override;  // PHP的ini文件被复写了所复写的地址

    void (*default_post_reader)(void); // 这里和前面的read_post有个差别,read_post负责如何获取POST数据,而这里的函数负责如何解析POST数据
    void (*treat_data)(int arg, char *str, zval *destArray); // 对数据进行处理,比如进行安全过滤等。 default_post_reader/tread_data/input_filter是三个能对输入进行过滤和处理的函数
    char *executable_location; // 执行的地理位置

    int php_ini_ignore; // 是否不使用任何ini配置文件,比如php -n 就将这个位置设置为1
    int php_ini_ignore_cwd; // 不在当前路径寻找php.ini

    int (*get_fd)(int *fd); // 获取执行文件的fd

    int (*force_http_10)(void); // 强制使用http1.0

    int (*get_target_uid)(uid_t *); // 获取执行程序的uid
    int (*get_target_gid)(gid_t *); // 获取执行程序的gid

    unsigned int (*input_filter)(int arg, char *var, char **val, size_t val_len, size_t *new_val_len); // 对输入进行过滤。比如将输入参数填充到自动全局变量$_GET, $_POST, $_COOKIE中

    void (*ini_defaults)(HashTable *configuration_hash); // 默认的ini配置
    int phpinfo_as_text; // 是否打印phpinfo信息

    char *ini_entries; // 有没有附带的ini配置,比如使用php -d date.timezone=America/Adak,可以在命令行中设置时区
    const zend_function_entry *additional_functions; // 每个SAPI模块特有的一些函数注册,比如cli的cli_get_process_title
    unsigned int (*input_filter_init)(void); // TODO:
};

以mod_php为例

AP_MODULE_DECLARE_DATA module php5_module = {
    STANDARD20_MODULE_STUFF,
        /* 宏,包括版本,小版本,模块索引,模块名,下一个模块指针等信息,其中模块名以__FILE__体现 */
    create_php_config,      /* create per-directory config structure */
    merge_php_config,       /* merge per-directory config structures */
    NULL,                   /* create per-server config structure */
    NULL,                   /* merge per-server config structures */
    php_dir_cmds,           /* 模块定义的所有的指令 */
    php_ap2_register_hook
        /* 注册钩子,此函数通过ap_hoo_开头的函数在一次请求处理过程中对于指定的步骤注册钩子 */
};

着重看php_ap2_register_hook这个钩子:

void php_ap2_register_hook(apr_pool_t *p)
{
    ap_hook_pre_config(php_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_post_config(php_apache_server_startup, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_handler(php_handler, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_child_init(php_apache_child_init, NULL, NULL, APR_HOOK_MIDDLE);
}

ap_hook_post_config这个钩子在apache启动时调用php_apache_server_startup函数,然后函数内调用sapi_startup启动sapi。通过各模块mint()函数初始化模块,ap_hook_handler会在请求到来时运行php_handler函数,函数内再调用php_apache_request_ctor,执行rmint()操作

其他模式如cli、php-cgi等模式下,sapi_startup()函数是定义在int main主函数中,即直接运行就开始创建php程序,而mod_php一开始并不需运行,等到请求到来时会自动开始创建php程序。启动sapi后,php程序会再调用sapi_module中的startup函数指针,完成相应模式下php的创建工作。

cgi模式下的startup函数为php_cgi_startup,该函数再直接调用通用的php_module_stratup(&sapi_module)完成php创建,初始化zend引擎。

总结来说sapi定义了一种面向接口编程的规范,而php-cli、php-cgi等则是实现了该规范的程序

待续。。。。。。

注:

sapi_module_struct、sapi_startup()定义在SAPI.c中

php_module_startup()定义在main.c中


www.phpzy.comtrue/php/20242.htmlTechArticlePHP常说的SAPI是什么,PHP常说SAPI 什么是SAPI SAPI全称是Server Application Programming Interface,即服务器应用编程接口,实质上就是定义了一个统一的接口,它的核心就是一个结构体sapi_module_stru...

相关文章

    暂无相关文章

PHP之友评论

今天推荐