PHP钩子函数怎么注册

wen PHP项目 2

本文目录导读:

PHP钩子函数怎么注册

  1. WordPress风格的钩子系统
  2. 使用事件监听器(Event Listener)
  3. 中间件模式(Middleware Pattern)
  4. 使用属性(Attributes)注册(PHP 8+)
  5. 使用Composer事件(Composer Events)
  6. 最佳实践

PHP钩子(Hook)是一种设计模式,允许在特定事件发生时执行自定义代码,以下是几种常见的PHP钩子注册方式:

WordPress风格的钩子系统

class HookManager {
    private static $filters = [];
    private static $actions = [];
    // 注册过滤器(修改数据)
    public static function addFilter($hook, $callback, $priority = 10, $args = 1) {
        self::$filters[$hook][$priority][] = [
            'callback' => $callback,
            'args' => $args
        ];
    }
    // 注册动作(执行操作)
    public static function addAction($hook, $callback, $priority = 10, $args = 1) {
        self::$actions[$hook][$priority][] = [
            'callback' => $callback,
            'args' => $args
        ];
    }
    // 执行过滤器
    public static function applyFilters($hook, $value) {
        if (!isset(self::$filters[$hook])) {
            return $value;
        }
        $args = array_slice(func_get_args(), 2);
        ksort(self::$filters[$hook]);
        foreach (self::$filters[$hook] as $priority => $callbacks) {
            foreach ($callbacks as $filter) {
                $callback_args = array_merge(array($value), $args);
                $value = call_user_func_array($filter['callback'], 
                    array_slice($callback_args, 0, $filter['args']));
            }
        }
        return $value;
    }
    // 执行动作
    public static function doAction($hook, ...$args) {
        if (!isset(self::$actions[$hook])) {
            return;
        }
        ksort(self::$actions[$hook]);
        foreach (self::$actions[$hook] as $priority => $callbacks) {
            foreach ($callbacks as $action) {
                call_user_func_array($action['callback'], 
                    array_slice($args, 0, $action['args']));
            }
        }
    }
}
// 注册钩子
HookManager::addFilter('text_content', function($text) {
    return strtoupper($text);
}, 10, 1);
HookManager::addAction('user_login', function($username) {
    // 记录日志
    error_log("用户登录: $username");
}, 10, 1);
// 触发钩子
echo HookManager::applyFilters('text_content', 'hello world');
HookManager::doAction('user_login', 'JohnDoe');

使用事件监听器(Event Listener)

interface EventListenerInterface {
    public function handle($event, $data);
}
class EventDispatcher {
    private $listeners = [];
    // 注册监听器
    public function addListener($event, EventListenerInterface $listener, $priority = 0) {
        $this->listeners[$event][$priority][] = $listener;
        krsort($this->listeners[$event]);
    }
    // 触发事件
    public function dispatch($event, $data = []) {
        if (!isset($this->listeners[$event])) {
            return;
        }
        foreach ($this->listeners[$event] as $listeners) {
            foreach ($listeners as $listener) {
                $listener->handle($event, $data);
            }
        }
    }
}
class UserEventListener implements EventListenerInterface {
    public function handle($event, $data) {
        switch ($event) {
            case 'user.created':
                $this->sendWelcomeEmail($data['email']);
                break;
            case 'user.deleted':
                $this->cleanupUserData($data['user_id']);
                break;
        }
    }
}
$dispatcher = new EventDispatcher();
$dispatcher->addListener('user.created', new UserEventListener());
$dispatcher->dispatch('user.created', ['email' => 'test@example.com']);

中间件模式(Middleware Pattern)

class Middleware {
    private $handlers = [];
    // 注册中间件
    public function use(callable $handler) {
        $this->handlers[] = $handler;
    }
    public function handle($request, callable $finalHandler) {
        $middleware = array_reverse($this->handlers);
        $next = $finalHandler;
        // 从最后一个中间件开始包装
        foreach ($middleware as $handler) {
            $next = function($req) use ($handler, $next) {
                return $handler($req, $next);
            };
        }
        return $next($request);
    }
}
$app = new Middleware();
$app->use(function($request, $next) {
    // 日志中间件
    error_log("开始处理请求");
    $response = $next($request);
    error_log("请求处理完成");
    return $response;
});
$app->use(function($request, $next) {
    // 认证中间件
    if (!$request['authenticated']) {
        return "未授权访问";
    }
    return $next($request);
});
// 最终处理器
$response = $app->handle(['authenticated' => true], function($request) {
    return "处理业务逻辑";
});

使用属性(Attributes)注册(PHP 8+)

#[Attribute]
class Hook {
    public function __construct(
        public string $name,
        public int $priority = 10
    ) {}
}
class HooksCollector {
    public static function registerFromClass(string $className) {
        $reflection = new ReflectionClass($className);
        $methods = $reflection->getMethods();
        foreach ($methods as $method) {
            $attributes = $method->getAttributes(Hook::class);
            foreach ($attributes as $attribute) {
                $hook = $attribute->newInstance();
                HookManager::addAction(
                    $hook->name,
                    [$className, $method->getName()],
                    $hook->priority
                );
            }
        }
    }
}
class UserHooks {
    #[Hook('user.created', 5)]
    public static function onUserCreated($userId) {
        error_log("用户已创建: $userId");
    }
    #[Hook('user.deleted')]
    public static function onUserDeleted($userId) {
        // 清理相关数据
    }
}
// 自动注册所有钩子
HooksCollector::registerFromClass(UserHooks::class);

使用Composer事件(Composer Events)

利用Composer的autoload机制实现简单的钩子:

// hook.php 文件
return [
    'hooks' => [
        'before_render' => [
            function($content) {
                return str_replace(['<script>', '</script>'], '', $content);
            },
            function($content) {
                return nl2br($content);
            }
        ],
        'after_save' => [
            function($data) {
                // 缓存清理
            }
        ]
    ]
];
// 加载并使用
class HookLoader {
    private $hooks;
    public function __construct() {
        $this->hooks = require 'hooks.php';
    }
    public function getHooks($name) {
        return $this->hooks['hooks'][$name] ?? [];
    }
}

最佳实践

  1. 命名约定:使用清晰的事件名称(如user.loginorder.created
  2. 优先级处理:支持优先级排序,让高级别优先执行
  3. 参数传递:明确传递参数数量和类型
  4. 错误处理:在钩子执行时捕获异常
  5. 文档记录:记录所有已注册的钩子及其用途

选择哪种方式取决于项目需求,简单项目用函数回调即可,大型框架建议使用完整的事件系统。

抱歉,评论功能暂时关闭!