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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36577020/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 00:51:40  来源:igfitidea点击:

PHP - Failed to open stream : No such file or directory

phprequirefopeninclude-path

提问by Vic Seedoubleyew

In PHP scripts, whether calling include(), require(), fopen(), or their derivatives such as include_once, require_once, or even, move_uploaded_file(), one often runs into an error or warning:

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

Failed to open stream : No such file or directory.

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

What is a good process to quickly find the root cause of the problem?

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

回答by Vic Seedoubleyew

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"



Checklist

清单



1. Check the file path for typos

1.检查文件路径是否有拼写错误

  • 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";
    

    Then, in a terminal:

    cat <file path pasted>
    
  • 要么手动检查(通过目视检查路径)
  • 或移动任何被称为require*include*到其自己的变量,回声,复制它,并试图从终端访问它:

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

    然后,在终端中:

    cat <file path pasted>
    



2. Check that the file path is correct regarding relative vs absolute path considerations

2.检查文件路径是否正确关于相对与绝对路径的考虑

  • 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
  • if it is not starting by a forward slash then it is either relying on the include path (see below) or the path is relative. If it is relative, then PHP will calculate relatively to the path of the current working directory.
    • thus, not relative to the path of your web site's root, or to the file where you are typing
    • for that reason, always use absolute file paths
  • 如果它以正斜杠“/”开头,那么它不是指您网站文件夹的根目录(文档根目录),而是指您服务器的根目录。
    • 例如,您网站的目录可能是 /users/tony/htdocs
  • 如果它不是以正斜杠开头,那么它要么依赖于包含路径(见下文),要么路径是相对的。如果是相对的,那么PHP会相对于当前工作目录的路径进行计算。
    • 因此,与您网站根目录的路径或您键入的文件无关
    • 因此,始终使用绝对文件路径

Best practices :

最佳实践:

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 :

为了使您的脚本在您四处移动时保持稳健,同时在运行时仍生成绝对路径,您有 2 个选项:

  1. use require __DIR__ . "/relative/path/from/current/file". The __DIR__magic constantreturns the directory of the current file.
  2. define a SITE_ROOTconstant yourself :

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

      define('SITE_ROOT', __DIR__);
      
    • in every file where you want to reference the site root folder, include config.php, and then use the SITE_ROOTconstant wherever you like :

      require_once __DIR__."/../config.php";
      ...
      require_once SITE_ROOT."/other/file.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";
      

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

这 2 种做法还使您的应用程序更具可移植性,因为它不依赖于包含路径之类的 ini 设置。



3. Check your include path

3. 检查你的包含路径

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.

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

Such an inclusion will look like this :

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

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

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

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

You can check the include path with :

您可以使用以下命令检查包含路径:

echo get_include_path();

You can add a folder to it with :

您可以使用以下命令向其中添加文件夹:

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



4. Check that your server has access to that file

4. 检查您的服务器是否有权访问该文件

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.

总的来说,运行服务器进程(Apache 或 PHP)的用户根本没有读取或写入该文件的权限。

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

要检查服务器正在运行的用户,您可以使用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>

and look at permission symbolic notation

并查看许可符号



5. Check PHP settings

5.检查PHP设置

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

如果以上都不起作用,那么问题可能是某些 PHP 设置禁止它访问该文件。

Three settings could be relevant :

三个设置可能相关:

  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
  2. safe mode
    • if this is turned on restrictions might apply. However, this has been removed in PHP 5.4. If you are still on a version that supports safe mode upgrade to a PHP version that is still being supported.
  3. allow_url_fopen and allow_url_include
    • 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")
  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")



Corner cases

角箱

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

如果以上都无法诊断问题,以下是可能发生的一些特殊情况:



1. The inclusion of library relying on the include path

1.依赖包含路径的包含库

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

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

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

But then you still get the same kind of error.

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

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.

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

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

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

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

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.

这既不是相对路径的包含,也不是绝对路径的包含。假设 Zend 框架目录已添加到包含路径中。

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

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



2. SELinux

2.SELinux

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.

如果您运行的是 Security-Enhanced Linux,则可能是由于拒绝从服务器访问文件而导致问题的原因。

To check whether SELinux is enabledon your system, run the sestatuscommand 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,请sestatus在终端中运行该命令。如果该命令不存在,则 SELinux 不在您的系统上。如果它确实存在,那么它应该告诉您它是否被强制执行。

To check whether SELinux policies are the reasonfor 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.

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

setenforce 0

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.

要解决它,您必须相应地配置 SELinux。

The following context types will be necessary :

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

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

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

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

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

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

如果您的文件位于主目录中,您还需要打开httpd_enable_homedirsboolean :

setsebool -P httpd_enable_homedirs 1

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. Hereis a tutorial specifically on configuring SELinux for a web server.

在任何情况下,SELinux 拒绝访问文件的原因可能有多种,具体取决于您的策略。所以你需要对此进行调查。是一个专门为 Web 服务器配置 SELinux 的教程。



3. Symfony

3. Symfony

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/cachehas been uploaded, or that cache hasn't been cleared.

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

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

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

cache:clear



4. Non ACSII characters inside Zip file

4. Zip 文件中的非 ACSII 字符

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 "é".

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

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

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

Credits to Fran Canofor identifying and suggesting a solution to this issue

感谢Fran Cano确定并提出了解决此问题的方法

回答by Machavity

To add to the (really good) existing answer

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

Shared Hosting Software

共享主机软件

open_basediris one that can stump you because it can be specified in a web server configuration. While this is easily remedied if you run your own dedicated server, there are some shared hosting software packages out there (like Plesk, cPanel, etc) that will configure a configuration directive on a per-domain basis. Because the software builds the configuration file (i.e. httpd.conf) you cannot change that file directly because the hosting software will just overwrite it when it restarts.

open_basedir是一个可以让您难堪的方法,因为它可以在 Web 服务器配置中指定。虽然如果您运行自己的专用服务器,这很容易解决,但有一些共享主机软件包(如 Plesk、cPanel 等)将在每个域的基础上配置配置指令。因为该软件构建了配置文件(即httpd.conf),您不能直接更改该文件,因为托管软件会在重新启动时覆盖它。

With Plesk, they provide a place to override the provided httpd.confcalled vhost.conf. Only the server admin can write this file. The configuration for Apache looks something like this

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

Have your server admin consult the manual for the hosting and web server software they use.

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

File Permissions

文件权限

It's important to note that executing a file through your web server is very different from a command line or cron job execution. The big difference is that your web server has its own user and permissions. For security reasons that user is pretty restricted. Apache, for instance, is often apache, www-dataor httpd(depending on your server). A cron job or CLI execution has whatever permissions that the user running it has (i.e. running a PHP script as root will execute with permissions of root).

需要注意的是,通过 Web 服务器执行文件与命令行或 cron 作业执行非常不同。最大的区别在于您的 Web 服务器有自己的用户和权限。出于安全原因,该用户受到很大限制。例如,Apache 通常是apache,www-datahttpd(取决于您的服务器)。cron 作业或 CLI 执行具有运行它的用户所拥有的任何权限(即以 root 身份运行 PHP 脚本将以 root 权限执行)。

A lot of times people will solve a permissions problem by doing the following (Linux example)

很多时候人们会通过执行以下操作来解决权限问题(Linux 示例)

chmod 777 /path/to/file

This is not a smart idea, because the file or directory is now world writable. If you own the server and are the only user then this isn't such a big deal, but if you're on a shared hosting environment you've just given everyone on your server access.

这不是一个聪明的主意,因为文件或目录现在是全局可写的。如果您拥有服务器并且是唯一的用户,那么这没什么大不了的,但是如果您在共享托管环境中,则您刚刚为服务器上的每个人提供了访问权限。

What you need to do is determine the user(s) that need access and give only those them access. Once you know which users need access you'll want to make sure that

您需要做的是确定需要访问权限的用户并仅授予他们访问权限。一旦您知道哪些用户需要访问权限,您就需要确保

  1. That user owns the file and possibly the parent directory(especially the parent directory if you want to write files). In most shared hosting environments this won't be an issue, because your user should own all the files underneath your root. A Linux example is shown below

    chown apache:apache /path/to/file
    
  2. The user, and only that user, has access. In Linux, a good practice would be chmod 600(only owner can read and write) or chmod 644(owner can write but everyone can read)

  1. 该用户拥有该文件,可能还拥有父目录(如果要写入文件,尤其是父目录)。在大多数共享托管环境中,这不会成为问题,因为您的用户应该拥有根目录下的所有文件。一个 Linux 示例如下所示

    chown apache:apache /path/to/file
    
  2. 用户,并且只有该用户,才能访问。在 Linux 中,一个好的做法是chmod 600(只有所有者可以读写)或chmod 644(所有者可以写但每个人都可以读)

You can read a more extended discussion of Linux/Unix permissions and users here

你可以在这里阅读更多关于 Linux/Unix 权限和用户的讨论

回答by Hammad Khan

  1. Look at the exacterror
  1. 看看具体的错误

My code worked fine on all machines but only on this one started giving problem (which used to work find I guess). Used echo "document_root" path to debug and also looked closely at the error, found this

我的代码在所有机器上都运行良好,但只有在这台机器上才开始出现问题(我猜这曾经可以正常工作)。使用 echo "document_root" 路径调试并仔细查看错误,发现了这个

Warning: include(D:/MyProjects/testproject//functions/connections.php): failed to open stream:

警告:包括(D:/MyProjects/testproject//functions/connections.php):无法打开流:

You can easily see where the problems are. The problems are // before functions

您可以轻松查看问题所在。问题是 // 在函数之前

$document_root = $_SERVER['DOCUMENT_ROOT'];
echo "root: $document_root";
include($document_root.'/functions/connections.php');

So simply remove the lading / from include and it should work fine. What is interesting is this behaviors is different on different versions. I run the same code on Laptop, Macbook Pro and this PC, all worked fine untill. Hope this helps someone.

所以只需从包含中删除提货 / ,它应该可以正常工作。有趣的是这种行为在不同版本上是不同的。我在笔记本电脑、Macbook Pro 和这台 PC 上运行相同的代码,直到一切正常。希望这可以帮助某人。

  1. Copy past the file location in the browser to make sure file exists. Sometimes files get deleted unexpectedly (happened with me) and it was also the issue in my case.
  1. 复制浏览器中的文件位置以确保文件存在。有时文件会被意外删除(发生在我身上),这也是我的问题。

回答by Stephan Brunker

Samba Shares

桑巴股份

If you have a Linux test server and you work from a Windows Client, the Samba share interferes with the chmodcommand. So, even if you use:

如果您有一台 Linux 测试服务器并且您在 Windows 客户端上工作,则 Samba 共享会干扰chmod命令。所以,即使你使用:

chmod -R 777 myfolder

on the Linux side it is fully possible that the Unix Group\www-data still doesn't have write access. One working solution if your share is set up that Windows admins are mapped to root: From Windows, open the Permissions, disable Inheritance for your folder with copy, and then grant full access for www-data.

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

回答by Paul Lynn

Add script with query parameters

添加带有查询参数的脚本

That was my case. It actually links to question #4485874, but I'm going to explain it here shortly.
When you try to require path/to/script.php?parameter=value, PHP looks for file named script.php?parameter=value, because UNIX allows you to have paths like this.
If you are really need to pass some data to included script, just declare it as $variable=...or $GLOBALS[]=...or other way you like.

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

回答by zMeadz

Another possible cause: Renaming and/or moving files while in a text editor. I went through all the steps above without success until I deleted the file that kept throwing this error and created a new one, which fixed the issue.

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