在PHP脚本中,无论是调用include(), require(), fopen(),还是它们的衍生物,如include_once, require_once,甚至move_uploaded_file(),都经常会遇到错误或警告:
无法打开流:没有这样的文件或目录。
什么是快速找到问题根源的良好流程?
在PHP脚本中,无论是调用include(), require(), fopen(),还是它们的衍生物,如include_once, require_once,甚至move_uploaded_file(),都经常会遇到错误或警告:
无法打开流:没有这样的文件或目录。
什么是快速找到问题根源的良好流程?
有很多原因可能会导致这种错误,因此一个好的检查清单可以帮助很大。
让我们考虑一下,我们正在对以下一行进行故障排除:
require "/path/to/file"
检查表
1. 检查文件路径是否有错别字
手动检查(通过视觉检查路径) 或者将require*或include*调用的任何东西移动到它自己的变量中,回显它,复制它,并尝试从终端访问它: $path = "/path/to/file"; echo“Path: $ Path”; 美元需要“路径”; 然后,在终端中: Cat <文件路径粘贴>
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
最佳实践:
为了让你的脚本健壮,以防你移动东西,同时仍然在运行时生成一个绝对路径,你有两个选择:
use require __DIR__ . "/relative/path/from/current/file". The __DIR__ magic constant returns the directory of the current file. define a SITE_ROOT constant 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_ROOT constant wherever you like : 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_getpwid:
$user = posix_getpwuid(posix_geteuid());
var_dump($user);
要找出文件的权限,在终端输入以下命令:
ls -l <path/to/file>
看看许可符号表示法
5. 检查PHP设置
如果以上方法都不起作用,那么问题可能是某些PHP设置禁止它访问该文件。
有三个设置可能是相关的:
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 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. 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. 库的包含依赖于include路径
您可能会使用相对路径或绝对路径包含一个库,例如Zend框架。例如:
require "/usr/share/php/libzend-framework-php/Zend/Mail/Protocol/Imap.php"
但你还是会得到同样的误差。
之所以会发生这种情况,是因为您已经(成功)包含的文件本身有一个用于另一个文件的include语句,而第二个include语句假定您已经将该库的路径添加到include路径中。
例如,前面提到的Zend框架文件可以包含以下内容:
include "Zend/Mail/Protocol/Exception.php"
既不是相对路径,也不是绝对路径。这里假设Zend框架目录已经添加到include路径。
在这种情况下,唯一可行的解决方案是将目录添加到include路径中。
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_homedirs布尔值:
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()包装文件名。
感谢弗兰·卡诺确定并提出了解决这个问题的方案
为现有的(非常好的)答案补充
共享托管软件
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服务器软件的手册。
文件权限
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-data or 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).
很多时候人们会通过以下方法来解决权限问题(以Linux为例)
chmod 777 /path/to/file
这不是一个明智的想法,因为文件或目录现在是全局可写的。如果您拥有服务器,并且是唯一的用户,那么这不是什么大问题,但如果您在共享托管环境中,您只是给了服务器上的每个人访问权。
您需要做的是确定需要访问权限的用户,并只给他们访问权限。一旦你知道哪些用户需要访问,你就会想要确保这一点
该用户拥有文件,可能还拥有父目录(如果您想写入文件,则尤其拥有父目录)。在大多数共享主机环境中,这不是问题,因为您的用户应该拥有根目录下的所有文件。下面显示了一个Linux示例 Chown apache:apache /path/to/file 用户,且只有该用户具有访问权限。在Linux中,一个好的做法是chmod 600(只有所有者可以读写)或chmod 644(所有者可以写,但每个人都可以读)
你可以在这里阅读更多关于Linux/Unix权限和用户的讨论
添加带有查询参数的脚本
那是我的案子。它实际上链接到问题#4485874,但我将在这里简短地解释它。 当你试图要求路径/到/script.php?参数=值,PHP查找名为script.php的文件?parameter=value,因为UNIX允许您使用这样的路径。 如果你真的需要传递一些数据到包含的脚本,只需声明它为$variable=…或全局美元[]=…或者其他你喜欢的方式。
看看准确的误差
我的代码在所有机器上都工作得很好,但只有在这一台机器上开始出现问题(我猜过去是这样的)。使用echo“document_root”路径进行调试,并仔细查看了错误,发现了这个
警告: 包括(D: / MyProjects / testproject / /功能/ connections.php): 打开流失败:
你很容易就能看出问题在哪里。问题在函数之前
$document_root = $_SERVER['DOCUMENT_ROOT'];
echo "root: $document_root";
include($document_root.'/functions/connections.php');
因此,只需从include中删除提单/,它就可以正常工作。有趣的是这种行为在不同的版本上是不同的。我在笔记本电脑,Macbook Pro和这台PC上运行相同的代码,都工作得很好,直到。希望这能帮助到一些人。
复制过去浏览器中的文件位置,以确保文件存在。有时文件会被意外删除(发生在我身上),这也是我的情况。
Samba股票
如果您有一个Linux测试服务器,并且从Windows客户端工作,那么Samba共享会干扰chmod命令。所以,即使你使用:
chmod -R 777 myfolder
在Linux方面,Unix组\www-data仍然没有写权限是完全可能的。如果您的共享设置为Windows管理员映射到根目录,一个可行的解决方案是:在Windows中,打开权限,禁用带有副本的文件夹的继承,然后授予www-data的完全访问权限。
下面的PHP设置在PHP .ini中如果设置为不存在的目录也会引发
PHP警告:未知:未能打开流:权限被拒绝 第0行未知
sys_temp_dir
upload_tmp_dir
session.save_path
对我来说,我得到了这个错误,因为我试图读取一个需要HTTP认证的文件,有用户名和密码。希望能帮助别人。可能是另一个极端情况。
Edit
你可以通过检查头文件来检查是否存在这种类型的身份验证:
$file_headers = get_headers($url);
if (!$file_headers) echo 'File headers missing';
else if (strpos($file_headers[0], '401 Unauthorized') > -1) echo '401 Unauthorized';
无法打开流:在mac中没有这样的文件或目录
例如,我会上传一张图片。但是我得到了这个错误。第一件事,我会做右键点击图像和获取信息。
$ thepathofmpicture = "/Users/misstugba/Desktop/"; 配合功能使用
if(move_uploaded_file($_FILES["file"]["tmp_name"],$thePathOfMyPicture.$_FILES["file"]["name"])){
echo "image uploaded successfully";
}
除了其他精彩的回答,在Windows上写一个简单的脚本时,我忽略了一件事:当试图打开一个带有Windows不支持的文件名字符的文件时,会出现这个错误。
例如:
$file = fopen(date('Y-m-d_H:i:s'), 'w+');
将:
fopen(2022-06-01_22:53:03): Failed to open stream: No such file or directory in…
Windows不喜欢:在文件名中,以及其他一些字符。