深入解析PHP8底层内核源码之SAPI(一)
本篇文章给大家深入解析PHP8底层内核源码,了解一下SAPI。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。
相关文章推荐:《解析PHP8底层内核源码-数组(一)》
在docker下 搭建里如下的环境
[root@a951700e857d cui-php]# php -v PHP 8.0.2 (cli) (built: Mar 2 2021 02:40:03) ( NTS ) Copyright (c) The PHP Group Zend 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设置null sapi_module = *sf; // 把传进来的结构体赋值给sapi_module // 上面有关于sap_module的 定义 sapi_module_struct sapi_module; //这里你可以理解为 初始化了sapi_module_struct #ifdef ZTS ts_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 #else sapi_globals_ctor(&sapi_globals); #endif #ifdef PHP_WIN32 tsrm_win32_startup(); #endif reentrancy_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_startup(&cgi_sapi_module); fastcgi = fcgi_is_fastcgi(); cgi_sapi_module.php_ini_path_override = NULL;
开始执行fcgi_is_fastcgi 方法
跟进去 指向文件 fastcgi.c 文件
int fcgi_is_fastcgi(void) { if (!is_initialized) { return fcgi_init(); } else { return is_fastcgi; } }
其中 is_initialized 默认初始值为0
所以走到fcgi_init()函数
int fcgi_init(void) { if (!is_initialized) { #ifndef _WIN32 sa_t sa; socklen_t len = sizeof(sa); #endif zend_hash_init(&fcgi_mgmt_vars, 8, NULL, fcgi_free_mgmt_var_cb, 1); fcgi_set_mgmt_var("FCGI_MPXS_CONNS", sizeof("FCGI_MPXS_CONNS")-1, "0", sizeof("0")-1); is_initialized = 1; #ifdef _WIN32 # if 0 /* TODO: Support for TCP sockets */ WSADATA wsaData; if (WSAStartup(MAKEWORD(2,0), &wsaData)) { fprintf(stderr, "Error starting Windows Sockets. Error: %d", WSAGetLastError()); return 0; } # endif if ((GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) && (GetStdHandle(STD_ERROR_HANDLE) == INVALID_HANDLE_VALUE) && (GetStdHandle(STD_INPUT_HANDLE) != INVALID_HANDLE_VALUE)) { char *str; DWORD pipe_mode = PIPE_READMODE_BYTE | PIPE_WAIT; HANDLE pipe = GetStdHandle(STD_INPUT_HANDLE); SetNamedPipeHandleState(pipe, &pipe_mode, NULL, NULL); str = getenv("_FCGI_SHUTDOWN_EVENT_"); if (str != NULL) { zend_long ev; HANDLE shutdown_event; ZEND_ATOL(ev, str); shutdown_event = (HANDLE) ev; if (!CreateThread(NULL, 0, fcgi_shutdown_thread, shutdown_event, 0, NULL)) { return -1; } } str = getenv("_FCGI_MUTEX_"); if (str != NULL) { zend_long mt; ZEND_ATOL(mt, str); fcgi_accept_mutex = (HANDLE) mt; } return is_fastcgi = 1; } else { return is_fastcgi = 0; } #else errno = 0; if (getpeername(0, (struct sockaddr *)&sa, &len) != 0 && errno == ENOTCONN) { fcgi_setup_signals(); return is_fastcgi = 1; } else { return is_fastcgi = 0; } #endif } return is_fastcgi; }
初始化 fgi协议相关内容
本篇写的太流水账了 明天再补上 吧 先gdb分析cli模式
本文经原作者PHP崔雪峰同意,发布在php中文网,原文地址:https://zhuanlan.zhihu.com/p/356037371
推荐学习:《PHP视频教程》
Atas ialah kandungan terperinci 深入解析PHP8底层内核源码之SAPI(一). Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

AI Hentai Generator
Menjana ai hentai secara percuma.

Artikel Panas

Alat panas

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina
Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6
Alat pembangunan web visual

SublimeText3 versi Mac
Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Topik panas



Perbezaan antara php5 dan php8 adalah dari segi prestasi, struktur bahasa, sistem jenis, pengendalian ralat, pengaturcaraan tak segerak, fungsi perpustakaan standard dan keselamatan. Pengenalan terperinci: 1. Peningkatan prestasi Berbanding dengan PHP5, PHP8 mempunyai peningkatan besar dalam prestasi PHP8 memperkenalkan pengkompil JIT, yang boleh menyusun dan mengoptimumkan beberapa kod pelaksanaan frekuensi tinggi, dengan itu meningkatkan kelajuan berjalan; PHP8 memperkenalkan beberapa struktur dan fungsi bahasa baharu PHP8 menyokong parameter yang dinamakan, membenarkan pembangun menghantar nama parameter dan bukannya susunan parameter, dsb.

Langkah-langkah untuk menambah sambungan mysql ke php8 ialah: 1. Pasang pustaka klien MySQL 2. Pasang alat pembangunan untuk PHP 8 3. Muat turun kod sumber sambungan MySQL 5. Dayakan Sambungan MySQL; 6. Mulakan Semula Hanya pelayan web.

Artikel ini akan memperkenalkan kepada anda cara memasang apache2.4 dan cara mengkonfigurasi php8.0 Artikel disertakan dengan gambar dan langkah terperinci Mari kita lihat cara memasang dan mengkonfigurasi apache2.4+php8.0~

PHP8 boleh menggunakan mysqli dan PDO untuk menyambung ke pangkalan data. Pengenalan terperinci: 1. Gunakan mysqli untuk menyambung ke pangkalan data dengan memasukkan nama pelayan pangkalan data, nama pengguna, kata laluan dan nama pangkalan data untuk disambungkan. Kemudian, gunakan atribut `connect_error` untuk menyemak sama ada sambungan berjaya dan mengeluarkan mesej ralat jika sambungan gagal. Akhir sekali, tutup sambungan dengan memanggil kaedah `close()` 2. Gunakan PDO untuk menyambung ke pangkalan data, dan sambung dengan memasukkan nama pelayan pangkalan data, kata laluan dan nama pangkalan data, dsb.

Kaedah jenis data php8 termasuk menukar rentetan kepada integer, menukar integer kepada rentetan, menukar rentetan kepada nombor titik terapung, menukar nombor titik terapung kepada rentetan, menukar tatasusunan kepada rentetan, menukar rentetan kepada tatasusunan, menukar nilai Boolean kepada integer, penukaran integer kepada Nilai boolean dan penentuan jenis pembolehubah dan penukaran. Pengenalan terperinci: 1. Menukar rentetan kepada integer termasuk fungsi intval() dan (int) penukaran jenis paksa 2. Menukar integer kepada rentetan termasuk fungsi strval() dan (rentetan) penukaran jenis paksa; rentetan kepada apungan Mata dan sebagainya.

Artikel ini akan membawa anda melalui JIT dalam PHP 8 dan bercakap tentang cara JIT mengambil bahagian dalam proses tafsiran saya harap ia akan membantu semua orang.

Peningkatan prestasi php8 termasuk: 1. Pengenalan pengkompil JIT; 2. Pengoptimuman panggilan fungsi; . Memperbaik pemprosesan tatasusunan; 8. Memperkenalkan mekanisme pengurusan memori baharu; Pengenalan terperinci: 1. Pengenalan pengkompil JIT PHP8 memperkenalkan pengkompil JIT, yang merupakan teknologi kompilasi dinamik yang boleh menukar kod PHP kepada kod mesin untuk pelaksanaan yang lebih cekap, dsb.

Ciri-ciri php8: 1. Pengkompil JIT; 3. Jenis kesatuan; ; 10. Penambahbaikan konsisten. Pengenalan terperinci: 1. Pengkompil JIT, PHP8 memperkenalkan teknologi kompilasi tepat dalam masa, yang menyusun kod PHP ke dalam kod mesin asli, dengan itu meningkatkan kelajuan pelaksanaan program 2. Parameter yang dinamakan, PHP8 menyokong parameter yang dinamakan, membenarkan penggunaan nama parameter semasa memanggil fungsi dll.
