PHP的include和require

环境

  • Windows 11 专业版
  • XAMPP v3.3.0
    • PHP 8.2.12
    • Apache 2.4.58
  • VSCode 1.99.3

require和include

requireinclude 有点类似于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><?php
    echo "当前页面:$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

requireinclude 的主要区别在于,对于被引用文件不存在时的行为:

  • 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 ,以避免重复定义,冲突报错。

includeinclude_once 也同理。

路径问题

当前工作目录对相对路径的影响

在上面的例子里,引入的是同一目录里的文件,所以没有指定目录:

require 'xxx.php';

这里使用的是相对路径(相对于当前脚本文件)。

使用相对路径的问题是,如果“当前工作目录”发生了变化,则相对路径所代表的绝对路径也改变了。

现有目录结构如下:

htdocs
    |----dir1
    |  |----1.php
    |----dir2
       |----test0504_3.php

1.php 里,定义了变量 $aaa

<?php
    $aaa = 123;
?>

test0504_3.php 里引入 1.php

<?php
    require '../dir1/1.php';
    echo $aaa . PHP_EOL;
?>

在VSCode里运行,是没有问题的,在浏览器里访问 http://localhost/dir2/test0504_3.php ,也没有问题。

修改文件如下:

<?php
    echo "__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 8

Fatal 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 文件,并修改相对路径,如下:

<?php
    echo "__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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值