PHP内核学习SAPI
Posted LemonTree
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PHP内核学习SAPI相关的知识,希望对你有一定的参考价值。
学习php-src之前,我准备了一份源文件:
GitHub下载->https://github.com/helingfeng/php-src
简单分析一下源码的目录结构:
1. root根目录下,包含项目的说明文件以及设计方案,大部分文件是必读的。
2. build顾名思义,放置一些和源码编译相关的文件,比如编译前脚本配置、环境监测等。
3. ext官方扩展,包含了绝大数PHP函数的定义和实现,包括date、pdo、ftp、curl等。
4. main 放置PHP核心文件,主要实现PHP的基础设施,这里和Zend engine不一样,Zend engine主要完成最核心的语言运行环境。
5. Zend 放置Zend engine实现文件,包含脚本语法解析,扩展机制的实现等。
6. pear PHP的扩展与应用仓库
7. sapi 包含多种服务器的抽象层代码,例如apache的mod_php、cgi、fcgi以及fpm等接口。
8. TSRM(Thread Safe Resource Manager) php的线程安全是构建在TSRM库之上的。
9. tests php的测试脚本集合,包含各个模块功能的测试文件。
10. win32 包含windows平台下的相关实现,比如socket的实现在windows与*Nix平台就不太相同,同时包含了在windows下编译php的相关脚本。
讲完php目录结构,先来一张php架构图:
从架构图中,很清楚看出SAPI(Server Application Programming Interface)应用编程接口,是非常重要的东东。
今天,拿最简单的CGI接口来学习SAPI。
先百度百科一下什么是CGI:Common Gateway Interface 是WWW技术中最重要的技术之一,有着不可替代的重要地位。CGI是外部应用程序(CGI程序)与Web服务器之间的接口标准,是在CGI程序和Web服务器之间传递信息的规程。CGI规范允许Web服务器执行外部程序,并将它们的输出发送给Web浏览器,CGI将Web的一组简单的静态超媒体文档变成一个完整的新的交互式媒体。
脚本执行的开始都是以SAPI接口实现开始的。只是不同的SAPI接口实现会完成他们特定的工作, 例如Apache的mod_php SAPI实现需要初始化从Apache获取的一些信息,在输出内容是将内容返回给Apache, 其他的SAPI实现也类似。
以CGI为例了解一下源码结构:
cgi_main.c
定义SAPI:
1 /* {{{ sapi_module_struct cgi_sapi_module 2 */ 3 static sapi_module_struct cgi_sapi_module = { 4 "cgi-fcgi", /* name */ 5 "CGI/FastCGI", /* pretty name */ 6 7 php_cgi_startup, /* startup */ 8 php_module_shutdown_wrapper, /* shutdown */ 9 10 sapi_cgi_activate, /* activate */ 11 sapi_cgi_deactivate, /* deactivate */ 12 13 sapi_cgi_ub_write, /* unbuffered write */ 14 sapi_cgi_flush, /* flush */ 15 NULL, /* get uid */ 16 sapi_cgi_getenv, /* getenv */ 17 18 php_error, /* error handler */ 19 20 NULL, /* header handler */ 21 sapi_cgi_send_headers, /* send headers handler */ 22 NULL, /* send header handler */ 23 24 sapi_cgi_read_post, /* read POST data */ 25 sapi_cgi_read_cookies, /* read Cookies */ 26 27 sapi_cgi_register_variables, /* register server variables */ 28 sapi_cgi_log_message, /* Log message */ 29 NULL, /* Get request time */ 30 NULL, /* Child terminate */ 31 32 STANDARD_SAPI_MODULE_PROPERTIES 33 };
定义了一些常量字符串,以及定义初始化函数、销毁函数、以及一些函数指针,告诉Zend如何获取与输出数据。
例如:php_cgi_startup 调用php初始化。
1 static int php_cgi_startup(sapi_module_struct *sapi_module) 2 { 3 if (php_module_startup(sapi_module, &cgi_module_entry, 1) == FAILURE) { 4 return FAILURE; 5 } 6 return SUCCESS; 7 }
例如:php_module_shutdown_wrapper php关闭函数。
1 int php_module_shutdown_wrapper(sapi_module_struct *sapi_globals) 2 { 3 php_module_shutdown(); 4 return SUCCESS; 5 }
例如:sapi_cgi_activate 处理request进行初始化。
1 static int sapi_cgi_activate(void) 2 { 3 char *path, *doc_root, *server_name; 4 size_t path_len, doc_root_len, server_name_len; 5 6 /* PATH_TRANSLATED should be defined at this stage but better safe than sorry :) */ 7 if (!SG(request_info).path_translated) { 8 return FAILURE; 9 } 10 11 if (php_ini_has_per_host_config()) { 12 /* Activate per-host-system-configuration defined in php.ini and stored into configuration_hash during startup */ 13 if (fcgi_is_fastcgi()) { 14 fcgi_request *request = (fcgi_request*) SG(server_context); 15 16 server_name = FCGI_GETENV(request, "SERVER_NAME"); 17 } else { 18 server_name = getenv("SERVER_NAME"); 19 } 20 /* SERVER_NAME should also be defined at this stage..but better check it anyway */ 21 if (server_name) { 22 server_name_len = strlen(server_name); 23 server_name = estrndup(server_name, server_name_len); 24 zend_str_tolower(server_name, server_name_len); 25 php_ini_activate_per_host_config(server_name, server_name_len); 26 efree(server_name); 27 } 28 } 29 30 if (php_ini_has_per_dir_config() || 31 (PG(user_ini_filename) && *PG(user_ini_filename)) 32 ) { 33 /* Prepare search path */ 34 path_len = strlen(SG(request_info).path_translated); 35 36 /* Make sure we have trailing slash! */ 37 if (!IS_SLASH(SG(request_info).path_translated[path_len])) { 38 path = emalloc(path_len + 2); 39 memcpy(path, SG(request_info).path_translated, path_len + 1); 40 path_len = zend_dirname(path, path_len); 41 path[path_len++] = DEFAULT_SLASH; 42 } else { 43 path = estrndup(SG(request_info).path_translated, path_len); 44 path_len = zend_dirname(path, path_len); 45 } 46 path[path_len] = 0; 47 48 /* Activate per-dir-system-configuration defined in php.ini and stored into configuration_hash during startup */ 49 php_ini_activate_per_dir_config(path, path_len); /* Note: for global settings sake we check from root to path */ 50 51 /* Load and activate user ini files in path starting from DOCUMENT_ROOT */ 52 if (PG(user_ini_filename) && *PG(user_ini_filename)) { 53 if (fcgi_is_fastcgi()) { 54 fcgi_request *request = (fcgi_request*) SG(server_context); 55 56 doc_root = FCGI_GETENV(request, "DOCUMENT_ROOT"); 57 } else { 58 doc_root = getenv("DOCUMENT_ROOT"); 59 } 60 /* DOCUMENT_ROOT should also be defined at this stage..but better check it anyway */ 61 if (doc_root) { 62 doc_root_len = strlen(doc_root); 63 if (doc_root_len > 0 && IS_SLASH(doc_root[doc_root_len - 1])) { 64 --doc_root_len; 65 } 66 #ifdef PHP_WIN32 67 /* paths on windows should be case-insensitive */ 68 doc_root = estrndup(doc_root, doc_root_len); 69 zend_str_tolower(doc_root, doc_root_len); 70 #endif 71 php_cgi_ini_activate_user_config(path, path_len, doc_root, doc_root_len, (doc_root_len > 0 && (doc_root_len - 1))); 72 73 #ifdef PHP_WIN32 74 efree(doc_root); 75 #endif 76 } 77 } 78 79 efree(path); 80 } 81 82 return SUCCESS; 83 }
例如:sapi_cgi_deactivate 处理完request关闭函数,与activate对应。
1 static int sapi_cgi_deactivate(void) 2 { 3 /* flush only when SAPI was started. The reasons are: 4 1. SAPI Deactivate is called from two places: module init and request shutdown 5 2. When the first call occurs and the request is not set up, flush fails on FastCGI. 6 */ 7 if (SG(sapi_started)) { 8 if (fcgi_is_fastcgi()) { 9 if ( 10 !parent && 11 !fcgi_finish_request((fcgi_request*)SG(server_context), 0)) { 12 php_handle_aborted_connection(); 13 } 14 } else { 15 sapi_cgi_flush(SG(server_context)); 16 } 17 } 18 return SUCCESS; 19 }
例如:sapi_cgibin_ub_write 告诉Zend如何输出数据。
1 static size_t sapi_cgibin_ub_write(const char *str, size_t str_length) /* {{{ */ 2 { 3 const char *ptr = str; 4 uint remaining = str_length; 5 size_t ret; 6 7 while (remaining > 0) { 8 ret = sapi_cgibin_single_write(ptr, remaining); 9 if (!ret) { 10 php_handle_aborted_connection(); 11 return str_length - remaining; 12 } 13 ptr += ret; 14 remaining -= ret; 15 } 16 17 return str_length; 18 }
例如:sapi_cgi_flush Zend刷新缓存。
1 static void sapi_cgi_flush(void *server_context) 2 { 3 if (fflush(stdout) == EOF) { 4 php_handle_aborted_connection(); 5 } 6 }
例如:sapi_cgi_getenv 获取环境信息。
1 static char *sapi_cgi_getenv(char *name, size_t name_len) 2 { 3 return getenv(name); 4 }
例如:php_error 异常输出。
CGI只是简单的调用了PHP提供的错误处理函数
例如:sapi_cgi_read_post 读取post data。
1 static size_t sapi_cgi_read_post(char *buffer, size_t count_bytes) 2 { 3 size_t read_bytes = 0; 4 int tmp_read_bytes; 5 size_t remaining_bytes; 6 7 assert(SG(request_info).content_length >= SG(read_post_bytes)); 8 9 remaining_bytes = (size_t)(SG(request_info).content_length - SG(read_post_bytes)); 10 11 count_bytes = MIN(count_bytes, remaining_bytes); 12 while (read_bytes < count_bytes) { 13 #ifdef PHP_WIN32 14 size_t diff = count_bytes - read_bytes; 15 unsigned int to_read = (diff > UINT_MAX) ? UINT_MAX : (unsigned int)diff; 16 17 tmp_read_bytes = read(STDIN_FILENO, buffer + read_bytes, to_read); 18 #else 19 tmp_read_bytes = read(STDIN_FILENO, buffer + read_bytes, count_bytes - read_bytes); 20 #endif 21 if (tmp_read_bytes <= 0) { 22 break; 23 } 24 read_bytes += tmp_read_bytes; 25 } 26 return read_bytes; 27 }
......
在编译php时,如果你需要fastcgi支持:
You must add \'--enable-fastcgi\' to the configure command on Linux or
OSX based systems to get fastcgi support in the php-cgi binary. You
also must not use \'--enable-discard-path\'.
如果在Apache中使用:
Using FastCGI PHP with Apache
=============================
#LoadModule php7_module /usr/lib/apache/2.0/libphp7.so
ScriptAlias /fcgi-bin/ /space/fcgi-bin/
<Location /fcgi-bin/>
Options ExecCGI
SetHandler fastcgi-script
</Location>
......
SAPI 服务器端抽象层代码实现。
看完哪些C语言代码我是头晕的,每天进步一点点!
以上是关于PHP内核学习SAPI的主要内容,如果未能解决你的问题,请参考以下文章
[PHP内核]PHP内核学习------sapi_module_struct结构体解析(以cli为例)
[PHP内核]PHP内核学习------sapi_module_struct结构体解析(以cli为例)