PHP_SELF變量解析和重復路徑解決

最近升級PHPPHP7版本,并重新部署了新的Nginx,啟動的時候發現了一個問題,全局變量$_SERVER['PHP_SELF']的值發生了改變,從而影響到代碼的功能。因此我們來了解下$_SERVER全局變量中的PHP_SELF/PATH_INFO/SCRIPT_NAME等參數以及其關系。

CGI 1.1規范

之前的文章 [ php-fpm進程數管理 ] 已經簡單說過CGI的內容,這里我們再詳細講一下。

CGICommon Gateway Interface(通用網管協議),用于讓交互程序和Web服務器通信的協議。它負責處理URL的請求,啟動一個進程,將客戶端發送的數據作為輸入,由Web服務器收集程序的輸出并加上合適的頭部,再發送回客戶端。

FastCGI是基于CGI的增強版本的協議,不同于創建新的進程來服務請求,使用持續的進程和創建的子進程來處理一連串的進程,這些進程由FastCGI服務器管理,開銷更小,效率更高。

CGI誕生于1993年美國國家計算機中心,目的是為不同的動態頁面處理語言(php/python/java)在不同的服務器下(apache/nginx)提供一致的接口規范,提供會話環境變量、會話客戶端等信息。

在RFC-CGI1.1文檔中包含了協議的全部內容,我們現在只關注它的 4.1節:Request Meta-Variables

標準中定義了處理請求應該實現的17個屬性和如何自定義新屬性,比如:

  • 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 POSTPUT,這是數據的內容類型。
  • 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_INFONginx/Apache中的不同默認狀態,因此,如果需要頁面指向自己時,除非如上面示例中的那種url,建議使用SCRIPT_NAME變量即可。

參考資料

  1. segmentfault-php-fpm進程數管理: https://segmentfault.com/a/11...
  2. RFC-CGI1.1: https://tools.ietf.org/html/r...
  3. CGI規范及其歷史:http://www.voidcn.com/article...
  4. php關于$_SERVER中一些和環境有關的參數詳解: https://www.jianshu.com/p/fea...
  5. PHP文檔-$_SERVER:http://php.net/manual/zh/rese...

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/252297.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/252297.shtml
英文地址,請注明出處:http://en.pswp.cn/news/252297.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

pep 8 規范的一些記錄

一、pep8起源 龜叔創立Python的初衷里就有創立一個容易閱讀的編程語言,所以親自操刀寫了pep8 代碼規范,每個項目開始前都要有一個共識,就是自己的代碼規范,pep8 就是一個很好的范本。 二、官網鏈接 https://www.python.org/dev/pe…

C++中的類加多線程代碼修煉之二

背景:在上一篇文章中 寫到了我第一次使用C使用多個類多個線程進行編程,由于是第一接手“這么大一個工程”,所以還是要有個參照物的,由于我呢之前好幾年一直在看的一個C代碼工程就是ORB-SLAM了,這個工程使用C語言&#…

Primer c++ 第5版pdf

下載地址:網盤下載 內容簡介 這本久負盛名的 C經典教程,時隔八年之久,終迎來史無前例的重大升級。除令全球無數程序員從中受益,甚至為之迷醉的——C 大師 Stanley B. Lippman 的豐富實踐經驗,C標準委員會原負責…

eclispe修改project Explorer字體大小

切換到Eclipse目錄下: D:\JAVA\spring-tool-suite\sts-4.0.2.RELEASE\plugins\org.eclipse.ui.themes_1.2.200.v20180828-1350 找到下面的這個css文件 打開找到下面的位置在下面添加代碼:CTabFolder Tree{font-size: 15px;} 復制代碼重啟Eclipse,就可以看到效果 修改前 修改后…

3-4 第三天 Generator生成器

Generator是ES6里面的新增規范,ES6其實就是ES2015。ES5、ES6、ES7這些術語大家上網一查就都明白了。JavaScript是一個范程,就是我們說的JS。ES就是ECMA Script,是JavaScript標準的書面說法。ES4、5、6、7其實是JS這門語言發展中的不同的版本。…

神奇的事情--長見識了

背景:我的這個工程里有多個命名空間,之所以是這樣是因為,有一個跟蹤算法他本身有幾個namespace,然后我在他的基礎上進行整合代碼,將其作為一個功能接口,供其他函數調用,我在整合代碼時,將我新加…

箭頭函數的使用用法(一)

1 //箭頭函數的一個好處是簡化回調函數2 //箭頭函數沒有參數,就使用圓刮號代表參數部分;3 var f () >5;4 console.log(f());5 //兩個參數的情況,代碼塊只有一條語句,可以省略{}6 var f (a,b)> console.log(ab);7 f(1,3);8…

LinkedList中查詢(contains)和刪除(remove)源碼分析

一、contains源碼分析 本文分析雙向鏈表LinkedList的查詢操作源碼實現。jdk中源程序中,LinkedList的查詢操作,通過contains(Object o)函數實現。具體見下面兩部分程序:① public boolean contains(Object o) {return indexOf(o) ! -1; } ② p…

分塊入門

我貌似和所有的數據結構都有些誤會。。。。。。 在處理一些修改查詢問題的時候,我們可以利用分治的思想,比如說把一個線性的數據不斷分成一棵二叉樹,也就是我們所說的線段樹,這樣我們就可以在logn的時限里做到修改和查詢。同理我們…

開始使用gitlab

不得不說,我真不是一個合格的程序猿,工作馬上兩年了,github和gitlab用的一點也不熟練,每次興致來了就搞幾下,可是每次都淺嘗輒止,不求甚解,時間一長,上一次練習的步驟就都記不起來了…

Spark 2.2.0 文檔中文版 Collaborative Filtering 協同過濾 JAVA推薦系統

協同過濾常用于推薦系統,這項技術旨在填補 丟失的user-item關聯矩陣 的條目,spark.ml目前支持基于模型的協同過濾(用一些丟失條目的潛在因素在描述用戶和產品)。spark.ml使用ALS(交替最小二乘法)去學習這些…

淘寶top平臺調用接口響應時間優化

我的專欄地址:我的segmentfault,歡迎瀏覽 一、背景 調用top接口的響應時間長(160ms左右),超時和連接異常頻繁發生。導致消息組件消費工程的tps遇到瓶頸(單實例單消息隊列250tps),只能通過增加實…

樹上倍增一些理解和寫法

樹上倍增可以比較容易求得i節點的第k個父親,我們定義一個二維數組fa[i][j]代表節點i的第2^j個父親,關于有什么用我們等會再說,現在先學會怎么去求這個fa數組 我們可以通過從根節點開始一遍dfs求得所有fa數組,首先我們發現fa數組有…

圖像去畸變和添加畸變

背景:最近的項目中用到的圖像去畸變的知識,剛開始是直接調用opencv中提供的函數cv::initUndistortRectifyMap()和cv::remap()函數,實現圖像的全局去畸變,但是由于圖像的分辨率很高,再加上,實際過程中我們只…

win10上編譯libharu庫

背景: 最近的項目需要自動的生成pdf文件,我在網上查看相關的資料,發現目前比較流行的生成pdf文件的庫有兩個,一個是libpdf,另一個是libharu。libpdf個人使用時免費的但是商業使用就需要收費了,否則得到的p…

爬蟲——正則表達式re模塊

為什么要學習正則表達式 實際上爬蟲一共就四個主要步驟: 明確目標:需清楚目標網站爬:將所有的目標網站的內容全部爬下來取:在爬下來的網站內容中去掉對我們沒有用處的數據,只留取我們需要的數據處理數據:按…

深入Spring Boot:快速集成Dubbo + Hystrix

2019獨角獸企業重金招聘Python工程師標準>>> 背景 Hystrix 旨在通過控制那些訪問遠程系統、服務和第三方庫的節點,從而對延遲和故障提供更強大的容錯能力。Hystrix具備擁有回退機制和斷路器功能的線程和信號隔離,請求緩存和請求打包&#xff…

BZOJ2333 [SCOI2011]棘手的操作 【離線 + 線段樹】

題目 有N個節點,標號從1到N,這N個節點一開始相互不連通。第i個節點的初始權值為a[i],接下來有如下一些操作: U x y: 加一條邊,連接第x個節點和第y個節點 A1 x v: 將第x個節點的權值增加v A2 x v: 將第x個節點所在的連通…

opencv圖像仿射變換和普通旋轉

背景:今天需要對程序生成的圖像進行旋轉90度和下采樣操作,當然還有改變圖像類型的操作,就是把原來.png的圖像轉換為.jpg的圖像,主要是我目前使用libharu庫,無法成功從本地加載png圖像到pdf中去,不得不使用j…

討厭麻煩的ora 01722無效數字

webservice開發過程中,數據庫由原來的oracle改為現在的sql server。然后重新調試,結果報出ora 01722無效數字的錯誤。 由于連接oracle數據庫的時候并沒有問題,所以一開始我以為是數據庫不同,導致部分數據類型差異,&…