handler class name (must implement ScheduledJobHandlerInterface). * * To register a new job: * 1. Create lib/Service/Scheduler/Handler/YourJobHandler.php implementing the interface. * 2. Add a public const for the job key above. * 3. Add one line here: self::YOUR_JOB_KEY => YourJobHandler::class, * * No other file needs to be changed. * * @return array> */ private static function handlers(): array { return [ self::USER_LIFECYCLE_RUN => UserLifecycleJobHandler::class, ]; } /** * Returns all job definitions keyed by job_key. * Consumed by ScheduledJobService::ensureSystemJobs() to sync registry with the database. */ public static function definitions(): array { $result = []; foreach (self::handlers() as $jobKey => $handlerClass) { $result[$jobKey] = $handlerClass::definition(); } return $result; } /** * Returns the definition for a single job key, or null if not registered. */ public static function get(string $jobKey): ?array { $jobKey = trim($jobKey); if ($jobKey === '') { return null; } $handlers = self::handlers(); return isset($handlers[$jobKey]) ? $handlers[$jobKey]::definition() : null; } /** * Dispatches execution to the registered handler for the given job key and returns * a normalized result envelope. Unknown job keys return status 'failed' with * error_code 'job_not_supported' rather than throwing an exception. * * @param string $jobKey Registered job identifier (see class constants). * @param int|null $actorUserId Null for scheduler-triggered runs; user ID for manual runs. * @return array{status:string,error_code:?string,error_message:?string,result:array} */ public static function execute(string $jobKey, ?int $actorUserId): array { $handlers = self::handlers(); if (!isset($handlers[$jobKey])) { return [ 'status' => 'failed', 'error_code' => 'job_not_supported', 'error_message' => null, 'result' => [], ]; } return $handlers[$jobKey]::execute($actorUserId); } }