Heim  >  Artikel  >  Backend-Entwicklung  >  PHP-Kern – SAPI-Schnittstelle

PHP-Kern – SAPI-Schnittstelle

伊谢尔伦
伊谢尔伦Original
2016-11-21 16:41:271884Durchsuche

SAPI: Server Application Programming Interface, serverseitiger Anwendungsprogrammierport. Studenten, die PHP-Architektur studiert haben, sollten wissen, wie wichtig diese Sache ist. Sie stellt eine Schnittstelle bereit, die es PHP ermöglicht, mit anderen Anwendungen zu interagieren. In diesem Artikel wird nicht jede PHP-SAPI im Detail vorgestellt, sondern nur der SAPI-Mechanismus für die einfachste CGI-SAPI erläutert.

Werfen wir zunächst einen Blick auf das Architekturdiagramm von PHP:

PHP-Kern – SAPI-Schnittstelle

SAPI bezieht sich auf die Programmierschnittstelle für bestimmte PHP-Anwendungen, genau wie ein PC, nein Egal, was installiert ist. Solange das Betriebssystem den PC-Schnittstellenspezifikationen entspricht, kann es normal auf dem PC ausgeführt werden. Es gibt viele Möglichkeiten, PHP-Skripte über den Webserver, direkt unter der Befehlszeile oder eingebettet in andere Programme auszuführen.

Normalerweise verwenden wir Webserver wie Apache oder Nginx, um PHP-Skripte zu testen oder sie über ein PHP-Interpreterprogramm in der Befehlszeile auszuführen. Nachdem das Skript ausgeführt wurde, antwortet der Webserver und der Browser zeigt die Antwortinformationen an oder zeigt den Inhalt in der Standardausgabe der Befehlszeile an.

Es ist uns egal, wo der PHP-Interpreter ist. Obwohl die Ausführung von Skripten über einen Webserver und ein Befehlszeilenprogramm sehr unterschiedlich aussieht, ist der Arbeitsablauf tatsächlich derselbe. Die Befehlszeilenparameter werden an das vom PHP-Interpreter auszuführende Skript übergeben, was dem Anfordern einer PHP-Seite über die URL entspricht. Nachdem das Skript ausgeführt wurde, wird das Antwortergebnis zurückgegeben, aber das Antwortergebnis der Befehlszeile wird auf dem Terminal angezeigt.

Die Skriptausführung beginnt mit der Implementierung der SAPI-Schnittstelle. Es ist nur so, dass verschiedene SAPI-Schnittstellenimplementierungen ihre spezifische Arbeit erledigen. Beispielsweise muss die SAPI-Implementierung mod_php von Apache einige von Apache erhaltene Informationen initialisieren und den Inhalt bei der Ausgabe von Inhalten an Apache zurückgeben.

SAPI bietet eine Schnittstelle für die Kommunikation mit der Außenwelt. Viele Arten von SAPI werden standardmäßig für Apache, CGI, ISAPI für IIS und Shell CLI bereitgestellt Beginnen Sie mit CGI SAPI und stellen Sie den Mechanismus von SAPI vor. Auch wenn CGI einfach ist, machen Sie sich keine Sorgen, es enthält den Großteil des Inhalts, der ausreicht, um Ihnen ein tiefes Verständnis der Funktionsweise von SAPI zu vermitteln.

Um eine SAPI zu definieren, müssen Sie zuerst eine sapi_module_struct definieren, überprüfen Sie PHP-SRC/sapi/cgi/cgi_main.c:

*/
static sapi_module_struct cgi_sapi_module = {
#if PHP_FASTCGI
    "cgi-fcgi",                     /* name */
    "CGI/FastCGI",                  /* pretty name */
#else
    "cgi",                          /* name */
    "CGI",                          /* pretty name */
#endif
    php_cgi_startup,                /* startup */
    php_module_shutdown_wrapper,    /* shutdown */
    NULL,                           /* activate */
    sapi_cgi_deactivate,            /* deactivate */
    sapi_cgibin_ub_write,           /* unbuffered write */
    sapi_cgibin_flush,              /* flush */
    NULL,                           /* get uid */
    sapi_cgibin_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 */
    STANDARD_SAPI_MODULE_PROPERTIES
};

Diese Struktur enthält einige Konstanten, wie z. B. Name, Dies wird verwendet, wenn wir php_info() aufrufen. Einige Initialisierungs- und Abschlussfunktionen sowie einige Funktionszeiger werden verwendet, um Zend mitzuteilen, wie Daten abgerufen und ausgegeben werden sollen.

1. php_cgi_startup, wenn eine Anwendung PHP aufruft, wird diese Funktion aufgerufen. Für CGI wird einfach die Initialisierungsfunktion von PHP aufgerufen:

static int php_cgi_startup(sapi_module_struct *sapi_module)
{
    if (php_module_startup(sapi_module, NULL, 0) == FAILURE) {
        return FAILURE;
    }
    return SUCCESS;
}

2. php_module_shutdown_wrapper, ein einfacher Wrapper für PHP Shutdown-Funktion. Rufen Sie einfach php_module_shutdown;

3 auf. PHP verarbeitet bei jeder Anfrage einige Initialisierungs- und Ressourcenzuweisungstransaktionen. In diesem Teil wird das Aktivierungsfeld definiert. Aus der obigen Struktur können wir ersehen, dass es für CGI kein Initialisierungshandle bereitstellt. Bei mod_php5 ist das anders. Er muss den Ressourcendestruktor im Apache-Pool registrieren, Speicherplatz beantragen, Umgebungsvariablen initialisieren usw.

4. sapi_cgi_deactivate, dies ist die Funktion, die aktiviert werden soll. Wie der Name schon sagt, stellt sie einen Handler zur Verfügung, der die Endbearbeitung erledigt. Er aktualisiert einfach den Puffer, um sicherzustellen, dass der Benutzer alle Ausgaben erhält Daten, bevor Zend geschlossen wird:

static int sapi_cgi_deactivate(TSRMLS_D)
{
    /* flush only when SAPI was started. The reasons are:
        1. SAPI Deactivate is called from two places: module init and request shutdown
        2. When the first call occurs and the request is not set up, flush fails on
            FastCGI.
    */
    if (SG(sapi_started)) {
        sapi_cgibin_flush(SG(server_context));
    }
    return SUCCESS;
}

5. sapi_cgibin_ub_write, dieser Handler teilt Zend mit, wie Daten ausgegeben werden sollen. Für mod_php5 stellt diese Funktion eine Schnittstelle zum Schreiben von Antwortdaten bereit, und für CGI wird einfach darauf geschrieben 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;
}

Die eigentliche Schreiblogik wird entfernt, um einfach eine mit fastcgi kompatible Schreibmethode zu implementieren.

6. sapi_cgibin_flush, dies ist das von Zend bereitgestellte Funktionshandle, um den Cache zu aktualisieren.

7.NULL, dies Teil wird verwendet. Zend kann den Status einer auszuführenden Skriptdatei überprüfen, um festzustellen, ob die Datei über Ausführungsberechtigungen usw. verfügt. CGI bietet dies nicht.

8. sapi_cgibin_getenv bietet Zend eine Schnittstelle zum Suchen von Umgebungsvariablen basierend auf dem Namen. Wenn wir getenv im Skript aufrufen, wird dieses Handle indirekt aufgerufen. Da der Betriebsmechanismus von CGI dem von CLI sehr ähnlich ist, ist Shell das übergeordnete Element des direkten Aufrufs. Daher wird einfach die vom System bereitgestellte Genenv aufgerufen:

static char *sapi_cgibin_getenv(char *name, size_t name_len TSRMLS_DC)
{
#if PHP_FASTCGI
    /* when php is started by mod_fastcgi, no regular environment
       is provided to PHP.  It is always sent to PHP at the start
       of a request.  So we have to do our own lookup to get env
       vars.  This could probably be 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, Fehlerbehandlungsfunktion, hier. Lassen Sie uns ein paar Abschweifungen machen. Als ich die PHP-Mailliste das letzte Mal sah, habe ich den Fehlerbehandlungsmechanismus von PHP komplett OO gemacht, das heißt, ich habe dieses Funktionshandle so umgeschrieben, dass es bei jedem Auftreten eines Fehlers eine Fehlermeldung ausgibt. CGI ruft einfach die von PHP bereitgestellte Fehlerbehandlungsfunktion auf.

10. Diese Funktion wird aufgerufen, wenn wir die Header()-Funktion von PHP aufrufen, die für CGI nicht bereitgestellt wird.

11. sapi_cgi_send_headers, diese Funktion wird aufgerufen, wenn Header tatsächlich gesendet werden, im Allgemeinen, bevor eine Ausgabe gesendet werden soll:

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 != 200)
    {
        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;
   }

12. Dies wird zum Senden verwendet jeden Header separat, was von CGI nicht bereitgestellt wird

13. sapi_cgi_read_post, 这个句柄指明了如何获取POST的数据,如果做过CGI编程的话,我们就知道CGI是从stdin中读取POST DATA的:

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

14. sapi_cgi_read_cookies, 这个和上面的函数一样,只不过是去获取cookie值:

static char *sapi_cgi_read_cookies(TSRMLS_D)
{
   return sapi_cgibin_getenv((char *) "HTTP_COOKIE", sizeof("HTTP_COOKIE")-1 TSRMLS_CC);
}
15. sapi_cgi_register_variables, 这个函数给了一个接口,用以给$_SERVER变量中添加变量,对于CGI来说,注册了一个PHP_SELF,这样我们就可以在脚本中访问$_SERVER[&#39;PHP_SELF&#39;]来获取本次的request_uri:
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 ? SG(request_info).request_uri : ""), track_vars_array TSRMLS_CC);
}
16. sapi_cgi_log_message ,用来输出错误信息,对于CGI来说,只是简单的输出到stderr:
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);
}

经过分析,我们已经了解了一个SAPI是如何实现的了, 分析过CGI以后,我们也就可以想象mod_php5, embed等SAPI的实现机制。


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn