84669 人學習
152542 人學習
20005 人學習
5487 人學習
7821 人學習
359900 人學習
3350 人學習
180660 人學習
48569 人學習
18603 人學習
40936 人學習
1549 人學習
1183 人學習
32909 人學習
在PHP 腳本中,無論是呼叫include()、require()、fopen()或其衍生類,例如 include_once、require_once,甚至move_uploaded_file(),經常會遇到錯誤或警告:< /p>
include()
require()
fopen()
include_once
require_once
move_uploaded_file()
無法開啟流:沒有這樣的檔案或目錄。
快速找到問題根本原因的好流程是什麼?
添加到(非常好的)現有答案
open_basedir可能會難倒您,因為它可以在 Web 伺服器設定中指定。雖然如果您運行自己的專用伺服器,這很容易解決,但有一些共享託管軟體包(如 Plesk、cPanel 等)可以在每個網域的基礎上配置配置指令。由於軟體會建置設定檔(即httpd.conf),因此您無法直接變更該文件,因為託管軟體在重新啟動時只會覆寫它。
open_basedir
httpd.conf
透過 Plesk,他們提供了一個位置來覆寫所提供的httpd.conf(稱為vhost.conf)。只有伺服器管理員可以寫入此文件。 Apache 的設定看起來像這樣
vhost.conf
php_admin_flag engine on php_admin_flag safe_mode off php_admin_value open_basedir "/var/www/vhosts/domain.com:/tmp:/usr/share/pear:/local/PEAR"
讓您的伺服器管理員查閱他們使用的託管和 Web 伺服器軟體的手冊。
要注意的是,透過 Web 伺服器執行檔與命令列或 cron 作業執行有很大不同。最大的區別是您的網頁伺服器有自己的使用者和權限。出於安全原因,該用戶受到很大限制。例如,Apache 通常是apache、www-data或httpd(取決於您的伺服器)。 cron 作業或 CLI 執行具有執行它的使用者所擁有的任何權限(即以 root 身分執行 PHP 腳本將以 root 權限執行)。
apache
www-data
httpd
很多時候人們會透過執行以下操作來解決權限問題(Linux 範例)
chmod 777 /path/to/file
這不是一個聰明的主意,因為檔案或目錄現在是全域可寫的。如果您擁有該伺服器並且是唯一的用戶,那麼這並不是什麼大問題,但如果您位於共享託管環境中,您就授予了伺服器上的每個人存取權限。
您需要做的是確定需要存取權限的使用者並只授予他們存取權限。一旦您知道哪些使用者需要存取權限,您就需要確保
該使用者擁有該檔案並且可能擁有父目錄(尤其是如果您要寫入檔案則為父目錄)。在大多數共享託管環境中,這不會成為問題,因為您的用戶應該擁有根目錄下的所有檔案。下面顯示了一個 Linux 範例
chown apache:apache /path/to/file
該使用者(且只有該使用者)具有存取權限。在Linux 中,一個好的做法是chmod 600(只有所有者可以讀寫)或chmod 644(所有者可以寫入,但每個人都可以讀取)
chmod 600
chmod 644
您可以閱讀有關 Linux 的更廣泛的討論/此處的 Unix 權限和用戶
可能會遇到此錯誤的原因有很多,因此首先檢查哪些內容的良好清單會很有幫助。
假設我們正在對以下行進行故障排除:
require "/path/to/file"
或將require*或include*呼叫的任何內容移動到自己的變數中,回顯它,複製它,然後嘗試從最終端存取它:
require*
include*
$path = "/path/to/file"; echo "Path : $path"; require "$path";
然後,在終端機中:
cat
/users/tony/htdocs
最佳實踐:
為了讓您的腳本在移動內容時保持健壯,同時仍在執行時間產生絕對路徑,您有 2 個選項:
require __DIR__ 。 “/相對/路徑/來自/當前/檔案”
__DIR__
自己定義一個SITE_ROOT常數:
SITE_ROOT
config.php
在config.php中寫入
define('SITE_ROOT', __DIR__);
在要引用網站根資料夾的每個檔案中,包含config.php,然後在任何位置使用SITE_ROOT常數: p>
require_once __DIR__."/../config.php"; ... require_once SITE_ROOT."/other/file.php";
這 2 種做法也使您的應用程式更具可移植性,因為它不依賴包含路徑等 ini 設定。
另一種包含檔案的方法,既不是相對也不是純粹絕對,是依賴包含路徑。對於諸如 Zend 框架之類的庫或框架來說,通常就是這種情況。
這樣的包含將如下所示:
include "Zend/Mail/Protocol/Imap.php"
在這種情況下,您需要確保「Zend」所在的資料夾是包含路徑的一部分。
您可以使用以下命令檢查包含路徑:
echo get_include_path();
您可以使用以下命令向其中新增資料夾:
set_include_path(get_include_path().":"."/path/to/new/folder");
總而言之,執行伺服器進程(Apache 或 PHP)的使用者可能根本沒有讀取或寫入該檔案的權限。
要檢查伺服器正在哪個使用者下運行,您可以使用posix_getpwuid:
$user = posix_getpwuid(posix_geteuid()); var_dump($user);
要尋找檔案的權限,請在終端機中鍵入以下命令:
ls -l
並查看權限符號表示法
如果以上方法都不起作用,問題可能是某些 PHP 設定禁止它存取該檔案。
三個設定可能相關:
phpinfo()
ini_get("open_basedir")
ini_get("allow_url_include")
ini_set("allow_url_include", "1")
如果上述方法都無法診斷問題,則可能會發生以下一些特殊情況:
您可能會使用相對或絕對路徑包含一個庫,例如 Zend 框架。例如:
require "/usr/share/php/libzend-framework-php/Zend/Mail/Protocol/Imap.php"
但是你還是會遇到同類型的錯誤。
發生這種情況的原因是您(成功)包含的檔案本身俱有另一個檔案的包含語句,而第二個包含語句假定您已將該程式庫的路徑新增至包含路徑。
例如,前面提到的 Zend 框架檔案可能包含以下內容:
include "Zend/Mail/Protocol/Exception.php"
既不是透過相對路徑包含,也不是透過絕對路徑包含。假設Zend框架目錄已新增至包含路徑。
在這種情況下,唯一實用的解決方案是將目錄新增至包含路徑。
如果您運行的是安全增強型 Linux,那麼這可能是問題的原因,因為拒絕從伺服器存取該檔案。
要檢查系統上是否啟用了 SELinux,請在終端機中執行sestatus指令。如果該命令不存在,則表示您的系統上不存在 SELinux。如果它確實存在,那麼它應該告訴您它是否被強制執行。
sestatus
要檢查 SELinux 策略是否是問題的原因,您可以嘗試暫時關閉。但要小心,因為這將完全禁用保護。不要在生產伺服器上執行此操作。
setenforce 0
如果您在關閉 SELinux 後不再遇到問題,那麼這就是根本原因。
要解決這個問題,您必須相應地設定 SELinux。
以下上下文類型是必要的:
httpd_sys_content_t
httpd_sys_rw_content_t
httpd_log_t
httpd_cache_t
例如,若要將httpd_sys_content_t上下文類型指派給您的網站根目錄,請執行:
semanage fcontext -a -t httpd_sys_content_t "/path/to/root(/.*)?" restorecon -Rv /path/to/root
如果您的檔案位於主目錄中,您還需要開啟httpd_enable_homedirs布林值:
httpd_enable_homedirs
setsebool -P httpd_enable_homedirs 1
無論如何,SELinux 拒絕存取檔案的原因可能有多種,具體取決於您的策略。所以你需要對此進行調查。此處是專門為 Web 伺服器設定 SELinux 的教學。
如果您使用 Symfony,並且在上傳到伺服器時遇到此錯誤,則可能是應用程式的快取尚未重置,因為app/cache已上傳,或者該快取尚未清除。
app/cache
您可以透過執行以下控制台命令來測試並修復此問題:
cache:clear
顯然,當 zip 中的某些檔案的檔案名稱中包含非 ASCII 字元(例如“é”)時,呼叫zip->close()時也會發生此錯誤。
zip->close()
一個可能的解決方案是在建立目標檔案之前將檔案名稱包裝在utf8_decode()中。
utf8_decode()
感謝Fran Cano確定了此問題並提出了解決方案#
添加到(非常好的)現有答案
共享託管軟體
open_basedir
可能會難倒您,因為它可以在 Web 伺服器設定中指定。雖然如果您運行自己的專用伺服器,這很容易解決,但有一些共享託管軟體包(如 Plesk、cPanel 等)可以在每個網域的基礎上配置配置指令。由於軟體會建置設定檔(即httpd.conf
),因此您無法直接變更該文件,因為託管軟體在重新啟動時只會覆寫它。透過 Plesk,他們提供了一個位置來覆寫所提供的
httpd.conf
(稱為vhost.conf
)。只有伺服器管理員可以寫入此文件。 Apache 的設定看起來像這樣讓您的伺服器管理員查閱他們使用的託管和 Web 伺服器軟體的手冊。
檔案權限
要注意的是,透過 Web 伺服器執行檔與命令列或 cron 作業執行有很大不同。最大的區別是您的網頁伺服器有自己的使用者和權限。出於安全原因,該用戶受到很大限制。例如,Apache 通常是
apache
、www-data
或httpd
(取決於您的伺服器)。 cron 作業或 CLI 執行具有執行它的使用者所擁有的任何權限(即以 root 身分執行 PHP 腳本將以 root 權限執行)。很多時候人們會透過執行以下操作來解決權限問題(Linux 範例)
這不是一個聰明的主意,因為檔案或目錄現在是全域可寫的。如果您擁有該伺服器並且是唯一的用戶,那麼這並不是什麼大問題,但如果您位於共享託管環境中,您就授予了伺服器上的每個人存取權限。
您需要做的是確定需要存取權限的使用者並只授予他們存取權限。一旦您知道哪些使用者需要存取權限,您就需要確保
該使用者擁有該檔案並且可能擁有父目錄(尤其是如果您要寫入檔案則為父目錄)。在大多數共享託管環境中,這不會成為問題,因為您的用戶應該擁有根目錄下的所有檔案。下面顯示了一個 Linux 範例
該使用者(且只有該使用者)具有存取權限。在Linux 中,一個好的做法是
chmod 600
(只有所有者可以讀寫)或chmod 644
(所有者可以寫入,但每個人都可以讀取)您可以閱讀有關 Linux 的更廣泛的討論/此處的 Unix 權限和用戶
#可能會遇到此錯誤的原因有很多,因此首先檢查哪些內容的良好清單會很有幫助。
假設我們正在對以下行進行故障排除:
#清單
#1。檢查檔案路徑是否有拼字錯誤
或將
require*
或include*
呼叫的任何內容移動到自己的變數中,回顯它,複製它,然後嘗試從最終端存取它:然後,在終端機中:
#2。檢查相對與絕對路徑注意事項的檔案路徑是否正確
/users/tony/htdocs
最佳實踐:
為了讓您的腳本在移動內容時保持健壯,同時仍在執行時間產生絕對路徑,您有 2 個選項:
require __DIR__ 。 “/相對/路徑/來自/當前/檔案”
。__DIR__
魔術常數傳回目前檔案的目錄。自己定義一個
SITE_ROOT
常數:config.php
在
config.php
中寫入在要引用網站根資料夾的每個檔案中,包含
config.php
,然後在任何位置使用SITE_ROOT
常數: p>這 2 種做法也使您的應用程式更具可移植性,因為它不依賴包含路徑等 ini 設定。
#3。檢查您的包含路徑
另一種包含檔案的方法,既不是相對也不是純粹絕對,是依賴包含路徑。對於諸如 Zend 框架之類的庫或框架來說,通常就是這種情況。
這樣的包含將如下所示:
在這種情況下,您需要確保「Zend」所在的資料夾是包含路徑的一部分。
您可以使用以下命令檢查包含路徑:
您可以使用以下命令向其中新增資料夾:
#4。檢查您的伺服器是否有權存取該檔案
總而言之,執行伺服器進程(Apache 或 PHP)的使用者可能根本沒有讀取或寫入該檔案的權限。
要檢查伺服器正在哪個使用者下運行,您可以使用posix_getpwuid:
要尋找檔案的權限,請在終端機中鍵入以下命令:
並查看權限符號表示法
#5。檢查 PHP 設定
如果以上方法都不起作用,問題可能是某些 PHP 設定禁止它存取該檔案。
三個設定可能相關:
phpinfo()
进行检查>或使用ini_get("open_basedir")
ini_get("allow_url_include")
檢查並使用ini_set("allow_url_include", "1")
設定
#極端情況
如果上述方法都無法診斷問題,則可能會發生以下一些特殊情況:
#1。依賴包含路徑的庫的包含
您可能會使用相對或絕對路徑包含一個庫,例如 Zend 框架。例如:
但是你還是會遇到同類型的錯誤。
發生這種情況的原因是您(成功)包含的檔案本身俱有另一個檔案的包含語句,而第二個包含語句假定您已將該程式庫的路徑新增至包含路徑。
例如,前面提到的 Zend 框架檔案可能包含以下內容:
既不是透過相對路徑包含,也不是透過絕對路徑包含。假設Zend框架目錄已新增至包含路徑。
在這種情況下,唯一實用的解決方案是將目錄新增至包含路徑。
#2。 SELinux
如果您運行的是安全增強型 Linux,那麼這可能是問題的原因,因為拒絕從伺服器存取該檔案。
要檢查系統上是否啟用了 SELinux,請在終端機中執行
sestatus
指令。如果該命令不存在,則表示您的系統上不存在 SELinux。如果它確實存在,那麼它應該告訴您它是否被強制執行。要檢查 SELinux 策略是否是問題的原因,您可以嘗試暫時關閉。但要小心,因為這將完全禁用保護。不要在生產伺服器上執行此操作。
如果您在關閉 SELinux 後不再遇到問題,那麼這就是根本原因。
要解決這個問題,您必須相應地設定 SELinux。
以下上下文類型是必要的:
httpd_sys_content_t
用於您希望伺服器能夠讀取的檔案httpd_sys_rw_content_t
用於您想要讀寫存取權限的檔案httpd_log_t
用於日誌檔案httpd_cache_t
用於快取目錄例如,若要將
httpd_sys_content_t
上下文類型指派給您的網站根目錄,請執行:如果您的檔案位於主目錄中,您還需要開啟
httpd_enable_homedirs
布林值:無論如何,SELinux 拒絕存取檔案的原因可能有多種,具體取決於您的策略。所以你需要對此進行調查。此處是專門為 Web 伺服器設定 SELinux 的教學。
#3。交響樂
如果您使用 Symfony,並且在上傳到伺服器時遇到此錯誤,則可能是應用程式的快取尚未重置,因為
app/cache
已上傳,或者該快取尚未清除。您可以透過執行以下控制台命令來測試並修復此問題:
#4。 Zip 檔案中的非 ACSII 字元
顯然,當 zip 中的某些檔案的檔案名稱中包含非 ASCII 字元(例如“é”)時,呼叫
zip->close()
時也會發生此錯誤。一個可能的解決方案是在建立目標檔案之前將檔案名稱包裝在
utf8_decode()
中。感謝Fran Cano確定了此問題並提出了解決方案#