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

253 lines
5.8 KiB
Markdown
Executable File

---
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, '<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.
```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`