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