Présentation de l'Interface SAPI
L'interface SAPI (Server Application Programming Interface) est un composant fondamental de PHP, servant de point d'entrée pour l'interaction avec des applications externes. Elle permet à PHP de fonctionner dans divers environnements, tels que les serveurs web, les lignes de commande ou les applications embarquées. Dans cette exploration, nous analyserons les mécanismes de SAPI en prenant pour exemple l'implémentation CGI, qui illustre clairement les principes essentiels.
Architecture Générale de PHP
PHP repose sur une architecture modulaire où SAPI agit comme une couche d'abstraction. Que ce soit via Apache, Nginx ou le CLI, le processus d'exécution des scripts partage des similarités : réception des paramètres, exécution et renvoi des résultats. L'interface SAPI encapsule ces opérations, permettant au moteur Zend de se concentrer sur l'interprétation du code.
Focus sur l'Implémentation CGI
Le SAPI CGI, bien que simple, offre une vision complète du fonctionnement interne. Il est défini par une structure sapi_module_struct, qui spécifie les fonctions de callback pour la communication avec l'environnement. Ci-dessous, une version remaniée de cette structure, avec des noms et commentaires adaptés :
static sapi_module_struct cgi_adapter = {
"cgi-php", /* Identifiant du module */
"CGI pour PHP", /* Nom lisible */
cgi_init_module, /* Initialisation */
cgi_term_module, /* Arrêt */
NULL, /* Activation (non utilisée ici) */
cgi_cleanup, /* Désactivation */
cgi_output_data, /* Écriture non tamponnée */
cgi_flush_buffer, /* Vidage du tampon */
NULL, /* Récupération d'UID */
cgi_fetch_env, /* Accès aux variables d'environnement */
cgi_handle_errors, /* Gestion des erreurs */
NULL, /* Traitement des en-têtes personnalisés */
cgi_emit_headers, /* Envoi des en-têtes HTTP */
NULL, /* Envoi d'en-tête individuel */
cgi_obtain_post, /* Lecture des données POST */
cgi_retrieve_cookies, /* Lecture des cookies */
cgi_populate_server_vars, /* Enregistrement des variables serveur */
cgi_log_error, /* Journalisation des messages */
NULL, /* Temps de requête */
STANDARD_SAPI_MODULE_PROPERTIES
};
Analyse des Composants Clés
Chaque champ de la structure correspond à une responsabilité spécifique :
Initialisation et Terminaison
La fonction cgi_init_module est appelée lors du démarrage de l'application. Elle initialise le moteur PHP :
static int cgi_init_module(sapi_module_struct *sapi_desc) {
if (php_module_startup(sapi_desc, NULL, 0) == FAILURE) {
return FAILURE;
}
return SUCCESS;
}
Le wrapper cgi_term_module appelle simplement la fonction d'arrêt interne de PHP, assurant un nettoyage cohérent.
Gestion du Cycle de Vie des Requêtes
La désactivation (cgi_cleanup) garantit que tous les tampons sont vidés avant la fin de l'exécution :
static int cgi_cleanup(TSRMLS_D) {
if (SG(sapi_started)) {
cgi_flush_buffer(SG(server_context));
}
return SUCCESS;
}
Sortie des Données
L'écriture via cgi_output_data gère à la fois les environnements CGI et FastCGI. La logique est refactorisée pour plus de clarté :
static inline size_t cgi_write_chunk(const char *content, uint content_len TSRMLS_DC) {
size_t written = 0;
#if PHP_FASTCGI
if (fcgi_is_fastcgi()) {
fcgi_request *req = (fcgi_request*) SG(server_context);
written = fcgi_write(req, FCGI_STDOUT, content, content_len);
return (written <= 0) ? 0 : written;
}
#endif
written = fwrite(content, 1, content_len, stdout);
return written;
}
static int cgi_output_data(const char *data, uint data_length TSRMLS_DC) {
uint remaining = data_length;
const char *current_pos = data;
while (remaining > 0) {
size_t written = cgi_write_chunk(current_pos, remaining TSRMLS_CC);
if (!written) {
php_handle_aborted_connection();
return data_length - remaining;
}
current_pos += written;
remaining -= written;
}
return data_length;
}
Accès aux Variables d'Environnement
La fonction cgi_fetch_env adapte la recherche en fonction du mode d'exécution :
static char *cgi_fetch_env(char *env_name, size_t env_name_len TSRMLS_DC) {
#if PHP_FASTCGI
if (fcgi_is_fastcgi()) {
fcgi_request *req = (fcgi_request*) SG(server_context);
return fcgi_getenv(req, env_name, env_name_len);
}
#endif
return getenv(env_name);
}
Traitement des En-têtes et des Erreurs
L'envoi des en-têtes (cgi_emit_headers) formate et écrit les métadonnées HTTP. La gestion des erreurs (cgi_handle_errors) déléguée au mécanisme interne de PHP, avec des perspectives pour une gestion orientée objet future.
Lecture des Données Entrantes
Pour les requêtes POST, cgi_obtain_post lit depuis stdin ou via FastCGI, en respectant la longueur du contenu :
static int cgi_obtain_post(char *buffer, uint max_bytes TSRMLS_DC) {
uint total_read = 0;
uint to_read = MIN(max_bytes, SG(request_info).content_length - SG(read_post_bytes));
while (total_read < to_read) {
ssize_t chunk = read(STDIN_FILENO, buffer + total_read, to_read - total_read);
if (chunk <= 0) break;
total_read += chunk;
}
return total_read;
}
Variables Serveur et Journalisation
La fonction cgi_populate_server_vars importe l'environnement et ajoute des variables spécifiques comme PHP_SELF. La journalisation via cgi_log_error dirige les messages vers stderr ou les canaux FastCGI, assurant une traçabilité correcte.
Cette exploration du SAPI CGI met en lumière la flexibilité et la modularité de PHP, où chaque composant interagit via des interfaces bien définies, facilitant ainsi son intégration dans des contextes variés.