在PHP脚本中,无论是调用 include() , require() , fopen() 还是其衍 生产环境 品(如 include_once , require_once ,甚至是 move_uploaded_file() ),都会遇到错误或警告:
include()
require()
fopen()
include_once
require_once
move_uploaded_file()
无法打开流:没有这样的文件或目录 .
快速找到问题根本原因的好过程是什么?
有很多原因导致人们可能遇到这个错误,因此首先检查的检查清单有很大帮助 .
让我们考虑一下我们正在对以下行进行故障排除:
require "/path/to/file"
手动检查(通过目视检查路径)
或将 require* 或 include* 调用的任何内容移动到它自己的变量,回显它,复制它,并尝试从终端访问它:
require*
include*
$path = "/path/to/file"; echo "Path : $path"; require "$path";
然后,在终端:
cat <file path pasted>
如果以正斜杠"/"开头,那么它不是指网站文件夹的根目录(文档根目录),而是指服务器的根目录 .
例如,您网站的目录可能是 /users/tony/htdocs
/users/tony/htdocs
如果它不是以正斜杠开头那么它依赖于包含路径(见下文)或路径是相对的 . 如果它是相对的,那么PHP将相对于current working directory的路径进行计算 .
因此,不是相对于网站根目录的路径,也不是相对于您键入的文件
因此,始终使用绝对文件路径
最佳做法:
为了使您的脚本在您移动时仍然健壮,同时仍然在运行时生成绝对路径,您有两个选项:
使用 require __DIR__ . "/relative/path/from/current/file" . DIR magic constant返回当前文件的目录 .
require __DIR__ . "/relative/path/from/current/file"
自己定义 SITE_ROOT 常量:
SITE_ROOT
在您网站目录的根目录下,创建一个文件,例如 config.php
config.php
在 config.php ,写
define('SITE_ROOT', __DIR__);
require_once __DIR__."/../config.php"; ... require_once SITE_ROOT."/other/file.php";
这两种做法还使您的应用程序更具可移植性,因为它不依赖于包含路径等ini设置 .
包含文件的另一种方式,无论是相对还是纯粹绝对,都依赖于include path . 对于诸如Zend框架之类的库或框架,通常就是这种情况 .
这样的包含将如下所示:
include "Zend/Mail/Protocol/Imap.php"
在这种情况下,您需要确保“Zend”所在的文件夹是包含路径的一部分 .
您可以使用以下命令检查包含路径:
echo get_include_path();
您可以使用以下命令添加文件夹:
set_include_path(get_include_path().":"."/path/to/new/folder");
可能是一起运行服务器进程(Apache或PHP)的用户根本没有读取或写入该文件的权限 .
要检查服务器运行的用户,可以使用posix_getpwuid:
$user = posix_getpwuid(posix_geteuid()); var_dump($user);
要查找文件的权限,请在终端中键入以下命令:
ls -l <path/to/file>
看看permission symbolic notation
如果以上都不起作用,那么问题可能是某些PHP设置禁止它访问该文件 .
三个设置可能是相关的:
open_basedir
如果设置了此项,PHP将无法访问指定目录之外的任何文件(甚至不能通过符号链接) .
但是,默认行为是不设置它,在这种情况下没有限制
可以通过调用phpinfo()或使用 ini_get("open_basedir") 来检查
ini_get("open_basedir")
您可以通过编辑php.ini文件或httpd.conf文件来更改设置
safe mode
如果启用此限制可能适用 . 但是,这已在PHP 5.4中删除 . 如果您仍然使用支持安全模式升级到still being supported的PHP版本的版本 .
allow_url_fopen and allow_url_include
这仅适用于在尝试在本地文件系统上包含文件时通过网络进程(例如http://)包含或打开文件
可以使用 ini_get("allow_url_include") 检查并使用 ini_set("allow_url_include", "1") 进行设置
ini_get("allow_url_include")
ini_set("allow_url_include", "1")
如果上述任何一个都无法诊断问题,则可能会发生以下特殊情况:
您可以使用相对路径或绝对路径包含库,例如Zend框架 . 例如 :
require "/usr/share/php/libzend-framework-php/Zend/Mail/Protocol/Imap.php"
但是你仍然会遇到同样的错误 .
这可能是因为您拥有(成功)包含的文件本身就是另一个文件的include语句,而第二个include语句假定您已将该库的路径添加到包含路径 .
例如,之前提到的Zend框架文件可能包含以下内容:
include "Zend/Mail/Protocol/Exception.php"
这既不是通过相对路径包含,也不通过绝对路径包含 . 假设Zend框架目录已添加到包含路径 .
在这种情况下,唯一可行的解决方案是将目录添加到包含路径 .
如果您正在运行Security-Enhanced Linux,则可能是因为拒绝从服务器访问该文件而导致问题的原因 .
在系统上 To check whether SELinux is enabled ,在终端中运行 sestatus 命令 . 如果该命令不存在,则SELinux不在您的系统上 . 如果它确实存在,那么它应该告诉你它是否被强制执行 .
sestatus
To check whether SELinux policies are the reason 对于此问题,您可以尝试暂时关闭它 . 但要小心,因为这将完全禁用保护 . 不要在 生产环境 服务器上执行此操作 .
setenforce 0
如果你不再关闭SELinux的问题,那么这就是根本原因 .
To solve it ,您必须相应地配置SELinux .
以下上下文类型是必要的:
httpd_sys_content_t 用于您希望服务器能够读取的文件
httpd_sys_content_t
httpd_sys_rw_content_t 用于您想要读写访问权限的文件
httpd_sys_rw_content_t
httpd_log_t 用于日志文件
httpd_log_t
httpd_cache_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 布尔值:
httpd_enable_homedirs
setsebool -P httpd_enable_homedirs 1
在任何情况下,SELinux拒绝访问文件的原因可能有多种,具体取决于您的策略 . 所以你需要调查一下 . Here是专门为Web服务器配置SELinux的教程 .
如果您正在使用Symfony,并在上传到服务器时遇到此错误,则可能是因为 app/cache 已上载或尚未清除缓存而重置了应用程序's cache hasn' .
app/cache
您可以通过运行以下控制台命令来测试并修复此问题:
cache:clear
显然,当zip中的某些文件的文件名中包含非ASCII字符(例如"é")时,也会在调用 zip->close() 时发生此错误 .
zip->close()
可能的解决方案是在创建目标文件之前将文件名包装在 utf8_decode() 中 .
utf8_decode()
致Fran Cano用于识别和建议此问题的解决方案
添加(非常好)现有答案
open_basedir 是可以阻止您的,因为它可以在Web服务器配置中指定 . 虽然如果您运行自己的专用服务器,这很容易解决,但有一些共享主机软件包(如Plesk,cPanel等)将基于每个域配置配置指令 . 由于软件构建配置文件(即 httpd.conf ),因此您无法直接更改该文件,因为托管软件只会在重新启动时覆盖它 .
httpd.conf
使用Plesk,它们提供了一个覆盖提供的 httpd.conf 的地方,称为 vhost.conf . 只有服务器管理员才能写入此文件 . Apache的配置看起来像这样
vhost.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通常是 apache , www-data 或 httpd (取决于您的服务器) . cron作业或CLI执行具有运行它的用户具有的任何权限(即,以root身份运行PHP脚本将以root权限执行) .
apache
www-data
httpd
很多时候人们会通过执行以下操作来解决权限问题(Linux示例)
chmod 777 /path/to/file
这不是一个明智的想法,因为文件或目录现在是世界可写的 . 如果您拥有服务器并且是唯一的用户,那么这不是什么大问题,但是如果您在共享托管环境中,那么您刚刚在服务器上访问了所有人 .
您需要做的是确定需要访问的用户,并仅授予他们访问权限 . 一旦您知道哪些用户需要访问权限,您就可以确保这一点
chown apache:apache /path/to/file
chmod 600
chmod 644
你可以read a more extended discussion of Linux/Unix permissions and users here
那是我的情况 . 它实际上链接到question #4485874,但我很快就会解释它 .当您尝试要求 path/to/script.php?parameter=value 时,PHP会查找名为 script.php?parameter=value 的文件,因为UNIX允许您使用像这样的路径 .如果您确实需要将一些数据传递给包含的脚本,只需将其声明为 $variable=... 或 $GLOBALS[]=... 或您喜欢的其他方式 .
path/to/script.php?parameter=value
script.php?parameter=value
$variable=...
$GLOBALS[]=...
另一个可能的原因:在文本编辑器中重命名和/或移动文件 . 我完成了上述所有步骤,但没有成功,直到我删除了不断抛出此错误并创建了新错误的文件,从而解决了问题 .
我的代码在所有机器上运行良好,但只有这一个开始给出问题(过去常常找到工作) . 使用echo“document_root”路径进行调试并仔细查看错误,发现了这一点
警告:include(D:/MyProjects/testproject//functions/connections.php):无法打开流:
您可以轻松查看问题所在 . 问题是//在功能之前
$document_root = $_SERVER['DOCUMENT_ROOT']; echo "root: $document_root"; include($document_root.'/functions/connections.php');
因此,只需从包含中删除提货/它就可以正常工作 . 有趣的是,这种行为在不同版本上有所不同 . 我在笔记本电脑,Macbook Pro和这台电脑上运行相同的代码,一切工作都很顺利 . 希望这有助于某人 .
5 回答
有很多原因导致人们可能遇到这个错误,因此首先检查的检查清单有很大帮助 .
让我们考虑一下我们正在对以下行进行故障排除:
清单
1.检查拼写错误的文件路径
手动检查(通过目视检查路径)
或将
require*
或include*
调用的任何内容移动到它自己的变量,回显它,复制它,并尝试从终端访问它:然后,在终端:
2.检查相对于绝对路径注意事项的文件路径是否正确
如果以正斜杠"/"开头,那么它不是指网站文件夹的根目录(文档根目录),而是指服务器的根目录 .
例如,您网站的目录可能是
/users/tony/htdocs
如果它不是以正斜杠开头那么它依赖于包含路径(见下文)或路径是相对的 . 如果它是相对的,那么PHP将相对于current working directory的路径进行计算 .
因此,不是相对于网站根目录的路径,也不是相对于您键入的文件
因此,始终使用绝对文件路径
最佳做法:
为了使您的脚本在您移动时仍然健壮,同时仍然在运行时生成绝对路径,您有两个选项:
使用
require __DIR__ . "/relative/path/from/current/file"
. DIR magic constant返回当前文件的目录 .自己定义
SITE_ROOT
常量:在您网站目录的根目录下,创建一个文件,例如
config.php
在
config.php
,写config.php
,然后在任何您喜欢的地方使用SITE_ROOT
常量:这两种做法还使您的应用程序更具可移植性,因为它不依赖于包含路径等ini设置 .
3.检查您的包含路径
包含文件的另一种方式,无论是相对还是纯粹绝对,都依赖于include path . 对于诸如Zend框架之类的库或框架,通常就是这种情况 .
这样的包含将如下所示:
在这种情况下,您需要确保“Zend”所在的文件夹是包含路径的一部分 .
您可以使用以下命令检查包含路径:
您可以使用以下命令添加文件夹:
4.检查您的服务器是否有权访问该文件
可能是一起运行服务器进程(Apache或PHP)的用户根本没有读取或写入该文件的权限 .
要检查服务器运行的用户,可以使用posix_getpwuid:
要查找文件的权限,请在终端中键入以下命令:
看看permission symbolic notation
5.检查PHP设置
如果以上都不起作用,那么问题可能是某些PHP设置禁止它访问该文件 .
三个设置可能是相关的:
open_basedir
如果设置了此项,PHP将无法访问指定目录之外的任何文件(甚至不能通过符号链接) .
但是,默认行为是不设置它,在这种情况下没有限制
可以通过调用phpinfo()或使用
ini_get("open_basedir")
来检查您可以通过编辑php.ini文件或httpd.conf文件来更改设置
safe mode
如果启用此限制可能适用 . 但是,这已在PHP 5.4中删除 . 如果您仍然使用支持安全模式升级到still being supported的PHP版本的版本 .
allow_url_fopen and allow_url_include
这仅适用于在尝试在本地文件系统上包含文件时通过网络进程(例如http://)包含或打开文件
可以使用
ini_get("allow_url_include")
检查并使用ini_set("allow_url_include", "1")
进行设置转角案件
如果上述任何一个都无法诊断问题,则可能会发生以下特殊情况:
1.包含依赖于包含路径的库
您可以使用相对路径或绝对路径包含库,例如Zend框架 . 例如 :
但是你仍然会遇到同样的错误 .
这可能是因为您拥有(成功)包含的文件本身就是另一个文件的include语句,而第二个include语句假定您已将该库的路径添加到包含路径 .
例如,之前提到的Zend框架文件可能包含以下内容:
这既不是通过相对路径包含,也不通过绝对路径包含 . 假设Zend框架目录已添加到包含路径 .
在这种情况下,唯一可行的解决方案是将目录添加到包含路径 .
2. SELinux
如果您正在运行Security-Enhanced Linux,则可能是因为拒绝从服务器访问该文件而导致问题的原因 .
在系统上 To check whether SELinux is enabled ,在终端中运行
sestatus
命令 . 如果该命令不存在,则SELinux不在您的系统上 . 如果它确实存在,那么它应该告诉你它是否被强制执行 .To check whether SELinux policies are the reason 对于此问题,您可以尝试暂时关闭它 . 但要小心,因为这将完全禁用保护 . 不要在 生产环境 服务器上执行此操作 .
如果你不再关闭SELinux的问题,那么这就是根本原因 .
To solve it ,您必须相应地配置SELinux .
以下上下文类型是必要的:
httpd_sys_content_t
用于您希望服务器能够读取的文件httpd_sys_rw_content_t
用于您想要读写访问权限的文件httpd_log_t
用于日志文件httpd_cache_t
用于缓存目录例如,要将
httpd_sys_content_t
上下文类型分配给您的网站根目录,请运行:如果您的文件位于主目录中,则还需要打开
httpd_enable_homedirs
布尔值:在任何情况下,SELinux拒绝访问文件的原因可能有多种,具体取决于您的策略 . 所以你需要调查一下 . Here是专门为Web服务器配置SELinux的教程 .
3. Symfony
如果您正在使用Symfony,并在上传到服务器时遇到此错误,则可能是因为
app/cache
已上载或尚未清除缓存而重置了应用程序's cache hasn' .您可以通过运行以下控制台命令来测试并修复此问题:
4. Zip文件中的非ACSII字符
显然,当zip中的某些文件的文件名中包含非ASCII字符(例如"é")时,也会在调用
zip->close()
时发生此错误 .可能的解决方案是在创建目标文件之前将文件名包装在
utf8_decode()
中 .致Fran Cano用于识别和建议此问题的解决方案
添加(非常好)现有答案
共享主机软件
open_basedir
是可以阻止您的,因为它可以在Web服务器配置中指定 . 虽然如果您运行自己的专用服务器,这很容易解决,但有一些共享主机软件包(如Plesk,cPanel等)将基于每个域配置配置指令 . 由于软件构建配置文件(即httpd.conf
),因此您无法直接更改该文件,因为托管软件只会在重新启动时覆盖它 .使用Plesk,它们提供了一个覆盖提供的
httpd.conf
的地方,称为vhost.conf
. 只有服务器管理员才能写入此文件 . Apache的配置看起来像这样让您的服务器管理员查阅他们使用的托管和Web服务器软件的手册 .
文件权限
请注意,通过Web服务器执行文件与命令行或cron作业执行非常不同,这一点很重要 . 最大的区别是您的Web服务器拥有自己的用户和权限 . 出于安全原因,用户非常受限制 . 例如,Apache通常是
apache
,www-data
或httpd
(取决于您的服务器) . cron作业或CLI执行具有运行它的用户具有的任何权限(即,以root身份运行PHP脚本将以root权限执行) .很多时候人们会通过执行以下操作来解决权限问题(Linux示例)
这不是一个明智的想法,因为文件或目录现在是世界可写的 . 如果您拥有服务器并且是唯一的用户,那么这不是什么大问题,但是如果您在共享托管环境中,那么您刚刚在服务器上访问了所有人 .
您需要做的是确定需要访问的用户,并仅授予他们访问权限 . 一旦您知道哪些用户需要访问权限,您就可以确保这一点
chmod 600
(只有所有者可以读写)或chmod 644
(所有者可以写但每个人都可以阅读)你可以read a more extended discussion of Linux/Unix permissions and users here
使用查询参数添加脚本
那是我的情况 . 它实际上链接到question #4485874,但我很快就会解释它 .
当您尝试要求
path/to/script.php?parameter=value
时,PHP会查找名为script.php?parameter=value
的文件,因为UNIX允许您使用像这样的路径 .如果您确实需要将一些数据传递给包含的脚本,只需将其声明为
$variable=...
或$GLOBALS[]=...
或您喜欢的其他方式 .另一个可能的原因:在文本编辑器中重命名和/或移动文件 . 我完成了上述所有步骤,但没有成功,直到我删除了不断抛出此错误并创建了新错误的文件,从而解决了问题 .
我的代码在所有机器上运行良好,但只有这一个开始给出问题(过去常常找到工作) . 使用echo“document_root”路径进行调试并仔细查看错误,发现了这一点
您可以轻松查看问题所在 . 问题是//在功能之前
因此,只需从包含中删除提货/它就可以正常工作 . 有趣的是,这种行为在不同版本上有所不同 . 我在笔记本电脑,Macbook Pro和这台电脑上运行相同的代码,一切工作都很顺利 . 希望这有助于某人 .