Understanding PHP SAPI Internals: CGI Module Structure and Startup Process
This article provides an in‑depth English overview of PHP’s SAPI architecture, detailing the CGI SAPI module structure, the core _sapi_module_struct and sapi_globals definitions, and walking through the startup sequence including sapi_startup, sapi_globals_ctor, and FastCGI initialization with code examples.
This article examines the internal implementation of PHP's Server API (SAPI), focusing on the CGI SAPI module used when PHP runs under a web server.
The CGI SAPI module is defined by the cgi_sapi_module structure, which populates a sapi_module_struct containing function pointers for startup, shutdown, request handling, I/O, and other operations.
<code>//* sapi_module_struct cgi_sapi_module
static sapi_module_struct cgi_sapi_module = {
"cgi-fcgi", /* name */
"CGI/FastCGI", /* pretty name */
php_cgi_startup, /* startup */
php_module_shutdown_wrapper, /* shutdown */
sapi_cgi_activate, /* activate */
sapi_cgi_deactivate, /* deactivate */
sapi_cgi_ub_write, /* unbuffered write */
sapi_cgi_flush, /* flush */
NULL, /* get uid */
sapi_cgi_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 */
NULL, /* Child terminate */
STANDARD_SAPI_MODULE_PROPERTIES
};
</code>The core definition of the SAPI module resides in sapi.h as the _sapi_module_struct structure, which lists all callbacks and configuration fields required by a SAPI implementation.
<code>struct _sapi_module_struct {
char *name;
char *pretty_name;
int (*startup)(struct _sapi_module_struct *sapi_module);
int (*shutdown)(struct _sapi_module_struct *sapi_module);
int (*activate)(void);
int (*deactivate)(void);
size_t (*ub_write)(const char *str, size_t str_length);
void (*flush)(void *server_context);
zend_stat_t *(*get_stat)(void);
char *(*getenv)(const char *name, size_t name_len);
void (*sapi_error)(int type, const char *error_msg, ...) ZEND_ATTRIBUTE_FORMAT(printf, 2, 3);
int (*header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers);
int (*send_headers)(sapi_headers_struct *sapi_headers);
void (*send_header)(sapi_header_struct *sapi_header, void *server_context);
size_t (*read_post)(char *buffer, size_t count_bytes);
char *(*read_cookies)(void);
void (*register_server_variables)(zval *track_vars_array);
void (*log_message)(char *message, int syslog_type_int);
double (*get_request_time)(void);
void (*terminate_process)(void);
/* ... other fields ... */
};
</code>Another crucial data structure is sapi_globals_struct , accessed via the SG macro, which holds runtime state such as request information, headers, and configuration values.
<code>typedef struct _sapi_globals_struct {
void *server_context;
sapi_request_info request_info;
sapi_headers_struct sapi_headers;
int64_t read_post_bytes;
unsigned char post_read;
unsigned char headers_sent;
zend_stat_t global_stat;
char *default_mimetype;
char *default_charset;
HashTable *rfc1867_uploaded_files;
zend_long post_max_size;
int options;
zend_bool sapi_started;
double global_request_time;
HashTable known_post_content_types;
zval callback_func;
zend_fcall_info_cache fci_cache;
} sapi_globals_struct;
</code>The startup sequence begins with a call to sapi_startup(&cgi_sapi_module) , which copies the module definition into a global sapi_module variable, allocates or constructs the sapi_globals structure (depending on whether ZTS is enabled), and registers default content types.
<code>void sapi_startup(sapi_module_struct *sf) {
sf->ini_entries = NULL;
sapi_module = *sf;
#ifdef ZTS
ts_allocate_fast_id(&sapi_globals_id, &sapi_globals_offset,
sizeof(sapi_globals_struct), (ts_allocate_ctor) sapi_globals_ctor,
(ts_allocate_dtor) sapi_globals_dtor);
#else
sapi_globals_ctor(&sapi_globals);
#endif
reentrancy_startup();
}
</code>The sapi_globals_ctor function zero‑initialises the globals and creates a hash table for known POST content types before calling php_setup_sapi_content_types() , which registers default POST readers and input filters.
<code>static void sapi_globals_ctor(sapi_globals_struct *sapi_globals) {
memset(sapi_globals, 0, sizeof(*sapi_globals));
zend_hash_init(&sapi_globals->known_post_content_types, 8, NULL, _type_dtor, 1);
php_setup_sapi_content_types();
}
</code>After the SAPI is initialized, the code checks whether the process is running under FastCGI by invoking fcgi_is_fastcgi() , which may call fcgi_init() to set up FastCGI management variables, socket handling, and signal handling.
<code>int fcgi_is_fastcgi(void) {
if (!is_initialized) {
return fcgi_init();
} else {
return is_fastcgi;
}
}
</code>Overall, the article walks through the key structures and functions that enable PHP to operate as a CGI/FastCGI SAPI, providing concrete code excerpts to illustrate each step.
php中文网 Courses
php中文网's platform for the latest courses and technical articles, helping PHP learners advance quickly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.