PHP - 无法打开流:没有这样的文件或目录

新手上路,请多包涵

In PHP scripts, whether calling include() , require() , fopen() , or their derivatives such as include_once , require_once ,甚至 move_uploaded_file() ,经常会遇到错误或警告:

无法打开流:没有这样的文件或目录。

什么是快速找到问题根源的好过程?

原文由 Vic Seedoubleyew 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.5k
2 个回答

可能会遇到此错误的原因有很多,因此首先检查的内容的良好清单会大有帮助。

假设我们正在对以下行进行故障排除:

 require "/path/to/file"

清单

1.检查文件路径是否有错别字

  • 手动检查(通过目视检查路径)
  • 或将 require*include* ad1ae725bc4a21f61937b7d33— 调用的任何内容移动到自己的变量中,回显它,复制它,然后尝试从终端访问它:
   $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"__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 boolean :

 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 确定并提出解决此问题的方法

原文由 Vic Seedoubleyew 发布,翻译遵循 CC BY-SA 4.0 许可协议

除了其他出色的答案之外,我在编写简单脚本时在 Windows 上忽略了一件事:尝试打开文件名中包含 Windows 不支持的字符的文件时会显示此错误。

例如:

 $file = fopen(date('Y-m-d_H:i:s'), 'w+');

会给:

fopen(2022-06-01_22:53:03):无法打开流:…中没有这样的文件或目录

Windows 不喜欢文件名中的 : 以及许多其他字符。

原文由 Ynhockey 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题