最近升級PHP
到PHP7
版本,並從新部署了新的Nginx
,啓動的時候發現了一個問題,全局變量$_SERVER['PHP_SELF']
的值發生了改變,從而影響到代碼的功能。所以咱們來了解下$_SERVER
全局變量中的PHP_SELF/PATH_INFO/SCRIPT_NAME
等參數以及其關係。php
CGI 1.1
規範以前的文章 [ php-fpm進程數管理 ] 已經簡單說過CGI的內容,這裏咱們再詳細講一下。html
CGI
是Common Gateway Interface(通用網管協議)
,用於讓交互程序和Web服務器通訊的協議。它負責處理URL的請求,啓動一個進程,將客戶端發送的數據做爲輸入,由Web服務器收集程序的輸出並加上合適的頭部,再發送回客戶端。java
FastCGI
是基於CGI
的加強版本的協議,不一樣於建立新的進程來服務請求,使用持續的進程和建立的子進程來處理一連串的進程,這些進程由FastCGI服務器管理,開銷更小,效率更高。python
CGI
誕生於1993
年美國國家計算機中心,目的是爲不一樣的動態頁面處理語言(php/python/java)
在不一樣的服務器下(apache/nginx)
提供一致的接口規範,提供會話環境變量、會話客戶端等信息。nginx
在RFC-CGI1.1文檔中包含了協議的所有內容,咱們如今只關注它的 4.1節:Request Meta-Variables
。apache
標準中定義了處理請求應該實現的17
個屬性和如何自定義新屬性,好比:segmentfault
SERVER_PROTOCOL
:信息協議的名字和修訂版。格式爲protocol/reVision
。SERVER_PORT
:發送請求的端口號。REQUEST_METHOD
:請求的方法。對於HTTP
,有"GET"、 "HEAD"、 "POST"
等等。PATH_INFO
:額外的路徑信息,由客戶端給出的。換句話說,腳本能夠由他們的虛擬路徑名來訪問,在這個路徑的末尾附帶額外的信息。這個額外信息被做爲PATH_INFO
發送。這個信息若是在傳遞給CGI
腳本以前來自URL
就能夠由服務器來解碼。PATH_TRANSLATED
:服務器提供了一個PATH_INFO
的轉換版本,它須要路徑而且爲它作虛擬到物理的映射。SCRIPT_NAME
:將要執行的腳本的一個虛擬路徑。QUERY_STRING
:在引用腳本的URL
中緊跟在?
以後的信息。這是一個查詢信息。它不能以任何方式來解碼。這個變量老是能夠在有查詢信息的時候被設置,而無論命令行解碼。REMOTE_HOST
:產生請求的主機名。若是服務器沒有這個信息,它應該設置REMOTE_ADDR
而且讓這個爲未設置狀態。REMOTE_ADDR
:產生請求的遠程主機的IP
地址。AUTH_TYPE
:若是服務器支持用戶驗證,腳本就受保護。這是一個協議規範受權方法,用於驗證用戶。REMOTE_USER
:若是服務器支持用戶驗證,腳本就受保護。這是他們受權的用戶名。REMOTE_IDENT
:若是HTTP
服務器支持RFC931
認證,這個變量將被設置爲從服務器取出的遠程用戶名。這個變量的用法應該只限制在登錄的時候。CONTENT_TYPE
:對於哪些已經附上信息的請求,好比 HTTP POST
和PUT
,這是數據的內容類型。CONTENT_LENGTH
:客戶端給的數據內容的長度。這些變量須要各個語言和服務器進行本身的實現,同時他們也會有本身定義的一些變量。如咱們今天要說的PHP
語言中的$_SERVER['PHP_SELF']
變量。數組
PHP
的超全局變量$_SERVER
$_SERVER
是一個包含了諸如頭信息(header)、路徑(path)、以及腳本位置(script locations)
等等信息的數組。這個數組中的項目由Web
服務器建立。不能保證每一個服務器都提供所有項目;服務器可能會忽略一些,或者提供一些沒有在這裏列舉出來的項目。這也就意味着大量的此類變量都會在» CGI 1.1
規範中說明,因此應該仔細研究一下。
__FILE__
常量包含當前(例如包含)文件的完整路徑和文件名。服務器
與此相關的,咱們這裏主要關注的幾個變量是:架構
PHP_SELF
: 當前執行腳本的文件名,與 document root
有關。例如,在地址爲 http://example.com/foo/bar.php
的腳本中值爲 /foo/bar.php
。SCRIPT_NAME
: 包含當前腳本的路徑。這在頁面須要指向本身時很是有用。PATH_INFO
: 包含由客戶端提供的、跟在真實腳本名稱以後而且在查詢語句(query string)
以前的路徑信息,若是存在的話。例如,若是當前腳本是經過 URL http://www.example.com/php/path_info.php/some/stuff?foo=bar
被訪問,那麼值爲 /some/stuff
。文檔裏表述的Web
服務器,在個人環境裏指代的是Nginx
。在Apache
中,當不加配置的時候對於PHP
腳本, AcceptPathInfo
是默認接受的。而對於Nginx
下, 是不支持PATH INFO
的, 也就是它不會默認設置PATH_INFO
.
所以,對於一個Nginx
架構的常規請求來講,這幾個字段的值分別是:
# http://www.baidu.com:8080/odp/index.php?r=update PHP_SELF: /odp/index.php SCRIPT_NAME: /odp/index.php PATH_INFO: null
PHP_SELF
中出現重複路徑在我部署完成新的Nginx
服務後,獲得的上面三個字段的值爲:
# http://www.baidu.com:8080/odp/index.php?r=update PHP_SELF: /odp/index.php/odp/index.php SCRIPT_NAME: /odp/index.php PATH_INFO: /odp/index.php
注意這裏的PHP_SELF
字段存在重複的路徑,而PATH_INFO
也存在了值,此時的nginx.conf
配置爲:
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param QUERY_STRING $query_string; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length; # 注意這一行,咱們配置了PATH_INFO字段 fastcgi_param PATH_INFO $fastcgi_script_name; fastcgi_param SCRIPT_NAME $fastcgi_script_name; fastcgi_param REQUEST_URI $request_uri; fastcgi_param DOCUMENT_URI $document_uri; fastcgi_param DOCUMENT_ROOT $document_root; fastcgi_param SERVER_PROTOCOL $server_protocol; fastcgi_param HTTPS $https if_not_empty; fastcgi_param GATEWAY_INTERFACE CGI/1.1; fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
那麼咱們爲何配置了PATH_INFO
就會影響PHP_SELF
的值了呢?這一點,咱們首先會想到PHP_SELF
這個自定義屬性的來源是什麼,然而,我並無找到任何的文檔說明。但咱們能夠經過重命名的方式,來探究一下它的定義:
fastcgi_param PATH_INFO PATH_INFO; # fastcgi_param PATH_INFO $fastcgi_script_name; fastcgi_param SCRIPT_NAME SCRIPT_NAME; # fastcgi_param SCRIPT_NAME $fastcgi_script_name;
變動這兩行,咱們將其重命名爲指定字符串,而不是請求傳入的變量,nginx reload
後,此時的結果是:
# http://www.baidu.com:8080/odp/index.php?r=update PHP_SELF: SCRIPT_NAMEPATH_INFO SCRIPT_NAME: SCRIPT_NAME PATH_INFO: PATH_INFO
而其餘變量均正常,所以咱們能夠進一步理解:
PHP_SELF = SCRIPT_NAME + PATH_INFO
PHP_SELF
那麼PHP
爲何要自定義這個屬性呢?在官方文檔裏有這麼一個url
請求,此時:
# http://www.example.com/php/path_info.php/some/stuff?foo=bar PHP_SELF: /php/path_info.php/some/stuff SCRIPT_NAME: /php/path_info.php PATH_INFO: /some/stuff
因此,在這種場景下,只有PHP_SELF
才能拿到完整的當前執行腳本的文件或路徑。
爲了避免同服務器、不一樣語言之間的請求通訊,因而有了CGI
協議規範,這個規範在不一樣的服務器和語言中有本身的實現,在Web Server: Nginx
的配置文件中,能夠設置不一樣變量的值,解析後傳遞給PHP-FPM(PHP-FastCGI Process Manager)
,再進一步傳遞給負責響應請求的PHP
子進程,而PHP
中也定義了關於請求通訊的全局變量$_SERVER
,用於解析請求和處理邏輯。這就是整個關於解析請求信息的流程。
因爲PHP
中$_SERVER
中的這幾個變量的定義有必定混淆,也依賴於不一樣的實現和Server
環境,如PATH_INFO
在Nginx/Apache
中的不一樣默認狀態,所以,若是須要頁面指向本身時,除非如上面示例中的那種url
,建議使用SCRIPT_NAME
變量便可。