簡體   English   中英

PHP - 無法打開 stream:沒有這樣的文件或目錄

[英]PHP - Failed to open stream : No such file or directory

在 PHP 腳本中,無論是調用include()require()fopen()還是它們的派生詞,例如include_oncerequire_once ,甚至是move_uploaded_file() ,都經常遇到錯誤或警告:

無法打開 stream:沒有這樣的文件或目錄。

什么是快速找到問題根源的好過程?

可能會遇到此錯誤的原因有很多,因此首先檢查的內容的良好清單會大有幫助。

假設我們正在對以下行進行故障排除:

require "/path/to/file"


清單


1.檢查文件路徑是否有錯別字

  • 手動檢查(通過目視檢查路徑)
  • 或者將require*include*調用的任何內容移動到它自己的變量中,回顯它,復制它,然后嘗試從終​​端訪問它:

     $path = "/path/to/file"; echo "Path : $path"; require "$path";

    然后,在終端中:

     cat <file path pasted>


2. 檢查相對路徑與絕對路徑注意事項的文件路徑是否正確

  • 如果它以正斜杠“/”開頭,那么它不是指您網站文件夾的根目錄(文檔根目錄),而是指您的服務器的根目錄。
    • 例如,您網站的目錄可能是/users/tony/htdocs
  • 如果它不是以正斜杠開頭,那么它要么依賴於包含路徑(見下文),要么路徑是相對的。 如果是相對的,那么 PHP 會相對於當前工作目錄的路徑進行計算。
    • 因此,與您網站根目錄的路徑或您輸入的文件無關
    • 因此,請始終使用絕對文件路徑

最佳實踐:

為了使您的腳本在您四處移動時保持健壯,同時在運行時仍生成絕對路徑,您有 2 個選項:

  1. 使用require __DIR__ . "/relative/path/from/current/file" require __DIR__ . "/relative/path/from/current/file" __DIR__魔術常量返回當前文件的目錄。
  2. 自己定義一個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 設置。


3.檢查你的包含路徑

另一種包含文件的方法,既不是相對也不是絕對絕對,是依賴於 包含路徑 庫或框架(如 Zend 框架)通常是這種情況。

這樣的包含將如下所示:

include "Zend/Mail/Protocol/Imap.php"

在這種情況下,您需要確保“Zend”所在的文件夾是包含路徑的一部分。

您可以使用以下命令檢查包含路徑:

echo get_include_path();

您可以使用以下命令向其中添加文件夾:

set_include_path(get_include_path().":"."/path/to/new/folder");


4. 檢查您的服務器是否有權訪問該文件

可能是,運行服務器進程(Apache 或 PHP)的用戶根本沒有讀取或寫入該文件的權限。

要檢查服務器在哪個用戶下運行,您可以使用posix_getpwuid

$user = posix_getpwuid(posix_geteuid());

var_dump($user);

要找出文件的權限,請在終端中鍵入以下命令:

ls -l <path/to/file>

並查看權限符號


5.檢查PHP設置

如果上述方法都不起作用,那么問題可能是某些 PHP 設置禁止它訪問該文件。

三個設置可能是相關的:

  1. open_basedir
    • 如果設置了這個,PHP 將不能訪問指定目錄之外的任何文件(甚至不能通過符號鏈接)。
    • 但是,默認行為是不設置它,在這種情況下沒有限制
    • 這可以通過調用phpinfo()或使用ini_get("open_basedir")來檢查
    • 您可以通過編輯 php.ini 文件或 httpd.conf 文件來更改設置
  2. 安全模式
    • 如果啟用此功能,可能會應用限制。 但是,這已在 PHP 5.4 中刪除。 如果您仍在使用支持安全模式的版本,請升級到仍受支持的 PHP 版本。
  3. allow_url_fopen 和 allow_url_include
    • 這僅適用於通過網絡進程(例如 http://)包含或打開文件,而不適用於嘗試在本地文件系統上包含文件時
    • 這可以用ini_get("allow_url_include")檢查並用ini_set("allow_url_include", "1")設置


角落案例

如果以上都不能診斷問題,這里有一些可能發生的特殊情況:


1.依賴包含路徑的庫的包含

您可能會使用相對或絕對路徑包含庫,例如 Zend 框架。 例如 :

require "/usr/share/php/libzend-framework-php/Zend/Mail/Protocol/Imap.php"

但是你仍然會得到同樣的錯誤。

這可能會發生,因為您(成功)包含的文件本身具有另一個文件的包含語句,並且第二個包含語句假定您已將該庫的路徑添加到包含路徑。

例如,前面提到的 Zend 框架文件可能包含以下內容:

include "Zend/Mail/Protocol/Exception.php" 

這既不是相對路徑的包含,也不是絕對路徑的包含。 假設 Zend 框架目錄已添加到包含路徑。

在這種情況下,唯一實用的解決方案是將目錄添加到包含路徑中。


2. SELinux

如果您正在運行 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 的教程。


3. Symfony

如果您正在使用 Symfony,並且在上傳到服務器時遇到此錯誤,則可能是應用程序的緩存尚未重置,可能是因為app/cache已上傳,或者該緩存尚未清除。

您可以通過運行以下控制台命令來測試和修復此問題:

cache:clear


4. Zip 文件中的非 ACSII 字符

顯然,當 zip 中的某些文件的文件名中包含非 ASCII 字符(例如“é”)時,調用zip->close()也會發生此錯誤。

一個潛在的解決方案是在創建目標文件之前將文件名包裝在utf8_decode()中。

感謝Fran Cano確定並提出解決此問題的方法

添加到(非常好的)現有答案

共享主機軟件

open_basedir是一個可以難倒你的工具,因為它可以在 Web 服務器配置中指定。 雖然如果您運行自己的專用服務器,這很容易解決,但有一些共享托管軟件包(如 Plesk、cPanel 等)將在每個域的基礎上配置配置指令。 因為軟件構建了配置文件(即httpd.conf ),所以您不能直接更改該文件,因為托管軟件會在重新啟動時覆蓋它。

使用 Plesk,它們提供了一個地方來覆蓋提供的名為vhost.confhttpd.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 通常是apachewww-datahttpd (取決於您的服務器)。 cron 作業或 CLI 執行具有運行它的用戶所擁有的任何權限(即以 root 身份運行 PHP 腳本將以 root 權限執行)。

很多時候人們會通過執行以下操作來解決權限問題(Linux 示例)

chmod 777 /path/to/file

這不是一個聰明的主意,因為文件或目錄現在是世界可寫的。 如果您擁有服務器並且是唯一的用戶,那么這沒什么大不了的,但是如果您在共享托管環境中,您只是為服務器上的每個人提供了訪問權限。

您需要做的是確定需要訪問權限的用戶並僅授予他們訪問權限。 一旦您知道哪些用戶需要訪問權限,您需要確保

  1. 該用戶擁有該文件,並且可能擁有父目錄(如果您想寫入文件,尤其是父目錄)。 在大多數共享主機環境中,這不是問題,因為您的用戶應該擁有您根目錄下的所有文件。 下面顯示了一個 Linux 示例

     chown apache:apache /path/to/file
  2. 該用戶並且只有該用戶具有訪問權限。 在 Linux 中,一個好的做法是chmod 600 (只有所有者可以讀寫)或chmod 644 (所有者可以寫但每個人都可以閱讀)

您可以在此處閱讀有關 Linux/Unix 權限和用戶的更詳細討論

  1. 查看確切的錯誤

我的代碼在所有機器上都運行良好,但只有這台機器開始出現問題(我猜它曾經工作過)。 使用 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 上運行相同的代碼,一切都運行良好,直到。 希望這可以幫助某人。

  1. 復制過去瀏覽器中的文件位置以確保文件存在。 有時文件會被意外刪除(發生在我身上),這也是我的問題。

桑巴股份

如果您有一台 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

PHP - 無法打開流:mac中沒有這樣的文件或目錄

例如,我將上傳一張圖片。 但我收到了這個錯誤。 我要做的第一件事是右鍵單擊圖像並獲取信息。

$thePathOfMyPicture = "/Users/misstugba/Desktop/"; 與功能一起使用

if(move_uploaded_file($_FILES["file"]["tmp_name"],$thePathOfMyPicture.$_FILES["file"]["name"])){
echo "image uploaded successfully";

} 在此處輸入圖像描述

另一個可能的原因:在文本編輯器中重命名和/或移動文件。 我經歷了上述所有步驟,但沒有成功,直到刪除了不斷拋出該錯誤的文件並創建了一個新文件,該文件便解決了該問題。

對我來說,我收到了這個錯誤,因為我試圖使用用戶名和密碼讀取需要 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_oncerequire_once ,甚至move_uploaded_file() ,都會經常出現錯誤或警告:

無法打開流:沒有這樣的文件或目錄。

有什么好的方法可以快速找到問題的根本原因?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM