深入理解PHP之require/include顺序

在大型的Web项目中, include_path是一个模块化设计的根本中的根本(当然,现在也有很多基于autoload的设计, 这个不影响本文的探讨), 但是正是因为include_path, 经常会让我们遇到一些因为没有找到正确的文件而导致的看似”诡异”的问题.

也就有了如下的疑问:

include_path是怎么起作用的?

如果有多个include_path顺序是怎么样的?

什么情况下include_path不起作用?

今天, 我就全面的介绍下这个问题, 先从一个例子开始吧.

如下的目录结构:

在1.php中:

  root
├ 1.php
├ 3.php
└ subdir
├ 2.php
└ 3.php

而在2.php中:

<?php
require("3.php");
?>

而在root目录下的3.php打印出”root”, 在subdir目录下的3.php打印出”subdir”;

现在, 我的问题来了:

1. 当在root目录下运行1.php, 会得到什么输出?

2. 在subdir下运行上一级目录的1.php, 有会得到什么输出?

3. 当取消include_path中的当前目录path(也就是include_path=”path_to_subdir”), 上面俩个问题又会是什么输出?

PHP中的include_path

PHP在遇到require(_once)/include(_once)的指令的时候, 首先会做如下的判断:

要包含的文件路径是绝对路径么?
如果是, 则直接包含, 并结束.
如果不是, 进入另外的逻辑(经过多次调用, 宏展开后进入_php_stream_fopen_with_path)寻找此文件.

接下来, 在_php_stream_fopen_with_path中, 会做如下判断:

要包含的文件路径是相对路径么(形如./file, ../dir/file, 以下用"目录相对路径代替")?
如果是, 则跳过include_path的作用逻辑, 直接解析相对路径(随后单独介绍).

会根据include_path,和当前执行文件的path组成一个待选的目录列表, 比如对于文章前面的例子来说, 会形成一个如下的待选列表

".:path_to_subdir:current_script_dir"

然后, 依次从待选列表头部开始, 根据DEFAULT_DIR_SEPARATOR(本文的环境是”:”)取出待选列表中的一个路径, 然后把要包含的文件名附加在这个路径后面, 进行尝试. 如果成功包含, 则返回, 否则继续下一个待选路径.

到现在为止, 我们已经可以回答我开头提出的3个问题了.

1. 因为在root目录下执行, 所以在1.php中包含2.php的时候, include_path的第二个待选路径起了作用(path_to_subdir), 找到了path_to_subdir/2.php, 而在2.php包含3.php的时候, 当前工作目录是root下, 所以在包含3.php的时候, include_path的第一个待选路径”.”(当前工作目录)下就找到的匹配的文件, 所以得到的输出是”root”.

2. 同1, 只不过当前的路径是subdir, 所以得到的输出是”subdir”.

3. 因为没有了当前路径为include_path, 所以在root目录下运行的时候2.php中包含3.php的时候, 是path_to_subdir起了作用, 所以无论在root还是subdir都将得到”subdir”的输出.
而如果在2.php中清空include_path,

<?php
ini_set("include_path", '');
require("3.php");
?>

那么将会是current_script_dir起作用, 而这个时候current_script_dir是2.php的路径, 所以还是会得到”subdir”的输出.

目录相对路径

在使用目录相对路径的情况下, 相对路径的基点, 永远都是当前工作目录.

为了说明在目录相对路径下的情况,我们再看个列子, 还是上面的目录结构, 只不过1.php变成了:

<?php
ini_set("include_path", "/");
require("./subdir/2.php");
?>

2.php变成了:

<?php
require("./3.php");
?>

如果在root目录下执行, 2.php中寻找3.php将会在当前目录的相对路径下寻找, 所以得到的输出是”root”, 而如果是在subdir下执行上一级目录的1.php(php -f ../1.php), 将会因为在subdir下找不到”./subdir/2.php”而异常退出.

后记

1. 因为使用include_path和相对路径的情况下, 性能会和寻找的次数有关, 最坏的情况下, 如果你有10个include_path, 那么最多可能会重试11次才能找到要包含的文件, 所以, 在能使用绝对路径的情况下最好使用绝对路径.

2. 因为目录相对路径的basedir, 永远都是当前工作路径, 如果要使用, 需要和实际部署路径相关, 所以实际使用的很少(当然, 也有借助chdir来完成的模块).

3. 在模块化的系统设计中, 一般应该在模块内, 通过获取模块的部署路径(dirname(__FILE__), php5.3以后更是提供了__DIR__常量)从而使用绝对路径.

深入理解PHP之require/include顺序的更多相关文章

  1. [转]require(),include(),require_once()和include_once()区别

    require(),include(),require_once()和include_once()区别 面试中最容易提到的一个PHP的问题,我想和大家共勉一下: require()和include() ...

  2. 2018/03/09 每日一学PHP 之 require_once require include include_once 包含文件的区别

    require_once require include include_once 方法的区别 对于包含文件来说,如果只是使用框架来说的话,应该会很少碰到,因为框架底层对于文件的引用等做了很好的封装, ...

  3. (转)深入理解PHP之数组(遍历顺序)

    深入理解PHP之数组(遍历顺序)(转) http://www.laruence.com/2009/08/23/1065.html (鸟哥) 经常会有人问我, PHP的数组, 如果用foreach来访问 ...

  4. 深入理解require/include的顺序

    在大型的Web项目中, include_path是一个模块化设计的根本中的根本(当然,现在也有很多基于autoload的设计, 这个不影响本文的探讨), 但是正是因为include_path, 经常会 ...

  5. require,include,require_once,include_once的区别

    最近面试时被问到,一时间还真没想到太多,仅仅回答了大概的几个,于是回来再确认一下. 以下内容为网络摘抄: ①作用及用法  可以减少代码的重复 include(_once)("文件的路径&qu ...

  6. C++-理解构造函数、析构函数执行顺序

    先初始化序列中的函数调用,如果基类构造函数为非引用传递,则引起参数的拷贝构造 再: 先类内的成员构造函数(拷贝/默认),再类的构造函数:先基类,再派生类: 本文主要说明对象创建时构造函数的执行顺序,对 ...

  7. require include 一个隐藏的用法:作用域。

    最近在研究php底层框架,奇怪的一点.控制器里为什么要把从model里获取的数据 需要$this->assign('items', $items); 这种形式模板文件里才可以调用到这个变量.控制 ...

  8. require(),include(),require_once()和include_once()之间的区别

    引用文件的方法有两种:require 及 include. require 的使用方法如 require("file.php"); .这个函数通常放在 PHP 程序的最前面,PHP ...

  9. require include php5中最新区别,百度上好多错的。

    二者报错机制不同,include是warning 继续执行程序,require会报致命错误,中断程序运行. 前者有返回值,后者则没有. 注意了,php5里有一个区别没了,之前说的是require是无条 ...

随机推荐

  1. Python3基础教程2——Python的标准数据类型

    2018年3月12日 这次介绍一些python里面的标准数据类型 当然还是推荐一个比较系统的教程 http://www.runoob.com/python3/python3-tutorial.html ...

  2. Loadrunner 11在win7录制中失败“the recording of the application was terminated by windows due to window data execution prevention feature"

    在录制过程中遇到如下问题: 查了下如何Enable, Disable Data Execution Prevention (DEP) in Windows 10/8/7 (http://www.the ...

  3. Ubuntu上将终端安装到右键上

    Ubuntu上将终端安装到右键上 author:headsen chen    2017-10-12  10:26:12 个人原创,允许转载,请注明作者和出处,否则依法追究法律责任 chen@chen ...

  4. Unity的常用API

    1.Event Function:事件函数   Reset() :被附加脚本时.在游戏物体的组件上按Reset时会触发该事件函数 Start() :在游戏初始化时会执行一次 Update() :每一帧 ...

  5. java项目和java-web项目中文件和文件夹的含义

    1. java项目 .project:是工程构建配置文件 .classpath:保存的是项目所用的外部引用包的路径 .settings:记录项目配置变化的记录文件夹 src:sourcefolder项 ...

  6. poj1321 棋盘(dfs)

    #include<iostream> #include<cstring> using namespace std; ]={},sum=; ][]; void dfs(int a ...

  7. [SDOI2011]染色

    [SDOI2011]染色 题目描述 输入输出格式 输出格式: 对于每个询问操作,输出一行答案. 解法 ps:这题本来是树剖的,但我用lct写的,以下是lct的写法,树剖会有所不同 我们考虑把不同色点的 ...

  8. WEB 表格测试点

    Web页面的表格测试点: 1.表格列名 2.表格翻页.表格跳转到多少页.最后一页.首页 3.表格每页显示的数据, 数据的排序 4.表格无数据 5.表格支持的最大数据量 6.表格中数据内容超长时,显示是 ...

  9. 循环while do---while for循环

    一.循环结构 (.^▽^) 1.循环不是无休止进行的,满足一定条件的时候循环才会继续,称为"循环条件",循环条件不满足的时候,循环退出 2.循环结构是反复进行相同的或类似的一系列操 ...

  10. Repeating Decimals UVA - 202

    The / repeats indefinitely with no intervening digits. In fact, the decimal expansion of every ratio ...