我们知道定义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; }
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; }
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; }
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; }
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; }
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); } }
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); }