1. 問題
低版本的PHP可能會遇到不支持中文路徑的情況:
(1) require('http://localhost/中文路徑/test.php');
(2) require('\中文路徑\test.php');
(3) $file = fopen('http://localhost/中文路徑/test.php');
(4) $file = fopen('\中文路徑\test.php');
(5) 通過瀏覽器訪問: http://127.0.0.1/中文路徑/test.php
在Windows10+Apache2.4.41+PHP5.6.40環境下,測驗檔案編碼為UTF-8,會發現除了用fopen打開URL外(3),其他情況(包括用fopen打開物理路徑)PHP都會報錯:No such file or directory
注:使用fopen('url')或require('url')需要在php.ini中進行配置
; Whether to allow the treatment of URLs (like http:// or ftp://) as files. ; http://php.net/allow-url-fopen allow_url_fopen = On ; Whether to allow include/require to open URLs (like http:// or ftp://) as files. ; http://php.net/allow-url-include allow_url_include = On
查看瀏覽器請求頭,(5)的“中文路徑”也被編碼為UTF-8,如果對路徑字串進行轉碼:
(1) require(iconv('utf-8', 'gbk', 'http://localhost/中文路徑/test.php'));
(2) require(iconv('utf-8', 'gbk', '\中文路徑\test.php');
(3) $file = fopen(iconv('utf-8', 'gbk', 'http://localhost/中文路徑/test.php'));
(4) $file = fopen(iconv('utf-8', 'gbk', '\中文路徑\test.php');
(5) 通過瀏覽器訪問: http://127.0.0.1/php/%D6%D0%CE%C4%C2%B7%BE%B6/form.php
發現:(2)和(4)成功運行,(1),(3),(5)報錯,但這次不是PHP報錯,而是Apache報錯:HTTP/1.1 403 Forbidden
|
|
物理路徑(utf-8) |
物理路徑(gbk) |
URL(utf-8) |
URL(gbk) |
|
fopen |
Invalid argument/ No such file or directory |
成功 |
成功 |
HTTP/1.1 403 Forbidden |
|
require |
Invalid argument/ No such file or directory |
成功 |
Invalid argument/ No such file or directory |
HTTP/1.1 403 Forbidden |
據此得出結論:
- 用require和fopen打開URL,會向Apache服務器請求資源,但Apache只支持決議UTF-8編碼的URL
- 用require和fopen打開物理路徑,直接與Windows檔案系統互動,即ANSI編碼
注:require會呼叫compile_filename,而在compile_filename會進行路徑決議,
但是,為什么用瀏覽器或者require打開UTF-8或者GBK編碼的URL都會失敗呢?
2. PHP與Apache主要通信方式
2.1 CGI方式
CGI英文叫做公共網關介面,就是Apache在遇到PHP腳本的時候會將PHP程式提交給CGI應用程式(php-cgi.exe)解釋,解釋之后的結果回傳給Apache,然后再回傳給相應的請求用戶,這種模式速度慢(php解釋器不是常駐記憶體的)、占用空間(請求一次開啟一次cgi),已經被fast-cgi代替,
在Apache中配置:
Action application/x-httpd-php “/php/php-cgi.exe”
2.2 模塊化方式(默認)
PHP作為Apache的一個模塊(mod_php)運行,隨apache一起啟動,屬于一個行程,之間的通信是內部通信,mod_php 這種嵌入的方式最大的弊端就是記憶體占用大,不論是否用到 PHP 解釋器都會將其加載到記憶體中,典型的就是處理CSS、JS之類的靜態檔案是完全沒有必要加載解釋器,

2.3 FastCGI方式
這種形式是CGI的加強版本,CGI是單行程,多執行緒的運行方式,程式執行完成之后就會銷毀,所以每次都需要加載配置和環境變數fork-and-execute(創建-執行),而FastCGI則不同,FastCGI 像是一個常駐 (long-live) 型的 CGI,它可以一直執行著,只要激活后,不會每次都要花費時間去 fork 一次,FastCGI行程管理器自身初始化,啟動多個CGI解釋器行程 (在任務管理器中可見多個php-cgi.exe)并等待來自Web Server的連接,FastCGI與Apache是兩個獨立的行程,通過埠通信,
它的具體實作到php中就是php的php-fpm模塊,但是在apache中是用的專門的fastcgi模塊,需要下載.so檔案,php-fpm在php5.3以后不再作為第三方的模塊而是集成到了php中,它會提前的開啟多個cgi程式,管理這些行程,并提供方式合理有效的調度,保證了并發性,
3. Apache與PHP互動程序
3.1 Apache生命周期

3.2 Apache處理流程

- Post-Read-Request階段: 在正常請求處理流程中,這是模塊可以插入鉤子的第一個階段,對于那些想很早進入處理請求的模塊來說,這個階段可以被利用,
- URI Translation階段 : Apache在本階段的主要作業:將請求的URL映射到本地檔案系統,模塊可以在這階段插入鉤子,執行自己的映射邏輯,mod_alias就是利用這個階段作業的,
- Header Parsing階段 : Apache在本階段的主要作業:檢查請求的頭部,由于模塊可以在請求處理流程的任何一個點上執行檢查請求頭部的任務,因此這個鉤子很少被使用,mod_setenvif就是利用這個階段作業的,
- Access Control階段 : Apache在本階段的主要作業:根據組態檔檢查是否允許訪問請求的資源,Apache的標準邏輯實作了允許和拒絕指令,mod_authz_host就是利用這個階段作業的,
- Authentication階段 : Apache在本階段的主要作業:按照組態檔設定的策略對用戶進行認證,并設定用戶名區域,模塊可以在這階段插入鉤子,實作一個認證方法,
- Authorization階段 : Apache在本階段的主要作業:根據組態檔檢查是否允許認證過的用戶執行請求的操作,模塊可以在這階段插入鉤子,實作一個用戶權限管理的方法,
- MIME Type Checking階段 : Apache在本階段的主要作業:根據請求資源的MIME型別的相關規則,判定將要使用的內容處理函式,標準模塊mod_negotiation和mod_mime實作了這個鉤子,
- FixUp階段 : 這是一個通用的階段,允許模塊在內容生成器之前,運行任何必要的處理流程,和Post_Read_Request類似,這是一個能夠捕獲任何資訊的鉤子,也是最常使用的鉤子,
- Response階段 : Apache在本階段的主要作業:生成回傳客戶端的內容,負責給客戶端發送一個恰當的回復,這個階段是整個處理流程的核心部分,
- Logging階段 : Apache在本階段的主要作業:在回復已經發送給客戶端之后記錄事務,模塊可能修改或者替換Apache的標準日志記錄,
- CleanUp階段 : Apache在本階段的主要作業:清理本次請求事務處理完成之后遺留的環境,比如檔案、目錄的處理或者Socket的關閉等等,這是Apache一次請求處理的最后一個階段,
3.3 mod_php作業周期
- Apache接受請求
- Apache傳遞請求給mod_php
- mod_php定位磁盤檔案,并加載到記憶體中
- mod_php編譯源代碼成為opcode樹(APC)
- mod_php執行opcode樹
4. 結論
4.1 兩次資源檢查
根據上述作業流程可大致推斷:
- 通過瀏覽器訪問url產生一個request,Apache服務器會檢查是否允許訪問請求的資源,要求url是UTF-8編碼;
- mod_php會通過Apache傳遞的請求定位磁盤檔案,這里可能要求是GBK編碼,因此PHP報錯(僅猜測,具體Apache傳遞給PHP的請求是什么格式暫時不清楚);
4.2 遠程檔案
對于require(url),這個程序可能更加復雜:
參照:PHP: include - Manual
安全警告
遠程檔案可能會經遠程服務器處理(根據檔案后綴以及遠程服務器是否在運行 PHP 而定),但必須產生出一個合法的 PHP 腳本,因為其將被本地服務器處理,
根據這條警告的意思,如果遠程服務器在運行PHP,通過include/require請求的PHP檔案可能會被處理,然后收到一個包含了靜態的HTML的PHP腳本,如果遠程服務器沒有運行PHP,那么將收到原始的PHP腳本在本地進行執行,由于測驗請求的是本地的PHP檔案,遠程服務器就相當于本地環境,遠程服務器同樣進行了兩次資源檢查導致出錯,
4.3 替代解決方案
設定->語言設定->管理語言設定->更改系統區域設定->勾選Beta版使所有程式都用Unicode UTF-8提供語言支持,但在運行其他檔案時可能出現亂碼,
4.4 使用PHP7
如果使用PHP7,以上問題全都不會出現,
參考
- Apache運行機制 - 簡書 (jianshu.com)
- POST-READ-REQUEST階段 - 豆丁網 (docin.com)
- php底層深度探索(4) ---Apache運行階段分析 王澤賓 - 編程寶庫 - 博客園 (cnblogs.com)
- apache是怎么運行php的_PHP與WEB服務器是如何互動的_weixin_39761481的博客-CSDN博客
- Windows下Apache服務器運行PHP的三種運行方式(php_mod、cgi、fastcgi) | 夜風博客 (homedt.net)
- (5條訊息) 深入理解php原理之include include_once require require_once_冰心丹的博客-CSDN博客
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/538430.html
標籤:PHP
上一篇:常用工具類
下一篇:定義(創建)、呼叫函式及回傳值
