2021-10-25 19:57:43 -07:00
|
|
|
<?php
|
2025-03-29 10:50:03 +00:00
|
|
|
|
2022-02-14 20:48:15 -08:00
|
|
|
/**
|
|
|
|
|
* Handles registering and triggering events.
|
2021-10-25 19:57:43 -07:00
|
|
|
*
|
2022-02-14 20:48:15 -08:00
|
|
|
* @package NamelessMC\Events
|
|
|
|
|
* @author Samerton
|
|
|
|
|
* @author Aberdeener
|
2025-05-02 19:19:52 +02:00
|
|
|
* @version 2.3.0
|
2022-02-14 20:48:15 -08:00
|
|
|
* @license MIT
|
2021-10-25 19:57:43 -07:00
|
|
|
*/
|
2024-03-09 03:19:55 -08:00
|
|
|
class EventHandler
|
|
|
|
|
{
|
2021-10-29 22:00:05 -07:00
|
|
|
private static array $_events = [];
|
|
|
|
|
private static array $_webhooks = [];
|
2021-10-25 19:57:43 -07:00
|
|
|
|
|
|
|
|
/**
|
2022-02-14 20:48:15 -08:00
|
|
|
* Register webhooks.
|
2021-11-14 19:20:21 +01:00
|
|
|
*
|
2021-10-25 19:57:43 -07:00
|
|
|
* @param array $webhooks Array of webhooks to register
|
|
|
|
|
*/
|
2024-03-09 03:19:55 -08:00
|
|
|
public static function registerWebhooks(array $webhooks): void
|
|
|
|
|
{
|
2021-10-25 19:57:43 -07:00
|
|
|
self::$_webhooks = $webhooks;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-07 22:14:12 -08:00
|
|
|
/**
|
2022-02-14 20:48:15 -08:00
|
|
|
* Register an event.
|
|
|
|
|
* This must be called in the module's constructor.
|
2021-12-07 22:14:12 -08:00
|
|
|
*
|
2025-05-02 19:19:52 +02:00
|
|
|
* @param class-string<AbstractEvent> $event Event to add.
|
|
|
|
|
* @throws Exception
|
2021-12-07 22:14:12 -08:00
|
|
|
*/
|
2025-05-02 19:19:52 +02:00
|
|
|
public static function registerEvent(string $event): void
|
|
|
|
|
{
|
|
|
|
|
if (!(class_exists($event) && is_subclass_of($event, AbstractEvent::class))) {
|
|
|
|
|
throw new Exception('Event param must be a class string of type AbstractEvent');
|
2023-03-01 11:35:10 -08:00
|
|
|
}
|
2022-03-31 19:47:08 +01:00
|
|
|
|
2025-05-02 19:19:52 +02:00
|
|
|
$name = $event::name();
|
|
|
|
|
// We lazy load descriptions for class-based events to avoid loading new Language instances unnecessarily
|
|
|
|
|
$description = fn () => $event::description();
|
|
|
|
|
$internal = $event::internal();
|
|
|
|
|
|
2023-03-01 11:35:10 -08:00
|
|
|
// Don't re-register if the event already exists, just update the params
|
|
|
|
|
// and description. This is to "fix" when registerListener is called
|
|
|
|
|
// for an event that has not been registered yet.
|
|
|
|
|
if (isset(self::$_events[$name])) {
|
|
|
|
|
self::$_events[$name] = [
|
|
|
|
|
'description' => $description,
|
|
|
|
|
'internal' => $internal,
|
|
|
|
|
'listeners' => self::$_events[$name]['listeners'],
|
|
|
|
|
];
|
2024-03-09 03:19:55 -08:00
|
|
|
|
2021-12-07 22:14:12 -08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-01 11:35:10 -08:00
|
|
|
self::$_events[$name] = [
|
2021-12-07 22:14:12 -08:00
|
|
|
'description' => $description,
|
2022-03-31 19:47:08 +01:00
|
|
|
'internal' => $internal,
|
2021-12-07 22:14:12 -08:00
|
|
|
'listeners' => [],
|
2025-05-02 19:19:52 +02:00
|
|
|
'class_name' => $event,
|
2021-12-07 22:14:12 -08:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-22 00:27:05 -08:00
|
|
|
/**
|
|
|
|
|
* Register an event listener for a module.
|
|
|
|
|
* This must be called in the module's constructor.
|
|
|
|
|
*
|
2025-05-02 19:19:52 +02:00
|
|
|
* @param class-string<AbstractEvent> $event Event to listen to.
|
|
|
|
|
* @param callable|class-string $callback Listener callback to execute when event is executed. If class name is provided, we will assume there is a static "execute" method on the class.
|
|
|
|
|
* @param int $priority Execution priority - higher gets executed first
|
|
|
|
|
* @throws Exception
|
2021-12-22 00:27:05 -08:00
|
|
|
*/
|
2024-03-09 03:19:55 -08:00
|
|
|
public static function registerListener(string $event, $callback, int $priority = 10): void
|
|
|
|
|
{
|
2025-05-02 19:19:52 +02:00
|
|
|
if (!(class_exists($event) && is_subclass_of($event, AbstractEvent::class))) {
|
|
|
|
|
throw new Exception('Event param must be a class string of type AbstractEvent');
|
|
|
|
|
}
|
2023-03-01 11:35:10 -08:00
|
|
|
|
2025-05-02 19:19:52 +02:00
|
|
|
$name = $event::name();
|
2023-03-01 11:35:10 -08:00
|
|
|
if (!isset(self::$_events[$name])) {
|
2022-02-14 20:48:15 -08:00
|
|
|
// Silently create event if it doesn't exist, maybe throw exception instead?
|
2025-05-02 19:19:52 +02:00
|
|
|
self::registerEvent($event);
|
2021-12-22 00:27:05 -08:00
|
|
|
}
|
|
|
|
|
|
2023-03-01 11:35:10 -08:00
|
|
|
if (is_string($callback) && class_exists($callback)) {
|
|
|
|
|
$callback = [$callback, 'execute'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self::$_events[$name]['listeners'][] = [
|
2021-12-22 00:27:05 -08:00
|
|
|
'callback' => $callback,
|
2022-03-31 19:47:08 +01:00
|
|
|
'priority' => $priority,
|
2021-12-22 00:27:05 -08:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-25 19:57:43 -07:00
|
|
|
/**
|
|
|
|
|
* Execute an event.
|
2021-11-14 19:20:21 +01:00
|
|
|
*
|
2025-05-02 19:19:52 +02:00
|
|
|
* @template T of AbstractEvent
|
|
|
|
|
* @param T $event
|
|
|
|
|
* @return T
|
2021-10-25 19:57:43 -07:00
|
|
|
*/
|
2025-05-02 19:19:52 +02:00
|
|
|
public static function executeEvent(AbstractEvent $event): AbstractEvent
|
2024-03-09 03:19:55 -08:00
|
|
|
{
|
2025-05-02 19:19:52 +02:00
|
|
|
$name = $event::name();
|
2023-03-01 11:35:10 -08:00
|
|
|
|
2023-04-30 22:05:56 +01:00
|
|
|
if ((defined('DEBUGGING') && DEBUGGING) && class_exists('DebugBar\DebugBar')) {
|
2025-05-02 19:19:52 +02:00
|
|
|
EventCollector::getInstance()->called($name, $event->params());
|
2021-10-25 19:57:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Execute module listeners
|
2023-03-01 11:35:10 -08:00
|
|
|
if (isset(self::$_events[$name]['listeners'])) {
|
|
|
|
|
$listeners = self::$_events[$name]['listeners'];
|
2021-11-27 15:11:04 -08:00
|
|
|
|
2024-03-09 03:19:55 -08:00
|
|
|
usort($listeners, static function ($a, $b) {
|
2022-03-31 19:47:08 +01:00
|
|
|
return $b['priority'] <=> $a['priority'];
|
|
|
|
|
});
|
2021-11-27 15:11:04 -08:00
|
|
|
|
2022-03-31 19:47:08 +01:00
|
|
|
foreach ($listeners as $listener) {
|
2023-03-01 11:35:10 -08:00
|
|
|
$callback = $listener['callback'];
|
2025-05-02 19:19:52 +02:00
|
|
|
$callback($event);
|
2021-10-25 19:57:43 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-01 11:35:10 -08:00
|
|
|
// Execute webhooks
|
2021-10-25 19:57:43 -07:00
|
|
|
foreach (self::$_webhooks as $webhook) {
|
2023-03-01 11:35:10 -08:00
|
|
|
if (in_array($name, $webhook['events'])) {
|
|
|
|
|
// Since forum events are specific to certain hooks, we
|
|
|
|
|
// need to check that this hook is enabled for the event.
|
2025-05-02 19:19:52 +02:00
|
|
|
if (isset($event->available_hooks) && !in_array($webhook['id'], $event->available_hooks)) {
|
2023-03-01 11:35:10 -08:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$callback = $webhook['action'];
|
2025-05-02 19:19:52 +02:00
|
|
|
$callback($event, $webhook['url']);
|
2021-10-25 19:57:43 -07:00
|
|
|
}
|
|
|
|
|
}
|
2022-03-31 19:47:08 +01:00
|
|
|
|
2025-05-02 19:19:52 +02:00
|
|
|
return $event;
|
2021-10-25 19:57:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2023-03-01 11:35:10 -08:00
|
|
|
* Get a list of events to display on the StaffCP webhooks page.
|
2021-11-14 19:20:21 +01:00
|
|
|
*
|
2023-03-01 11:35:10 -08:00
|
|
|
* @return array List of all currently registered events
|
2021-10-25 19:57:43 -07:00
|
|
|
*/
|
2024-03-09 03:19:55 -08:00
|
|
|
public static function getEvents(bool $showInternal = false): array
|
|
|
|
|
{
|
2021-10-29 22:00:05 -07:00
|
|
|
$return = [];
|
2021-10-25 19:57:43 -07:00
|
|
|
|
|
|
|
|
foreach (self::$_events as $name => $meta) {
|
2023-04-10 13:34:29 -07:00
|
|
|
if ($meta['internal'] && !$showInternal) {
|
|
|
|
|
continue;
|
2022-03-31 19:47:08 +01:00
|
|
|
}
|
2023-04-10 13:34:29 -07:00
|
|
|
|
|
|
|
|
if (is_callable($meta['description'])) {
|
|
|
|
|
$description = $meta['description']();
|
|
|
|
|
} else {
|
|
|
|
|
$description = $meta['description'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$class = $meta['class_name'];
|
|
|
|
|
$return[$name] = [
|
|
|
|
|
'description' => $description,
|
|
|
|
|
'supports_discord' => $class !== null && is_subclass_of($class, DiscordDispatchable::class),
|
|
|
|
|
'supports_normal' => $class !== null && is_subclass_of($class, HasWebhookParams::class),
|
|
|
|
|
];
|
2021-10-25 19:57:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $return;
|
|
|
|
|
}
|
2021-11-14 19:20:21 +01:00
|
|
|
|
2021-11-14 19:09:08 +01:00
|
|
|
/**
|
|
|
|
|
* Get data about an event.
|
|
|
|
|
* Not used internally, currently for WebSend.
|
|
|
|
|
*
|
|
|
|
|
* @param string $event Name of event to get data for.
|
|
|
|
|
* @returns array Event data.
|
|
|
|
|
*/
|
2024-03-09 03:19:55 -08:00
|
|
|
public static function getEvent(string $event): array
|
|
|
|
|
{
|
2021-11-14 19:09:08 +01:00
|
|
|
if (!isset(self::$_events[$event])) {
|
2021-11-14 19:09:59 -08:00
|
|
|
throw new InvalidArgumentException("Invalid event name: $event");
|
2021-11-14 19:09:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return self::$_events[$event];
|
|
|
|
|
}
|
2021-10-25 19:57:43 -07:00
|
|
|
}
|