PHP项目插件机制与热加载实现
插件机制设计
插件接口定义
<?php
// PluginInterface.php
interface PluginInterface {
public function getName(): string;
public function getVersion(): string;
public function initialize(): void;
public function activate(): void;
public function deactivate(): void;
public function getHooks(): array;
}
// Hookable.php
interface Hookable {
public function execute(string $hook, ...$args);
public function addHook(string $hook, callable $callback, int $priority = 10);
public function removeHook(string $hook, callable $callback);
}
插件管理器核心类
<?php
// PluginManager.php
class PluginManager implements Hookable {
private static ?PluginManager $instance = null;
private array $plugins = [];
private array $hooks = [];
private string $pluginDir;
private string $cacheDir;
private bool $hotReloadEnabled = false;
private function __construct(string $pluginDir, string $cacheDir) {
$this->pluginDir = $pluginDir;
$this->cacheDir = $cacheDir;
$this->initializeAutoloader();
}
public static function getInstance(string $pluginDir = '', string $cacheDir = ''): self {
if (self::$instance === null) {
self::$instance = new self($pluginDir ?: __DIR__ . '/plugins',
$cacheDir ?: __DIR__ . '/cache');
}
return self::$instance;
}
private function initializeAutoloader(): void {
spl_autoload_register(function ($class) {
// 检查插件目录
foreach ($this->plugins as $plugin) {
$file = $plugin['dir'] . '/' . str_replace('\\', '/', $class) . '.php';
if (file_exists($file)) {
require_once $file;
return true;
}
}
return false;
});
}
// 扫描并加载所有插件
public function loadAllPlugins(): void {
$directories = glob($this->pluginDir . '/*', GLOB_ONLYDIR);
foreach ($directories as $dir) {
$this->loadPlugin($dir);
}
}
// 加载单个插件
public function loadPlugin(string $dir): ?PluginInterface {
$manifest = $dir . '/manifest.json';
$mainFile = $dir . '/main.php';
if (!file_exists($manifest) || !file_exists($mainFile)) {
return null;
}
try {
$config = json_decode(file_get_contents($manifest), true);
if (!isset($config['main_class'])) {
throw new \Exception("Missing main_class in manifest");
}
require_once $mainFile;
$className = $config['main_class'];
if (!class_exists($className)) {
throw new \Exception("Class $className not found");
}
$plugin = new $className();
if (!$plugin instanceof PluginInterface) {
throw new \Exception("Plugin must implement PluginInterface");
}
$plugin->initialize();
$this->plugins[$plugin->getName()] = [
'instance' => $plugin,
'dir' => $dir,
'config' => $config,
'loaded' => true
];
// 注册插件钩子
$this->registerPluginHooks($plugin);
return $plugin;
} catch (\Exception $e) {
error_log("Failed to load plugin from $dir: " . $e->getMessage());
return null;
}
}
// 注册插件钩子
private function registerPluginHooks(PluginInterface $plugin): void {
foreach ($plugin->getHooks() as $hook => $callbacks) {
foreach ($callbacks as $callback) {
$this->addHook($hook, $callback[0], $callback[1] ?? 10);
}
}
}
// 执行钩子
public function execute(string $hook, ...$args) {
if (!isset($this->hooks[$hook])) {
return $args[0] ?? null;
}
$result = $args[0] ?? null;
foreach ($this->hooks[$hook] as $callback) {
$result = call_user_func_array($callback, [$result, ...$args]);
}
return $result;
}
// 添加钩子
public function addHook(string $hook, callable $callback, int $priority = 10): void {
$this->hooks[$hook][$priority][] = $callback;
ksort($this->hooks[$hook]); // 按优先级排序
}
// 移除钩子
public function removeHook(string $hook, callable $callback): void {
if (isset($this->hooks[$hook])) {
foreach ($this->hooks[$hook] as $priority => &$callbacks) {
$callbacks = array_filter($callbacks, function ($cb) use ($callback) {
return $cb !== $callback;
});
}
}
}
// 启用热加载
public function enableHotReload(): void {
$this->hotReloadEnabled = true;
if (php_sapi_name() === 'cli') {
$this->enableCLIHotReload();
} else {
$this->enableWebHotReload();
}
}
// CLI模式热加载
private function enableCLIHotReload(): void {
$this->initialFileHash = $this->getPluginDirectoryHash();
while (true) {
usleep(500000); // 0.5秒检查一次
$currentHash = $this->getPluginDirectoryHash();
if ($currentHash !== $this->initialFileHash) {
echo "Detected plugin changes, reloading...\n";
$this->reloadAllPlugins();
$this->initialFileHash = $currentHash;
}
}
}
// Web模式热加载
private function enableWebHotReload(): void {
// 使用文件修改时间检查
$cacheKey = 'plugin_hash_' . md5($this->pluginDir);
$lastHash = $this->getCache($cacheKey);
$currentHash = $this->getPluginDirectoryHash();
if ($lastHash !== $currentHash) {
$this->reloadAllPlugins();
$this->setCache($cacheKey, $currentHash);
}
}
// 重新加载所有插件
public function reloadAllPlugins(): void {
// 停用所有插件
foreach ($this->plugins as $name => $pluginInfo) {
$pluginInfo['instance']->deactivate();
}
// 清空插件和钩子
$this->plugins = [];
$this->hooks = [];
// 重新加载
$this->loadAllPlugins();
}
// 重新加载单个插件
public function reloadPlugin(string $name): bool {
if (isset($this->plugins[$name])) {
// 停用并移除
$this->plugins[$name]['instance']->deactivate();
unset($this->plugins[$name]);
// 重新加载
$dir = $this->plugins[$name]['dir'] ?? $this->pluginDir . '/' . $name;
return $this->loadPlugin($dir) !== null;
}
return false;
}
// 获取插件目录哈希
private function getPluginDirectoryHash(): string {
$hash = '';
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($this->pluginDir)
);
foreach ($iterator as $file) {
if ($file->isFile() && $file->getExtension() === 'php') {
$hash .= md5_file($file->getRealPath());
}
}
return md5($hash);
}
// 缓存操作
private function getCache(string $key): ?string {
$file = $this->cacheDir . '/' . $key;
if (file_exists($file)) {
return file_get_contents($file);
}
return null;
}
private function setCache(string $key, string $value): void {
if (!is_dir($this->cacheDir)) {
mkdir($this->cacheDir, 0755, true);
}
file_put_contents($this->cacheDir . '/' . $key, $value);
}
// 获取所有插件信息
public function getPlugins(): array {
return $this->plugins;
}
// 私有化克隆方法
private function __clone() {}
private function __wakeup() {}
}
示例插件实现
示例插件:日志记录插件
<?php
// plugins/logger/manifest.json
/*
{
"name": "logger",
"version": "1.0.0",
"main_class": "LoggerPlugin",
"description": "Logs all application events"
}
*/
// plugins/logger/main.php
class LoggerPlugin implements PluginInterface {
private string $logFile;
public function getName(): string {
return 'Logger Plugin';
}
public function getVersion(): string {
return '1.0.0';
}
public function initialize(): void {
$this->logFile = __DIR__ . '/logs/app.log';
if (!is_dir(dirname($this->logFile))) {
mkdir(dirname($this->logFile), 0755, true);
}
}
public function activate(): void {
$pluginManager = PluginManager::getInstance();
// 注册钩子
$pluginManager->addHook('after_request', [$this, 'logRequest'], 10);
$pluginManager->addHook('after_error', [$this, 'logError'], 20);
}
public function deactivate(): void {
$pluginManager = PluginManager::getInstance();
$pluginManager->removeHook('after_request', [$this, 'logRequest']);
$pluginManager->removeHook('after_error', [$this, 'logError']);
}
public function getHooks(): array {
return [
'after_request' => [[[$this, 'logRequest'], 10]],
'after_error' => [[[$this, 'logError'], 20]]
];
}
public function logRequest($response, $request = null) {
$logEntry = sprintf(
"[%s] %s %s\n",
date('Y-m-d H:i:s'),
$_SERVER['REQUEST_METHOD'] ?? 'UNKNOWN',
$_SERVER['REQUEST_URI'] ?? 'UNKNOWN'
);
file_put_contents($this->logFile, $logEntry, FILE_APPEND);
return $response;
}
public function logError($error) {
$logEntry = sprintf(
"[%s] ERROR: %s\n",
date('Y-m-d H:i:s'),
$error
);
file_put_contents($this->logFile, $logEntry, FILE_APPEND);
return $error;
}
}
缓存插件示例
<?php
// plugins/cache/manifest.json
/*
{
"name": "cache",
"version": "1.0.0",
"main_class": "CachePlugin",
"description": "Response caching plugin"
}
*/
// plugins/cache/main.php
class CachePlugin implements PluginInterface {
private array $cache = [];
private int $ttl = 3600;
public function getName(): string {
return 'Cache Plugin';
}
public function getVersion(): string {
return '1.0.0';
}
public function initialize(): void {
// 初始化缓存
}
public function activate(): void {
$pluginManager = PluginManager::getInstance();
// 高优先级缓存钩子
$pluginManager->addHook('before_response', [$this, 'getCached'], 1);
$pluginManager->addHook('after_response', [$this, 'setCached'], 100);
}
public function deactivate(): void {
$pluginManager = PluginManager::getInstance();
$pluginManager->removeHook('before_response', [$this, 'getCached']);
$pluginManager->removeHook('after_response', [$this, 'setCached']);
$this->clearCache();
}
public function getHooks(): array {
return [
'before_response' => [[[$this, 'getCached'], 1]],
'after_response' => [[[$this, 'setCached'], 100]]
];
}
public function getCached($response, $request = null) {
$key = $this->generateKey($request);
if (isset($this->cache[$key]) && $this->cache[$key]['expires'] > time()) {
return $this->cache[$key]['data'];
}
return $response;
}
public function setCached($response, $request = null) {
$key = $this->generateKey($request);
$this->cache[$key] = [
'data' => $response,
'expires' => time() + $this->ttl
];
return $response;
}
private function generateKey($request): string {
return md5($_SERVER['REQUEST_URI'] ?? '');
}
private function clearCache(): void {
$this->cache = [];
}
}
热加载实现细节
文件监控类
<?php
// FileWatcher.php
class FileWatcher {
private string $directory;
private array $fileHashes = [];
private array $callbacks = [];
public function __construct(string $directory) {
$this->directory = $directory;
$this->scanFiles();
}
public function watch(callable $callback): void {
$this->callbacks[] = $callback;
}
public function checkForChanges(): bool {
$currentHashes = $this->scanFiles();
if ($currentHashes !== $this->fileHashes) {
$this->fileHashes = $currentHashes;
$this->notifyCallbacks();
return true;
}
return false;
}
private function scanFiles(): array {
$hashes = [];
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($this->directory)
);
foreach ($iterator as $file) {
if ($file->isFile() && in_array($file->getExtension(), ['php', 'json'])) {
$hashes[$file->getRealPath()] = [
'md5' => md5_file($file->getRealPath()),
'mtime' => $file->getMTime()
];
}
}
return $hashes;
}
private function notifyCallbacks(): void {
foreach ($this->callbacks as $callback) {
call_user_func($callback, $this->fileHashes);
}
}
}
热加载整合
<?php
// HotReloadIntegration.php
class HotReloadIntegration {
private PluginManager $pluginManager;
private FileWatcher $fileWatcher;
private bool $running = false;
public function __construct(PluginManager $pluginManager) {
$this->pluginManager = $pluginManager;
$this->fileWatcher = new FileWatcher($pluginManager->pluginDir);
}
public function start(): void {
$this->running = true;
$this->fileWatcher->watch(function ($hashes) {
$this->handleChanges($hashes);
});
// 启动轮询
while ($this->running) {
$this->fileWatcher->checkForChanges();
usleep(500000); // 0.5秒
}
}
public function stop(): void {
$this->running = false;
}
private function handleChanges(array $hashes): void {
echo "Plugin changes detected, reloading...\n";
// 获取更改的文件
$changedFiles = $this->getChangedFiles($hashes);
foreach ($changedFiles as $file) {
$pluginName = $this->extractPluginName($file);
if ($pluginName) {
$this->pluginManager->reloadPlugin($pluginName);
}
}
}
private function getChangedFiles(array $currentHashes): array {
// 比较当前和之前的哈希值
// 实现省略...
return [];
}
private function extractPluginName(string $filePath): ?string {
// 从文件路径提取插件名称
preg_match('/plugins\/([^\/]+)\//', $filePath, $matches);
return $matches[1] ?? null;
}
}
使用示例
基本用法
<?php
// index.php
require_once 'PluginManager.php';
require_once 'PluginInterface.php';
require_once 'FileWatcher.php';
// 初始化插件管理器
$pluginManager = PluginManager::getInstance(__DIR__ . '/plugins', __DIR__ . '/cache');
// 加载所有插件
$pluginManager->loadAllPlugins();
// 激活所有插件
foreach ($pluginManager->getPlugins() as $name => $pluginInfo) {
$pluginInfo['instance']->activate();
}
// 启用热加载(可选)
$pluginManager->enableHotReload();
// 使用钩子系统
$response = $pluginManager->execute('before_response', $initialResponse);
// ... 处理请求 ...
$response = $pluginManager->execute('after_response', $finalResponse);
// 输出最终响应
echo $response;
CLI模式热加载
<?php // hotreload.php require_once 'PluginManager.php'; require_once 'HotReloadIntegration.php'; $pluginManager = PluginManager::getInstance(); $pluginManager->loadAllPlugins(); $hotReload = new HotReloadIntegration($pluginManager); $hotReload->start(); // 开始监控
注意事项
- 性能考虑:文件监控会消耗系统资源,生产环境建议使用缓存
- 安全性:确保插件目录有适当权限,防止恶意插件注入
- 兼容性:不同PHP版本对匿名类、闭包等支持不同
- 依赖管理:插件可能需要特定的PHP扩展或库
- 错误处理:插件加载失败不应影响主应用运行
这个实现提供了完整的插件机制和热加载支持,可以根据实际需求进行扩展和优化。

版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播