PHP 多级嵌套的相对目录解决办法

方法1:realpath("../../../") 的绝对路径(目录)

当php目录层级比较少时(只有一级目录),使用php的include包含文件时并没有发现什么问题。

当php目录层级比较多时,从二级目录开始的引用 include,便会出现问题,三四五级目录则更加混乱。

先来慢慢讲解一个二级很简单的目录问题,帮助理解,例如:

<web>(网站根目录/web_root/)
├<A>文件夹
│ │
│ └1.php
├<B>文件夹
│ │
│ └2.php
└index.php


在1.php中通过include("../B/2.php"); 来引入B目录下的2.php文件,正确!

在index.php中通过include("A/1.php");来引入A目录下的1.php文件,正确!

运行出来呢,致命错误!

错误提示:找不到../B/2.php文件

静下心来好好分析一下,index.php中引用了A目录下的1.php文件,这时候 1.php被编译到index.php中执行,也就是相当于1.php同index.php一样位于网站根目录下,但是在1.php文件中又引用了一段代码 include("../B/2.php");  其中:"../"意味着什么?上一级目录?可现在1.php已经在根目录下了,这时候再上一级,那就已经找不到2.php了(网站所有的文件都在根目录下 /path_root/下,超出了根目录肯定找不到了),所以问题就出现在此。

那么如何解决呢?

很多人会想到 include("/B/2.php") 都是用绝对路径不就好了,同样不行!

php不同于JSP、Java等,在include中使用"/"并不是我们所想象的网站根目录,它代表的的当前的目录,对,就是本级目录的当前目录,不是网站的根目录 /path_root/,把目录路径都打印日志调试可以看到,因此还是不行。

例如:

在 A/1.php文件中 include("/B/2.php") ,表示 /B就在与 1.php 是同一级目录,即在 1.php中会编译成 A/B/1.php,肯定找不到呀,错误!

在 index.php 文件中通过include("A/1.php"); 当 1.php编译进了 index.php 后,1.php的引用为  include("/B/2.php") ,即网站根目录下的 /B/2.php(/,即表示当前目录),正确!

综上分析解释,看明白了没,没看明白,就多看几次呗,看多了,就想明白了,也可以结合错误日志看哈

总之,A/1.php 和  index.php 文件(通过include("A/1.php");),二者总有一个不能满足,会报错!

有人说,一级目录为啥要引用二级目录呢?哈哈哈,一看是没实践,给你看一个场景:

index.php

price.php

conf/config.php (包含了价格定义)

lib/function.cal_price.php

若想在一级目录下的价格文件 price.php 显示和计算价格函数,则必须引用 conf/config.php 和 lib/function.cal_price.php

总不能都写在同一级目录下吧,这是非常普通高频的应用场景啊!宝宝想不到,也办不到啊!不想学编程。。。

 

那是不是没有方法可解决了?当然是有的!

既然不能用相对的,那我们可以改用绝对路径的方式。

在根目录 c:\wamp\www\ 下的 index.php 文件中(这个前提非常重要,对于此项目只有二级目录,尚可用,若超过二级目录嵌套则不行

realpath("./") 用于获取当前网站根目录的绝对路径,即:c:\wamp\www\web_root\

因此,我们可以将include("../B/2.php"); 改成include(realpath("./")."/B/2.php");

说明:realpath("./")返回的结果为:c:\wamp\www\web_root(不含最右侧的斜杠,需自己加上)

这样的话,不管页面在哪一级目录,我都可以去引用,不用再担心路径问题了!

划重点:

以上解决方案是临时的,或容易误导人,现在给出 realpath(file | dir) 的代码示例,全面认识:

 realpath(file | dir) :参数可以是 file 文件,也可以是绝对或相对目录 "/", "./", "../", "../../",

vim /Users/homer/Downloads/myCode/workspace/proxy_client/doc/buy/index.php

<?php
require_once(dirname(__FILE__) . "/../doc_header.php");
echo "<script>document.title = '隧道代理 - 产品文档 - 文档中心 - 米扑代理' </script>";
 
echo "<br> buy/index.php  __FILE__ : " . __FILE__;
echo "<br> buy/index.php  __DIR__ : " . __DIR__;
echo "<br> buy/index.php  dirname(__FILE__) : " . dirname(__FILE__);
echo "<br> buy/index.php  dirname(dirname(__FILE__)) : " . dirname(dirname(__FILE__));
echo "<br> buy/index.php  getcwd() : " . getcwd();
echo "<br> buy/index.php  realpath('/') : " . realpath('/');
echo "<br> buy/index.php  realpath(__FILE__) : " . realpath(__FILE__);
echo "<br> buy/index.php  realpath(realpath(__FILE__)) : " . realpath(realpath(__FILE__));
echo "<br> buy/index.php  realpath('./') : " . realpath('./');
echo "<br> buy/index.php  realpath('../') : " . realpath('../');
echo "<br> buy/index.php  realpath('../../') : " . realpath('../../');
echo "<br> buy/index.php  realpath('../../../') : " . realpath('../../../');
echo "<br> buy/index.php  realpath('../../../../') : " . realpath('../../../../');
echo "<br> buy/index.php  realpath('../../../../../') : " . realpath('../../../../../');
?>

运行结果(在 index.php 当前目录下运行):

buy/index.php __FILE__ : /Users/homer/Downloads/myCode/workspace/proxy_client/doc/buy/index.php
buy/index.php __DIR__ : /Users/homer/Downloads/myCode/workspace/proxy_client/doc/buy
buy/index.php dirname(__FILE__) : /Users/homer/Downloads/myCode/workspace/proxy_client/doc/buy
buy/index.php dirname(dirname(__FILE__)) : /Users/homer/Downloads/myCode/workspace/proxy_client/doc
buy/index.php getcwd() : /Users/homer/Downloads/myCode/workspace/proxy_client/doc/buy
buy/index.php realpath('/') : /
buy/index.php realpath(__FILE__) : /Users/homer/Downloads/myCode/workspace/proxy_client/doc/buy/index.php
buy/index.php realpath(realpath(__FILE__)) : /Users/homer/Downloads/myCode/workspace/proxy_client/doc/buy/index.php
buy/index.php realpath('./') : /Users/homer/Downloads/myCode/workspace/proxy_client/doc/buy
buy/index.php realpath('../') : /Users/homer/Downloads/myCode/workspace/proxy_client/doc
buy/index.php realpath('../../') : /Users/homer/Downloads/myCode/workspace/proxy_client
buy/index.php realpath('../../../') : /Users/homer/Downloads/myCode/workspace
buy/index.php realpath('../../../../') : /Users/homer/Downloads/myCode
buy/index.php realpath('../../../../../') : /Users/homer/Downloads

 

 

方法2:dirname(__FILE__) 获取当前文件的绝对目录,全部使用绝对路径推荐

dirname(__FILE__) 这个是得到当前脚本的绝对路径,即当前文件的路径绝对路径(目录)

对于目录文件:

/path/root/11.php

/path/root/aa/bb/cc/dd.php

根目录文件 11.php 比文件 dd.php 高了三层,在 dd.php 文件引用 11.php 如下:

include dirname(__FILE__). '/../../../11.php';

实际的路径过程:

dirname(__FILE__). '/../../../11.php';

dd.php 文件的绝对目录为:/path/root/aa/bb/cc/

dirname(__FILE__). '/../../../11.php' 为:/path/root/aa/bb/cc/../../../11.php

然后 ../ 向上消除一级目录:

/path/root/aa/bb/cc/../../../11.php

/path/root/aa/bb/../../11.php

/path/root/aa/../11.php

/path/root/11.php   

路径定位完成,成功!

 

小结:

dirname(__FILE__) 和 realpath('./') 的作用是一样的,都是获取当前目录的绝对路径(目录值),二者可以通用

dirname — 返回路径中的目录部分 (PHP 4, PHP 5, PHP 7)

realpath — 返回规范化的绝对路径名  (PHP 4, PHP 5, PHP 7)

其中,dirname(__FILE__)  语义直观,可读性强,兼容性更好,推荐

因此,以上两种都是用 PHP 绝对路径来嵌套引用,解决路径的问题!

 

 

方法3:自定义网站根目录 ROOT,然后 ROOT + 相对目录(/xxx/xxx.php)【强烈推荐

此方法,是在网站根目录下,获取到根目录赋值给 ROOT,再引用 ROOT + 相对目录(/xxx/xxx.php)

本质上,其实也是绝对路径,因为在本质上,相对路径最终都会拼接或转化为绝对路径,才定位到文件

在网站根目录下,创建一个宏文件 mimvp_macro.php

vim /Users/homer/Downloads/myCode/workspace/websiteRoot/mimvp_macro.php

<?php 
	// 全局网站根目录
	if(!defined('ROOT')) {
		define('ROOT' , pathinfo(__FILE__, PATHINFO_DIRNAME));	// ROOT = '/Users/homer/Downloads/myCode/workspace/websiteRoot'
	}
	require_once(ROOT . "/conf/config.php");			// 注意:配置文件不宜过大,防止加载过慢,对于特殊的配置常量(如省市县配置文件过大),在需要的页面额外加载即可
	require_once(ROOT . "/common/yglogger.php");		// 每一个文件最好都可以打印日志
?>

说明:

 

然后,在各级母版页,使用 ROOT + 相对路径(/xxx/xxx.php),例如:

在一级目录下的母版页,引用宏定义 ROOT 网站根目录

vim /Users/homer/Downloads/myCode/workspace/websiteRoot/mimvp_header.php

<?php
require_once( 'mimvp_macro.php' );
...
?>

在一级目录的其它网页,引用母版页 mimvp_header.php 及其 ROOT 网站根目录

<?php 
require_once './mimvp_header.php';
require_once(ROOT . "/lib/login_check.php");	
require_once(ROOT . "/lib/user_whiteip.php");
require_once(ROOT . "/common/encrypt.php");
...
?>

或者

<?php 
if(!defined('ROOT')) {
	require_once './mimvp_macro.php';
}
require_once(ROOT . "/mimvp_header.php");
require_once(ROOT . "/lib/login_check.php");	
require_once(ROOT . "/common/encrypt.php");
...
?>

说明:引用母版 require_once './mimvp_header.php';  ,母版中引用了 require_once( 'mimvp_macro.php' ); 其中定义了 ROOT 网站根目录

在二级目录的网页,引用一级母版页 mimvp_header.php 及其 ROOT 网站根目录

<?php
if(!defined('ROOT')) {
	require_once '../mimvp_macro.php';
}
require_once(ROOT . "/mimvp_header.php");
require_once(ROOT . "/lib/login_check.php");
require_once(ROOT . "/common/dtime.php");
....
?>

说明:在一级目录使用 require_once './mimvp_macro.php'; ,在二级目录使用 require_once '../mimvp_macro.php';

 

在二级目录下,定义二级的母版页

vim /Users/homer/Downloads/myCode/workspace/websiteRoot/doc/doc_header.php

<?php
if(!defined('ROOT')) {
	require_once '../mimvp_macro.php';
}
require_once(ROOT . "/mimvp_header.php");
.....
?>

 

在二级目录的其它网页,引用二级母版页 require_once (ROOT . "/doc/doc_header.php");

vim /Users/homer/Downloads/myCode/workspace/websiteRoot/doc/index.php

<?php
if(!defined('ROOT')) {
	require_once '../mimvp_macro.php';
}
require_once (ROOT . "/doc/doc_header.php");
?>

 

在三级目录的其它网页,引用二级母版页  require_once (ROOT . "/doc/doc_header.php");

vim /Users/homer/Downloads/myCode/workspace/websiteRoot/doc/demo/index.php

<?php
if(!defined('ROOT')) {
	require_once '../../mimvp_macro.php';
}
require_once (ROOT . "/doc/doc_header.php");

echo "<script>document.title = '隧道代理 - 产品文档 - 文档中心 - 米扑代理' </script>";
?>

 

在第四级的其它网页,引用二级母版页  require_once (ROOT . "/doc/doc_header.php");

vim /Users/homer/Downloads/myCode/workspace/websiteRoot/doc/demo/test/index.php

<?php
if(!defined('ROOT')) {
	require_once '../../../mimvp_macro.php';
}
require_once (ROOT . "/doc/doc_header.php");

echo "<script>document.title = '定制开发 - 隧道代理 - 产品文档 - 文档中心 - 米扑代理' </script>";
?>

注意了吗,二、三、四级目录的其它文件引入第二级的母版页,只需要轻轻修改宏定义文件,其它统统不动(ROOT全局变量 + 绝对路径了哦)

require_once '../mimvp_macro.php';         // 二级其他网页

require_once '../../mimvp_macro.php';       // 三级其他网页

require_once '../../../mimvp_macro.php';       // 四级其他网页

小提示:以上示例都是 header.php 母版引入,记得 footer.php 模板引入时,也要  ROOT + 相对路径(/xxx/xxx/xxx.php)

vim /Users/homer/Downloads/myCode/workspace/websiteRoot/doc/doc_footer.php

<?php 
include ROOT . '/mimvp_footer.php';
?>

 

vim /Users/homer/Downloads/myCode/workspace/websiteRoot/doc/demo/index.php

<?php
include ROOT . '/doc/doc_footer.php';
?>

原理解释:首先判断 宏定义 ROOT,若没定义,则按照相对目录请求宏定义 ROOT,然后 ROOT + 相对路径(/xxx/xxx.php)

由此类推,不管母版嵌套多少层级,优先以当前文件的相对目录调用宏定义 ROOT,然后 ROOT + 相对路径(/xxx/xxx/xxx.php)

 

以上三种方法,推荐使用第三种方法宏定义,优点如下:

1)在网站根目录的宏定义文件 mimvp_macro.php 定义网站根目录 ROOT 全局变量,容易维护

2)每一个文件若要使用根目录,都可以先相对路径,调用一次宏文件 mimvp_macro.php 获取网站根目录 ROOT (关键优势)

3)获取了网站根目录 ROOT 全局变量后,一切都好办了, ROOT + 相对路径(/xxx/xxx/xxx.php)

前两种方法,在目录层级、母版层级不多时,勉强可用,若层级多了,一要数目录层级,二没全局根目录变量,不直观啊

简单说,第三种方法的优势,就是前两种绝对路径方法的不足,

因此,推荐使用 米扑代理 发明的第三种宏定义方法,无限嵌套哈

 

 

参考推荐:

PHP 路径详解 dirname,realpath,__FILE__,getcwd

PHP 文件导入 require, require_once, include, include_once 区别

PHP 出现 Notice: Undefined index: 的几种解决方法

PHP 常见错误处理

PHP在 linux上执行外部命令

PHP 文件操作常用函数

PHP 常用函数总结(数组,字符串,时间,文件操作)

PHP + Selenium + WebDriver 抓取米扑科技首页

PHP 获取网页标题(title)、描述(description)、关键字(keywords)等meta信息

PHP 下载保存文件到本地

PHP下载远程图片

PHP抓取网站ico图标