本文目录导读:

我来帮你了解 PHP 的预提取(预加载/预加载机制)。
PHP 预加载(Preloading)
PHP 8.0 引入了预加载(Preloading)功能,允许在服务器启动时预先加载 PHP 文件到内存中。
基本概念
// opcache.preload 配置文件 // 在 php.ini 中设置 opcache.preload=/path/to/preload.php opcache.preload_user=www-data // 可选,指定预加载的用户
创建预加载脚本
<?php
// preload.php - 预加载脚本
// 方法1:手动预加载类
require_once __DIR__ . '/src/Database.php';
require_once __DIR__ . '/src/User.php';
require_once __DIR__ . '/src/Logger.php';
// 方法2:自动扫描目录
function preloadDirectory($dir) {
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir),
RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($files as $file) {
if ($file->getExtension() === 'php') {
require_once $file->getPathname();
}
}
}
preloadDirectory(__DIR__ . '/src');
预加载优化示例
<?php
// optimized_preload.php
class Preloader {
private array $classes = [];
private array $paths = [];
public function addPath(string $path): self {
$this->paths[] = $path;
return $this;
}
public function exclude(string $pattern): self {
// 排除不需要预加载的类
$this->excludes[] = $pattern;
return $this;
}
public function load(): void {
foreach ($this->paths as $path) {
$this->loadDirectory($path);
}
}
private function loadDirectory(string $dir): void {
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir),
RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($files as $file) {
if ($file->getExtension() === 'php') {
require_once $file->getPathname();
}
}
}
}
// 使用预加载器
$preloader = new Preloader();
$preloader
->addPath(__DIR__ . '/vendor')
->addPath(__DIR__ . '/app')
->load();
PHP 7.4+ Opcache 预加载
; php.ini 配置 opcache.enable=1 opcache.memory_consumption=256 opcache.interned_strings_buffer=16 opcache.max_accelerated_files=10000 opcache.revalidate_freq=2 opcache.fast_shutdown=1 opcache.enable_cli=1
性能监控与验证
<?php
// check_preload.php
// 检查预加载状态
echo "Opcache 状态:\n";
print_r(opcache_get_status());
// 检查特定类是否预加载
$class = 'App\\Services\\DatabaseService';
if (opcache_is_script_cached(__DIR__ . '/src/DatabaseService.php')) {
echo "类 {$class} 已缓存\n";
}
// 查看预加载统计
$status = opcache_get_status();
echo "预加载文件数量: " . count($status['scripts'] ?? []) . "\n";
echo "缓存命中率: " . ($status['opcache_statistics']['hits'] ?? 0) . "\n";
实战示例:Laravel 预加载配置
<?php
// config/preload.php
namespace App\Preload;
class LaravelPreloader {
public function __invoke(): void {
$this->preloadCore();
$this->preloadServices();
$this->preloadModels();
}
private function preloadCore(): void {
$files = [
base_path('vendor/laravel/framework/src/Illuminate/Support/Str.php'),
base_path('vendor/laravel/framework/src/Illuminate/Support/Collection.php'),
base_path('vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php'),
];
foreach ($files as $file) {
if (file_exists($file)) {
require_once $file;
}
}
}
private function preloadServices(): void {
$path = app_path('Services');
if (is_dir($path)) {
$files = glob($path . '/*.php');
foreach ($files as $file) {
require_once $file;
}
}
}
private function preloadModels(): void {
$path = app_path('Models');
if (is_dir($path)) {
$files = glob($path . '/*.php');
foreach ($files as $file) {
require_once $file;
}
}
}
}
// 在 index.php 或框架入口调用
(new \App\Preload\LaravelPreloader)();
注意事项
<?php
// preload_caution.php
/**
* 预加载注意事项:
*
* 1. 只加载稳定的、不常修改的类
* 2. 避免加载包含数据库连接的文件
* 3. 注意内存消耗
* 4. 开发环境不建议启用
* 5. 修改预加载文件后需要重启服务器
*/
class PreloadValidator {
public static function validate(string $file): bool {
// 检查文件大小
if (filesize($file) > 1024 * 1024) {
return false; // 超过1MB不预加载
}
// 检查文件内容
$content = file_get_contents($file);
// 排除包含数据库连接的文件
if (preg_match('/new\s+PDO|mysqli_connect|mysql_connect/', $content)) {
return false;
}
// 排除测试文件
if (strpos($file, '/tests/') !== false ||
strpos($file, '/Tests/') !== false) {
return false;
}
return true;
}
}
性能测试对比
<?php
// benchmark_preload.php
$startTime = microtime(true);
$startMemory = memory_get_usage();
// 测试预加载效果
for ($i = 0; $i < 1000; $i++) {
new SomeClass(); // 假设这是一个预加载的类
}
$endTime = microtime(true);
$endMemory = memory_get_usage();
echo "执行时间: " . ($endTime - $startTime) * 1000 . "ms\n";
echo "内存使用: " . ($endMemory - $startMemory) / 1024 . "KB\n";
最佳实践建议
- 生产环境使用:预加载主要用于生产环境
- 合理选择类:只预加载频繁使用的核心类
- 监控性能:使用
opcache_get_status()监控效果 - 重启策略:修改预加载文件后必须重启 PHP-FPM
这样可以显著提升框架应用(如 Laravel、Symfony)的性能,减少每次请求的文件加载开销。