Vic*_*yew 148 php fopen require include-path
在PHP脚本,调用是否include()
,require()
,fopen()
,或它们的衍生物,例如include_once
,require_once
或者甚至move_uploaded_file()
,一个经常遇到错误或警告:
无法打开流:没有这样的文件或目录.
快速找到问题根本原因的好过程是什么?
Vic*_*yew 234
有很多原因导致人们可能遇到这个错误,因此一个好的检查列表首先要检查的内容有很大帮助.
让我们考虑一下我们正在对以下行进行故障排除:
require "/path/to/file"
Run Code Online (Sandbox Code Playgroud)
或移动任何被其调用require*
或调用include*
其自己的变量,回显它,复制它,并尝试从终端访问它:
$path = "/path/to/file";
echo "Path : $path";
require "$path";
Run Code Online (Sandbox Code Playgroud)
然后,在一个终端:
cat <file path pasted>
Run Code Online (Sandbox Code Playgroud)/users/tony/htdocs
最佳做法:
为了使您的脚本在您移动的同时保持健壮,同时仍然在运行时生成绝对路径,您有2个选项:
require __DIR__ . "/relative/path/from/current/file"
.该__DIR__
幻常量返回当前文件的目录.SITE_ROOT
自己定义一个常量:
config.php
在config.php
,写
define('SITE_ROOT', __DIR__);
Run Code Online (Sandbox Code Playgroud)在每个要引用站点根文件夹的文件中,包括config.php
,然后在SITE_ROOT
任何您喜欢的地方使用常量:
require_once __DIR__."/../config.php";
...
require_once SITE_ROOT."/other/file.php";
Run Code Online (Sandbox Code Playgroud)这两种做法也使您的应用程序更具可移植性,因为它不依赖于包含路径等ini设置.
包含文件的另一种方式,无论是相对还是纯粹绝对,都是依赖于包含路径.对于诸如Zend框架之类的库或框架,通常就是这种情况.
这样的包含将如下所示:
include "Zend/Mail/Protocol/Imap.php"
Run Code Online (Sandbox Code Playgroud)
在这种情况下,您需要确保"Zend"所在的文件夹是包含路径的一部分.
您可以使用以下命令检查包含路径:
echo get_include_path();
Run Code Online (Sandbox Code Playgroud)
您可以使用以下方法为其添加文件夹:
set_include_path(get_include_path().":"."/path/to/new/folder");
Run Code Online (Sandbox Code Playgroud)
可能一起,运行服务器进程的用户(Apache或PHP)根本没有读取或写入该文件的权限.
要检查服务器运行的用户,可以使用posix_getpwuid:
$user = posix_getpwuid(posix_geteuid());
var_dump($user);
Run Code Online (Sandbox Code Playgroud)
要查找文件的权限,请在终端中键入以下命令:
ls -l <path/to/file>
Run Code Online (Sandbox Code Playgroud)
并查看权限符号表示法
如果以上都不起作用,那么问题可能是某些PHP设置禁止它访问该文件.
三个设置可能是相关的:
phpinfo()
或使用来检查ini_get("open_basedir")
ini_get("allow_url_include")
和设置ini_set("allow_url_include", "1")
如果上述任何一个都无法诊断问题,则可能会发生以下特殊情况:
您可以使用相对路径或绝对路径包含库,例如Zend框架.例如 :
require "/usr/share/php/libzend-framework-php/Zend/Mail/Protocol/Imap.php"
Run Code Online (Sandbox Code Playgroud)
但是你仍然会遇到同样的错误.
这可能是因为您(成功)包含的文件本身具有另一个文件的include语句,而第二个include语句假定您已将该库的路径添加到包含路径.
例如,之前提到的Zend框架文件可能包含以下内容:
include "Zend/Mail/Protocol/Exception.php"
Run Code Online (Sandbox Code Playgroud)
这既不是相对路径的包含,也不是绝对路径.假设Zend框架目录已添加到包含路径.
在这种情况下,唯一可行的解决方案是将目录添加到包含路径.
如果您正在运行安全增强型Linux,则可能是因为拒绝从服务器访问该文件而导致问题的原因.
要检查系统上是否已启用SELinux,请sestatus
在终端中运行该命令.如果该命令不存在,则SELinux不在您的系统上.如果它确实存在,那么它应该告诉你它是否被强制执行.
要检查SELinux策略是否是问题的原因,您可以尝试暂时关闭它.但要小心,因为这将完全禁用保护.不要在生产服务器上执行此操作.
setenforce 0
Run Code Online (Sandbox Code Playgroud)
如果您不再关闭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
Run Code Online (Sandbox Code Playgroud)
如果您的文件位于主目录中,则还需要打开httpd_enable_homedirs
布尔值:
setsebool -P httpd_enable_homedirs 1
Run Code Online (Sandbox Code Playgroud)
在任何情况下,SELinux拒绝访问文件的原因可能有多种,具体取决于您的策略.所以你需要调查一下.这是一个专门为Web服务器配置SELinux的教程.
如果您正在使用Symfony,并在上传到服务器时遇到此错误,则可能是应用程序的缓存未被重置,因为app/cache
已上载或尚未清除缓存.
您可以通过运行以下控制台命令来测试并修复此问题:
cache:clear
Run Code Online (Sandbox Code Playgroud)
显然,zip->close()
当zip中的某些文件的文件名中包含非ASCII字符(例如"é")时,也会发生此错误.
可能的解决方案是utf8_decode()
在创建目标文件之前包装文件名.
致Fran Cano确认并建议解决此问题的方法
Mac*_*ity 14
添加(非常好)现有答案
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>
Run Code Online (Sandbox Code Playgroud)
让您的服务器管理员查阅他们使用的托管和Web服务器软件的手册.
请注意,通过Web服务器执行文件与命令行或cron作业执行非常不同,这一点很重要.最大的区别是您的Web服务器拥有自己的用户和权限.出于安全原因,用户非常受限制.Apache的,比如,往往是apache
,www-data
或httpd
(取决于您的服务器上).cron作业或CLI执行具有运行它的用户具有的任何权限(即,以root身份运行PHP脚本将以root权限执行).
很多时候人们会通过执行以下操作来解决权限问题(Linux示例)
chmod 777 /path/to/file
Run Code Online (Sandbox Code Playgroud)
这不是一个明智的想法,因为文件或目录现在是世界可写的.如果您拥有服务器并且是唯一的用户,那么这不是什么大问题,但是如果您在共享托管环境中,那么您只是在服务器上访问了所有人.
您需要做的是确定需要访问的用户,并仅授予他们访问权限.一旦您知道哪些用户需要访问权限,您就可以确保这一点
该用户拥有该文件,可能还有父目录(如果您要编写文件,则可能是父目录).在大多数共享托管环境中,这不是问题,因为您的用户应该拥有根目录下的所有文件.Linux示例如下所示
chown apache:apache /path/to/file
Run Code Online (Sandbox Code Playgroud)用户(仅限该用户)具有访问权限.在Linux中,一个好的做法是chmod 600
(只有所有者可以读写)或者chmod 644
(所有者可以写但每个人都可以阅读)
您可以在此处阅读有关Linux/Unix权限和用户的更多扩展讨论
我的代码在所有机器上都运行良好,但只有在这台机器上才开始出现问题(我猜这曾经可以正常工作)。使用 echo "document_root" 路径调试并仔细查看错误,发现了这个
警告:包括(D:/MyProjects/testproject//functions/connections.php):无法打开流:
您可以轻松查看问题所在。问题是 // 在函数之前
$document_root = $_SERVER['DOCUMENT_ROOT'];
echo "root: $document_root";
include($document_root.'/functions/connections.php');
Run Code Online (Sandbox Code Playgroud)
所以只需从包含中删除提货 / ,它应该可以正常工作。有趣的是这种行为在不同版本上是不同的。我在笔记本电脑、Macbook Pro 和这台 PC 上运行相同的代码,直到一切正常。希望这可以帮助某人。
小智 5
桑巴股
如果您有 Linux 测试服务器并且使用 Windows 客户端工作,则 Samba 共享会干扰chmod命令。所以,即使你使用:
chmod -R 777 myfolder
Run Code Online (Sandbox Code Playgroud)
在 Linux 端,Unix Group\www-data 完全有可能仍然没有写访问权限。如果您的共享设置为将 Windows 管理员映射到 root,则一种可行的解决方案是:从 Windows 中,打开“权限”,禁用带有副本的文件夹的继承,然后授予对 www-data 的完全访问权限。