lthn.io/app/Core/Events/LifecycleEvent.php
Claude 41a90cbff8
feat: lthn.io API serving live chain data
Fixed: basePath self→static binding, namespace detection, event wiring,
SQLite cache, file cache driver. All Mod Boot classes converted to
$listens pattern for lifecycle event discovery.

Working endpoints:
- /v1/explorer/info — live chain height, difficulty, aliases
- /v1/explorer/stats — formatted chain statistics
- /v1/names/directory — alias directory grouped by type
- /v1/names/available/{name} — name availability check
- /v1/names/lookup/{name} — name details

Co-Authored-By: Charon <charon@lethean.io>
2026-04-03 17:17:42 +01:00

562 lines
24 KiB
PHP

<?php
/*
* Core PHP Framework
*
* Licensed under the European Union Public Licence (EUPL) v1.2.
* See LICENSE file for details.
*/
declare(strict_types=1);
namespace Core\Events;
/**
* Base class for lifecycle events.
*
* Lifecycle events are fired at key points during application bootstrap. Modules
* listen to these events via static `$listens` arrays in their Boot class and
* register their resources through the request methods provided here.
*
* ## Event Flow Diagram
*
* The following diagram shows how lifecycle events flow through the system:
*
* ```
* ┌─────────────────────────────────────────────────────────────────────────────┐
* │ LIFECYCLE EVENT FLOW │
* └─────────────────────────────────────────────────────────────────────────────┘
*
* ┌──────────────────────┐
* │ Application Start │
* └──────────┬───────────┘
* │
* ▼
* ┌──────────────────────┐ ┌─────────────────────────────────────────┐
* │ ModuleScanner │────►│ Scans app/Core, app/Mod, app/Website │
* │ (Discovery Phase) │ │ for Boot.php files with $listens │
* └──────────┬───────────┘ └─────────────────────────────────────────┘
* │
* ▼
* ┌──────────────────────┐ ┌─────────────────────────────────────────┐
* │ ModuleRegistry │────►│ Registers LazyModuleListener for each │
* │ (Registration) │ │ event-module pair with Laravel Events │
* └──────────┬───────────┘ └─────────────────────────────────────────┘
* │
* ▼
* ┌──────────────────────────────────────────────────────────────────────┐
* │ REQUEST CONTEXT DETECTION │
* └──────────────────────────────────────────────────────────────────────┘
* │
* ┌───────┴───────┬───────────┬───────────┬───────────┬───────────┐
* │ │ │ │ │ │
* ▼ ▼ ▼ ▼ ▼ ▼
* ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
* │ Web │ │ Admin │ │ API │ │ Client │ │ Console │ │ Queue │
* │ Routes │ │ Panel │ │ Routes │ │ Routes │ │ Booting │ │ Worker │
* └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘
* │ │ │ │ │ │
* ▼ ▼ ▼ ▼ ▼ ▼
* ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
* │WebRoutes│ │AdminPanel││ApiRoutes│ │Client │ │Console │ │QueueWkr │
* │Register-│ │Booting │ │Register-│ │Routes │ │Booting │ │Booting │
* │ing │ │ │ │ing │ │Register-│ │ │ │ │
* └────┬────┘ └────┬────┘ └────┬────┘ │ing │ └────┬────┘ └────┬────┘
* │ │ │ └────┬────┘ │ │
* └───────────────┴───────────┴───────────┴───────────┴───────────┘
* │
* ▼
* ┌──────────────────────┐
* │ FrameworkBooted │
* │ (After all booted) │
* └──────────────────────┘
* ```
*
* ## Event Order
*
* Events fire in a specific order based on request context:
*
* | Order | Event | Context | Middleware |
* |-------|-------|---------|------------|
* | 1 | `WebRoutesRegistering` | Web requests | 'web' |
* | 1 | `AdminPanelBooting` | Admin requests | 'admin' |
* | 1 | `ApiRoutesRegistering` | API requests | 'api' |
* | 1 | `ClientRoutesRegistering` | Client dashboard | 'client' |
* | 1 | `ConsoleBooting` | CLI commands | - |
* | 1 | `QueueWorkerBooting` | Queue workers | - |
* | 1 | `McpToolsRegistering` | MCP server | - |
* | 2 | `FrameworkBooted` | All contexts | - |
*
* Note: Events marked "1" are mutually exclusive based on context.
*
* ## Module Registration Flow
*
* ```
* ┌─────────────────────────────────────────────────────────────────────────────┐
* │ MODULE REGISTRATION FLOW │
* └─────────────────────────────────────────────────────────────────────────────┘
*
* Module Boot.php Framework
* ───────────────── ─────────
* │
* │ public static array $listens = [
* │ WebRoutesRegistering::class => 'onWebRoutes',
* │ AdminPanelBooting::class => ['onAdmin', 10],
* │ ];
* │
* │ │
* │◄──────────────────────────────────┤ ModuleScanner reads $listens
* │ │ without instantiation
* │ │
* │ ▼
* │ ┌─────────────────────┐
* │ │ ModuleRegistry │
* │ │ sorts by priority │
* │ │ (10 runs before 0) │
* │ └──────────┬──────────┘
* │ │
* │ ▼
* │ ┌─────────────────────┐
* │ │ LazyModuleListener │
* │ │ registered with │
* │ │ Laravel Events │
* │ └──────────┬──────────┘
* │ │
* │ │ Event fires
* │ ▼
* │ ┌─────────────────────┐
* │ │ LazyModuleListener │
* │◄───────────────────────────┤ instantiates module │
* │ Module instantiated │ via container │
* │ only when event fires └──────────┬──────────┘
* │ │
* ▼ │
* ┌─────────────┐ │
* │ onWebRoutes │◄──────────────────────────────┘
* │ ($event) │ Method called with event
* └──────┬──────┘
* │
* ▼
* ┌──────────────────────────────────────────────────────┐
* │ $event->routes(fn () => require __DIR__.'/web.php'); │
* │ $event->views('mymod', __DIR__.'/Views'); │
* │ $event->livewire('my-comp', MyComponent::class); │
* └──────────────────────────────────────────────────────┘
* │
* │ Requests collected in event
* ▼
* ┌─────────────────────────────────────────────────────────────┐
* │ LifecycleEventProvider processes requests: │
* │ - Registers view namespaces │
* │ - Registers Livewire components │
* │ - Wraps routes with appropriate middleware │
* │ - Refreshes route lookups │
* └─────────────────────────────────────────────────────────────┘
* ```
*
* ## Request/Collect Pattern
*
* This class implements a "request/collect" pattern rather than direct mutation:
*
* 1. **Modules request** resources via methods like `routes()`, `views()`, etc.
* 2. **Requests are collected** in arrays during event dispatch
* 3. **LifecycleEventProvider processes** collected requests with validation
*
* This pattern ensures modules cannot directly mutate infrastructure and allows
* the framework to validate, sort, and process requests centrally.
*
* ## Available Request Methods
*
* | Method | Purpose |
* |--------|---------|
* | `routes()` | Register route files/callbacks |
* | `views()` | Register view namespaces |
* | `livewire()` | Register Livewire components |
* | `middleware()` | Register middleware aliases |
* | `command()` | Register Artisan commands |
* | `translations()` | Register translation namespaces |
* | `bladeComponentPath()` | Register anonymous Blade component paths |
* | `policy()` | Register model policies |
* | `navigation()` | Register navigation items |
*
* ## Event Versioning
*
* Events support versioning for backwards compatibility. The version number
* indicates the API contract version:
*
* - Version 1: Original API (current)
* - Future versions may add methods but maintain backwards compatibility
*
* Check version with `$event->version()` in your handlers to support multiple
* event versions during transitions.
*
* ## Usage Example
*
* ```php
* public function onWebRoutes(WebRoutesRegistering $event): void
* {
* $event->views('mymodule', __DIR__.'/Views');
* $event->livewire('my-component', MyComponent::class);
* $event->routes(fn () => require __DIR__.'/Routes/web.php');
* }
* ```
*
*
* @method void navigation(array $item) Request a navigation item be added
* @method void routes(callable $callback) Request routes be registered
* @method void views(string $namespace, string $path) Request a view namespace be registered
* @method void middleware(string $alias, string $class) Request a middleware alias be registered
* @method void livewire(string $alias, string $class) Request a Livewire component be registered
* @method void command(string $class) Request an Artisan command be registered
* @method void translations(string $namespace, string $path) Request translations be loaded
* @method void bladeComponentPath(string $path, ?string $namespace = null) Request a Blade component path
* @method void policy(string $model, string $policy) Request a policy be registered
* @method array navigationRequests() Get collected navigation requests
* @method array routeRequests() Get collected route requests
* @method array viewRequests() Get collected view requests
* @method array middlewareRequests() Get collected middleware requests
* @method array livewireRequests() Get collected Livewire requests
* @method array commandRequests() Get collected command requests
* @method array translationRequests() Get collected translation requests
* @method array bladeComponentRequests() Get collected Blade component requests
* @method array policyRequests() Get collected policy requests
*
* @see LifecycleEventProvider For event processing
*/
abstract class LifecycleEvent
{
/**
* Event API version.
*
* Increment this when making breaking changes to the event interface.
* Handlers can check this to maintain backwards compatibility.
*
* Version history:
* - 1: Initial release (Core PHP 1.0)
*/
public const VERSION = 1;
/**
* Minimum supported handler version.
*
* Handlers declaring a version lower than this will receive a deprecation warning.
*/
public const MIN_SUPPORTED_VERSION = 1;
/** @var array<int, array<string, mixed>> Collected navigation item requests */
protected array $navigationRequests = [];
/** @var array<int, callable> Collected route registration callbacks */
protected array $routeRequests = [];
/** @var array<int, array{0: string, 1: string}> Collected view namespace requests [namespace, path] */
protected array $viewRequests = [];
/** @var array<int, array{0: string, 1: string}> Collected middleware alias requests [alias, class] */
protected array $middlewareRequests = [];
/** @var array<int, array{0: string, 1: string}> Collected Livewire component requests [alias, class] */
protected array $livewireRequests = [];
/** @var array<int, string> Collected Artisan command class names */
protected array $commandRequests = [];
/** @var array<int, array{0: string, 1: string}> Collected translation namespace requests [namespace, path] */
protected array $translationRequests = [];
/** @var array<int, array{0: string, 1: string|null}> Collected Blade component path requests [path, namespace] */
protected array $bladeComponentRequests = [];
/** @var array<int, array{0: string, 1: string}> Collected policy requests [model, policy] */
protected array $policyRequests = [];
/**
* Request a navigation item be added.
*
* Navigation items are collected and processed by the admin menu system.
* Consider implementing AdminMenuProvider for more control over menu items.
*
* @param array<string, mixed> $item Navigation item configuration
*/
public function navigation(array $item): void
{
$this->navigationRequests[] = $item;
}
/**
* Request routes be registered.
*
* The callback is invoked within the appropriate middleware group
* (web, admin, api, client) depending on which event fired.
*
* ```php
* $event->routes(fn () => require __DIR__.'/Routes/web.php');
* // or
* $event->routes(function () {
* Route::get('/example', ExampleController::class);
* });
* ```
*
* @param callable $callback Route registration callback
*/
public function routes(callable $callback): void
{
$this->routeRequests[] = $callback;
}
/**
* Request a view namespace be registered.
*
* After registration, views can be referenced as `namespace::view.name`.
*
* ```php
* $event->views('commerce', __DIR__.'/Views');
* // Later: view('commerce::products.index')
* ```
*
* @param string $namespace The view namespace (e.g., 'commerce')
* @param string $path Absolute path to the views directory
*/
public function views(string $namespace, string $path): void
{
$this->viewRequests[] = [$namespace, $path];
}
/**
* Request a middleware alias be registered.
*
* @param string $alias The middleware alias (e.g., 'commerce.auth')
* @param string $class Fully qualified middleware class name
*/
public function middleware(string $alias, string $class): void
{
$this->middlewareRequests[] = [$alias, $class];
}
/**
* Request a Livewire component be registered.
*
* ```php
* $event->livewire('commerce-cart', CartComponent::class);
* // Later: <livewire:commerce-cart />
* ```
*
* @param string $alias The component alias used in Blade templates
* @param string $class Fully qualified Livewire component class name
*/
public function livewire(string $alias, string $class): void
{
$this->livewireRequests[] = [$alias, $class];
}
/**
* Request an Artisan command be registered.
*
* Only processed during ConsoleBooting event.
*
* @param string $class Fully qualified command class name
*/
public function command(string $class): void
{
$this->commandRequests[] = $class;
}
/**
* Request translations be loaded for a namespace.
*
* After registration, translations can be accessed as `namespace::key`.
*
* ```php
* $event->translations('commerce', __DIR__.'/Lang');
* // Later: __('commerce::products.title')
* ```
*
* @param string $namespace The translation namespace
* @param string $path Absolute path to the lang directory
*/
public function translations(string $namespace, string $path): void
{
$this->translationRequests[] = [$namespace, $path];
}
/**
* Request an anonymous Blade component path be registered.
*
* Anonymous components in this path can be used in templates.
*
* @param string $path Absolute path to the components directory
* @param string|null $namespace Optional prefix for component names
*/
public function bladeComponentPath(string $path, ?string $namespace = null): void
{
$this->bladeComponentRequests[] = [$path, $namespace];
}
/**
* Request a policy be registered for a model.
*
* @param string $model Fully qualified model class name
* @param string $policy Fully qualified policy class name
*/
public function policy(string $model, string $policy): void
{
$this->policyRequests[] = [$model, $policy];
}
/**
* Get all navigation requests for processing.
*
* @return array<int, array<string, mixed>>
*
* @internal Used by LifecycleEventProvider
*/
public function navigationRequests(): array
{
return $this->navigationRequests;
}
/**
* Get all route requests for processing.
*
* @return array<int, callable>
*
* @internal Used by LifecycleEventProvider
*/
public function routeRequests(): array
{
return $this->routeRequests;
}
/**
* Get all view namespace requests for processing.
*
* @return array<int, array{0: string, 1: string}>
*
* @internal Used by LifecycleEventProvider
*/
public function viewRequests(): array
{
return $this->viewRequests;
}
/**
* Get all middleware alias requests for processing.
*
* @return array<int, array{0: string, 1: string}>
*
* @internal Used by LifecycleEventProvider
*/
public function middlewareRequests(): array
{
return $this->middlewareRequests;
}
/**
* Get all Livewire component requests for processing.
*
* @return array<int, array{0: string, 1: string}>
*
* @internal Used by LifecycleEventProvider
*/
public function livewireRequests(): array
{
return $this->livewireRequests;
}
/**
* Get all command requests for processing.
*
* @return array<int, string>
*
* @internal Used by LifecycleEventProvider
*/
public function commandRequests(): array
{
return $this->commandRequests;
}
/**
* Get all translation requests for processing.
*
* @return array<int, array{0: string, 1: string}>
*
* @internal Used by LifecycleEventProvider
*/
public function translationRequests(): array
{
return $this->translationRequests;
}
/**
* Get all Blade component path requests for processing.
*
* @return array<int, array{0: string, 1: string|null}>
*
* @internal Used by LifecycleEventProvider
*/
public function bladeComponentRequests(): array
{
return $this->bladeComponentRequests;
}
/**
* Get all policy requests for processing.
*
* @return array<int, array{0: string, 1: string}>
*
* @internal Used by LifecycleEventProvider
*/
public function policyRequests(): array
{
return $this->policyRequests;
}
/**
* Get the event API version.
*
* Use this in your event handlers to check API compatibility:
*
* ```php
* public function onWebRoutes(WebRoutesRegistering $event): void
* {
* if ($event->version() >= 2) {
* // Use new v2 features
* } else {
* // Fallback to v1 behavior
* }
* }
* ```
*
* @return int The event API version number
*/
public function version(): int
{
return static::VERSION;
}
/**
* Check if this event supports a specific version.
*
* Returns true if the event's version is greater than or equal to the
* requested version.
*
* @param int $version The version to check against
* @return bool True if the event supports the specified version
*/
public function supportsVersion(int $version): bool
{
return static::VERSION >= $version;
}
/**
* Get the event class name without namespace.
*
* Useful for logging and debugging.
*
* @return string The short class name (e.g., 'WebRoutesRegistering')
*/
public function eventName(): string
{
return class_basename(static::class);
}
}