musicode / test

test
14 stars 1 forks source link

[PHP] require/include #6

Open musicode opened 10 years ago

musicode commented 10 years ago

关于网上的文章


在网上搜索二者的区别,有非常多的文章,其中很多内容都已过时,下面我将分条阐述:

  1. require 和 include 只是一种语言特性,不是函数(正确

    框架普遍的写法是 require 'x.php',而不是 require('x.php'),具体请参考 php 官方文档

  2. 报错机制不同(正确

    这里测试两种错误,第一种是加载的文件不存在,第二种是加载的文件内部报错(以语法错误为例)。

    文件不存在:require 报 fatal error,include 报 warning 文件内部报错:require 和 include 都报 parse error

  3. require 预加载,include 按需加载(错误

    关于这一条,最常见的论述是当不进入 if 分支时,是否加载文件。

    根据实际测试,如果不进入分支,require 和 include 都不会加载文件

  4. require 和 include 加载的文件不能使用 return(错误

    require 和 include 的文件都可以 return,比如配置文件通常使用全局 return 一个关联数组的方式。

  5. require 适合加载静态内容,include 适合加载脚本(错误

    框架几乎很难见到 include,基本都是 require,所以这条十分可笑。


如上所述,不论从美观(跟其他语言保持一致,比如 nodejs、python),还是严谨的角度来看,require 都是上选

require_onceinclude_once,基本跟 require/include 一样,唯一的不同是,require/include_once 需要查询一次已加载的文件列表,判断是否存在,不存在再加载。

但是 require/include_once 自身实现有一些问题,具体可参考 《再一次, 不要使用(include/require)_once》

从提升加载速度的角度来看,判断文件是否加载也是需要时间的,所以综合考虑还是不要使用为好

综上,推荐只使用 require

加载机制


分析之前,先根据我的前端经验,讲讲前端 AMD 规范的加载机制,它会把各种形式的路径,比如绝对路径、相对路径(相对自身)、别名等,格式化成绝对路径,再进行加载。用绝对路径统一所有路径,便于简单处理,而且内部存储的最终形式是绝对路径的映射表,这样才能实现最快的查找速度。

require 解析路径的顺序如下:

  1. 如果是绝对路径,直接返回
  2. 如果是相对路径,则相对于 当前工作目录 解析路径,这里容易理解成 当前脚本目录千万要注意
  3. 如果既不是绝对路径,也不是相对路径,则根据 include_path当前脚本目录 组成一个目录数组,依次遍历查找它们下面的文件
  4. 如果最终没找到文件,执行报错逻辑,不同的是,require 报 fatal error,include 报 warning

绝对路径的查找速度最快,所以框架经常用以下方式进行加载:

require __DIR__ . '/x.php';

这里需要详细说明第 2、3 步,最好跟着例子亲手写一遍,加深印象。

解析相对路径

测试目录结构如下:

root
  |- 1.php
  |- subdir
       |- 2.php 

1.php

require './subdir/2.php';

2.php

echo '2.php';

因此,相对路径是相对 当前工作目录,而不是 脚本当前目录

解析 include_path

测试以下代码:

echo get_include_path();

它会打印出一个以 PATH_SEPARATOR 分割的字符串,第一项是 .,表示 当前工作目录,查找顺序是从左到右,一旦找到就不再往后找了(类似环境变量 PATH 的查找方式)。

解析 include_path 的过程是,由 include_path当前脚本目录 组成一个目录数组,遍历查找,为了证明这一点,举个例子:

测试目录结构:

root
  |- 1.php
  |- t.php
  |- subdir
       |- 2.php
       |- t.php 

可以看到 rootroot/subdir 都有一个 t.php,我们用它来测试优先加载哪个目录下的文件。

1.php

require 't.php';

2.php

require 't.php';

root/t.php

echo 'root';

root/subdir/t.php

echo 'root/subdir';

可见优先加载 当前工作目录 下的文件。

因为 include_path 默认第一项是 当前工作目录,所以这种说法也不严谨,当我们改变 include_path 时,第一项可能就变化了。

在实现框架级别的加载时,为了提升速度,会把新的路径加到 include_path 前面,比如:

// 获取新添加的路径数组
$includePaths = require __DIR__ . '/include_paths.php';
// 把新的路径添加到 include_path 的开头
array_push($includePaths, get_include_path());
// 重新设值
set_include_path(join(PATH_SEPARATOR, $includePaths));