[英]PHP - Failed to open stream : No such file or directory
在 PHP 腳本中,無論是調用include()
、 require()
、 fopen()
還是它們的派生詞,例如include_once
、 require_once
,甚至是move_uploaded_file()
,都經常遇到錯誤或警告:
無法打開 stream:沒有這樣的文件或目錄。
什么是快速找到問題根源的好過程?
可能會遇到此錯誤的原因有很多,因此首先檢查的內容的良好清單會大有幫助。
假設我們正在對以下行進行故障排除:
require "/path/to/file"
或者將require*
或include*
調用的任何內容移動到它自己的變量中,回顯它,復制它,然后嘗試從終端訪問它:
$path = "/path/to/file"; echo "Path : $path"; require "$path";
然后,在終端中:
cat <file path pasted>
/users/tony/htdocs
最佳實踐:
為了使您的腳本在您四處移動時保持健壯,同時在運行時仍生成絕對路徑,您有 2 個選項:
require __DIR__ . "/relative/path/from/current/file"
require __DIR__ . "/relative/path/from/current/file"
。 __DIR__
魔術常量返回當前文件的目錄。 自己定義一個SITE_ROOT
常量:
config.php
在config.php
中,寫
define('SITE_ROOT', __DIR__);
在您要引用站點根文件夾的每個文件中,包含config.php
,然后在您喜歡的任何地方使用SITE_ROOT
常量:
require_once __DIR__."/../config.php"; ... require_once SITE_ROOT."/other/file.php";
這兩種做法還使您的應用程序更具可移植性,因為它不依賴於包含路徑之類的 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 <path/to/file>
並查看權限符號
如果上述方法都不起作用,那么問題可能是某些 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 框架目錄已添加到包含路徑。
在這種情況下,唯一實用的解決方案是將目錄添加到包含路徑中。
如果您正在運行 Security-Enhanced Linux,那么這可能是問題的原因,即拒絕從服務器訪問文件。
要檢查您的系統上是否啟用了 SELinux ,請在終端中運行sestatus
命令。 如果該命令不存在,則 SELinux 不在您的系統上。 如果它確實存在,那么它應該告訴你它是否被強制執行。
要檢查 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
布爾值:
setsebool -P httpd_enable_homedirs 1
在任何情況下,SELinux 拒絕訪問文件的原因可能有多種,具體取決於您的策略。 因此,您需要對此進行調查。 這是專門為 Web 服務器配置 SELinux 的教程。
如果您正在使用 Symfony,並且在上傳到服務器時遇到此錯誤,則可能是應用程序的緩存尚未重置,可能是因為app/cache
已上傳,或者該緩存尚未清除。
您可以通過運行以下控制台命令來測試和修復此問題:
cache:clear
顯然,當 zip 中的某些文件的文件名中包含非 ASCII 字符(例如“é”)時,調用zip->close()
也會發生此錯誤。
一個潛在的解決方案是在創建目標文件之前將文件名包裝在utf8_decode()
中。
感謝Fran Cano確定並提出解決此問題的方法
添加到(非常好的)現有答案
open_basedir
是一個可以難倒你的工具,因為它可以在 Web 服務器配置中指定。 雖然如果您運行自己的專用服務器,這很容易解決,但有一些共享托管軟件包(如 Plesk、cPanel 等)將在每個域的基礎上配置配置指令。 因為軟件構建了配置文件(即httpd.conf
),所以您不能直接更改該文件,因為托管軟件會在重新啟動時覆蓋它。
使用 Plesk,它們提供了一個地方來覆蓋提供的名為vhost.conf
的httpd.conf
。 只有服務器管理員可以寫入此文件。 Apache 的配置看起來像這樣
<Directory /var/www/vhosts/domain.com>
<IfModule mod_php5.c>
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"
</IfModule>
</Directory>
讓您的服務器管理員查閱他們使用的托管和 Web 服務器軟件的手冊。
請務必注意,通過 Web 服務器執行文件與命令行或 cron 作業執行非常不同。 最大的不同是您的 Web 服務器有自己的用戶和權限。 出於安全原因,該用戶受到很大限制。 例如,Apache 通常是apache
、 www-data
或httpd
(取決於您的服務器)。 cron 作業或 CLI 執行具有運行它的用戶所擁有的任何權限(即以 root 身份運行 PHP 腳本將以 root 權限執行)。
很多時候人們會通過執行以下操作來解決權限問題(Linux 示例)
chmod 777 /path/to/file
這不是一個聰明的主意,因為文件或目錄現在是世界可寫的。 如果您擁有服務器並且是唯一的用戶,那么這沒什么大不了的,但是如果您在共享托管環境中,您只是為服務器上的每個人提供了訪問權限。
您需要做的是確定需要訪問權限的用戶並僅授予他們訪問權限。 一旦您知道哪些用戶需要訪問權限,您需要確保
該用戶擁有該文件,並且可能擁有父目錄(如果您想寫入文件,尤其是父目錄)。 在大多數共享主機環境中,這不是問題,因為您的用戶應該擁有您根目錄下的所有文件。 下面顯示了一個 Linux 示例
chown apache:apache /path/to/file
該用戶並且只有該用戶具有訪問權限。 在 Linux 中,一個好的做法是chmod 600
(只有所有者可以讀寫)或chmod 644
(所有者可以寫但每個人都可以閱讀)
我的代碼在所有機器上都運行良好,但只有這台機器開始出現問題(我猜它曾經工作過)。 使用 echo "document_root" 路徑進行調試,還仔細查看了錯誤,發現了這個
警告:包括( D:/MyProjects/testproject//functions/connections.php ):未能打開流:
您可以輕松查看問題所在。 問題是 // 在函數之前
$document_root = $_SERVER['DOCUMENT_ROOT'];
echo "root: $document_root";
include($document_root.'/functions/connections.php');
因此,只需從 include 中刪除 ladding / 它應該可以正常工作。 有趣的是這種行為在不同的版本上是不同的。 我在筆記本電腦、Macbook Pro 和這台 PC 上運行相同的代碼,一切都運行良好,直到。 希望這可以幫助某人。
桑巴股份
如果您有一台 Linux 測試服務器並且使用 Windows 客戶端工作,那么 Samba 共享會干擾chmod命令。 所以,即使你使用:
chmod -R 777 myfolder
在 Linux 方面,完全有可能 Unix Group\www-data 仍然沒有寫訪問權限。 如果您的共享設置為將 Windows 管理員映射到 root,則一種可行的解決方案:從 Windows,打開權限,禁用帶有副本的文件夾的繼承,然后授予對 www-data 的完全訪問權限。
那是我的情況。 它實際上鏈接到問題 #4485874 ,但我將很快在這里解釋它。
當您嘗試要求path/to/script.php?parameter=value
時,PHP 會查找名為script.php?parameter=value
的文件,因為 UNIX 允許您擁有這樣的路徑。
如果您確實需要將一些數據傳遞給包含的腳本,只需將其聲明為$variable=...
或$GLOBALS[]=...
或您喜歡的其他方式。
php.ini
中的以下 PHP 設置如果設置為不存在的目錄也可以引發
PHP 警告:未知:無法打開流:第 0 行的未知中的權限被拒絕
sys_temp_dir
upload_tmp_dir
session.save_path
另一個可能的原因:在文本編輯器中重命名和/或移動文件。 我經歷了上述所有步驟,但沒有成功,直到刪除了不斷拋出該錯誤的文件並創建了一個新文件,該文件便解決了該問題。
對我來說,我收到了這個錯誤,因為我試圖使用用戶名和密碼讀取需要 HTTP 身份驗證的文件。 希望對其他人有所幫助。 可能是另一個極端案例。
您可以通過檢查標頭來檢查是否存在這種類型的身份驗證:
$file_headers = get_headers($url);
if (!$file_headers) echo 'File headers missing';
else if (strpos($file_headers[0], '401 Unauthorized') > -1) echo '401 Unauthorized';
在 PHP 中,啟動 Apache,然后寫入您的數據庫名稱和密碼(如果您的環境(.env)中存在)。
除了其他出色的答案之外,我在編寫簡單腳本時在 Windows 上忽略了一件事:嘗試打開文件名中包含 Windows 不支持的字符的文件時會顯示此錯誤。
例如:
$file = fopen(date('Y-m-d_H:i:s'), 'w+');
會給:
fopen(2022-06-01_22:53:03):無法打開流:...中沒有這樣的文件或目錄
Windows 不喜歡文件名中的:
以及一些其他字符。
在PHP腳本中,無論是調用include()
, require()
, fopen()
還是它們的派生類,例如include_once
, require_once
,甚至move_uploaded_file()
,都會經常出現錯誤或警告:
無法打開流:沒有這樣的文件或目錄。
有什么好的方法可以快速找到問題的根本原因?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.