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


166

在PHP脚本,调用是否include()require()fopen(),或它们的衍生物,例如include_oncerequire_once或者甚至move_uploaded_file(),一个经常遇到错误或警告:

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

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


5
我已经清理了这篇文章中的离题评论。请在meta中保留meta讨论。但是,请注意,关于规范性生存能力的讨论已经一遍又一遍地进行了。在这里查看示例。
马达拉的幽灵

1
我遇到了同样的问题,始终有效的唯一解决方案是:-1转到要包含的文件,右botton,属性,复制完整路径,例如:C:/......../ file.php 2-包括它。实际上我看到这个问题已经回答了,答案也得到了验证,但是对我来说在某些情况下是行不通的,直到我找到了上述的方式。
Rshad

@Rash感谢您的贡献。不幸的是,您的解决方案是错误的,因为它将提到绝对路径名,这是错误的。之所以这样做是错误的,是因为当您将项目复制到其他位置或将其移入计算机内部时,所有内容都会中断。
Vic Seedoubleyew

Answers:


259

有很多原因可能会导致此错误,因此对要首先检查的内容进行检查的清单很有意义。

让我们考虑对以下行进行故障排除:

require "/path/to/file"


检查清单


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

  • 手动检查(通过目视检查路径)
  • 或将由require*或调用的内容include*移至其自己的变量,将其回显,复制并尝试从终端访问它:

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

    然后,在终端中:

    cat <file path pasted>


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

  • 如果以反斜杠“ /”开头,则它不是指网站文件夹的根目录(文档根目录),而是服务器的根目录。
    • 例如,您网站的目录可能是 /users/tony/htdocs
  • 如果它不是以正斜杠开头,那么它要么依赖于include路径(参见下文),要么是相对路径。如果是相对的,则PHP将相对于当前工作目录的路径进行计算。
    • 因此,与您网站的根目录或您输入的文件的路径无关
    • 因此,请始终使用绝对文件路径

最佳做法:

为了使您的脚本健壮,以防万一,您仍然可以在运行时生成绝对路径,但有两个选择:

  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.检查您的包含路径

既不是相对也不是绝对的,包括文件的另一种方法是依靠include路径。对于库或框架(例如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语句假定您已将该库的路径添加到了include路径中。

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

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

既不是相对路径也不是绝对路径。假定Zend框架目录已添加到包含路径。

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


2. SELinux

如果您正在运行增强安全性的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_homedirsboolean:

setsebool -P httpd_enable_homedirs 1

无论如何,根据您的策略,SELinux可能会出于多种原因拒绝访问文件。因此,您需要对此进行调查。是专门针对Web服务器配置SELinux的教程。


3. Symfony

如果您使用的是Symfony,并且在上载到服务器时遇到此错误,则可能是由于app/cache上载或未清除该缓存而未重置该应用程序的缓存。

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

cache:clear


4. Zip文件中的非ACSII字符

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

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

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


4
我认为selinux在这里提到一个好主意。您将至少需要httpd_sys_content_t对包含的文件具有(Apache使用的只读目录和文件)权限。
bansi 2016年

非常感谢您的建议。由于我不熟悉SELinux,因此我做了一些阅读并尝试回答这种情况。如果有误,请随时提出反馈或提出一些建议。再次感谢您的评论!
Vic Seedoubleyew '16

chcon是临时的,将无法生存restorecon或重启。您可能需要使用semanage来更改文件的上下文。这是一个很好的网站简单教程
bansi

添加的另一种可能性:realpath缓存:lyte.id.au/2014/05/01/what-the-hell-php
chrishiestand

@chrishiestand非常感谢!那篇文章真的很有趣!您还记得导致该错误的事件发生的过程吗?最初是用户没有对文件的读取权限,然后才进行了更改,但是高速缓存仍然认为它不可读,因此在打开文件时抛出了该错误吗?
Vic Seedoubleyew

16

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

共享主机软件

open_basedir因为它可以在Web服务器配置中指定,所以它会让您感到困惑。如果您运行自己的专用服务器很容易解决,但是有一些共享的托管软件包(如Plesk,cPanel等)将在每个域的基础上配置配置指令。由于该软件会生成配置文件(即httpd.conf),因此您不能直接更改该文件,因为托管软件在重新启动时会覆盖它。

使用Plesk,他们提供了一个地方来覆盖所提供的httpd.conf所谓的vhost.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权限和用户的更多扩展讨论。


7
  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. 复制过去在浏览器中的文件位置,以确保文件存在。有时文件被意外删除(发生在我身上),这也是我的问题。

与下面的清单的步骤1有什么不同?
维克·塞维尤尤

第2步是一项附加检查,与第1步无关。只需在浏览器中导航到建议的路径,然后查看是否在该文件中看到了文件(在Windows资源管理器中没有,但是在浏览器中)。
哈马德·汗

2

添加带有查询参数的脚本

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


2

Samba股票

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

chmod -R 777 myfolder

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


1

另一个可能的原因:在文本编辑器中重命名和/或移动文件。我经历了上述所有步骤,但没有成功,直到删除了不断抛出该错误的文件并创建了一个新文件,该文件便解决了该问题。


1
如果我正确理解您的意思,那么列表中的第一笔检查将立即解决该问题
Vic Seedoubleyew

#1没有明确说明潜在原因
-zMeadz

是的,但是您不在乎原因,而是在寻找解决问题的方法。更重要的是,第1步将解决您的问题
Vic Seedoubleyew
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.