PHP的include和require
文章目录
- 环境
- require和include
- require VS include
- require(include) VS require_once(include_once)
- 路径问题
- 当前工作目录对相对路径的影响
- 题外话
- 总结
- 其它
- 参考
环境
- Windows 11 专业版
- XAMPP v3.3.0
- PHP 8.2.12
- Apache 2.4.58
- VSCode 1.99.3
require和include
require
和 include
有点类似于C语言的 include
和Java的 import
。
比如,创建文件 test0504_util1.php
如下:
<?php
function add($a, $b) {return $a + $b;
}const PI = 3.14; // 注意全部用大写字母,且不加 `$` 前缀。$name = '张三';echo "我是util1" . PHP_EOL;
?>
该PHP文件里:
- 定义了
add()
函数 - 定义了常量
PI
- 定义了变量
$name
- 有一段全局的代码
在同级目录里,创建文件 test0504_1.php
如下:
<?php
require 'test0504_util1.php';$x = add(1, 2);
echo "x = $x" . PHP_EOL;echo "PI = " . PI . PHP_EOL;echo "name = $name" . PHP_EOL;
?>
在该PHP文件里引入了 test0504_util1.php
文件。因此,就可以使用其中的函数、常量、变量。
运行结果如下:
我是util1
x = 3
PI = 3.14
name = 张三
注意,被引入文件的全局代码,也会被运行(本例中输出了 我是util1
)。
上面的例子里,引用文件使用了被引用文件的函数、常量、变量。反过来,被引用文件也可以使用引用文件的函数、常量、变量。
比如,可以把被引用文件当作一个模板,引用文件在引用模板前,先设置好数据。
一个典型的例子是页面的header。
创建文件 test0504_header1.php
如下:
<head><title><?phpecho "当前页面:$title";?></title>
</head>
该文件是将要被引用的模板文件,其中有一个变量 $titile
。
创建文件 test0504_page1.php
如下:
<html><?php$title = 'page1';require 'test0504_header1.php';?><body><p>这是页面1</p></body>
</html>
类似的,创建文件 test0504_page2.php
如下:
<html><?php$title = 'page2';require 'test0504_header1.php';?><body><p>这是页面2</p></body>
</html>
在这两个文件中,先设置好 $title
变量的值,然后引用模板文件。
访问页面1:
访问页面2:
可见,二者的header是相同的模板,只是title的值不同。
require VS include
require
和 include
的主要区别在于,对于被引用文件不存在时的行为:
require
:如果文件不存在,PHP 会抛出E_COMPILE_ERROR
,并立即终止脚本:
Fatal error: Uncaught Error: Failed opening required 'xxx.php' (include_path='C:\xampp\php\PEAR') in C:\xampp\htdocs\test0504_2.php:2
include
:如果文件不存在,PHP 会抛出E_WARNING
,但脚本会继续执行后续代码:
Warning: include(): Failed opening 'xxx.php' for inclusion (include_path='C:\xampp\php\PEAR') in C:\xampp\htdocs\test0504_2.php on line 3
使用场景:
require
:用于加载程序运行必需的依赖(如数据库连接、核心函数库)。也就是“必须要有,没有不行”的文件include
:用于加载可选的模板文件或用户自定义内容。也就是“可有可无,没有也影响不大”的文件
require(include) VS require_once(include_once)
二者的区别在于,当重复引入同一个文件时:
require
:会引入多次require_once
:只会引入一次
该使用 require
还是 require_once
,取决于具体的应用场景。比如,同一个页面上确实需要多次显示同一个东西(比如一个 div
元素),那就在不同的位置使用require
来多次引入同一文件。如果是要引入类、变量、常量等,那显然应该使用 require_once
,以避免重复定义,冲突报错。
include
和 include_once
也同理。
路径问题
当前工作目录对相对路径的影响
在上面的例子里,引入的是同一目录里的文件,所以没有指定目录:
require 'xxx.php';
这里使用的是相对路径(相对于当前脚本文件)。
使用相对路径的问题是,如果“当前工作目录”发生了变化,则相对路径所代表的绝对路径也改变了。
现有目录结构如下:
htdocs|----dir1| |----1.php|----dir2|----test0504_3.php
在 1.php
里,定义了变量 $aaa
:
<?php$aaa = 123;
?>
在 test0504_3.php
里引入 1.php
:
<?phprequire '../dir1/1.php';echo $aaa . PHP_EOL;
?>
在VSCode里运行,是没有问题的,在浏览器里访问 http://localhost/dir2/test0504_3.php
,也没有问题。
修改文件如下:
<?phpecho "__DIR__ : " . __DIR__ . PHP_EOL;echo "get_include_path() : " . get_include_path() . PHP_EOL;echo "getcwd() : " . getcwd() . PHP_EOL;echo "realpath('../dir1/1.php') : " . realpath('../dir1/1.php') . PHP_EOL; echo "stream_resolve_include_path('../dir1/1.php') : " . stream_resolve_include_path('../dir1/1.php') . PHP_EOL;require '../dir1/1.php';echo $aaa . PHP_EOL;
?>
其中:
__DIR__
:脚本文件所在的绝对路径get_include_path()
:PHP查找文件的路径getcwd()
:当前工作目录realpath()
:获取目标路径的绝对路径(比如目标路径可能会含有..
)stream_resolve_include_path()
:在include_path
指定的目录列表中查找目标文件,并返回第一个匹配的绝对路径。若文件不存在,则返回false
在VSCode里运行,输出结果是:
__DIR__ : C:\xampp\htdocs\dir2
get_include_path() : C:\xampp\php\PEAR
getcwd() : C:\xampp\htdocs\dir2
realpath('../dir1/1.php') : C:\xampp\htdocs\dir1\1.php
stream_resolve_include_path('../dir1/1.php') : C:\xampp\htdocs\dir1\1.php
123
如果在命令行里,在 dir2
目录下运行:
PS C:\xampp\htdocs\dir2> php .\test0504_3.php
Xdebug: [Step Debug] Time-out connecting to debugging client, waited: 200 ms. Tried: localhost:9003 (through xdebug.client_host/xdebug.client_port).
__DIR__ : C:\xampp\htdocs\dir2
get_include_path() : C:\xampp\php\PEAR
getcwd() : C:\xampp\htdocs\dir2
realpath('../dir1/1.php') : C:\xampp\htdocs\dir1\1.php
stream_resolve_include_path('../dir1/1.php') : C:\xampp\htdocs\dir1\1.php
123
可见和在VSCode运行是一样的。
注:输出结果里有个连接超时,这是因为 php.ini
里配置了Xdebug。在命令行运行php,会导致Xdebug连接debug client的错误(localhost:9003)。不过并不影响代码运行。加上 -n
选项,表示不使用 php.ini
配置,就不会提示这个信息了。
运行时加上 -n
选项(不使用 php.ini
配置文件):
PS C:\xampp\htdocs\dir2> php -n .\test0504_3.php
__DIR__ : C:\xampp\htdocs\dir2
get_include_path() : .;C:\php\pear
getcwd() : C:\xampp\htdocs\dir2
realpath('../dir1/1.php') : C:\xampp\htdocs\dir1\1.php
stream_resolve_include_path('../dir1/1.php') : C:\xampp\htdocs\dir1\1.php
123
可见,加不加 -n
,影响的是 include_path
。
如果在 dir2
以外的目录里运行,就会出错。比如在 C:\
目录运行:
PS C:\> php -n C:\xampp\htdocs\dir2\test0504_3.php
__DIR__ : C:\xampp\htdocs\dir2
get_include_path() : .;C:\php\pear
getcwd() : C:\
realpath('../dir1/1.php') :
stream_resolve_include_path('../dir1/1.php') :Warning: require(../dir1/1.php): Failed to open stream: No such file or directory in C:\xampp\htdocs\dir2\test0504_3.php on line 8Fatal error: Uncaught Error: Failed opening required '../dir1/1.php' (include_path='.;C:\php\pear') in C:\xampp\htdocs\dir2\test0504_3.php:8
Stack trace:
#0 {main}thrown in C:\xampp\htdocs\dir2\test0504_3.php on line 8
加不加 -n
选项都会报错,因为二者的 include_path
虽然不同,但都没找到所需文件。
可见,当前工作目录是 C:\
时,require语句出错了,因为找不到 ../dir1/1.php
文件。
题外话
如果不是在 dir2
目录里,而是在其父目录 htdocs
目录里创建 test0504_2.php
文件,并修改相对路径,如下:
<?phpecho "__DIR__ : " . __DIR__ . PHP_EOL;echo "get_include_path() : " . get_include_path() . PHP_EOL;echo "getcwd() : " . getcwd() . PHP_EOL;echo "realpath('dir1/1.php') : " . realpath('dir1/1.php') . PHP_EOL; echo "stream_resolve_include_path('dir1/1.php') : " . stream_resolve_include_path('dir1/1.php') . PHP_EOL;require 'dir1/1.php';echo $aaa . PHP_EOL;
?>
则不会报错:
PS C:\> php -n C:\xampp\htdocs\test0504_2.php
C:\
123
明明当前工作目录还是 C:\
,使用相对路径 dir1/1.php
为什么不报错呢?
这会不会与XAMPP或者 php.ini
有关呢?但我尝试停止了Apache,而且加上 -n
选项,甚至把 php.ini
里Xdebug相关的配置都删掉了,但仍然是这样的行为。
我问了DeepSeek,它也答不出个所以然来,大概意思是说:
- 对于非
../
开头的相对路径(如dir1/1.php
),某些PHP版本会"智能"地尝试从脚本所在目录解析 - 对于
../
开头的路径,PHP出于安全考虑会更严格地仅基于当前工作目录解析
总结
不管怎么样,使用相对路径总是不靠谱的,还是使用绝对路径吧。
// 在任何文件中都这样写
require __DIR__ . '/dir1/1.php'; // 当前脚本文件在 htdocs 目录下
require __DIR__ . '/../dir1/1.php'; // 当前脚本文件在 htdocs/dir2 目录下
这里使用了魔术常量 __DIR__
,表示脚本文件的绝对路径。
通过使用 __DIR__
再加上目标文件的相对路径,就得到了目标文件的绝对路径,这样做可以确保:
- 不受工作目录影响
- 不受
include_path
设置影响 - 不受-n参数影响
其它
Windows和Linux的路径分隔符是不同的:
- Windows:
\
- Linux:
/
在上面的代码中,路径分隔符用的都是 /
。在Windows下运行也是没问题的。所以,不管什么操作系统,统一都用 /
好了。
注:如果需要,可以使用 DIRECTORY_SEPARATOR
,以适配不同操作系统,比如 'dir' . DIRECTORY_SEPARATOR . 'file.php'
。不过一般没必要这么做,直接用 /
就行。
参考
https://www.php.net/docs.php