天天看点

PHP内核-Apache2的SAPI

我们知道定义SAPI之前,首先要定义sapi_module_struct这个结构,相看源码:/soft/php-5.2.9/sapi/apache2handler/sapi_apache2.c,可以看到定义该结构,我直接复制过来:

static sapi_module_struct apache2_sapi_module = {
        "apache2handler",
        "Apache 2.0 Handler",

        php_apache2_startup,                            /* startup */
        php_module_shutdown_wrapper,                    /* shutdown */

        NULL,                                           /* activate */
        NULL,                                           /* deactivate */

        php_apache_sapi_ub_write,                       /* unbuffered write */
        php_apache_sapi_flush,                          /* flush */
        php_apache_sapi_get_stat,                       /* get uid */
        php_apache_sapi_getenv,                         /* getenv */

        php_error,                                      /* error handler */

        php_apache_sapi_header_handler,                 /* header handler */
        php_apache_sapi_send_headers,                   /* send headers handler */
        NULL,                                           /* send header handler */

        php_apache_sapi_read_post,                      /* read POST data */
        php_apache_sapi_read_cookies,                   /* read Cookies */

        php_apache_sapi_register_variables,
        php_apache_sapi_log_message,                    /* Log message */
        php_apache_sapi_get_request_time,               /* Request Time */

        STANDARD_SAPI_MODULE_PROPERTIES
};
           

1,php_apache2_startup:当通过apache调用PHP时,这个函数会被调用。该函数定义如下,主要是对PHP进行初始化。

static int php_apache2_startup(sapi_module_struct *sapi_module)
{
        if (php_module_startup(sapi_module, &php_apache_module, 1)==FAILURE) {
                return FAILURE;
        }
        return SUCCESS;
}
           

2,php_module_shutdown_wrapper :PHP的关闭函数。

3,PHP会在每个request的时候,处理一些初始化,资源分配的事务。这部分就是activate字段要定义的。

4,与activate的函数,就是deactiveate,它会提供一个handler, 用来处理收尾工作。

5,php_apache_sapi_ub_write:提供一个向Response数据写的接口。

static int
php_apache_sapi_ub_write(const char *str, uint str_length TSRMLS_DC)
{
        request_rec *r;
        php_struct *ctx;

        ctx = SG(server_context);
        r = ctx->r;

        if (ap_rwrite(str, str_length, r) < 0) {
                php_handle_aborted_connection();
        }

        return str_length; /* we always consume all the data passed to us. */
}
           

6,php_apache_sapi_flush:提供给zend刷新缓存的句柄。

static void
php_apache_sapi_flush(void *server_context)
{
        php_struct *ctx;
        request_rec *r;
        TSRMLS_FETCH();

        ctx = server_context;

        /* If we haven't registered a server_context yet,
         * then don't bother flushing. */
        if (!server_context) {
                return;
        }

        r = ctx->r;

        sapi_send_headers(TSRMLS_C);

        r->status = SG(sapi_headers).http_response_code;
        SG(headers_sent) = 1;

        if (ap_rflush(r) < 0 || r->connection->aborted) {
                php_handle_aborted_connection();
        }
}
           

7,php_apache_sapi_get_stat:这部分用来让Zend可以验证一个要执行脚本文件的state,从而判断文件是否据有执行权限等等。

static struct stat*
php_apache_sapi_get_stat(TSRMLS_D)
{
        php_struct *ctx = SG(server_context);

        ctx->finfo.st_uid = ctx->r->finfo.user;
        ctx->finfo.st_gid = ctx->r->finfo.group;
        ctx->finfo.st_dev = ctx->r->finfo.device;
        ctx->finfo.st_ino = ctx->r->finfo.inode;
#if defined(NETWARE) && defined(CLIB_STAT_PATCH)
        ctx->finfo.st_atime.tv_sec = apr_time_sec(ctx->r->finfo.atime);
        ctx->finfo.st_mtime.tv_sec = apr_time_sec(ctx->r->finfo.mtime);
        ctx->finfo.st_ctime.tv_sec = apr_time_sec(ctx->r->finfo.ctime);
#else
        ctx->finfo.st_atime = apr_time_sec(ctx->r->finfo.atime);
        ctx->finfo.st_mtime = apr_time_sec(ctx->r->finfo.mtime);
        ctx->finfo.st_ctime = apr_time_sec(ctx->r->finfo.ctime);
#endif

        ctx->finfo.st_size = ctx->r->finfo.size;
        ctx->finfo.st_nlink = ctx->r->finfo.nlink;

        return &ctx->finfo;
}
           

8,php_apache_sapi_getenv:为Zend提供了一个根据name来查找环境变量的接口,当我们在脚本中调用getenv的时候,就会间接的调用这个句柄。

static char *
php_apache_sapi_getenv(char *name, size_t name_len TSRMLS_DC)
{
        php_struct *ctx = SG(server_context);
        const char *env_var;

        env_var = apr_table_get(ctx->r->subprocess_env, name);

        return (char *) env_var;
}
           

9,php_error:错误处理函数,直接调用PHP错误处理函数。

10,php_apache_sapi_header_handler:在调用PHP的header()函数时,会调用这个函数。

static int
php_apache_sapi_header_handler(sapi_header_struct *sapi_header,sapi_headers_struct *sapi_headers TSRMLS_DC)
{
        php_struct *ctx;
        char *val, *ptr;

        ctx = SG(server_context);

        val = strchr(sapi_header->header, ':');

        if (!val) {
                sapi_free_header(sapi_header);
                return 0;
        }
        ptr = val;

        *val = '\0';

        do {
                val++;
        } while (*val == ' ');
        if (!strcasecmp(sapi_header->header, "content-type")) {
                if (ctx->content_type) {
                        efree(ctx->content_type);
                }
                ctx->content_type = estrdup(val);
        } else if (sapi_header->replace) {
                apr_table_set(ctx->r->headers_out, sapi_header->header, val);
        } else {
                apr_table_add(ctx->r->headers_out, sapi_header->header, val);
        }
        *ptr = ':';

        return SAPI_HEADER_ADD;
}
           

11,php_apache_sapi_send_headers:当要真正发送header的时候,这个函数会被调用。

static int
php_apache_sapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
{
        php_struct *ctx = SG(server_context);
        const char *sline = SG(sapi_headers).http_status_line;

        ctx->r->status = SG(sapi_headers).http_response_code;

        /* httpd requires that r->status_line is set to the first digit of
         * the status-code: */
        if (sline && strlen(sline) > 12 && strncmp(sline, "HTTP/1.", 7) == 0 && sline[8] == ' ') {
                ctx->r->status_line = apr_pstrdup(ctx->r->pool, sline + 9);
                ctx->r->proto_num = 1000 + (sline[7]-'0');
                if ((sline[7]-'0') == 0) {
                        apr_table_set(ctx->r->subprocess_env, "force-response-1.0", "true");
                }
        }

        /*      call ap_set_content_type only once, else each time we call it,
                configured output filters for that content type will be added */
        if (!ctx->content_type) {
                ctx->content_type = sapi_get_default_content_type(TSRMLS_C);
        }
        ap_set_content_type(ctx->r, apr_pstrdup(ctx->r->pool, ctx->content_type));
        efree(ctx->content_type);
        ctx->content_type = NULL;

        return SAPI_HEADER_SENT_SUCCESSFULLY;
}
           

12,在php_apache_sapi_send_headers指针下面有一个域,用来指明发送每一个单独的header时调用。

13,php_apache_sapi_read_post:表示如何读取POST数据。

static int 
php_apache_sapi_read_post(char *buf, uint count_bytes TSRMLS_DC)
{
        apr_size_t len, tlen=0;
        php_struct *ctx = SG(server_context);
        request_rec *r;
        apr_bucket_brigade *brigade;

        r = ctx->r;
        brigade = ctx->brigade;
        len = count_bytes;

        /*
         * This loop is needed because ap_get_brigade() can return us partial data
         * which would cause premature termination of request read. Therefor we
         * need to make sure that if data is available we fill the buffer completely.
         */

        while (ap_get_brigade(r->input_filters, brigade, AP_MODE_READBYTES, APR_BLOCK_READ, len) == APR_SUCCESS) {
                apr_brigade_flatten(brigade, buf, &len);
                apr_brigade_cleanup(brigade);
                tlen += len;
                if (tlen == count_bytes || !len) {
                        break;
                }
                buf += len;
                len = count_bytes - tlen;
        }

        return tlen;
}
           

14,php_apache_sapi_read_cookie:如何读取cookie。

static char *
php_apache_sapi_read_cookies(TSRMLS_D)
{
        php_struct *ctx = SG(server_context);
        const char *http_cookie;

        http_cookie = apr_table_get(ctx->r->headers_in, "cookie");

        /* The SAPI interface should use 'const char *' */
        return (char *) http_cookie;
}
           

15,php_apache_sapi_register_variables:提供接口,用于给$_SERVER[]数组提供变量。

static void
php_apache_sapi_register_variables(zval *track_vars_array TSRMLS_DC)
{
        php_struct *ctx = SG(server_context);
        const apr_array_header_t *arr = apr_table_elts(ctx->r->subprocess_env);
        char *key, *val;
        int new_val_len;

        APR_ARRAY_FOREACH_OPEN(arr, key, val)
                if (!val) {
                        val = "";
                }
                if (sapi_module.input_filter(PARSE_SERVER, key, &val, strlen(val), &new_val_len TSRMLS_CC)) {
                        php_register_variable_safe(key, val, new_val_len, track_vars_array TSRMLS_CC);
                }
        APR_ARRAY_FOREACH_CLOSE()

        if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &ctx->r->uri, strlen(ctx->r->uri), &new_val_len TSRMLS_CC)) {
                php_register_variable_safe("PHP_SELF", ctx->r->uri, new_val_len, track_vars_array TSRMLS_CC);
        }
}
           

16,php_apache_sapi_log_message:输出错误信息。

static void php_apache_sapi_log_message(char *msg)
{
        php_struct *ctx;
        TSRMLS_FETCH();

        ctx = SG(server_context);

        if (ctx == NULL) { /* we haven't initialized our ctx yet, oh well */
                ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, 0, NULL, "%s", msg);
        } else {
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, "%s", msg);
        }
}
           

17,php_apache_sapi_get_request_time:获取请求时间。

static time_t php_apache_sapi_get_request_time(TSRMLS_D) {
        php_struct *ctx = SG(server_context);
        return apr_time_sec(ctx->r->request_time);
}
           

这就完成了apache的SAPI定义。之后,就是当用户用URL请求apache服务,这些函数指针就会在适当的时候,发挥作用了(被调用)。