L'interface SAPI (Server API) gère les interactions entre PHP et le serveur web pendant le cycle de vie de l'application. Les implémentations physiques se trouvent dans le répertoire SAPI des sources PHP, qui contient les abstractions pour différents serveurs comme la ligne de commande, le module Apache mod_php ou FastCGI.
Chaque abstraction serveur respecte un ensemble de conventions communes, formant l'interface SAPI. Dans le code source, toute opération liée au serveur passe par des appels à des méthodes spécifiques de cette interface, dont les implémentations varient selon le serveur.
Par exemple, l'initialisation pour le mode CGI et Apache 2 s'effectue comme suit :
// Mode CGI : point d'entrée dans cgi/cgi_main.c
module_sapi_cgi.demarrage(&module_sapi_cgi);
// Serveur Apache 2 : point d'entrée dans apache2handler/sapi_apache2.c
module_sapi_apache2.demarrage(&module_sapi_apache2);
Ici, module_sapi_cgi est une variable statique de type struct_sapi. Sa méthode demarrage pointe vers la fonction php_cgi_demarrage. Cette structure contient plusieurs champs et pointeurs de fonction, comme défini ci-dessous :
struct struct_sapi {
char *identifiant; // Identifiant unique
char *nom_affiche; // Nom lisible
int (*demarrage)(struct struct_sapi *sapi); // Fonction d'initialisation
int (*arret)(struct struct_sapi *sapi); // Fonction d'arrêt
int (*activation)(void); // Activation du contexte
int (*desactivation)(void); // Désactivation du contexte
int (*ecriture_directe)(const char *contenu, unsigned int taille);
// Écriture sans tampon
void (*vider_tampon)(void *contexte_serveur); // Vider les tampons
struct stat *(*obtenir_statut)(void); // Obtenir les statuts
char *(*recuperer_env)(char *nom, size_t taille_nom);
void (*gerreur_sapi)(int type, const char *message, ...); // Gestionnaire d'erreurs
int (*traite_entete)(struct entete_sapi *entete, enum operation_entete op,
struct entetes_sapi *entetes); // Traitement des en-têtes
int (*envoi_entetes)(struct entetes_sapi *entetes); // Envoi des en-têtes
void (*envoi_entete_unique)(struct entete_sapi *entete,
void *contexte_serveur); // Envoi d'une en-tête unique
int (*lire_post)(char *tampon, uint octets); // Lire les données POST
char *(*lire_cookies)(void); // Lire les cookies
void (*enregistrer_variables_serveur)(zval *tableau_suivi); // Variables serveur
void (*journal_message)(char *message); // Journalisation
time_t (*heure_requete)(void); // Temps de la requête
void (*terminer_processus)(void); // Terminer le processus enfant
char *chemin_ini_remplace; // Chemin INI alternatif
// ... autres champs
};
Ces structures sont définies pour chaque serveur. Par exemple, l'implémentation Apache 2 ressemble à :
static struct struct_sapi module_sapi_apache2 = {
"gestionnaire_apache2",
"Gestionnaire Apache 2.0",
php_apache2_demarrage, /* démarrage */
php_module_arret_emballage, /* arret */
// ... autres initialisations
};
De nombreuses implémentations SAPI intégrées dans PHP ne sont plus maintenues ou sont devenues secondaires. La communauté PHP envisage de déplacer certaines dans le dépôt PECL, sauf pour les fonctionnalités essentielles ou très répandues, comme l'extension APC qui sera intégrée au noyau.
L'architecture SAPI s'apparente au patron de conception méthode modèle. Les fichiers SAPI.c et SAPI.h contiennent les méthodes abstraites, tandis que chaque serveur fournit une implémentation concrète via la structure struct_sapi.
Ce modèle se retrouve dans d'autres parties des sources PHP, notamment dans le développement d'extensions. Chaque extension définit une structure zend_module_entry, similaire à struct_sapi, pour gérer son cycle de vie. Par exemple, l'initialisation de requête pour une extension est unifiée via fonction_rinit, définie par la macro PHP_RINIT. Dans l'extension VLD, cela se manifeste ainsi :
PHP_RINIT_FUNCTION(vld) {
// Initialisation spécifique à la requête
}
Ainsi, le développement d'extensions ou d'interfaces serveur nécessite l'implémentation de ces contrats définis par les structures dédiées.