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.

php Courses
php Courses
php Courses
Understanding PHP SAPI Internals: CGI Module Structure and Startup Process

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.

//* 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
};

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.

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 ... */
};

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.

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;

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.

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();
}

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.

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();
}

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.

int fcgi_is_fastcgi(void) {
    if (!is_initialized) {
        return fcgi_init();
    } else {
        return is_fastcgi;
    }
}

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

CCGIfastcgiSAPI++
php Courses
Written by

php Courses

php中文网's platform for the latest courses and technical articles, helping PHP learners advance quickly.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.