PHP是怎麼運行的

這篇文章,研究一下PHP代碼是如何解釋和執行以及PHP腳本運行的生命週期。php

概述編程

PHP服務的啓動。嚴格來講,PHP的相關進程是不須要手動啓動的,它是隨着Apache的啓動而運行的。固然,若是有須要重啓PHP服務的狀況下也是能夠手動重啓PHP服務的。好比說在有開啓opcode的正式環境更新了代碼以後,須要重啓PHP以從新編譯PHP代碼。api

從宏觀上來看,PHP內核的實現就是接收輸入的數據,內部作相應的處理而後輸出結果。對於PHP內核來講,咱們編寫的PHP代碼就是內核接收的輸入數據,PHP內核接收代碼數據後,對咱們編寫的的代碼進行代碼解析和運算執行,最後返回相應的運算結果。服務器

然而,不一樣於平時的C語言代碼,要執行PHP代碼,首先須要將PHP代碼「翻譯」成機器語言來執行相應的功能。而要執行「翻譯」這個步驟,就須要PHP內核進行:詞法分析、語法分析等步驟。最後交給PHP內核的Zend Engine進行順次的執行。函數

詞法分析post

將PHP代碼分隔成一個個的「單元」(TOKEN)命令行

語法分析
將「單元」轉換爲Zend Engine可執行的操做線程

Zend Engine執行
對語法分析獲得的操做進行順次的執行翻譯

一切PHP程序(CGI/CLI)的開始都是從SAPI(Server Application Programming Interface)接口開始。SAPI指的是PHP具體應用的編程接口。例如Apache的mod_php。指針

PHP開始執行之後會通過兩個主要的階段:處理請求以前的開始階段和請求以後的結束階段。

開始階段

PHP的整一個開始階段會經歷模塊初始化和模塊激活兩個階段。

MINIT

即模塊初始化階段,發生在Apache/Nginx啓動之後的整個生命週期或者命令行程序整個執行過程當中,此階段只進行一次

RINIT
模塊激活,發生在請求階段。作一些初始化工做:如註冊常量、定義模塊使用的類等等

模塊在實現時能夠經過以下宏來實現這些回調函數:

PHP_MINIT_FUNCTION(myphpextension)
{
//註冊常量或者類等初始化操做
return SUCCESS;
}

PHP_RINIT_FUNCTION(myphpextension)
{
//例如記錄請求開始時間
//隨後在請求結束的時候記錄結束時間。這樣咱們就可以記錄處理請求所花費時間了
return SUCCESS;
}

PHP腳本請求處理完就進入告終束階段,通常腳本執行到末尾或者調用exit或die函數,PHP就進入結束階段。

結束階段

PHP的結束階段分爲停用模塊和關閉模塊兩個環節。

RSHUTDOWN
停用模塊(對應RINIT)

MSHUTDOWN
關閉模塊(對應MINIT)

CLI/CGI模式的PHP屬於單進程的SAPI模式。意思就是說,PHP腳本在執行一次以後就關閉掉,全部的變量和函數都不能繼續使用。即在CGI模式下,同一個php文件的變量在其餘php文件中不能使用。

下面用一個例子看看單線程PHP的SAPI生命週期。

單線程SAPI生命週期
如:

php -f test.php

調用各個擴展的MINIT 模塊初始化
  請求test.php
    調用各個擴展的RINIT 模塊激活
      執行test.php
    調用各個擴展的RSHUTDOWN 停用模塊
  執行完test.php後清理變量和內存
調用各個擴展的MSHUTDOWN 關閉模塊
中止PHP執行

以上是一個簡單的執行流程,下面作一些補充。

PHP在調用每一個模塊的模塊初始化前,會有一個初始化的過程,包括:

初始化若干全局變量
大多數狀況下是將其設置爲NULL。

初始化若干常量
這裏的常量是PHP自身的一些常量。

初始化Zend引擎和核心組件
這裏的初始化操做包括內存管理初始化、全局使用的函數指針初始化,對PHP源文件進行詞法分析、語法分析、中間代碼執行的函數指針的賦值,初始化若干HashTable(好比函數表,常量表等等),爲ini文件解析作準備,爲PHP源文件解析作準備,註冊內置函數、標準常量、GLOBALS全局變量等

解析php.ini
讀取php.ini文件,設置配置參數,加載zend擴展並註冊PHP擴展函數。

全局操做函數的初始化
初始化在用戶空間所使用頻率很高的一些全局變量,如:$\_GET、$\_POST、$\_FILES 等。

初始化靜態構建的模塊和共享模塊(MINIT)
初始化默認加載的模塊。
模塊初始化執行操做:
將模塊註冊到已註冊模塊列表
將每一個模塊中包含的函數註冊到函數表

禁用函數和類

會調用zend_disable_function函數將PHP的配置文件中的disable_functions變量表明的函數從CG(function_table)函數表中刪除。

激活Zend引擎
使用init_compiler函數來初始化編譯器。

激活SAPI
使用sapi_activate函數來初始化SG(sapi_headers)和SG(request_info),而且針對HTTP請求的方法設置一些內容。

環境初始化
初始化在用戶控件須要用到的一些環境變量。包括服務器環境、請求數據環境等。

模塊請求初始化
PHP調用zend_activate_modules函數遍歷註冊在module_registry變量中的全部模塊,調用其RINIT方法方法實現模塊的請求初始化操做。

在處理了文件相關的內容後,PHP會調用php_request_startup作請求初始化操做:

激活Zend引擎
激活SAPI
環境初始化
模塊請求初始化

代碼的運行
以上全部準備工做完成後,就開始執行PHP程序。PHP經過zend_compile_file作詞法分析、語法分析和中間代碼生成操做,返回此文件的全部中間代碼。若是解析的文件有生成有效的中間代碼,則調用zend_excute執行中間代碼。。若是在執行過程當中出現異常而且用戶有定義對這些異常的處理,則調用這些異常處理函數。在全部的操做都處理完後,PHP經過EG(return_value_ptr_ptr)返回結果。

DEACTIVATION(關閉請求)
PHP關閉請求的過程是一個若干個關閉操做的集合,這個集合存在於php_request_shutdown函數中。這個包括:

調用全部經過register_shutdown_function()註冊的函數。這些在關閉時調用的函數是在用戶空間添加進來的。
執行全部可用的__destruct函數。這裏的析構函數包括在對象池(EG(objects_store)中的全部對象的析構函數以及EG(symbol_table)中各個元素的析構方法。
將全部的輸出刷出去。
發送HTTP應答頭。
銷燬全局變量表(PG(http_globals))的變量。
經過zend_deactivate函數,關閉詞法分析器、語法分析器和中間代碼執行器。
調用每一個擴展的post-RSHUTDOWN函數。只是基本每一個擴展的post_deactivate_func函數指針都是NULL。
關閉SAPI,經過sapi_deactivate銷燬SG(sapi_headers)、SG(request_info)等的內容。
關閉流的包裝器、關閉流的過濾器。
關閉內存管理。
從新設置最大執行時間

結束
PHP結束一個進程是,會調用sapi_flush函數將最後的內容刷新出去。而後調用zend_shutdown函數關閉Zend引擎。

參考:[http://www.php-internals.com/book/](http://www.php-internals.com/book/)

相關文章
相關標籤/搜索