定制 kristos80/hook 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

kristos80/hook

Composer 安装命令:

composer require kristos80/hook

包简介

A lightweight, WordPress-style hooks system for PHP. Add actions and filters with priority support to create extensible, event-driven applications.

README 文档

README

A lightweight, WordPress-style hooks system for PHP. Add actions and filters with priority support to create extensible, event-driven applications.

Quality Gate Status Bugs Coverage Reliability Rating Security Rating Maintainability Rating Vulnerabilities

Features

  • ✅ WordPress-inspired API (addAction, addFilter, doAction, applyFilter)
  • ✅ Priority-based execution order (with per-hook priority support)
  • ✅ Multiple callbacks per hook
  • ✅ Multiple hook names in a single call
  • ✅ Supports all PHP callable types (closures, functions, static methods, instance methods, invokables)
  • ✅ Optimized sorting (sorted once, cached until modified)
  • ✅ Type-safe with strict types
  • ✅ Interface-based design (HookInterface)
  • ✅ Optional type hint enforcement for callbacks
  • ✅ Priority introspection (getMinPriority, getMaxPriority)
  • ✅ Built-in FIRST and LAST priority constants
  • ✅ Zero dependencies

Installation

composer require kristos80/hook

Usage

Basic Filter

use Kristos80\Hook\Hook;

$hook = new Hook();

// Add a filter
$hook->addFilter('format_title', function(string $title) {
    return strtoupper($title);
});

// Apply the filter
$result = $hook->applyFilter('format_title', 'hello world');
echo $result; // HELLO WORLD

Priority-based Execution

Lower priority numbers run first (default is 10):

$hook->addFilter('modify_value', function(int $value) {
    return $value * 2;
}, 10);

$hook->addFilter('modify_value', function(int $value) {
    return $value + 5;
}, 5); // Runs first

$result = $hook->applyFilter('modify_value', 10);
echo $result; // 30 (first: 10 + 5 = 15, then: 15 * 2 = 30)

Actions

Actions are filters that don't return values:

$hook->addAction('user_login', function() {
    error_log('User logged in');
});

$hook->doAction('user_login');

Multiple Arguments

$hook->addFilter('format_name', function(string $name, string $prefix) {
    return $prefix . ' ' . $name;
});

$result = $hook->applyFilter('format_name', 'John', 'Mr.');
echo $result; // Mr. John

Multiple Hook Names

Register the same callback to multiple hooks at once:

$hook->addAction(['init', 'startup', 'boot'], function() {
    // Initialization logic
});

$hook->doAction('init');    // Executes callback
$hook->doAction('startup'); // Executes callback
$hook->doAction('boot');    // Executes callback

Per-Hook Priority

When registering multiple hook names, you can assign a different priority to each hook by passing an array of priorities. Each hook name is mapped to the priority at the same index, with a fallback to the first priority if the index doesn't exist:

// Different priority per hook: 'init' gets priority 1, 'save' gets priority 20
$hook->addFilter(['init', 'save'], function(string $data) {
    return $data;
}, [1, 20]);

// Partial array: 'init' gets priority 5, 'save' and 'cleanup' fall back to index 0 (priority 5)
$hook->addAction(['init', 'save', 'cleanup'], function() {
    // ...
}, [5]);

// Single int still works as before — all hooks get the same priority
$hook->addFilter(['init', 'save'], $callback, 10);

Callable Types

The library accepts any valid PHP callable:

// Closure
$hook->addFilter('my_filter', function(string $value) {
    return strtoupper($value);
});

// Function name (string)
$hook->addFilter('my_filter', 'strtoupper');

// Static method (string)
$hook->addFilter('my_filter', 'MyClass::transform');

// Static method (array)
$hook->addFilter('my_filter', [MyClass::class, 'transform']);

// Instance method (array)
$formatter = new TextFormatter();
$hook->addFilter('my_filter', [$formatter, 'format']);

// Invokable object
class MyTransformer {
    public function __invoke(string $value): string {
        return strtoupper($value);
    }
}
$hook->addFilter('my_filter', new MyTransformer());

Enforcing Type Hints on Callbacks

Use the requireTypedParameters named argument to enforce that all callback parameters have type hints:

$hook->addFilter('process_data', function(array $data): array {
    return array_map('strtoupper', $data);
});

// This will work - callback has typed parameters
$result = $hook->applyFilter('process_data', ['hello'], requireTypedParameters: true);

// Register an untyped callback
$hook->addFilter('other_filter', function($value) {
    return $value;
});

// This will throw MissingTypeHintException
$hook->applyFilter('other_filter', 'test', requireTypedParameters: true);

The requireTypedParameters argument is stripped and never passed to callbacks. This feature helps enforce stricter contracts when the hook owner wants to ensure all registered callbacks follow type safety conventions.

Priority Constants

Use HookInterface::FIRST and HookInterface::LAST to guarantee a callback runs before or after all others:

use Kristos80\Hook\HookInterface;

// Guaranteed to run before any other callback
$hook->addFilter('process', function(string $data) {
    return trim($data);
}, HookInterface::FIRST);

// Guaranteed to run after any other callback
$hook->addFilter('process', function(string $data) {
    return htmlspecialchars($data);
}, HookInterface::LAST);

The constants are also accessible via Hook::FIRST and Hook::LAST. Multiple callbacks at the same constant priority follow FIFO order, consistent with the rest of the priority system.

Priority Introspection

Query the lowest or highest registered priority for a given hook:

$hook->addFilter('my_filter', function(string $v) { return $v; }, 5);
$hook->addFilter('my_filter', function(string $v) { return $v; }, 20);
$hook->addFilter('my_filter', function(string $v) { return $v; }, 12);

$hook->getMinPriority('my_filter'); // 5
$hook->getMaxPriority('my_filter'); // 20

// Returns null for hooks with no registered callbacks
$hook->getMinPriority('nonexistent'); // null
$hook->getMaxPriority('nonexistent'); // null

API Reference

addFilter(string|array $hookNames, callable $callback, int|array $priority = 10): void

Add a filter callback to one or more hooks.

  • $hookNames - Hook name(s) to attach to
  • $callback - Callable to execute
  • $priority - Execution priority (lower = earlier, default: 10). Pass an array to assign a different priority per hook name (falls back to index 0 for missing indices)

addAction(string|array $hookNames, callable $callback, int|array $priority = 10): void

Alias for addFilter(). Use for hooks that don't return values.

Note: The $acceptedArgs parameter exists for backwards compatibility but is deprecated and no longer used. PHP natively handles argument count validation.

applyFilter(string $hookName, ...$arg): mixed

Execute all callbacks registered to a filter hook.

  • $hookName - Hook name to execute
  • ...$arg - Arguments to pass to callbacks
  • requireTypedParameters: bool - Named argument to enforce type hints on callbacks (default: false)
  • Returns the filtered value
  • Throws MissingTypeHintException if requireTypedParameters is true and a callback has untyped parameters

doAction(string $hookName, ...$arg): void

Execute all callbacks registered to an action hook.

  • $hookName - Hook name to execute
  • ...$arg - Arguments to pass to callbacks
  • requireTypedParameters: bool - Named argument to enforce type hints on callbacks (default: false)
  • Throws MissingTypeHintException if requireTypedParameters is true and a callback has untyped parameters

HookInterface::FIRST / HookInterface::LAST

Priority constants for guaranteed earliest (PHP_INT_MIN) and latest (PHP_INT_MAX) execution. Also accessible as Hook::FIRST / Hook::LAST.

getMinPriority(string $hookName): ?int

Get the lowest registered priority for a hook.

  • $hookName - Hook name to query
  • Returns the minimum priority value, or null if the hook has no registered callbacks

getMaxPriority(string $hookName): ?int

Get the highest registered priority for a hook.

  • $hookName - Hook name to query
  • Returns the maximum priority value, or null if the hook has no registered callbacks

Interface-based Design

The Hook class implements HookInterface, providing several benefits:

  • Dependency Injection - Type-hint against HookInterface in your constructors and methods, making dependencies explicit and swappable
  • Testability - Easily mock or stub the hook system in unit tests by creating test doubles that implement HookInterface
  • Decoupling - Your code depends on an abstraction rather than a concrete implementation, following the Dependency Inversion Principle
  • Extensibility - Create alternative implementations (e.g., a NullHook for disabled hooks, or a LoggingHook decorator) without modifying existing code
  • Contract Guarantee - The interface defines a clear API contract, ensuring any implementation provides the expected methods
// Type-hint against the interface for better architecture
class UserService {
    public function __construct(
        private HookInterface $hooks
    ) {}

    public function createUser(array $data): User {
        $data = $this->hooks->applyFilter('user_data', $data);
        // ...
    }
}

Testing

./vendor/bin/pest

License

MIT

Author

Christos Athanasiadis - chris.k.athanasiadis@gmail.com

kristos80/hook 适用场景与选型建议

kristos80/hook 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 123 次下载、GitHub Stars 达 0, 最近一次更新时间为 2025 年 10 月 03 日, 在 PHP 生态内属于活跃度较高的组件。

我们在过去多个企业项目中使用过 kristos80/hook 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 kristos80/hook 我们能提供哪些服务?
定制开发 / 二次开发

基于 kristos80/hook 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 123
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 20
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2025-10-03