且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

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

更新时间:2022-06-15 23:13:14

有很多原因可能会导致此错误,因此良好的清单首先要检查的内容很有帮助.

There are many reasons why one might run into this error and thus a good checklist of what to check first helps considerably.

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

Let's consider that we are troubleshooting the following line:

require "/path/to/file"



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

  • either check manually (by visually checking the path)
  • or move whatever is called by require* or include* to its own variable, echo it, copy it, and try accessing it from a terminal:

$path = "/path/to/file";

echo "Path : $path";

require "$path";

然后,在终端中:

cat <file path pasted>


  • 如果它以正斜杠"/"开头,则它不是指网站文件夹的根目录(文档根目录),而是服务器的根目录.
    • 例如,您的网站目录可能是/users/tony/htdocs
    • if it is starting by a forward slash "/" then it is not referring to the root of your website's folder (the document root), but to the root of your server.
      • for example, your website's directory might be /users/tony/htdocs
      • 因此,与您网站的根目录或您输入的文件的路径无关
      • 因此,请始终使用绝对文件路径

      ***做法:

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

      In order to make your script robust in case you move things around, while still generating an absolute path at runtime, you have 2 options :

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

      1. use require __DIR__ . "/relative/path/from/current/file". The __DIR__ magic constant returns the directory of the current file.
      2. define a SITE_ROOT constant yourself :

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

      • at the root of your web site's directory, create a file, e.g. config.php
      • in config.php, write

      define('SITE_ROOT', __DIR__);
      

    • 在每个您要引用站点根文件夹的文件中,
    • 都包含config.php,然后在任何需要的地方使用SITE_ROOT常量:

    • in every file where you want to reference the site root folder, include config.php, and then use the SITE_ROOT constant wherever you like :

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

    • 这2种做法还使您的应用程序更加可移植,因为它不依赖于ini设置(例如包含路径).

      These 2 practices also make your application more portable because it does not rely on ini settings like the include path.


      包含文件的另一种方法(既不是相对也不是绝对的)是依靠

      Another way to include files, neither relatively nor purely absolutely, is to rely on the include path. This is often the case for libraries or frameworks such as the Zend framework.

      这样的包含将看起来像这样:

      Such an inclusion will look like this :

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

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

    In that case, you will want to make sure that the folder where "Zend" is, is part of the include path.

    您可以使用:

    echo get_include_path();
    

    您可以使用:

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


    运行服务器进程(Apache或PHP)的用户可能根本没有权限读取或写入该文件.

    It might be that all together, the user running the server process (Apache or PHP) simply doesn't have permission to read from or write to that file.

    要检查服务器在哪个用户下运行,可以使用 posix_getpwuid :

    To check under what user the server is running you can use posix_getpwuid :

    $user = posix_getpwuid(posix_geteuid());
    
    var_dump($user);
    

    要查找文件的权限,请在终端中键入以下命令:

    To find out the permissions on the file, type the following command in the terminal:

    ls -l <path/to/file>
    

    并查看权限符号表示法


    如果以上方法均无效,则可能是某些PHP设置禁止其访问该文件.

    If none of the above worked, then the issue is probably that some PHP settings forbid it to access that file.

    三个设置可能是相关的:

    Three settings could be relevant :

    1. open_basedir
      • 如果已设置,PHP将无法访问指定目录之外的任何文件(甚至不能通过符号链接访问).
      • 但是,在没有限制的情况下,默认行为是不设置
      • 可以通过调用 phpinfo() 进行检查使用ini_get("open_basedir")
      • 您可以通过编辑php.ini文件或httpd.conf文件来更改设置
    1. open_basedir
      • If this is set PHP won't be able to access any file outside of the specified directory (not even through a symbolic link).
      • However, the default behavior is for it not to be set in which case there is no restriction
      • This can be checked by either calling phpinfo() or by using ini_get("open_basedir")
      • You can change the setting either by editing your php.ini file or your httpd.conf file
    • 如果启用此功能,则可能会受到限制.但是,此功能已在PHP 5.4中删除.如果您仍使用支持安全模式的版本,请升级到仍受支持的PHP版本.
    • 这仅适用于通过诸如http://之类的网络过程来包含或打开文件,不适用于试图在本地文件系统中包括文件的情况
    • 可以使用ini_get("allow_url_include")进行检查,并使用ini_set("allow_url_include", "1")
    • 进行设置
    • this applies only to including or opening files through a network process such as http:// not when trying to include files on the local file system
    • this can be checked with ini_get("allow_url_include") and set with ini_set("allow_url_include", "1")


    如果以上方法均无法诊断问题,则可能会出现一些特殊情况:

    If none of the above enabled to diagnose the problem, here are some special situations that could happen :


    您可能会使用相对或绝对路径包含一个库,例如Zend框架.例如:

    It can happen that you include a library, for example, the Zend framework, using a relative or absolute path. For example :

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

    但是您仍然会遇到同样的错误.

    But then you still get the same kind of error.

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

    This could happen because the file that you have (successfully) included, has itself an include statement for another file, and that second include statement assumes that you have added the path of that library to the include path.

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

    For example, the Zend framework file mentioned before could have the following include :

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

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

    which is neither an inclusion by relative path, nor by absolute path. It is assuming that the Zend framework directory has been added to the include path.

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

    In such a case, the only practical solution is to add the directory to your include path.


    如果您运行的是增强安全性的Linux,则可能是该问题的原因,它是通过拒绝从服务器访问文件来实现的.

    If you are running Security-Enhanced Linux, then it might be the reason for the problem, by denying access to the file from the server.

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

    To check whether SELinux is enabled on your system, run the sestatus command in a terminal. If the command does not exist, then SELinux is not on your system. If it does exist, then it should tell you whether it is enforced or not.

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

    To check whether SELinux policies are the reason for the problem, you can try turning it off temporarily. However be CAREFUL, since this will disable protection entirely. Do not do this on your production server.

    setenforce 0
    

    如果您再也没有关闭SELinux的问题,那么这就是根本原因.

    If you no longer have the problem with SELinux turned off, then this is the root cause.

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

    To solve it, you will have to configure SELinux accordingly.

    以下上下文类型将是必需的:

    The following context types will be necessary :

    • httpd_sys_content_t表示您希望服务器能够读取的文件
    • httpd_sys_rw_content_t用于您要对其进行读写访问的文件
    • httpd_log_t用于日志文件
    • httpd_cache_t用于缓存目录
    • httpd_sys_content_t for files that you want your server to be able to read
    • httpd_sys_rw_content_t for files on which you want read and write access
    • httpd_log_t for log files
    • httpd_cache_t for the cache directory

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

    For example, to assign the httpd_sys_content_t context type to your website root directory, run :

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

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

    If your file is in a home directory, you will also need to turn on the httpd_enable_homedirs boolean :

    setsebool -P httpd_enable_homedirs 1
    

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

    In any case, there could be a variety of reasons why SELinux would deny access to a file, depending on your policies. So you will need to enquire into that. Here is a tutorial specifically on configuring SELinux for a web server.


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

    If you are using Symfony, and experiencing this error when uploading to a server, then it can be that the app's cache hasn't been reset, either because app/cache has been uploaded, or that cache hasn't been cleared.

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

    You can test and fix this by running the following console command:

    cache:clear
    


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

    Apparently, this error can happen also upon calling zip->close() when some files inside the zip have non-ASCII characters in their filename, such as "é".

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

    A potential solution is to wrap the file name in utf8_decode() before creating the target file.

    Fran Cano 表示感谢,以标识并提出解决此问题的方法

    Credits to Fran Cano for identifying and suggesting a solution to this issue