在PHP中,有两个关键字为require和include。关于它们引用的文件路径,目前网上的教程还是有些缺漏,所以重新写了一篇。
首先先按照PHP官方教程,定义绝对路径和相对路径的概念。( http://php.net/manual/zh/function.include.php )
如果定义了路径——不管是绝对路径(在 Windows 下以盘符或者 \ 开头,在 Unix/Linux 下以 / 开头)还是当前目录的相对路径(以 . 或者 .. 开头)——include_path 都会被完全忽略。例如一个文件以 ../ 开头,则解析器会在当前目录的父目录下寻找该文件。
在此之外的路径,即开头非盘符、非/、非.的路径,我都称之为未定义路径。
然后再定义入口文件和工作路径。入口文件,即你用浏览器打开的.php文件。工作路径,即该PHP文件的所在路径。如你打开了a.php,在这里require 'b/b.php'
后b.php再require 'c/c.php'
。从c.php的角度看来,入口文件仍然为a.php,而工作路径为仍然为a.php所在路径。
先从未定义路径开始吧。
以include 'abcd/abcde.php'
为例,其查找顺序如下:
第一步:通过get_include_path
得到的路径查找。
首先,include_path的默认值通常如右:【.:/var/www/php55/lib/php
】。在Windows下分隔符为半角分号【;】,在Linux下为半角冒号【:】。这个时候,PHP会先查找【./abcd/abcde.php】,然后再查找【
/var/www/php55/lib/php/abcd/abcde.php
】。
也就是说,当我们调用set_include_path
时,其实只对未定义路径有效。这一点,在PHP官方手册也写的很明确。
第二步:在include_path
下没找到,通过脚本路径查找。
这个时候,它查找的顺序是这样的:从入口文件所在的工作路径开始查找。如果没有找到,它不会逐级深入。它就直接往调用include的文件所在路径查找,找不到就报错。
举个例子:
文件结构:
-- b
--- c
---- b.php
---- c.php
--- c.php
-- a.php
-- c.php
我们让a.php来include 'b/c/b.php'
,让b.php来include 'c.php'
,接着由c.php来var_dump(get_included_files());
首先,是和a.php在同一工作目录下,存在c.php的情况。它很明显调用了工作目录下的c.php。
Z:\>php a.php
array(3) {
[0] =>
string(8) "Z:\a.php"
[1] =>
string(12) "Z:\b\c\b.php"
[2] =>
string(8) "Z:\c.php"
}
其次,删除工作目录下的c.php,其就调用了b/c/下的c.php
Z:\>php a.php
array(3) {
[0] =>
string(8) "Z:\a.php"
[1] =>
string(12) "Z:\b\c\b.php"
[2] =>
string(12) "Z:\b\c\c.php"
}
再删除b/c下的c.php,此时报错。
然后是绝对路径。
这个最没什么好说的了。没有工作目录等概念,就是按照你给定的路径直接调用,找不到就报错。
最后,是相对路径。
首先,相对路径是不考虑include_path的!
其次,相对路径的“相对”,是相对于工作目录的!
然后,与未定义路径不同的是,其如果在工作目录下找不到文件,不会再到实际调用include的文件所在路径去查找。
路径之后,我们就需要解决几个坑了。
第一个坑是,如何保证自己require加载的路径是正确的?
我们从上面可以看出,相对路径和未定义路径都有多种被加载的可能。那怎么办?
PHP里有个函数,叫dirname
。还有个常量,叫__FILE__
。用这两个拼凑在一起,可以得到执行include这个操作的PHP自身的路径。所以,便可以这么写:
require dirname(__FILE__) . '/a.php';
一般意义下,其效果便等同于require './a.php';
。但由于其是使用绝对路径的,所以有效率更高(省去了查找文件)、定位更精准(防止某些奇葩情况造成的问题)等优点。
第二个坑是,怎么改变工作路径?
比如,我需要一个入口文件加载其他程序的子文件。其他程序里用了相对路径引用,但我不能修改其它程序的代码,怎么办?
没关系,我们有chdir函数,可以改变PHP的当前目录。以下是个示例:
$includePathAry = explode("/", $filePath);
array_pop($includePathAry);
$includePath = implode("/", $includePathAry) . "/";
chdir($includePath);
通过chdir改变当前的工作目录,则相对路径就工作正常了。