PHP require路径问题之研究

zsx in 记录整理 / 8 / 3020

在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改变当前的工作目录,则相对路径就工作正常了。


如果本文对你有帮助,你可以用支付宝支持一下:

Alipay QrCode
aps at 2015/6/27[回复]
学习了
qiukong.com at 2015/5/29[回复]
我是来感谢的。以前一直觉得360还不错,虽然有众多丑闻但觉得还是比较为用户考虑的。中途看到了你抨击360的一些文章,虽然并不苟同但也让我有了一些顾忌。多亏了这些到现在一直没敢用360云盘。今天看到360大肆屏蔽用户文件的事情,彻底看清了它的本质。我觉得没有选择云盘很大原因来自于你的文章和Andy的劝诫,才没有造成太大的损失,真的很感谢。
zsx at 2015/5/30[回复]
虽然当时只是意气用事,在什么都不懂的情况下就开喷……不过能有帮助还是不胜荣幸……(话说360大肆屏蔽用户文件是什么事情)
c at 2015/6/1[回复]
360云盘,屏蔽了大量片子和美剧、电影、动漫资源。不管用户分没分享,存在网盘里的文件都没法下载。相当于存进360云盘就跟把文件粉碎掉一样……
zsx at 2015/6/4[回复]
百度不是也有八秒还是几秒教育视频事件吗=_=
c at 2015/6/12[回复]
是呐,再加上百度贴吧那成堆的广告和空间关闭,早就不用它了……现在只用115,不知能否撑住了。反正最安全还是自己硬盘,国内都不大靠谱……
MrHe at 2015/5/25[回复]
这个代码高亮看着有些不舒服
zsx at 2015/5/30[回复]
默默从Eclipse式换回了Default