深入理解PHP之require/include顺序

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

也就有了如下的疑问:

include_path是怎么起作用的?

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

什么情况下include_path不起作用?

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

如下的目录结构:

在1.php中:

  1. root
  2. 1.php
  3. 3.php
  4. subdir
  5. 2.php
  6. 3.php

而在2.php中:

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

而在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)的指令的时候, 首先会做如下的判断:

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

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

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

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

  1. ".: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,

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

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

目录相对路径

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

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

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

2.php变成了:

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

如果在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. 论文学习:Fully Convolutional Networks for Semantic Segmentation

    发表于2015年这篇<Fully Convolutional Networks for Semantic Segmentation>在图像语义分割领域举足轻重. 1 CNN 与 FCN 通 ...

  2. AngularJS中Model和Controller传值问题

    最近由于工作原因,开始写点前端的东西.前两天刚开始了解AngularJS这门技术,当然,新手免不了会爬坑! 今天分享一篇关于--> 模型传参给Controller的实例: 需求: 具体是  首先 ...

  3. 如何利用wireshark破解网站密码

    在有进行破解意愿的想法诞生之前,博主得先来给各位泼一盆凉水,本文介绍的方法破解http的轻而易举,而对于https的就算了.因为Wireshark 没有session key ,不能解密SSL数据流. ...

  4. 大数据量情况下高效比较两个list

    比如,对两个list<object>进行去重,合并操作时,一般的写法为两个for循环删掉一个list中重复的,然后再合并. 如果数据量在千条级别,这个速度还是比较快的.但如果数据量超过20 ...

  5. WPF介绍

    WPF 为Windows Presentation Foundation的首字母缩写 ,中文译为“Windows呈现基础”.WPF是微软新一代图形系统,运行在.NET Framework 3.0及以上 ...

  6. 解决新版的vue-cli后无法通过ip访问vue项目

    今天遇到个问题,之前一直没遇到过,通过vue-cli脚手架搭建的项目,只能通过localhost访问,不能通过IP地址访问. 这是由 webpack 导致的,2017年11月12日的时候 webpac ...

  7. u-boot的SPL源码流程分析

    上次梳理了一下SPL的基本概念和代码总体思路,这次就针对代码跑的流程做个梳理.SPL中,入口在u-boot-spl.lds中 ENTRY(_start) SECTIONS { .text : { __ ...

  8. 2017/11/25 2D变换

    2D变换 一.盒模型解析模式 1.box-sizing:盒模型解析模式 1)content-box:标准盒模型(和css2一样的计算) 宽度和高度之外绘制元素的内边距和边框 width,height外 ...

  9. leetCode:206 反转链表

    206. 反转链表 题目:反转一个单链表. 进阶:链表可以迭代或递归地反转.你能否两个都实现一遍? 非递归代码: class Solution { public ListNode reverseLis ...

  10. modal verbs(一)

    什么是modal verb?翻译成中文就是情态动词. modal的意思是模式的,情态的,形式的.Bootstrap中的模态框就是这个词modal. 情态动词翻译挺准确的,就是表达说话人的情绪,态度或语 ...