--- name: event-hooks description: RSX PHP event system with filters, gates, and actions using the #[OnEvent] attribute. Use when extending framework behavior, implementing authorization hooks, transforming data through handlers, or triggering custom events. --- # RSX Event Hooks ## Overview RSX provides an attribute-based event system with three event types: | Type | Purpose | Return Handling | |------|---------|-----------------| | **Filter** | Transform data through chain | Each handler modifies and returns data | | **Gate** | Authorization checks | First non-true stops execution | | **Action** | Side effects | Return values ignored | Unlike Laravel's EventServiceProvider, RSX uses `#[OnEvent]` attributes with automatic manifest discovery. --- ## Creating Event Handlers Place handler classes in `/rsx/handlers/`: ```php 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' => 'Auth required'], 403); } return true; } #[OnEvent('file.upload.params', priority: 20)] public static function add_metadata($params) { $params['uploaded_by'] = Session::get_user_id(); return $params; } } ``` Handlers are automatically discovered - no manual registration needed. --- ## Filter Events Transform data through a chain of handlers. Each handler receives the result of the previous handler. ```php // Trigger $result = Rsx::trigger_filter('event.name', $data); // Handler - MUST return modified data #[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"; } ``` **Flow**: Handler 1 (priority 10) → Handler 2 (priority 20) → Final result --- ## Gate Events Authorization checks where first non-true response halts execution. ```php // Trigger $result = Rsx::trigger_gate('api.access.authorize', $data); if ($result !== true) { return $result; // Access denied } // Handler - return true to allow, anything else to deny #[OnEvent('api.access.authorize', priority: 10)] public static function check_auth($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' => 'Forbidden'], 403); } return true; } ``` **Flow**: Handler 1 returns true → Handler 2 returns false → STOPS, returns false --- ## Action Events Fire-and-forget side effects. Return values are ignored. ```php // Trigger Rsx::trigger_action('user.created', ['user' => $user]); // Handler - no return value needed #[OnEvent('user.created', priority: 10)] public static function send_welcome_email($data): void { Email_Service::send_welcome($data['user']); } #[OnEvent('user.created', priority: 20)] public static function log_signup($data): void { Activity_Model::log('signup', $data['user']->id); } ``` --- ## Priority Handlers execute in priority order (lowest number first): ```php #[OnEvent('my.event', priority: 10)] // Runs first #[OnEvent('my.event', priority: 20)] // Runs second #[OnEvent('my.event', priority: 30)] // Runs third ``` Default priority is 10 if not specified. --- ## Common Use Cases ### File Upload Authorization ```php #[OnEvent('file.upload.authorize', priority: 10)] public static function require_auth($data) { if (!Session::is_logged_in()) { return response()->json(['error' => 'Auth required'], 403); } return true; } #[OnEvent('file.download.authorize', priority: 10)] public static function check_file_access($data) { $attachment = $data['attachment']; if ($attachment->site_id !== Session::get_site_id()) { return response()->json(['error' => 'Access denied'], 403); } return true; } ``` ### Transforming Request Data ```php #[OnEvent('contact.save.params', priority: 10)] public static function normalize_phone($params) { if (!empty($params['phone'])) { $params['phone'] = preg_replace('/[^0-9]/', '', $params['phone']); } return $params; } ``` ### Logging Actions ```php #[OnEvent('project.deleted', priority: 10)] public static function log_deletion($data): void { Audit_Log::create([ 'action' => 'project_deleted', 'entity_id' => $data['project']->id, 'user_id' => Session::get_user_id(), ]); } ``` --- ## Triggering Custom Events ```php // In your controller or service class Project_Controller { #[Ajax_Endpoint] public static function delete(Request $request, array $params = []) { $project = Project_Model::find($params['id']); // Gate: Check if deletion allowed $auth = Rsx::trigger_gate('project.delete.authorize', [ 'project' => $project, 'user' => Session::get_user() ]); if ($auth !== true) { return $auth; } $project->delete(); // Action: Notify listeners Rsx::trigger_action('project.deleted', ['project' => $project]); return ['success' => true]; } } ``` --- ## Handler Organization ``` rsx/handlers/ ├── auth_handlers.php # Authentication hooks ├── file_handlers.php # File upload/download hooks ├── notification_handlers.php └── audit_handlers.php ``` Each file can contain multiple handler methods with different event subscriptions. ## More Information Details: `php artisan rsx:man event_hooks`