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

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

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

快速find问题的根源是什么是一个好的过程?

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

让我们考虑一下,我们正在解决以下行:

 require "/path/to/file" 

清单

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

  • 要么手动检查(通过目视检查path)
  • 或者将require*include*所调用的任何东西移动到自己的variables中,然后将其复制并复制,然后尝试从终​​端访问:

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

    然后,在一个terminal:

     cat <file path pasted> 

2.检查相对于绝对path考虑的文件path是否正确

  • 如果以正斜杠“/”开始,那么它不是指您的网站文件夹(文档根目录)的根目录,而是指向服务器的根目录。
    • 例如,您网站的目录可能是/users/tony/htdocs
  • 如果不是以正斜杠开始,那么它要么依赖于包含path(参见下文),要么path是相对的。 如果它是相对的,那么PHP将相对于当前工作目录的path进行计算。
    • 因此,不相对于您的网站的根,或您正在input的文件的path
    • 出于这个原因,总是使用绝对文件path

最佳做法:

为了使脚本在你移动的时候保持健壮,同时在运行时仍然生成一个绝对path,你有两个select:

  1. 使用require __DIR__ . "/relative/path/from/current/file" require __DIR__ . "/relative/path/from/current/file"__DIR__魔术常数返回当前文件的目录。
  2. 自己定义一个SITE_ROOT常量:

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

       define('SITE_ROOT', __DIR__); 
    • 在每个要引用站点根文件夹的文件中,包含config.php ,然后使用SITE_ROOT常量(无论你喜欢什么):

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

这两种做法也使您的应用程序更具可移植性,因为它不依赖于包含path等ini设置。

3.检查你的包含path

包含文件的另一种方式,既不相对也不纯粹绝对地依赖于包含path 。 对于像Zend框架这样的库或框架来说,情况经常是这样。

这样的包容将如下所示:

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

在这种情况下,您需要确保“Zend”所在的文件夹是包含path的一部分。

您可以通过以下方式检查包含path:

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

要找出文件的权限,请在terminal中input以下命令:

 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
    • 这仅适用于通过networking进程(如http://)包含或打开文件,而不是在尝试在本地文件系统中包含文件时
    • 这可以用ini_get("allow_url_include")来检查,并用ini_set("allow_url_include", "1")

angular落案件

如果以上都无法诊断问题,则可能会出现以下特殊情况:

包括依靠包含path的图书馆

可能会发生这样的情况,即包含一个库,例如Zend框架,使用相对path或绝对path。 例如 :

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

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

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

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

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

既不是相对path,也不是绝对path。 假设Zend框架目录已被添加到包含path中。

在这种情况下,唯一可行的解​​决scheme是将目录添加到包含path中。

2. SELinux

如果您正在运行安全增强型Linux,则可能是由于拒绝从服务器访问文件而导致问题的原因。

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

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

 setenforce 0 

如果您不再有SELinuxclosures的问题,那么这是根本原因。

要解决这个问题 ,你必须相应地configurationSELinux。

以下上下文types将是必要的:

  • httpd_sys_content_t用于希望服务器能够读取的文件
  • httpd_sys_rw_content_t用于要读取和写入访问权限的文件
  • httpd_log_t用于日志文件
  • httpd_log_t用于caching目录

例如,要将httpd_sys_content_t上下文types分配给您的网站根目录,请运行:

 semanage fcontext -a -t http_sys_content_t "/path/to/root(/.*)?" restorecon -Rv /path/to/root 

如果您的文件位于主目录中,则还需要打开httpd_enable_homedirs布尔值:

 setsebool -P httpd_enable_homedirs 1 

无论如何,根据您的政策,SELinux可能会有多种原因拒绝访问文件。 所以你需要查询一下。 这里是一个关于configurationSELinux的Web服务器的教程。

Symfony

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

您可以通过运行以下控制台命令来testing和解决此问题:

 cache:clear 

添加到(真的很好)现有的答案

共享主机软件

open_basedir是一个可以阻止你,因为它可以在Web服务器configuration中指定。 如果你运行你自己的专用服务器,这很容易解决,但是有一些共享的托pipe软件包(比如Plesk,cPanel等),它们将在每个域上configuration一个configuration指令。 因为软件build立了configuration文件(即httpd.conf ),所以不能直接更改该文件,因为托pipe软件在重新启动时会覆盖它。

使用Plesk,他们提供了一个地方来覆盖提供的名为vhost.conf httpd.conf 。 只有服务器pipe理员可以写这个文件。 Apache的configuration看起来像这样

 <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> 

让您的服务器pipe理员查阅他们使用的托pipe和networking服务器软件的手册。

文件权限

请注意,通过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权限和用户的讨论