argument('controller'); $action = $this->argument('action'); // Parse arguments if provided $args = []; if ($this->option('args')) { $args_json = $this->option('args'); $args = json_decode($args_json, true); if (json_last_error() !== JSON_ERROR_NONE) { $this->output_json_error('Invalid JSON in --args parameter: ' . json_last_error_msg(), 'invalid_json'); return 1; } } // Get options $user_input = $this->option('user'); $site_input = $this->option('site'); $debug_mode = $this->option('debug'); $show_context = $this->option('show-context'); // Validate and resolve user $user_id = null; if ($user_input !== null) { $user_id = $this->resolve_user($user_input); if ($user_id === null) { return 1; // Error already displayed } } // Validate site $site_id = null; if ($site_input !== null) { $site_id = $this->resolve_site($site_input); if ($site_id === null) { return 1; // Error already displayed } } // Rotate logs before test Debugger::logrotate(); // Set session context if provided if ($user_id !== null) { Session::set_login_user_id((int)$user_id); if ($show_context) { $this->error("Set login_user_id to {$user_id}"); } } if ($site_id !== null) { Session::set_site_id((int)$site_id); if ($show_context) { $this->error("Set site_id to {$site_id}"); } } try { // Call the Ajax method $response = Ajax::internal($controller, $action, $args); // Output response based on mode if ($debug_mode) { // Use shared format_ajax_response method for consistency with HTTP $wrapped_response = Ajax::format_ajax_response($response); $this->output_json($wrapped_response); } else { // Just output the raw response $this->output_json($response); } } catch (AjaxAuthRequiredException $e) { $this->output_json_error($e->getMessage(), 'auth_required'); return 1; } catch (AjaxUnauthorizedException $e) { $this->output_json_error($e->getMessage(), 'unauthorized'); return 1; } catch (AjaxFormErrorException $e) { $error_response = [ 'success' => false, 'error' => $e->getMessage(), 'error_type' => 'form_error', ]; $details = $e->get_details(); if (!empty($details)) { $error_response['details'] = $details; } $this->output_json($error_response); return 1; } catch (AjaxFatalErrorException $e) { $this->output_json_error($e->getMessage(), 'fatal_error', [ 'trace' => $e->getTraceAsString() ]); return 1; } catch (\InvalidArgumentException $e) { $this->output_json_error($e->getMessage(), 'invalid_argument'); return 1; } catch (\Exception $e) { $this->output_json_error($e->getMessage(), get_class($e), [ 'trace' => $e->getTraceAsString() ]); return 1; } // Rotate logs after test Debugger::logrotate(); return 0; } /** * Output JSON to stdout (pretty-printed) */ protected function output_json($data): void { $json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); $this->line($json); } /** * Output error as JSON */ protected function output_json_error(string $message, string $error_type, array $extra = []): void { $error = [ 'success' => false, 'error' => $message, 'error_type' => $error_type, ]; foreach ($extra as $key => $value) { $error[$key] = $value; } $this->output_json($error); } /** * Resolve user identifier to user ID * * Accepts either a numeric user ID or an email address. * Validates that the user exists in the database. * * @param string $user_input User ID or email address * @return int|null User ID or null if not found (error already displayed) */ protected function resolve_user(string $user_input): ?int { // Check if input is an email address if (str_contains($user_input, '@')) { $login_user = Login_User_Model::find_by_email($user_input); if (!$login_user) { $this->output_json_error("User not found: {$user_input}", 'user_not_found'); return null; } return $login_user->id; } // Input is a user ID - validate it exists if (!ctype_digit($user_input)) { $this->output_json_error("Invalid user identifier: {$user_input} (must be numeric ID or email address)", 'invalid_user'); return null; } $user_id = (int) $user_input; $login_user = Login_User_Model::find($user_id); if (!$login_user) { $this->output_json_error("User ID not found: {$user_id}", 'user_not_found'); return null; } return $user_id; } /** * Resolve site identifier to site ID * * Validates that the site exists in the database. * * @param string $site_input Site ID * @return int|null Site ID or null if not found (error already displayed) */ protected function resolve_site(string $site_input): ?int { if (!ctype_digit($site_input)) { $this->output_json_error("Invalid site identifier: {$site_input} (must be numeric ID)", 'invalid_site'); return null; } $site_id = (int) $site_input; $site = Site_Model::find($site_id); if (!$site) { $this->output_json_error("Site ID not found: {$site_id}", 'site_not_found'); return null; } return $site_id; } }