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

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

提问人:Vic Seedoubleyew 提问时间:4/12/2016 最后编辑:CœurVic Seedoubleyew 更新时间:3/8/2023 访问量:849709

问:

在 PHP 脚本中,无论是调用 、、 还是其派生词(如 、 、 甚至 ),都经常会遇到错误或警告:include()require()fopen()include_oncerequire_oncemove_uploaded_file()

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

快速找到问题根本原因的好流程是什么?

php 需要 fopen include-path

评论

5赞 Madara's Ghost 4/13/2016
我已经清理了这篇文章的偏离主题的评论。请在元中保留元讨论。但是,请注意,对规范问题可行性的讨论已经一遍又一遍地进行。请参阅此处的示例。
1赞 Rshad Zhran 5/13/2017
我遇到了同样的问题,唯一始终有效的解决方案是: -1 转到要包含的文件,对 botton 、properties,复制完整路径例如:C:/......../file.php 2- 包含它。其实我看到这个问题得到了回答,并且答案得到了验证,但对我来说在某些情况下不起作用,直到我找到上面描述的方法。
0赞 Vic Seedoubleyew 1/25/2018
@Rash感谢您的贡献。不幸的是,您的解决方案是错误的,因为它会提到绝对路径名,这是错误的。这是错误的原因是,当您将项目复制到其他地方或将其移动到计算机中时,一切都会中断。

答:

342赞 Vic Seedoubleyew 4/12/2016 #1

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

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

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. 用。__DIR__ magic 常量返回当前文件的目录。require __DIR__ . "/relative/path/from/current/file"
  2. 自己定义一个常量:SITE_ROOT

    • 在网站目录的根目录下,创建一个文件,例如config.php
    • 在 中,写入config.php

      define('SITE_ROOT', __DIR__);
      
    • 在要引用站点根文件夹的每个文件中,包括 ,然后在您喜欢的任何位置使用常量:config.phpSITE_ROOT

      require_once __DIR__."/../config.php";
      ...
      require_once SITE_ROOT."/other/file.php";
      

这 2 种做法还使应用程序更具可移植性,因为它不依赖于 ini 设置(如 include 路径)。


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"

但是你仍然会得到同样的错误。

发生这种情况的原因是,您(成功)包含的文件本身具有另一个文件的 include 语句,并且第二个 include 语句假定您已将该库的路径添加到包含路径中。

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

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

它既不是按相对路径包含,也不是按绝对路径包含。它假设 Zend 框架目录已添加到包含路径中。

在这种情况下,唯一实用的解决方案是将目录添加到包含路径中。


2. SELinux的

如果您运行的是安全增强型 Linux,则可能是问题的原因,因为它拒绝从服务器访问文件。

要检查系统上是否启用了 SELinux,请在终端中运行该命令。如果该命令不存在,则 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

setsebool -P httpd_enable_homedirs 1

在任何情况下,SELinux 拒绝访问文件的原因可能有多种,具体取决于您的策略。因此,您需要对此进行调查。这里有一个专门关于为 Web 服务器配置 SELinux 的教程。


3. 交响乐

如果您使用的是 Symfony,并且在上传到服务器时遇到此错误,则可能是应用程序的缓存尚未重置,因为已上传,或者缓存尚未清除。app/cache

您可以通过运行以下控制台命令来测试和修复此问题:

cache:clear


4. Zip 文件中的非 ACSII 字符

显然,当 zip 中的某些文件在其文件名中包含非 ASCII 字符(例如“é”)时,调用时也会发生此错误。zip->close()

一个可能的解决方案是在创建目标文件之前将文件名包装起来。utf8_decode()

感谢 Fran Cano 确定并提出此问题的解决方案

评论

4赞 bansi 4/14/2016
我认为在这里提及可能是一个好主意。您至少需要对包含的文件(Apache 使用的只读目录和文件)权限。selinuxhttpd_sys_content_t
0赞 Vic Seedoubleyew 4/15/2016
非常感谢您的建议。由于我对 SELinux 不熟悉,我做了一些阅读并试图回答这个案例。如果不正确,请随时反馈或提出一些编辑建议。再次感谢您的评论!
0赞 bansi 4/16/2016
chcon是暂时的,不会在重新启动后幸存下来。您可能需要使用来更改文件的上下文。这是一个网站的简单教程restoreconsemanage
0赞 chrishiestand 1/27/2017
另一种添加可能性:realpath 缓存:lyte.id.au/2014/05/01/what-the-hell-php
0赞 Vic Seedoubleyew 1/29/2018
@chrishiestand非常感谢!那篇文章真的很有意思!你还记得导致这个错误的事件过程是什么吗?是不是最初用户没有对文件的读取访问权限,然后它被更改了,但缓存仍然认为它是不可读的,所以它在文件打开时抛出该错误?
17赞 Machavity 4/22/2016 #2

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

共享主机软件

open_basedir可能会难倒您,因为它可以在 Web 服务器配置中指定。虽然如果您运行自己的专用服务器,这很容易解决,但有一些共享托管软件包(如 Plesk、cPanel 等)将基于每个域配置配置指令。由于软件构建配置文件(即 ),因此您不能直接更改该文件,因为托管软件在重新启动时只会覆盖它。httpd.conf

在 Plesk 中,它们提供了一个位置来覆盖提供的称为 .只有服务器管理员才能写入此文件。Apache 的配置如下所示httpd.confvhost.conf

<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 通常是 或(取决于您的服务器)。cron 作业或 CLI 执行具有运行它的用户所拥有的任何权限(即,以 root 身份运行 PHP 脚本将以 root 权限执行)。apachewww-datahttpd

很多时候,人们会通过执行以下操作来解决权限问题(Linux 示例)

chmod 777 /path/to/file

这不是一个聪明的主意,因为文件或目录现在是全局可写的。如果您拥有服务器并且是唯一的用户,那么这没什么大不了的,但是如果您在共享托管环境中,您只是授予了服务器上的每个人访问权限。

您需要做的是确定需要访问权限的用户,并仅授予他们访问权限的用户。一旦您知道哪些用户需要访问权限,您就需要确保

  1. 该用户拥有该文件,并可能拥有父目录(如果要写入文件,尤其是父目录)。在大多数共享托管环境中,这不会成为问题,因为您的用户应该拥有根目录下的所有文件。下面显示了一个 Linux 示例

     chown apache:apache /path/to/file
    
  2. 该用户(且只有该用户)具有访问权限。在 Linux 中,一个好的做法是(只有所有者可以读取和写入)或(所有者可以写入,但每个人都可以读取)chmod 600chmod 644

您可以在此处阅读有关 Linux/Unix 权限和用户的更详细的讨论

2赞 Paul Lynn 8/30/2018 #3

添加带有查询参数的脚本

我就是这种情况。它实际上链接到问题 #4485874,但我很快就会在这里解释它。
当您尝试 require 时,PHP 会查找名为 的文件,因为 UNIX 允许您拥有这样的路径。
如果您确实需要将一些数据传递给包含的脚本,只需将其声明为 or 或您喜欢的其他方式即可。
path/to/script.php?parameter=valuescript.php?parameter=value$variable=...$GLOBALS[]=...

10赞 TheTechGuy 9/7/2018 #4
  1. 查看确切的错误

我的代码在所有机器上都运行良好,但只有在这台机器上才开始出现问题(我猜这曾经工作过)。使用echo“document_root”路径进行调试,并仔细查看错误,发现这个

警告: include(D:/MyProjects/testproject//functions/connections.php): 无法打开流:

您可以轻松查看问题所在。问题是 // 函数之前

$document_root = $_SERVER['DOCUMENT_ROOT'];
echo "root: $document_root";
include($document_root.'/functions/connections.php');

因此,只需从包含中取出提货/,它应该可以正常工作。有趣的是,这种行为在不同的版本上是不同的。我在笔记本电脑、Macbook Pro 和这台 PC 上运行相同的代码,所有代码都运行良好。希望这对某人有所帮助。

  1. 复制浏览器中的文件位置以确保文件存在。有时文件会被意外删除(发生在我身上),这也是我的问题。

评论

0赞 Vic Seedoubleyew 9/26/2018
这与下面清单的第 1 步有何不同?
0赞 TheTechGuy 9/27/2018
步骤 2 附加检查,与步骤 1 无关。只需导航到浏览器中建议的路径,然后查看是否在那里看到该文件(在 Windows 资源管理器中没有,但在浏览器中)。
5赞 Stephan Brunker 9/19/2019 #5

Samba股票

如果您有 Linux 测试服务器,并且使用 Windows 客户端工作,则 Samba 共享会干扰 chmod 命令。因此,即使您使用:

chmod -R 777 myfolder

在 Linux 端,Unix Group\www-data 完全有可能仍然没有写入访问权限。如果将共享设置为将 Windows 管理员映射到 root,则一个可行的解决方案是:在 Windows 中,打开“权限”,禁用带有副本的文件夹的“继承”,然后授予对 www-data 的完全访问权限。

1赞 1000Gbps 8/27/2020 #6

如果设置为不存在的目录,则以下 PHP 设置也会引发php.ini

PHP警告:未知:无法打开流:权限被拒绝 第 0 行未知

sys_temp_dir
upload_tmp_dir
session.save_path
0赞 Sean H 1/27/2021 #7

对我来说,我收到此错误是因为我试图使用用户名和密码读取需要 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';

评论

0赞 Vic Seedoubleyew 1/29/2021
你能分享一下你使用的确切线路是什么吗?它是否使用了 、 或 ?includerequirefopen
0赞 Sean H 1/29/2021
@VicSeedoubleyew $fp = fopen($file, 'wb');不,我没有使用 include 或 require。您可以使用以下网址进行复制:12345:[email protected]/datatransfer/files/products.zip
0赞 Vic Seedoubleyew 2/1/2021
好的,谢谢。是否推荐从 HTTP 位置读取方法?fopen
0赞 Sean H 2/2/2021
我不知道它是否被推荐,但它似乎完全有效。在这方面,你可能比我更了解
0赞 Vic Seedoubleyew 2/4/2021
我已经很多年没有使用PHP了,所以我说不出来。我只是想知道解决您的情况是添加用户名和密码,还是使用其他功能
1赞 Tugba Alparslan 7/7/2021 #8

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

例如,我将上传一张图片。但是我收到这个错误。我要做的第一件事是右键单击图像并获取信息。

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

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

} enter image description here

-1赞 hend kagha 9/17/2021 #9

在PHP中,启动Apache,然后写下数据库名称和密码(如果存在于您的环境中)(.env)。

2赞 Ynhockey 6/2/2022 #10

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

例如:

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

将给予:

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

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

-1赞 Sorrymelame 3/8/2023 #11

如果运行控制台 CLI 脚本,请添加 1 行

#!/usr/bin/env php
<?php
require_once('common/autoload.php');

评论

0赞 Vic Seedoubleyew 3/9/2023
您能解释一下您的两条线中的哪一条可以解决问题吗,为什么?
0赞 Sorrymelame 3/9/2023
php-cli 无法正确解释脚本
0赞 Vic Seedoubleyew 3/13/2023
我对你的评论完全感到困惑。我重新提交我的问题:你能解释一下你的两条线中的哪一条可以解决问题吗,为什么?
0赞 Sorrymelame 4/24/2023
对不起,工作量很大 如果您之前没有在控制台中指定解释器,或者通过 ./script.php 运行文件,它将向从控制台工作的解释器清楚地表明脚本应通过此命令运行#!/usr/bin/env php