zoukankan      html  css  js  c++  java
  • 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语言代码我是头晕的,每天进步一点点!

  • 相关阅读:
    ELF和a.out文件格式的比较
    vim常用命令
    安装linux各种桌面环境
    使用virt-manager创建和管理虚拟机
    第一天 纪念一下
    i节点,容易被人遗忘的节点
    【Linux】服务器之间的免密登录脚本
    【python】python调用shell方法
    【ansible】ansible部署方式以及部署包
    【AWS】亚马逊云常用服务解释
  • 原文地址:https://www.cnblogs.com/helingfeng/p/5575952.html
Copyright © 2011-2022 走看看