PHP 包含
include vs require, the _once variants, and locating files with __DIR__.
The four statements
| Statement | On failure | Runs again | Use for |
|---|---|---|---|
| include | 警告,继续执行 | 会 | 可选片段、模板 |
| include_once | 警告,继续执行 | 不会 | 函数/类定义 |
| require | 致命错误,终止 | 会 | 必需依赖 |
| require_once | 致命错误,终止 | 不会 | 配置、库、类文件 |
**Real projects mostly use require_once**: failing fast beats running half a system, and _once prevents duplicate definitions.
Return values
An included file may return, and include hands the value back — letting config files return arrays.
<?php
// 动态写一个模块文件,模拟「被包含的文件」
$mod = __DIR__ . "/_mod.php";
file_put_contents($mod, '<?php return ["site" => "我的站点", "ver" => "1.0"];');
$conf = include $mod;
echo $conf["site"] . " " . $conf["ver"], "\n";
$again = include $mod;
echo $conf === $again ? "内容一致" : "内容不同", "\n";
require $mod;
echo "require 执行完毕\n";
include_once $mod;
echo "include_once 跳过\n";
unlink($mod);
echo file_exists($mod) ? "文件还在" : "临时文件已清理", "\n";我的站点 1.0
内容一致
require 执行完毕
include_once 跳过
临时文件已清理require $mod; as a statement discards the value. Assign it to capture: $x = require $mod;.
Variable scope
An included file inherits the including scope — unlike a function. That is exactly how template engines work.
<?php
$mod = __DIR__ . "/_tpl.php";
file_put_contents($mod, '<h1><?= $title ?></h1>');
$title = "页面标题";
include $mod;
echo "\n";
function render(string $tpl, array $vars): void {
extract($vars); // 把数组变成局部变量
include $tpl;
}
render($mod, ["title" => "函数里的标题"]);
echo "\n";
unlink($mod);<h1>页面标题</h1>
<h1>函数里的标题</h1>extract() turns keys into variables — calling it on external data is a backdoor. Use it only inside templates with trusted input.Locating with __DIR__
<?php
// 受工作目录影响,容易找不到
// require "config.php";
// 始终相对本文件
$path = __DIR__ . "/config.php";
echo basename($path), "\n";
// 相对本文件的上一级
$up = dirname(__DIR__) . "/shared/config.php";
echo basename(dirname($up)) . "/" . basename($up), "\n";
// 推荐:用常量表达项目根
define("BASE_PATH", dirname(__DIR__));
echo basename(BASE_PATH), "\n";config.php
shared/config.php
demoWith the file at /var/www/demo/inc/bootstrap.php, __DIR__ is .../inc and dirname(__DIR__) is .../demo. Define BASE_PATH once and build every require on it.
Autoloading instead
Class files need no manual require. Register an autoloader and each class loads on first use — exactly what Composer's vendor/autoload.php does.
<?php
require __DIR__ . "/vendor/autoload.php";
// 之后直接用类,不需要 require 每个文件
$app = new App\Kernel();
echo get_class($app), "\n";App\Kernelrequire_once are PHP 5 style. Modern projects have exactly one require __DIR__ . "/vendor/autoload.php";.四个语句
| 语句 | 失败时 | 重复执行 | 用途 |
|---|---|---|---|
| include | 警告,继续执行 | 会 | 可选片段、模板 |
| include_once | 警告,继续执行 | 不会 | 函数/类定义 |
| require | 致命错误,终止 | 会 | 必需依赖 |
| require_once | 致命错误,终止 | 不会 | 配置、库、类文件 |
**实际项目几乎只用 require_once**:依赖缺失时立刻停下比带着半个系统继续跑安全得多,_once 又避免了重复定义函数导致的致命错误。
返回值
被包含的文件可以 return,include 会把这个值带回来。这让「配置文件」可以写成返回数组的形式。
<?php
// 动态写一个模块文件,模拟「被包含的文件」
$mod = __DIR__ . "/_mod.php";
file_put_contents($mod, '<?php return ["site" => "我的站点", "ver" => "1.0"];');
$conf = include $mod;
echo $conf["site"] . " " . $conf["ver"], "\n";
$again = include $mod;
echo $conf === $again ? "内容一致" : "内容不同", "\n";
require $mod;
echo "require 执行完毕\n";
include_once $mod;
echo "include_once 跳过\n";
unlink($mod);
echo file_exists($mod) ? "文件还在" : "临时文件已清理", "\n";我的站点 1.0
内容一致
require 执行完毕
include_once 跳过
临时文件已清理require $mod; 作为语句使用时丢弃返回值,所以没有输出。要拿返回值就得写成 $x = require $mod;。
变量作用域
被包含的文件继承包含处的变量作用域,不是独立作用域。这是它和函数最大的区别,也是模板引擎能工作的原理。
<?php
$mod = __DIR__ . "/_tpl.php";
file_put_contents($mod, '<h1><?= $title ?></h1>');
$title = "页面标题";
include $mod;
echo "\n";
function render(string $tpl, array $vars): void {
extract($vars); // 把数组变成局部变量
include $tpl;
}
render($mod, ["title" => "函数里的标题"]);
echo "\n";
unlink($mod);<h1>页面标题</h1>
<h1>函数里的标题</h1>extract() 会把数组键变成变量名,用外部数据调它等于开了个后门——攻击者传 ["_SERVER" => ...] 之类能覆盖你的变量。只在模板内部、数据来源可信时用。用 __DIR__ 定位
<?php
// 受工作目录影响,容易找不到
// require "config.php";
// 始终相对本文件
$path = __DIR__ . "/config.php";
echo basename($path), "\n";
// 相对本文件的上一级
$up = dirname(__DIR__) . "/shared/config.php";
echo basename(dirname($up)) . "/" . basename($up), "\n";
// 推荐:用常量表达项目根
define("BASE_PATH", dirname(__DIR__));
echo basename(BASE_PATH), "\n";config.php
shared/config.php
demo上面假设文件在 /var/www/demo/inc/bootstrap.php:__DIR__ 是 /var/www/demo/inc,dirname(__DIR__) 是 /var/www/demo。项目里定义一次 BASE_PATH,所有 require 都基于它。
自动加载替代手工包含
类文件不需要一个个 require。注册自动加载器后,用到哪个类就加载哪个。Composer 生成的 vendor/autoload.php 做的就是这件事。
<?php
require __DIR__ . "/vendor/autoload.php";
// 之后直接用类,不需要 require 每个文件
$app = new App\Kernel();
echo get_class($app), "\n";App\Kernelrequire_once 是 PHP 5 时代的写法。现在项目里应该只有一句 require __DIR__ . "/vendor/autoload.php";,其余全靠 PSR-4。