萬盛學電腦網

 萬盛學電腦網 >> 網絡編程 >> php編程 >> php怎麼運行的?php運行原理

php怎麼運行的?php運行原理

這篇文章研究了php代碼是如何解釋和執行以及PHP腳本運行的生命周期,有興趣的同學可以看看。

概述

PHP服務的啟動。嚴格來說,PHP的相關進程是不需要手動啟動的,它是隨著Apache的啟動而運行的。當然,如果有需要重啟PHP服務的情況下也是可以手動重啟PHP服務的。比如說在有開啟opcode的正式環境更新了代碼之後,需要重啟PHP以重新編譯PHP代碼。

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

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

詞法分析

將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引擎。

copyright © 萬盛學電腦網 all rights reserved