include 和 require
include 和 require 是 PHP 中用于将一个文件的内容包含到当前脚本中的语言结构。它们是实现代码复用和模块化的基础机制。两者的核心区别在于:require 在文件不存在时产生致命错误(E_ERROR),脚本终止;include 在文件不存在时产生警告(E_WARNING),脚本继续执行。
基础概念
| 特性 | include | require |
|---|---|---|
| 文件不存在 | E_WARNING(警告),脚本继续 | E_ERROR(致命错误),脚本终止 |
| 文件不可读 | E_WARNING(警告),脚本继续 | E_ERROR(致命错误),脚本终止 |
| 适用场景 | 可选模块、模板、条件加载 | 核心依赖、配置文件、必需组件 |
| 性能 | 每次包含都重新解析 | 每次包含都重新解析(首次) |
| 重复包含 | 可以多次包含同一文件 | 可以多次包含同一文件 |
语言结构而非函数
include 和 require 是语言结构(language construct),不是函数。因此它们不需要括号包裹参数(虽然加括号也不会报错)。推荐不加括号:include 'file.php' 而非 include('file.php')。
语法结构
基本 include 用法
<?php
declare(strict_types=1);
// 包含一个文件
include 'helpers.php';
// 使用括号(不推荐,但合法)
// include('helpers.php');基本 require 用法
<?php
declare(strict_types=1);
// 包含核心配置文件
require 'config/database.php';
// 包含核心库文件
require 'lib/Router.php';include 在条件语句中使用
include 可以在条件语句中使用,根据条件决定是否包含文件。
<?php
declare(strict_types=1);
$debugMode = true;
// 只在调试模式下包含调试工具
if ($debugMode) {
include 'tools/debugger.php';
}
// 根据平台包含不同的文件
$platform = PHP_OS_FAMILY;
if ($platform === 'Darwin') {
include 'config/macos.php';
} elseif ($platform === 'Linux') {
include 'config/linux.php';
} elseif ($platform === 'Windows') {
include 'config/windows.php';
}include 的返回值
被包含的文件如果使用了 return 语句,include 会返回该值。如果被包含的文件没有 return,则返回 1(成功包含)。
<?php
declare(strict_types=1);
// 创建临时配置文件
$tempFile = sys_get_temp_dir() . '/test_config.php';
file_put_contents($tempFile, '<?php return ["name" => "test", "version" => "1.0"];');
// 使用 include 获取返回值
$config = include $tempFile;
if (is_array($config)) {
echo "应用:{$config['name']},版本:{$config['version']}\n";
}
// 没有 return 的文件返回 1
$simpleFile = sys_get_temp_dir() . '/simple.php';
file_put_contents($simpleFile, '<?php echo "Hello";');
$result = include $simpleFile;
echo "\ninclude 返回值:" . ($result === 1 ? '1' : $result) . "\n";
// 清理
unlink($tempFile);
unlink($simpleFile);include 在循环中使用
<?php
declare(strict_types=1);
// 在循环中包含多个配置文件
$modules = ['users', 'products', 'orders'];
foreach ($modules as $module) {
$filePath = sys_get_temp_dir() . "/config_{$module}.php";
// 创建临时文件
file_put_contents($filePath, "<?php return ['module' => '{$module}'];");
$config = include $filePath;
echo "加载模块:{$config['module']}\n";
unlink($filePath);
}详细说明
include vs require 的错误处理差异
<?php
declare(strict_types=1);
// include:文件不存在时产生警告,脚本继续
echo "尝试 include 不存在的文件\n";
@include 'nonexistent_file.php'; // 使用 @ 抑制警告
echo "脚本继续执行(include 后)\n";
// require:文件不存在时产生致命错误,脚本终止
// require 'nonexistent_file.php'; // Fatal error,后续代码不执行
// echo "这行不会执行\n";何时使用 require
对于脚本的正常运作所必需的文件(如数据库配置、路由定义、核心类库),应使用 require。对于可选的或条件性的功能模块,应使用 include。
include_path 配置
PHP 的 include_path 配置项定义了 include 和 require 搜索文件的目录列表。当指定相对路径时,PHP 会先在当前目录查找,然后按 include_path 中的顺序搜索。
<?php
declare(strict_types=1);
// 查看当前 include_path
echo "当前 include_path:\n " . get_include_path() . "\n\n";
// 添加自定义路径到 include_path
set_include_path(
get_include_path() . PATH_SEPARATOR . __DIR__ . '/libs'
);
echo "修改后 include_path:\n " . get_include_path() . "\n";include 的性能考虑
性能建议
在现代 PHP 应用中,使用 OPcache 可以缓存编译后的 PHP 字节码,因此 include/require 的性能问题已被大幅缓解。对于现代框架项目,通常使用 Composer 的自动加载(autoloading)代替手动 include/require。
<?php
declare(strict_types=1);
// 传统方式:手动 include 每个类文件
// require 'classes/User.php';
// require 'classes/Order.php';
// require 'classes/Product.php';
// 现代方式:使用 Composer 自动加载
// require 'vendor/autoload.php';
// 现代方式:在框架中,自动加载由框架引导文件处理
// 例如 Laravel 的 bootstrap/app.php文件包含与变量作用域
被包含的文件继承包含它的脚本中的变量作用域。在被包含的文件中定义的变量、函数、类等,在包含之后也可以在包含方中使用。
<?php
declare(strict_types=1);
// 主脚本中定义变量
$appName = 'MyApp';
$version = '2.0';
// 创建临时包含文件
$includedFile = sys_get_temp_dir() . '/vars.php';
file_put_contents($includedFile, '<?php
$loadedAt = date("Y-m-d H:i:s");
echo "在包含文件中:appName = " . $appName . "\n";
');
include $includedFile;
// 被包含文件中定义的变量在主脚本中也可用
echo "主脚本中:loadedAt = {$loadedAt}\n";
unlink($includedFile);作为表达式使用
<?php
declare(strict_types=1);
// include 可以在表达式中使用
$tempFile = sys_get_temp_dir() . '/data.php';
file_put_contents($tempFile, '<?php return ["count" => 42];');
// 作为函数参数
$totalItems = (include $tempFile)['count'] ?? 0;
echo "总项目数:{$totalItems}\n";
unlink($tempFile);实战示例
配置文件加载模式
<?php
declare(strict_types=1);
class ConfigLoader
{
private array $config = [];
public function load(string $filePath): self
{
if (!file_exists($filePath)) {
throw new \RuntimeException("配置文件不存在:{$filePath}");
}
$data = require $filePath;
if (!is_array($data)) {
throw new \RuntimeException("配置文件必须返回数组");
}
$this->config = array_merge($this->config, $data);
return $this;
}
public function get(string $key, mixed $default = null): mixed
{
return $this->config[$key] ?? $default;
}
public function all(): array
{
return $this->config;
}
}
// 使用示例
$tempDir = sys_get_temp_dir();
$appConfig = $tempDir . '/app.php';
file_put_contents($appConfig, '<?php return ["debug" => true, "timezone" => "Asia/Shanghai"];');
$dbConfig = $tempDir . '/db.php';
file_put_contents($dbConfig, '<?php return ["host" => "localhost", "port" => 3306];');
$loader = new ConfigLoader();
$loader->load($appConfig)
->load($dbConfig);
echo "调试模式:" . ($loader->get('debug') ? '开启' : '关闭') . "\n";
echo "时区:" . $loader->get('timezone') . "\n";
echo "数据库端口:" . $loader->get('port') . "\n";
// 清理
unlink($appConfig);
unlink($dbConfig);按需加载模块
<?php
declare(strict_types=1);
class ModuleLoader
{
private array $loadedModules = [];
private string $modulesPath;
public function __construct(string $modulesPath)
{
$this->modulesPath = $modulesPath;
}
public function load(string $moduleName): bool
{
if (isset($this->loadedModules[$moduleName])) {
return true; // 已加载
}
$filePath = "{$this->modulesPath}/{$moduleName}.php";
if (!file_exists($filePath)) {
return false;
}
include $filePath;
$this->loadedModules[$moduleName] = true;
return true;
}
public function isLoaded(string $moduleName): bool
{
return isset($this->loadedModules[$moduleName]);
}
public function getLoadedModules(): array
{
return array_keys($this->loadedModules);
}
}
// 演示
$loader = new ModuleLoader(sys_get_temp_dir());
// 创建临时模块文件
$modules = ['auth', 'cache', 'logger'];
foreach ($modules as $mod) {
$path = sys_get_temp_dir() . "/{$mod}.php";
file_put_contents($path, "<?php echo \"模块 {$mod} 已加载\\n\";");
}
foreach ($modules as $mod) {
if ($loader->load($mod)) {
echo "{$mod} 加载成功\n";
}
}
// 再次加载已加载的模块(不会重复)
$loader->load('auth');
echo "已加载模块:" . implode(', ', $loader->getLoadedModules()) . "\n";
// 清理
foreach ($modules as $mod) {
unlink(sys_get_temp_dir() . "/{$mod}.php");
}注意事项
路径安全:不要将用户输入直接作为
include的路径,这可能导致远程文件包含(RFI)漏洞。被包含文件的返回值:如果被包含文件有
return,include会返回该值;如果没有,返回1。变量污染:被包含的文件与包含方共享同一作用域,注意变量名冲突。
使用
require保护核心文件:对于必需的配置和库文件,使用require确保缺失时脚本立即终止。现代项目使用自动加载:使用 Composer 的 PSR-4 自动加载代替手动
include类文件。
最佳实践
核心依赖用
require:配置文件、引导文件、核心库用require。可选模块用
include:模板、插件、条件性功能用include。配置文件用
return:被包含的配置文件使用return返回数组。使用 Composer 自动加载:类文件的加载应交给 Composer 的 PSR-4 自动加载处理。
始终使用绝对路径:配合
__DIR__常量使用绝对路径,避免相对路径的不确定性。
下一节
include_once 和 require_once 确保文件只被包含一次。接下来学习 include_once/require_once。