NAME event_hooks - PHP event system with filters, gates, and actions SYNOPSIS Register event handlers with attributes: #[OnEvent('event.name', priority: 10)] public static function handler_method($data) { // Process event return $data; } Trigger events from code: // Filter: Transform data through handler chain $result = Rsx::trigger_filter('event.name', $data); // Gate: First non-true response halts execution $result = Rsx::trigger_gate('event.name', $data); // Action: Fire and forget side effects Rsx::trigger_action('event.name', $data); DESCRIPTION The RSX event system provides attribute-based event handling similar to WordPress hooks or JavaScript event systems, allowing applications to modify framework behavior without changing core code. Unlike Laravel's event/listener system which requires manual registration in service providers, RSX events use #[OnEvent] attributes that are automatically discovered during manifest compilation. RSX vs Laravel Events: - Laravel: Register listeners in EventServiceProvider or listener classes - RSX: Mark methods with #[OnEvent], automatic manifest discovery The system provides three event types optimized for different use cases: - Filters: Transform data through multiple handlers - Gates: Authorization checks where first denial wins - Actions: Side effects without return values All handlers execute in priority order (lowest number first). BASIC USAGE Creating Event Handlers Create a class in /rsx/handlers/ with static methods marked with #[OnEvent]: namespace Rsx\Handlers; class Upload_Handlers { #[OnEvent('file.upload.authorize', priority: 10)] public static function require_auth($data) { if (!Session::is_logged_in()) { return response()->json([ 'error' => 'Authentication required' ], 403); } return true; } #[OnEvent('file.upload.params', priority: 20)] public static function add_metadata($params) { $params['uploaded_by'] = Session::id(); return $params; } } Handlers are automatically discovered by the manifest system. No manual registration required. Triggering Events Use Rsx:: methods to trigger events from your code: // In a controller or service $upload_params = Rsx::trigger_filter('file.upload.params', [ 'site_id' => 1, 'filename' => 'document.pdf' ]); // Returns modified params after all handlers run EVENT TYPES Filter Events (trigger_filter) Filters pass data through a chain of handlers, with each handler modifying and returning the data for the next handler. Usage: $result = Rsx::trigger_filter('event.name', $data); Handler signature: public static function handler($data) { // Modify $data return $data; // Must return modified data } Flow: $data = ['value' => 1]; Handler 1 (priority 10): receives ['value' => 1], returns ['value' => 2] Handler 2 (priority 20): receives ['value' => 2], returns ['value' => 3] Final result: ['value' => 3] Use cases: - Transforming request parameters - Modifying response data - Adding fields to data structures - Sanitizing or validating input Example: #[OnEvent('post.content.filter', priority: 10)] public static function sanitize_html($content) { return strip_tags($content, '

'); } #[OnEvent('post.content.filter', priority: 20)] public static function add_signature($content) { return $content . "\n\nPosted via RSX"; } // Usage: $clean = Rsx::trigger_filter('post.content.filter', $raw_content); Gate Events (trigger_gate) Gates check authorization or validation. Handlers return true to allow or any other value to deny. The first non-true response immediately halts execution and is returned. Usage: $result = Rsx::trigger_gate('event.name', $data); if ($result !== true) { // Access denied, $result contains denial response return $result; } Handler signature: public static function handler($data) { if (authorized) { return true; // Allow, continue to next handler } return response()->json(['error' => 'Denied'], 403); } Flow: Handler 1 (priority 10): returns true (continue) Handler 2 (priority 20): returns JsonResponse (STOP) Handler 3 (priority 30): never executes Final result: JsonResponse from Handler 2 Use cases: - Authorization checks - Permission validation - Rate limiting - Blacklist checks Example: #[OnEvent('api.access.authorize', priority: 10)] public static function check_authentication($data) { if (!Session::is_logged_in()) { return response()->json(['error' => 'Login required'], 401); } return true; } #[OnEvent('api.access.authorize', priority: 20)] public static function check_permissions($data) { if (!$data['user']->can($data['permission'])) { return response()->json(['error' => 'Insufficient permissions'], 403); } return true; } // Usage: $auth = Rsx::trigger_gate('api.access.authorize', [ 'user' => Session::get_user(), 'permission' => 'edit_posts' ]); if ($auth !== true) return $auth; Action Events (trigger_action) Actions perform side effects. Return values are ignored. All handlers execute regardless of what previous handlers did. Usage: Rsx::trigger_action('event.name', $data); Handler signature: public static function handler($data): void { // Perform side effect // Return value ignored } Flow: Handler 1 (priority 10): executes Handler 2 (priority 20): executes Handler 3 (priority 30): executes All handlers always run Use cases: - Logging - Sending notifications - Triggering webhooks - Cache invalidation - Analytics tracking Example: #[OnEvent('user.registered', priority: 10)] public static function send_welcome_email($data) { Mail::to($data['user']->email)->send(new WelcomeEmail($data['user'])); } #[OnEvent('user.registered', priority: 20)] public static function log_registration($data) { Log::info('New user registered', ['user_id' => $data['user']->id]); } #[OnEvent('user.registered', priority: 30)] public static function notify_admins($data) { Notification::send_admin_alert('New user: ' . $data['user']->email); } // Usage: Rsx::trigger_action('user.registered', ['user' => $user]); PRIORITY SYSTEM Handlers execute in priority order (lowest number first). Default is 100. #[OnEvent('event.name', priority: 10)] // Runs first #[OnEvent('event.name', priority: 50)] // Runs second #[OnEvent('event.name', priority: 100)] // Runs third (default) Priority Guidelines: 1-10 Critical early processing (auth, validation) 11-50 Main application logic 51-100 Post-processing, cleanup 101+ Logging, analytics (fire last) For gate events, lower priority handlers can block higher priority ones from ever executing. API REFERENCE Rsx::trigger_filter(string $event, mixed $data): mixed Passes $data through all handlers for $event, with each handler receiving the output of the previous handler. Parameters: $event - Event name (e.g., 'file.upload.params') $data - Initial data to transform Returns: Final data after all handlers have processed it Example: $params = ['filename' => 'test.pdf']; $params = Rsx::trigger_filter('upload.params', $params); // $params may now have additional fields added by handlers Rsx::trigger_gate(string $event, mixed $data): mixed Executes authorization handlers until one returns non-true. Returns true if all handlers allow, or the first denial response. Parameters: $event - Event name (e.g., 'api.authorize') $data - Authorization context data Returns: true if all handlers allow access Response object (or any non-true value) if denied Example: $result = Rsx::trigger_gate('delete.authorize', ['post' => $post]); if ($result !== true) { return $result; // Return denial response } // Proceed with delete Rsx::trigger_action(string $event, mixed $data): void Executes all handlers as side effects. Return values ignored. Parameters: $event - Event name (e.g., 'user.logged_in') $data - Event context data Returns: void Example: Rsx::trigger_action('order.completed', [ 'order' => $order, 'user' => $user ]); NAMING CONVENTIONS Event names use dot notation to indicate scope and hierarchy: {module}.{entity}.{action} Examples: file.upload.authorize File upload authorization file.upload.params File upload parameter modification file.upload.complete File upload completion user.login.authorize User login authorization user.login.success User login success post.save.before Before saving post post.save.after After saving post Guidelines: - Use lowercase with dots - Start with module/feature name - Include entity being acted upon - End with action or lifecycle event - Be specific and descriptive FRAMEWORK EVENTS The framework fires these events automatically: File Upload Events file.upload.authorize (gate) When: Before processing upload Data: {request: Request, user: User|null, params: array} Use: Authorization, rate limiting, quota checks file.upload.params (filter) When: Before saving file Data: array of upload parameters Use: Add metadata, modify filename, set categories file.upload.complete (action) When: After file saved successfully Data: {attachment: File_Attachment_Model, request: Request, params: array} Use: Logging, notifications, post-processing file.upload.response (filter) When: Before sending response to client Data: {success: bool, attachment: array} Use: Add custom fields to response, modify URLs IMPLEMENTATION DETAILS Manifest Integration Event handlers are discovered during manifest compilation by scanning for #[OnEvent] attributes on public static methods. The manifest extracts: - Event name from first attribute parameter - Priority from second parameter or 'priority' key - Class and method name for callable construction Handlers are stored in manifest_data.php under 'event_handlers' key: 'event_handlers' => [ 'file.upload.authorize' => [ [ 'class' => 'Rsx\\Handlers\\Upload_Handlers', 'method' => 'require_auth', 'priority' => 10, 'file' => '/path/to/Upload_Handlers.php' ] ] ] This approach avoids runtime reflection - all handler discovery happens during manifest compilation. Event_Registry The Event_Registry class loads handler definitions from the manifest and constructs callables: Event_Registry::get_handlers('event.name') // Returns array of callables sorted by priority Handler callables are created as closures that invoke the static methods: function ($data) use ($class, $method) { return $class::$method($data); } Registry loads handlers lazily on first access. Performance Handler discovery: Zero runtime cost (manifest compilation time only) Event triggering: O(n) where n = number of handlers for that event Handler lookup: O(1) from manifest array EXAMPLES Complete Authorization Example File: /rsx/handlers/Upload_Security.php namespace Rsx\Handlers; use App\RSpade\Core\Session\Session; class Upload_Security { #[OnEvent('file.upload.authorize', priority: 10)] public static function require_login($data) { if (!Session::is_logged_in()) { return response()->json([ 'success' => false, 'error' => 'Login required' ], 401); } return true; } #[OnEvent('file.upload.authorize', priority: 20)] public static function check_quota($data) { $user = Session::get_user(); if ($user->storage_used >= $user->storage_quota) { return response()->json([ 'success' => false, 'error' => 'Storage quota exceeded' ], 403); } return true; } #[OnEvent('file.upload.authorize', priority: 30)] public static function check_file_type($data) { $allowed = ['jpg', 'png', 'pdf', 'doc', 'docx']; $ext = $data['request']->file('file')->getClientOriginalExtension(); if (!in_array(strtolower($ext), $allowed)) { return response()->json([ 'success' => false, 'error' => 'File type not allowed' ], 400); } return true; } } Complete Filter Chain Example File: /rsx/handlers/Content_Filters.php namespace Rsx\Handlers; class Content_Filters { #[OnEvent('post.content.filter', priority: 10)] public static function strip_dangerous_html($content) { return strip_tags($content, '