深入解析PHP8底层内核源码之SAPI(一)

广告:宝塔服务器面板,一键全能部署及管理,送你10850元礼包,点我领取~~~

深入解析PHP8底层内核源码之SAPI(一)

本篇文章给大家深入解析PHP8底层内核源码,了解一下SAPI。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。

相关文章推荐:《解析PHP8底层内核源码-数组(一)》

在docker下 搭建里如下的环境

[root@a951700e857d cui-php]# php -vPHP 8.0.2 (cli) (built: Mar  2 2021 02:40:03) ( NTS )Copyright (c) The PHP GroupZend Engine v4.0.2, Copyright (c) Zend Technologies[root@a951700e857d cui-php]#
登录后复制

引入一张 鸟哥的 图

深入理解Zend SAPIs(Zend SAPI Internals)

https://link.zhihu.com/?target=https%3A//www.laruence.com/2008/08/12/180.html

SAPI(Server Application Programimg Interface,服务端应用编程接口)相当于PHP外部环境的代理器。PHP可以应用在终端上,也可以应用在Web服务器中,应用在终端上的SAPI就叫作CLI SAPI,应用在Web服务器中的就叫作CGI SAPI。

他相当于一个中间层 或者叫他胶水 承上启下作用

sapi的核心定义 和宏文件 在 sapi.h中

cgi_main.c里面有个重要的结构体

//* sapi_module_struct cgi_sapi_module 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};
登录后复制

他”继承“自结构体 _sapi_module_struct

struct _sapi_module_struct {char *name;char *pretty_name;int (*startup)(struct _sapi_module_struct *sapi_module);int (*shutdown)(struct _sapi_module_struct *sapi_module);int (*activate)(void);int (*deactivate)(void);size_t (*ub_write)(const char *str, size_t str_length);void (*flush)(void *server_context);zend_stat_t *(*get_stat)(void);char *(*getenv)(const 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);int (*send_headers)(sapi_headers_struct *sapi_headers);void (*send_header)(sapi_header_struct *sapi_header, void *server_context);size_t (*read_post)(char *buffer, size_t count_bytes);char *(*read_cookies)(void);void (*register_server_variables)(zval *track_vars_array);void (*log_message)(const char *message, int syslog_type_int);double (*get_request_time)(void);void (*terminate_process)(void);char *php_ini_path_override;void (*default_post_reader)(void);void (*treat_data)(int arg, char *str, zval *destArray);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);int (*force_http_10)(void);int (*get_target_uid)(uid_t *);int (*get_target_gid)(gid_t *);unsigned int (*input_filter)(int arg, const char *var, char **val, size_t val_len, size_t *new_val_len);void (*ini_defaults)(HashTable *configuration_hash);int phpinfo_as_text;char *ini_entries;const zend_function_entry *additional_functions;unsigned int (*input_filter_init)(void);};
登录后复制

注释后如下

struct _sapi_module_struct {        char *name; // 名字,如cli、 fpm-fcgi等        char *pretty_name; // 更易理解的名字,比如fpm-fcgi对应的为FPM/FastCGI        int (*startup)(struct _sapi_module_struct *sapi_module);        //模块启动时调用的函数        int (*shutdown)(struct _sapi_module_struct *sapi_module);        //模块结束时调用的函数        int (*activate)(void); // 处理request时,激活需要调用的函数指针        int (*deactivate)(void); // 处理完request时,使要调用的函数指针无效        size_t (*ub_write)(const char *str, size_t str_length);        // 这个函数指针用于输出数据        void (*flush)(void *server_context); // 刷新缓存的函数指针        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);  // 调用exit退出时的函数指针        char *php_ini_path_override;  // PHP的ini文件被复写的地址        void (*default_post_reader)(void); //负责解析POST数据的函数指针        void (*treat_data)(int arg, char *str, zval *destArray);        // 对数据进行处理的函数指针        char *executable_location; // 执行的地理位置        int php_ini_ignore; // 是否不使用任何ini配置文件        int php_ini_ignore_cwd; // 忽略当前路径的php.ini        int (*get_fd)(int *fd); // 获取执行文件的fd的函数指针        int (*force_http_10)(void); // 强制使用http 1.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配置的函数指针,把ini配置信息存在HashTable中        int phpinfo_as_text; // 是否输出phpinfo信息        char *ini_entries; // 执行时附带的ini配置,可以使用php -d设置        const zend_function_entry *additional_functions;        // 每个SAPI模块特有的一些函数注册,比如cli的cli_get_process_title        unsigned int (*input_filter_init)(void);};
登录后复制

这个结构体非常关键 在CLI生命周期中,会调用其中定义的函数指针来实现各自的功能

另外还有一个重要的数据结构——sapi_globals,其对应的宏为SG(v)

typedef struct _sapi_globals_struct {void *server_context;sapi_request_info request_info;sapi_headers_struct sapi_headers;int64_t read_post_bytes;unsigned char post_read;unsigned char headers_sent;zend_stat_t global_stat;char *default_mimetype;char *default_charset;HashTable *rfc1867_uploaded_files;zend_long post_max_size;int options;zend_bool sapi_started;double global_request_time;HashTable known_post_content_types;zval callback_func;zend_fcall_info_cache fci_cache;} sapi_globals_struct;
登录后复制

这个 SG 共560个字节 整个PHP生命周期都多次用到了它

回过头看 cgi_main.c 整个cgi 的代码 一共有900多行

折叠一些 涉及ZTS (线程安全 之类的)的 代码

zend_signal_startup 信号处理方法; (linux 信号以后再讲吧。坑挖的太大了)

调用sapi_startup(&cgi_sapi_module),对sapi_model进行一些初始化工作

...        sapi_startup(&cgi_sapi_module);fastcgi = fcgi_is_fastcgi();cgi_sapi_module.php_ini_path_override = NULL;
登录后复制

跟到 sapi_starup 方法

SAPI_API void sapi_startup(sapi_module_struct *sf){sf->ini_entries = NULL // ini_entries设置nullsapi_module = *sf; // 把传进来的结构体赋值给sapi_module //  上面有关于sap_module的 定义   sapi_module_struct sapi_module;//这里你可以理解为 初始化了sapi_module_struct  #ifdef ZTSts_allocate_fast_id(&sapi_globals_id, &sapi_globals_offset, sizeof(sapi_globals_struct), (ts_allocate_ctor) sapi_globals_ctor, (ts_allocate_dtor) sapi_globals_dtor);# ifdef PHP_WIN32_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);# endif#elsesapi_globals_ctor(&sapi_globals);#endif#ifdef PHP_WIN32tsrm_win32_startup();#endifreentrancy_startup();}
登录后复制

if条件暂且忽略 最后走到 sapi_globals_ctor函数

sapi_globals_ctor()内部逻辑

static void sapi_globals_ctor(sapi_globals_struct *sapi_globals){memset(sapi_globals, 0, sizeof(*sapi_globals));zend_hash_init(&sapi_globals->known_post_content_types, 8, NULL, _type_dtor, 1);php_setup_sapi_content_types();}
登录后复制

memset 是 c语言原生的初始化内存的方法

下面是 memset() 函数的声明。void *memset(void *str, int c, size_t n)参数str -- 指向要填充的内存块。c -- 要被设置的值。该值以 int 形式传递,但是函数在填充内存块时是使用该值的无符号字符形式。n -- 要被设置为该值的字符数。返回值该值返回一个指向存储区 str 的指针。
登录后复制

上面就新建一个大小560个字节的内存

zend_hash_init 是定义一个hashtable(数组以后再讲吧。。)

php_setup_sapi_content_types内部实现是

/* {{{ php_startup_sapi_content_types */int php_startup_sapi_content_types(void){sapi_register_default_post_reader(php_default_post_reader);sapi_register_treat_data(php_default_treat_data);sapi_register_input_filter(php_default_input_filter, NULL);return SUCCESS;}
登录后复制

最后一个reentrancy_startup是一个线程安全开启的情况下才有效的函数

以上整个sapi_startup 方法执行完毕

//* sapi_module_struct cgi_sapi_module 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};0
登录后复制

开始执行fcgi_is_fastcgi 方法

跟进去 指向文件 fastcgi.c 文件

//* sapi_module_struct cgi_sapi_module 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};1
登录后复制

其中 is_initialized 默认初始值为0

所以走到fcgi_init()函数

//* sapi_module_struct cgi_sapi_module 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};2
登录后复制

初始化 fgi协议相关内容

本篇写的太流水账了 明天再补上 吧 先gdb分析cli模式

本文经原作者PHP崔雪峰同意,发布在9543建站博客,原文地址:https://zhuanlan.zhihu.com/p/356037371

推荐学习:《PHP视频教程》

以上就是深入解析PHP8底层内核源码之SAPI(一)的详细内容,更多请关注9543建站博客其它相关文章!

9543建站博客
一个专注于网站开发、微信开发的技术类纯净博客。
作者头像
admin创始人

肥猫,知名SEO博客站长,14年SEO经验。

上一篇:讲解Windows7下PHP7运行环境搭建
下一篇:探讨Laravel中间件出现错误的原因和解决方法

相关推荐

发表评论

关闭广告
关闭广告