PHP - 无法打开流:没有这样的文件或目录清单转角箱共享主机软件文件权限

2022-08-30 06:20:05

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

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

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


答案 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__魔术常量返回当前文件的目录。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 设置。


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 语句假定您已将该库的路径添加到 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确定并提出了这个问题的解决方案


答案 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权限和用户的更深入的讨论


推荐