Sapi:server abstraction API, the students who have studied PHP architecture should know the importance of this stuff, which provides an interface that allows PHP to interact with other applications. This article does not detail each PHP SAPI, but only for the simplest CGI SAPI, to illustrate the mechanism of SAPI.
First, let's take a look at the architecture diagram of PHP:
Figure 1 PHP Architecture
SAPI provides an interface to the external communication, for PHP5.2, the default provides many kinds of SAPI, common to Apache mod_php5,cgi, to IIS ISAPI, and the Shell CLI, this article from the CGI SAPI, introduced SAPI mechanism. Although CGI is simple, don't worry, it contains most of the content, enough to give you a deep understanding of how SAPI works.
To define a SAPI, first define a sapi_module_struct to see PHP-SRC/SAPI/CGI/CGI_MAIN.C:
*/static sapi_module_struct Cgi_sapi_module = {#if php_fastcgi "cgi-fcgi",/* n AME */"cgi/fastcgi",/* Pretty name */#else "CGI",/* Name */"CGI",/* Pretty name */#endif Php_cgi_sta Rtup,/* startup */Php_module_shutdown_wrapper,/* shutdown */NULL,/* activate */sapi_cgi_deactivate,/* d Eactivate */sapi_cgibin_ub_write,/* unbuffered write */Sapi_cgibin_flush,/* flush */NULL,/* Get UID */s Api_cgibin_getenv,/* getenv */php_error,/* ERROR handler */NULL,/* Header handler */sapi_cgi_send_he Aders,/* Send headers Handler */NULL,/* Send header handler */Sapi_cgi_read_post,/* Read POST data */SAP I_cgi_read_cookies,/* Read cookies */sapi_cgi_register_variables,/* Register Server variables */SAPI_CGI_LOG_MESSAG E,/* LOG message */NULL,/* Get Request time */standard_sapi_module_properties};
This structure contains constants, such as name, which will be used when we call Php_info (). Some initialization, closing functions, and some function pointers, are used to tell Zend, how to fetch, and output data.
1. Php_cgi_startup, when an application to invoke PHP, this function will be called, 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) { C0/>return FAILURE; } return SUCCESS;}
2. Php_module_shutdown_wrapper, a simple wrapper for the PHP shutdown function. Simply call the Php_module_shutdown;
3. PHP handles some initialization, resource-allocation transactions at each request. This is part of the Activate field to be defined, as we can see from the structure above that, for CGI, it does not provide a handle to the initialization handle. For mod_php, that's different, he's going to register the resource destructor in the Apache pool, request space, initialize the environment variables, and so on.
4. Sapi_cgi_deactivate, this is the corresponding function with the Activate, as the name implies, it will provide a handler, to handle the finishing work, for CGI, he simply flush buffer, To ensure that the user obtains all output data before the Zend is closed:
static int Sapi_cgi_deactivate (TSRMLS_D) {/* flush only if SAPI was started. The reasons is: 1. SAPI Deactivate is called from the Places:module init and request shutdown 2. When the first call occurs and the request are 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 to response data, and for CGI, it simply writes 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 real logic of writing is stripped out, simply to achieve the compatibility of fastcgi write way.
6. Sapi_cgibin_flush, this is a function handle that is provided to Zend to flush the cache, and for CGI, it simply invokes the fflush provided by the system;
7.NULL, which is used to allow Zend to validate a state to execute a script file, to determine whether the file has execute permissions, and so on, not provided by CGI.
8. Sapi_cgibin_getenv, provides an interface for Zend to find environment variables according to name, and for MOD_PHP5, when we call getenv in the script, we call the handle indirectly. And for CGI, because his operating mechanism is similar to the CLI, the direct call to the parent is the shell, so it simply invokes the system-provided genenv:
Static Char *sapi_cgibin_getenv (char *name, size_t Name_len tsrmls_dc) {#if php_fastcgi/* When PHP was started by MOD_FASTC GI, no regular environment is provided to PHP. It is always sent to PHP at the start of a request. So, we have the to does our own lookup to get Env VARs. This could probably is 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, say a few digression, last see php maillist mentioned make PHP error handling mechanism completely OO, that is, rewrite this function handle, so that whenever there is an error occurred, throw an exception. CGI simply invokes the error-handling function provided by PHP.
10. This function is called when we call the header () function of PHP and is not available for CGI.
Sapi_cgi_send_headers, this function will be called when the header is actually sent, in general, when there is any output to send:
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! =) {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; }
NULL, this is used to send each header separately, CGI does not provide
Sapi_cgi_read_post, this handle indicates how to get post data, and if you do CGI programming, 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_by TES)); 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;}
Sapi_cgi_read_cookies, this is the same as the above function, just 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);}
Sapi_cgi_register_variables, this function gives an interface to add a variable to the $_server variable, and for CGI, it registers a php_self so that we can access $_server[' php_ in the script Self '] to get
The Request_uri of this time:
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? S G (Request_info). Request_uri: ""), Track_vars_array tsrmls_cc);}
Sapi_cgi_log_message, which is used to output error messages, is simply output to stderr for CGI:
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 understood how a SAPI is implemented, after analyzing the CGI, we can also imagine mod_php, embed and other SAPI implementation mechanism. :)
How, this article introduces is not very detailed, hoped everybody likes.