This article mainly introduces your understanding of ZendSAPIs (ZendSAPIInternals). If you need it, refer to SAPI: Server transaction action API, students who have studied the PHP architecture should know the importance of this stuff. It provides an interface for PHP to interact with other applications. This article will not detail the SAPI of each PHP, but describes the SAPI Mechanism for the simplest cgi sapi.
First, let's take a look at the PHP architecture:
PHP Architecture
SAPI provides an interface for external communication. For PHP5.2, many types of sapis are provided by default. common interfaces include mod_php5 and CGI for apache, ISAPI for IIS, and CLI for Shell, this article starts with cgi sapi and introduces the SAPI mechanism. Although CGI is simple, you don't have to worry about it. It contains a vast majority of content, which is enough for you to deeply understand how SAPI works.
To define a SAPI, first define a sapi_module_struct to view the PHP-SRC/sapi/cgi/cgi_main.c:
*/static sapi_module_struct cgi_sapi_module = {#if PHP_FASTCGI "cgi-fcgi", /* name */ "CGI/FastCGI", /* pretty name */#else "cgi", /* name */ "CGI", /* pretty name */#endif php_cgi_startup, /* startup */ php_module_shutdown_wrapper, /* shutdown */ NULL, /* activate */ sapi_cgi_deactivate, /* deactivate */ sapi_cgibin_ub_write, /* unbuffered write */ sapi_cgibin_flush, /* flush */ NULL, /* get uid */ sapi_cgibin_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 */ STANDARD_SAPI_MODULE_PROPERTIES};
This structure contains some constants, such as name, which will be used when we call php_info. Some initialization, final functions, and some function pointers are used to tell Zend how to obtain and output data.
1. php_cgi_startup: This function is called when an application calls PHP. For CGI, it simply calls the PHP initialization function:
static int php_cgi_startup(sapi_module_struct *sapi_module){ if (php_module_startup(sapi_module, NULL, 0) == FAILURE) { return FAILURE; } return SUCCESS;}
2. php_module_shutdown_wrapper, a simple package for PHP function closures. It's just a simple call to php_module_shutdown;
3. PHP processes initialization and resource allocation transactions for each request. This part is the activate field to be defined. From the above structure, we can see that for CGI, it does not provide initialization processing handle. For mod_php, it is different. He wants to register resource destructor, apply for space, initialize environment variables, and so on in the apache pool.
4. sapi_cgi_deactivate, which corresponds to the activate function. As the name suggests, it provides a handler to handle the final work. For CGI, it just refreshes the buffer, to ensure that you get all output data before Zend is disabled:
static int sapi_cgi_deactivate(TSRMLS_D){ /* flush only when SAPI was started. The reasons are: 1. SAPI Deactivate is called from two places: module init and request shutdown 2. When the first call occurs and the request is not set up, flush fails on FastCGI. */ if (SG(sapi_started)) { sapi_cgibin_flush(SG(server_context)); } return SUCCESS;}
5. sapi_cgibin_ub_write. This hanlder tells Zend how to output data. For mod_php, this function provides an interface for writing data to response. For CGI, it is simply written to stdout:
static inline size_t sapi_cgibin_single_write(const char *str, uint str_length TSRMLS_DC){#ifdef PHP_WRITE_STDOUT long ret;#else size_t ret;#endif#if PHP_FASTCGI if (fcgi_is_fastcgi()) { fcgi_request *request = (fcgi_request*) SG(server_context); long ret = fcgi_write(request, FCGI_STDOUT, str, str_length); if (ret <= 0) { return 0; } return ret; }#endif#ifdef PHP_WRITE_STDOUT ret = write(STDOUT_FILENO, str, str_length); if (ret <= 0) return 0; return ret;#else ret = fwrite(str, 1, MIN(str_length, 16384), stdout); return ret;#endif}static int sapi_cgibin_ub_write(const char *str, uint str_length TSRMLS_DC){ const char *ptr = str; uint remaining = str_length; size_t ret; while (remaining > 0) { ret = sapi_cgibin_single_write(ptr, remaining TSRMLS_CC); if (!ret) { php_handle_aborted_connection(); return str_length - remaining; } ptr += ret; remaining -= ret; } return str_length;}
The true write logic is stripped out to implement a write method compatible with fastcgi.
6. sapi_cgibin_flush: This is the function handle provided to zend to refresh the cache. For CGI, it is just a simple call to the fflush provided by the system;
7. NULL. This part allows Zend to verify the state of the script file to be executed and determine whether the file has the execution permission. CGI does not provide it.
8. sapi_cgibin_getenv provides Zend with an interface for finding environment variables based on name. For mod_php5, when we call getenv in the script, this handle is indirectly called. For CGI, because its operating mechanism is similar to CLI, directly calling the parent class is Shell, it simply calls the genenv provided by the system:
static char *sapi_cgibin_getenv(char *name, size_t name_len TSRMLS_DC){#if PHP_FASTCGI /* when php is started by mod_fastcgi, no regular environment is provided to PHP. It is always sent to PHP at the start of a request. So we have to do our own lookup to get env vars. This could probably be faster somehow. */ if (fcgi_is_fastcgi()) { fcgi_request *request = (fcgi_request*) SG(server_context); return fcgi_getenv(request, name, name_len); }#endif /* if cgi, or fastcgi and not found in fcgi env check the regular environment */ return getenv(name);}
9. php_error, error handling function. Here, I will talk a few digons. The last time I saw the php maillist mentioned, I made PHP's error handling mechanism fully OO, that is, rewrite the function handle, in this way, throw an exception whenever an error occurs. CGI simply calls the error handler provided by PHP.
10. This function will be called when we call the PHP header () function. It is not provided for CGI.
11. sapi_cgi_send_headers, this function will be called when the header is actually sent. Generally, it is before any output is sent:
static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC){ char buf[SAPI_CGI_MAX_HEADER_LENGTH]; sapi_header_struct *h; zend_llist_position pos; if (SG(request_info).no_headers == 1) { return SAPI_HEADER_SENT_SUCCESSFULLY; } if (cgi_nph || SG(sapi_headers).http_response_code != 200) { int len; if (rfc2616_headers && SG(sapi_headers).http_status_line) { len = snprintf(buf, SAPI_CGI_MAX_HEADER_LENGTH, "%s\r\n", SG(sapi_headers).http_status_line); if (len > SAPI_CGI_MAX_HEADER_LENGTH) { len = SAPI_CGI_MAX_HEADER_LENGTH; } } else { len = sprintf(buf, "Status: %d\r\n", SG(sapi_headers).http_response_code); } PHPWRITE_H(buf, len); } h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos); while (h) { /* prevent CRLFCRLF */ if (h->header_len) { PHPWRITE_H(h->header, h->header_len); PHPWRITE_H("\r\n", 2); } h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos); } PHPWRITE_H("\r\n", 2); return SAPI_HEADER_SENT_SUCCESSFULLY; }
12. NULL. This is used to send each header separately. CGI does not provide
13. sapi_cgi_read_post. This handle specifies how to obtain post data. If CGI programming is done, we know that CGI reads post data from stdin,
static int sapi_cgi_read_post(char *buffer, uint count_bytes TSRMLS_DC){ uint read_bytes=0, tmp_read_bytes;#if PHP_FASTCGI char *pos = buffer;#endif count_bytes = MIN(count_bytes, (uint) SG(request_info).content_length - SG(read_post_bytes)); while (read_bytes < count_bytes) {#if PHP_FASTCGI if (fcgi_is_fastcgi()) { fcgi_request *request = (fcgi_request*) SG(server_context); tmp_read_bytes = fcgi_read(request, pos, count_bytes - read_bytes); pos += tmp_read_bytes; } else { tmp_read_bytes = read(0, buffer + read_bytes, count_bytes - read_bytes); }#else tmp_read_bytes = read(0, buffer + read_bytes, count_bytes - read_bytes);#endif if (tmp_read_bytes <= 0) { break; } read_bytes += tmp_read_bytes; } return read_bytes;}
14. sapi_cgi_read_cookies. This is the same as the above function, except to get the cookie value:
static char *sapi_cgi_read_cookies(TSRMLS_D){ return sapi_cgibin_getenv((char *) "HTTP_COOKIE", sizeof("HTTP_COOKIE")-1 TSRMLS_CC);}
15. sapi_cgi_register_variables. This function provides an interface to add a variable to the $ _ SERVER variable. For CGI, A PHP_SELF is registered, in this way, you can access $ _ SERVER ['php _ SELF '] in the script to obtain
This request_uri:
static void sapi_cgi_register_variables(zval *track_vars_array TSRMLS_DC){ /* In CGI mode, we consider the environment to be a part of the server * variables */ php_import_environment_variables(track_vars_array TSRMLS_CC); /* Build the special-case PHP_SELF variable for the CGI version */ php_register_variable("PHP_SELF", (SG(request_info).request_uri ? SG(request_info).request_uri : ""), track_vars_array TSRMLS_CC);}
16. sapi_cgi_log_message is used to output error messages. For CGI, it is simply output to stderr:
static void sapi_cgi_log_message(char *message){#if PHP_FASTCGI if (fcgi_is_fastcgi() && fcgi_logging) { fcgi_request *request; TSRMLS_FETCH(); request = (fcgi_request*) SG(server_context); if (request) { int len = strlen(message); char *buf = malloc(len+2); memcpy(buf, message, len); memcpy(buf + len, "\n", sizeof("\n")); fcgi_write(request, FCGI_STDERR, buf, len+1); free(buf); } else { fprintf(stderr, "%s\n", message); } /* ignore return code */ } else#endif /* PHP_FASTCGI */ fprintf(stderr, "%s\n", message);}
After analysis, we have learned how to implement an SAPI. After analyzing CGI, we can imagine the implementation mechanism of SAPI, such as mod_php and embed. :)
I hope you will like it.