Files
rspade_system/docs/skills/event-hooks/SKILL.md
root 1b46c5270c Add skills documentation and misc updates
Add form value persistence across cache revalidation re-renders

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-29 04:38:06 +00:00

5.8 KiB
Executable File

name, description
name description
event-hooks RSX PHP event system with filters, gates, and actions using the

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/:

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.

// 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, '<p><a><strong>');
}

#[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.

// 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.

// 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):

#[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

#[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

#[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

#[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

// 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