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

php require fopen include-path


在PHP脚本中,无论是调用 include()require()fopen() 还是它们的派生类,例如 include_oncerequire_once 或什至 move_uploaded_file() ,都会经常出现错误或警告:

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

有什么好的流程可以快速找到问题的根源?




Answer 1 Vic Seedoubleyew


遇到这种错误的原因有很多,因此,一个好的检查清单,首先要检查什么,会有很大的帮助。

我们来考虑一下,下面这条线的故障排除。

require "/path/to/file"


Checklist


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

  • 人工检查(通过目测路径)
  • 或将 require*include* 调用的内容移到其自己的变量中,将其回显,复制并尝试从终端进行访问:

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

    然后,在一个终端中。

    cat <file path pasted>


2.2.检查文件路径是否正确,关于相对路径和绝对路径的注意事项

  • 如果它是以前斜线""开头,那么它指的不是您网站的文件夹根目录(文档根目录),而是您服务器的根目录。
    • 例如,您网站的目录可能是 /users/tony/htdocs
  • 如果它不是以正斜杠开头,那么它要么依赖于include路径(参见下文),要么是相对路径。如果是相对的,则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";

这2种做法也让你的应用程序更加可移植,因为它不依赖include路径等ini设置。


3.3.检查你的包括路径

既不是相对也不是绝对的,包含文件的另一种方法是依靠include路径。对于库或框架(例如Zend框架),通常是这种情况。

这样的包含会是这样的。

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

在这种情况下,你要确保 "Zend "所在的文件夹是包含路径的一部分。

你可以用.NET的方式检查包含路径。

echo get_include_path();

你可以用.NET Framework 3.0添加一个文件夹到其中。

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


4.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.依赖于include path的include库的收录方式

当你使用相对或绝对路径包含一个库,例如Zend框架,可能会出现这样的情况。比如说.NET Framework,你可以用相对路径或绝对路径包含库。

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

但后来你还是会出现同样的错误。

这可能是因为你已经(成功地)包含了一个文件,而这个文件本身就有另一个文件的include语句,而第二个include语句假定你已经将该库的路径添加到了include路径中。

例如,前文提到的Zend框架文件可以有以下的include .NET文件。

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

既不是相对路径的include,也不是绝对路径的include。它是假设Zend框架目录已经被添加到include路径中。

在这种情况下,唯一可行的解决方案是将目录添加到你的include路径中。


2.2.SELinux

如果你运行的是安全增强型Linux,那么可能是拒绝从服务器上访问文件的原因。

要检查系统上是否启用SELinux,请在终端中运行 sestatus 命令。如果该命令不存在,则SELinux不在您的系统上。如果确实存在,那么它应该告诉您是否强制执行。

要检查SELinux策略是否是问题的原因,您可以尝试暂时将其关闭。但是请小心,因为这将完全禁用保护。不要在生产服务器上执行此操作。

setenforce 0

如果你不再出现SELinux关闭的问题,那么这就是根本原因。

要解决此问题,您将必须相应地配置SELinux。

以下的上下文类型将是必要的。

  • httpd_sys_content_t 您希望服务器能够读取的文件的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.4.Zip文件中的非ACSII字符

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

潜在的解决方案是在创建目标文件之前将文件名包装在 utf8_decode() 中。

感谢Fran Cano提出并建议了此问题的解决方案




Answer 2 Machavity


为了补充现有的(真的很好的)答案

共享主机软件

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服务器执行文件与执行命令行或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权限和用户的更多扩展讨论。




Answer 3 Hammad Khan


  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');

所以只需从include中删除提单,就可以正常工作了。有趣的是,这个行为在不同的版本上是不同的。我在Laptop、Macbook Pro和这台电脑上运行同样的代码,直到现在都正常工作。希望这能帮助到大家。

  1. 在浏览器中复制粘贴文件位置,确保文件存在。有时文件会被意外删除(我就遇到过这种情况),这也是我的问题。



Answer 4 Paul Lynn


添加带查询参数的脚本

那是我的情况。它实际上链接到问题#4485874,但我将在此处稍后解释。
当您尝试要求 path/to/script.php?parameter=value 时,PHP将查找名为 script.php?parameter=value 的文件,因为UNIX允许您使用这样的路径。
如果确实需要将一些数据传递到包含的脚本,只需将其声明为 $variable=...$GLOBALS[]=... 或其他您喜欢的方式。




Answer 5 Stephan Brunker


Samba股票

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

chmod -R 777 myfolder

在Linux方面,完全有可能Unix组www-data仍然没有写权限。一个可行的解决方案是,如果你的共享被设置成Windows管理员被映射到root。在Windows中,打开权限,禁用复制文件夹的继承,然后授予www-data的完全访问权限。