PHP 多级嵌套的相对目录解决办法
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 + Selenium + WebDriver 抓取米扑科技首页
PHP 获取网页标题(title)、描述(description)、关键字(keywords)等meta信息
版权所有: 本文系米扑博客原创、转载、摘录,或修订后发表,最后更新于 2020-01-23 01:08:31
侵权处理: 本个人博客,不盈利,若侵犯了您的作品权,请联系博主删除,莫恶意,索钱财,感谢!
转载注明: PHP 多级嵌套的相对目录解决办法 (米扑博客)