From f80580caba139425f747709dc6f6cb60755d758d Mon Sep 17 00:00:00 2001 From: Riddhesh Sanghvi Date: Mon, 22 Dec 2025 11:10:21 +0530 Subject: [PATCH 1/2] feat(backup): add comprehensive error tracking to EasyDash failure callbacks - Add error type constants for categorized error reporting (validation, config, filesystem, network, database, disk_space, lock, fatal, interrupted, unknown) - Add error tracking properties (message, type, code) to capture failure details - Create capture_error() method to store error information for API callbacks - Enhance dash_shutdown_handler() to auto-capture PHP fatal errors and process interruptions - Update send_dash_failure_callback() to include error details in API payload Error captures added for: - Invalid --dash-auth format (1001) - Unsupported site type (1003) - rclone not installed (2001) - rclone backend not configured (2002) - Concurrent backup process (lock file) (2003) - Insufficient disk space (3001) - Tool installation failures (2010, 2011) - rclone upload failures (4001) - PHP fatal errors (captured from error_get_last) - Process interruptions (Ctrl+C, SIGTERM, SIGKILL) --- src/helper/Site_Backup_Restore.php | 147 +++++++++++++++++++++++++++-- 1 file changed, 139 insertions(+), 8 deletions(-) diff --git a/src/helper/Site_Backup_Restore.php b/src/helper/Site_Backup_Restore.php index e713ff30..b7bcb16d 100644 --- a/src/helper/Site_Backup_Restore.php +++ b/src/helper/Site_Backup_Restore.php @@ -11,6 +11,18 @@ class Site_Backup_Restore { + // Error type constants for categorized error reporting + const ERROR_TYPE_VALIDATION = 'validation_error'; // Invalid input + const ERROR_TYPE_CONFIG = 'configuration_error'; // Missing config + const ERROR_TYPE_FILESYSTEM = 'filesystem_error'; // File/dir issues + const ERROR_TYPE_NETWORK = 'network_error'; // Upload/download + const ERROR_TYPE_DATABASE = 'database_error'; // DB operations + const ERROR_TYPE_DISK_SPACE = 'disk_space_error'; // Insufficient space + const ERROR_TYPE_LOCK = 'lock_error'; // Concurrent operation + const ERROR_TYPE_FATAL = 'fatal_error'; // PHP fatal + const ERROR_TYPE_INTERRUPTED = 'interrupted'; // Killed/stopped + const ERROR_TYPE_UNKNOWN = 'unknown_error'; // Unexpected + private $fs; public $site_data; private $rclone_config_path; @@ -24,6 +36,11 @@ class Site_Backup_Restore { private $dash_backup_completed = false; private $dash_new_backup_path; // Track new backup path for potential rollback + // Error tracking for EasyDash failure callbacks + private $dash_error_message = ''; + private $dash_error_type = 'unknown'; + private $dash_error_code = 0; + public function __construct() { $this->fs = new Filesystem(); } @@ -48,11 +65,16 @@ public function backup( $args, $assoc_args = [] ) { // Debug: Log the raw dash_auth value received EE::debug( 'Received --dash-auth value: ' . $dash_auth ); - // Parse backup-id:backup-verification-token format - $auth_parts = explode( ':', $dash_auth, 2 ); - if ( count( $auth_parts ) !== 2 || empty( $auth_parts[0] ) || empty( $auth_parts[1] ) ) { - EE::error( 'Invalid --dash-auth format. Expected: backup-id:backup-verification-token' ); - } + // Parse backup-id:backup-verification-token format + $auth_parts = explode( ':', $dash_auth, 2 ); + if ( count( $auth_parts ) !== 2 || empty( $auth_parts[0] ) || empty( $auth_parts[1] ) ) { + $this->capture_error( + 'Invalid --dash-auth format. Expected: backup-id:backup-verification-token', + self::ERROR_TYPE_VALIDATION, + 1001 + ); + EE::error( 'Invalid --dash-auth format. Expected: backup-id:backup-verification-token' ); + } // Check for ed-api-url configuration $ed_api_url = get_config_value( 'ed-api-url', '' ); @@ -92,6 +114,11 @@ public function backup( $args, $assoc_args = [] ) { $this->backup_php_wp( $backup_dir ); break; default: + $this->capture_error( + sprintf( 'Backup is not supported for site type: %s', $this->site_data['site_type'] ), + self::ERROR_TYPE_VALIDATION, + 1003 + ); EE::error( 'Backup is not supported for this site type.' ); } @@ -125,10 +152,40 @@ public function backup( $args, $assoc_args = [] ) { /** * Shutdown handler to send failure callback to EasyDash if backup didn't complete. * This is called when script terminates (including via EE::error which calls exit). + * + * Automatically captures fatal errors and interrupted processes if no error was + * explicitly captured during backup execution. */ public function dash_shutdown_handler() { // Only send failure callback if dash auth was enabled and backup didn't complete if ( $this->dash_auth_enabled && ! $this->dash_backup_completed ) { + + // If no error was captured yet, try to capture shutdown error + if ( empty( $this->dash_error_message ) ) { + $last_error = error_get_last(); + + // Check if this was a fatal PHP error + if ( $last_error && in_array( $last_error['type'], [ E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR ] ) ) { + $this->capture_error( + sprintf( + 'PHP Fatal Error: %s in %s:%d', + $last_error['message'], + basename( $last_error['file'] ), + $last_error['line'] + ), + self::ERROR_TYPE_FATAL, + $last_error['type'] + ); + } else { + // Script was killed, interrupted, or failed unexpectedly + $this->capture_error( + 'Backup process was interrupted or killed unexpectedly', + self::ERROR_TYPE_INTERRUPTED, + 0 + ); + } + } + $this->send_dash_failure_callback( $this->dash_api_url, $this->dash_backup_id, @@ -137,6 +194,30 @@ public function dash_shutdown_handler() { } } + /** + * Capture error details for EasyDash failure callback. + * Stores error information to be sent when backup fails. + * + * @param string $message Error message describing what went wrong. + * @param string $type Error type category (use ERROR_TYPE_* constants). + * @param int $code Error code for additional context (optional). + */ + private function capture_error( $message, $type = self::ERROR_TYPE_UNKNOWN, $code = 0 ) { + // Only capture the first error (root cause) + if ( empty( $this->dash_error_message ) ) { + $this->dash_error_message = $message; + $this->dash_error_type = $type; + $this->dash_error_code = $code; + + EE::debug( sprintf( + 'Captured error for EasyDash: [%s] %s (code: %d)', + $type, + $message, + $code + ) ); + } + } + public function restore( $args, $assoc_args = [] ) { delem_log( 'site restore start' ); @@ -659,6 +740,11 @@ private function pre_backup_restore_checks() { $return_code = EE::exec( $command ); if ( ! $return_code ) { + $this->capture_error( + 'rclone is not installed', + self::ERROR_TYPE_CONFIG, + 2001 + ); EE::error( 'rclone is not installed. Please install rclone for backup/restore: https://rclone.org/downloads/#script-download-and-install' ); } @@ -669,6 +755,11 @@ private function pre_backup_restore_checks() { $rclone_path = explode( ':', $rclone_path )[0] . ':'; if ( strpos( $output->stdout, $rclone_path ) === false ) { + $this->capture_error( + 'rclone backend easyengine does not exist', + self::ERROR_TYPE_CONFIG, + 2002 + ); EE::error( 'rclone backend easyengine does not exist. Please create it using `rclone config`' ); } @@ -686,6 +777,11 @@ private function pre_backup_restore_checks() { $lock_file = EE_BACKUP_DIR . '/' . $this->site_data['site_url'] . '.lock'; if ( $this->fs->exists( $lock_file ) ) { + $this->capture_error( + 'Another backup/restore process is already running for this site', + self::ERROR_TYPE_LOCK, + 2003 + ); EE::error( 'Another backup/restore process is running. Please wait for it to complete.' ); } else { $this->fs->dumpFile( $lock_file, 'lock' ); @@ -711,6 +807,16 @@ private function pre_backup_check() { if ( $site_size > $free_space ) { $error_message = $this->build_disk_space_error_message( 'backup', $site_size, $free_space ); + $this->capture_error( + sprintf( + 'Insufficient disk space for backup. Required: %s, Available: %s', + $this->format_bytes( $site_size ), + $this->format_bytes( $free_space ) + ), + self::ERROR_TYPE_DISK_SPACE, + 3001 + ); + $this->fs->remove( EE_BACKUP_DIR . '/' . $this->site_data['site_url'] . '.lock' ); EE::error( $error_message ); } @@ -751,6 +857,11 @@ private function check_and_install( $command, $name ) { $status = EE::exec( "command -v $command" ); if ( ! $status ) { if ( IS_DARWIN ) { + $this->capture_error( + sprintf( '%s is not installed (required for backup/restore)', $name ), + self::ERROR_TYPE_CONFIG, + 2010 + ); EE::error( "$name is not installed. Please install $name for backup/restore. You can install it using `brew install $name`." ); } else { $status = EE::exec( 'apt-get --version' ); @@ -758,6 +869,11 @@ private function check_and_install( $command, $name ) { EE::exec( 'apt-get update' ); EE::exec( "apt-get install -y $name" ); } else { + $this->capture_error( + sprintf( '%s is not installed and could not be auto-installed (apt-get not available)', $name ), + self::ERROR_TYPE_CONFIG, + 2011 + ); EE::error( "$name is not installed. Please install $name for backup/restore." ); } } @@ -1058,6 +1174,11 @@ private function rclone_upload( $path ) { $output = EE::launch( $command ); if ( $output->return_code ) { + $this->capture_error( + 'Failed to upload backup to remote storage via rclone', + self::ERROR_TYPE_NETWORK, + 4001 + ); EE::error( 'Error uploading backup to remote storage.' ); } else { @@ -1225,6 +1346,7 @@ private function send_dash_success_callback( $ed_api_url, $backup_id, $verify_to /** * Send failure callback to EasyDash API after failed backup. + * Includes error details (message, type, code) for debugging and user feedback. * * @param string $ed_api_url The EasyDash API URL. * @param string $backup_id The backup ID. @@ -1234,11 +1356,20 @@ private function send_dash_failure_callback( $ed_api_url, $backup_id, $verify_to $endpoint = rtrim( $ed_api_url, '/' ) . '/easydash.easydash.doctype.site_backup.site_backup.on_ee_backup_failure'; $payload = [ - 'site' => $this->site_data['site_url'], - 'backup' => $backup_id, - 'verify' => $verify_token, + 'site' => $this->site_data['site_url'], + 'backup' => $backup_id, + 'verify' => $verify_token, + 'error_message' => $this->dash_error_message ?: 'Unknown error occurred', + 'error_type' => $this->dash_error_type, + 'error_code' => $this->dash_error_code, ]; + EE::debug( 'Sending failure callback with error details: ' . json_encode( [ + 'error_message' => $payload['error_message'], + 'error_type' => $payload['error_type'], + 'error_code' => $payload['error_code'], + ] ) ); + $this->send_dash_request( $endpoint, $payload ); } From 00451b766aac3dda492ab183860b6d092fbac888 Mon Sep 17 00:00:00 2001 From: Riddhesh Sanghvi Date: Mon, 22 Dec 2025 11:16:21 +0530 Subject: [PATCH 2/2] fix(backup): add missing error captures and improve error code consistency - Fix indentation inconsistencies in backup functions - Change unsupported site type error code from 1003 to 1002 for consistency - Use consistent error codes for fatal errors (5001) and interruptions (6000) - Improve rclone backend error message to show actual backend name Add error captures for backup operations: - Site files archive creation failure (3002) - WordPress content archive creation failure (3002) - Nginx configuration archive creation failure (3002) - PHP configuration archive creation failure (3002) - Database dump failure (4002) - Database SQL compression failure (3002) Error code reference: - 1xxx: Validation errors (1001, 1002) - 2xxx: Configuration errors (2001, 2002, 2003, 2010, 2011) - 3xxx: Filesystem/archive errors (3001, 3002) - 4xxx: Network/database errors (4001, 4002) - 5xxx: PHP fatal errors (5001) - 6xxx: Process interruptions (6000) --- src/helper/Site_Backup_Restore.php | 168 +++++++++++++++++++++-------- 1 file changed, 125 insertions(+), 43 deletions(-) diff --git a/src/helper/Site_Backup_Restore.php b/src/helper/Site_Backup_Restore.php index b7bcb16d..db299bea 100644 --- a/src/helper/Site_Backup_Restore.php +++ b/src/helper/Site_Backup_Restore.php @@ -65,16 +65,16 @@ public function backup( $args, $assoc_args = [] ) { // Debug: Log the raw dash_auth value received EE::debug( 'Received --dash-auth value: ' . $dash_auth ); - // Parse backup-id:backup-verification-token format - $auth_parts = explode( ':', $dash_auth, 2 ); - if ( count( $auth_parts ) !== 2 || empty( $auth_parts[0] ) || empty( $auth_parts[1] ) ) { - $this->capture_error( - 'Invalid --dash-auth format. Expected: backup-id:backup-verification-token', - self::ERROR_TYPE_VALIDATION, - 1001 - ); - EE::error( 'Invalid --dash-auth format. Expected: backup-id:backup-verification-token' ); - } + // Parse backup-id:backup-verification-token format + $auth_parts = explode( ':', $dash_auth, 2 ); + if ( count( $auth_parts ) !== 2 || empty( $auth_parts[0] ) || empty( $auth_parts[1] ) ) { + $this->capture_error( + 'Invalid --dash-auth format. Expected: backup-id:backup-verification-token', + self::ERROR_TYPE_VALIDATION, + 1001 + ); + EE::error( 'Invalid --dash-auth format. Expected: backup-id:backup-verification-token' ); + } // Check for ed-api-url configuration $ed_api_url = get_config_value( 'ed-api-url', '' ); @@ -117,7 +117,7 @@ public function backup( $args, $assoc_args = [] ) { $this->capture_error( sprintf( 'Backup is not supported for site type: %s', $this->site_data['site_type'] ), self::ERROR_TYPE_VALIDATION, - 1003 + 1002 ); EE::error( 'Backup is not supported for this site type.' ); } @@ -152,7 +152,7 @@ public function backup( $args, $assoc_args = [] ) { /** * Shutdown handler to send failure callback to EasyDash if backup didn't complete. * This is called when script terminates (including via EE::error which calls exit). - * + * * Automatically captures fatal errors and interrupted processes if no error was * explicitly captured during backup execution. */ @@ -165,7 +165,13 @@ public function dash_shutdown_handler() { $last_error = error_get_last(); // Check if this was a fatal PHP error - if ( $last_error && in_array( $last_error['type'], [ E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR ] ) ) { + if ( $last_error && in_array( $last_error['type'], [ + E_ERROR, + E_PARSE, + E_CORE_ERROR, + E_COMPILE_ERROR, + E_USER_ERROR + ] ) ) { $this->capture_error( sprintf( 'PHP Fatal Error: %s in %s:%d', @@ -174,14 +180,14 @@ public function dash_shutdown_handler() { $last_error['line'] ), self::ERROR_TYPE_FATAL, - $last_error['type'] + 5001 ); } else { // Script was killed, interrupted, or failed unexpectedly $this->capture_error( 'Backup process was interrupted or killed unexpectedly', self::ERROR_TYPE_INTERRUPTED, - 0 + 6000 ); } } @@ -200,7 +206,7 @@ public function dash_shutdown_handler() { * * @param string $message Error message describing what went wrong. * @param string $type Error type category (use ERROR_TYPE_* constants). - * @param int $code Error code for additional context (optional). + * @param int $code Error code for additional context (optional). */ private function capture_error( $message, $type = self::ERROR_TYPE_UNKNOWN, $code = 0 ) { // Only capture the first error (root cause) @@ -425,7 +431,17 @@ private function backup_site_dir( $backup_dir ) { $backup_file = $backup_dir . '/' . $this->site_data['site_url'] . '.zip'; $backup_command = sprintf( 'cd %s && 7z a -mx=1 %s .', $site_dir, $backup_file ); - EE::exec( $backup_command ); + $result = EE::exec( $backup_command ); + + // Check if archive was created successfully + if ( ! $result || ! $this->fs->exists( $backup_file ) ) { + $this->capture_error( + 'Failed to create site backup archive', + self::ERROR_TYPE_FILESYSTEM, + 3002 + ); + EE::error( 'Failed to create backup archive. Please check disk space and file permissions.' ); + } return $backup_file; } @@ -454,7 +470,16 @@ private function backup_wp_content_dir( $backup_dir ) { } $backup_command = sprintf( 'cd %s && 7z a -mx=1 %s wp-config.php', $site_dir . '/../', $backup_file ); - EE::exec( $backup_command ); + $result = EE::exec( $backup_command ); + + if ( ! $result ) { + $this->capture_error( + 'Failed to create WordPress content backup archive', + self::ERROR_TYPE_FILESYSTEM, + 3002 + ); + EE::error( 'Failed to create backup archive. Please check disk space and file permissions.' ); + } // meta.json path $meta_file = $backup_dir . '/meta.json'; @@ -472,6 +497,16 @@ private function backup_wp_content_dir( $backup_dir ) { EE::exec( $backup_command ); } + // Final check that backup file was created successfully + if ( ! $this->fs->exists( $backup_file ) ) { + $this->capture_error( + 'Failed to create WordPress content backup archive', + self::ERROR_TYPE_FILESYSTEM, + 3002 + ); + EE::error( 'Failed to create backup archive. Please check disk space and file permissions.' ); + } + return $backup_file; } @@ -482,7 +517,16 @@ private function backup_nginx_conf( $backup_dir ) { $backup_file = $backup_dir . '/conf.zip'; $backup_command = sprintf( 'cd %s && 7z a -mx=1 %s nginx', $conf_dir, $backup_file ); - EE::exec( $backup_command ); + $result = EE::exec( $backup_command ); + + if ( ! $result ) { + $this->capture_error( + 'Failed to create nginx configuration backup archive', + self::ERROR_TYPE_FILESYSTEM, + 3002 + ); + EE::error( 'Failed to create nginx configuration backup archive. Please check disk space and file permissions.' ); + } } private function backup_php_conf( $backup_dir ) { @@ -492,7 +536,16 @@ private function backup_php_conf( $backup_dir ) { $backup_file = $backup_dir . '/conf.zip'; $backup_command = sprintf( 'cd %s && 7z u -mx=1 %s php', $conf_dir, $backup_file ); - EE::exec( $backup_command ); + $result = EE::exec( $backup_command ); + + if ( ! $result ) { + $this->capture_error( + 'Failed to create PHP configuration backup archive', + self::ERROR_TYPE_FILESYSTEM, + 3002 + ); + EE::error( 'Failed to create PHP configuration backup archive. Please check disk space and file permissions.' ); + } } private function backup_html( $backup_dir ) { @@ -541,10 +594,31 @@ private function backup_db( $backup_dir ) { $options = [ 'skip-tty' => true ]; EE::run_command( $args, $assoc_args, $options ); - EE::exec( sprintf( 'mv %s %s', EE_ROOT_DIR . '/sites/' . $this->site_data['site_url'] . '/app/htdocs/' . $sql_filename, $sql_file ) ); + + $sql_dump_path = EE_ROOT_DIR . '/sites/' . $this->site_data['site_url'] . '/app/htdocs/' . $sql_filename; + + // Check if database dump was created successfully + if ( ! $this->fs->exists( $sql_dump_path ) ) { + $this->capture_error( + sprintf( 'Database backup failed for database: %s', $db_name ), + self::ERROR_TYPE_DATABASE, + 4002 + ); + EE::error( 'Database backup failed. Please check database credentials and connectivity.' ); + } + + EE::exec( sprintf( 'mv %s %s', $sql_dump_path, $sql_file ) ); $backup_command = sprintf( 'cd %s && 7z u -mx=1 %s sql', $backup_dir, $backup_file ); - EE::exec( $backup_command ); + $result = EE::exec( $backup_command ); + if ( ! $result ) { + $this->capture_error( + 'Failed to compress database backup into archive', + self::ERROR_TYPE_FILESYSTEM, + 3002 + ); + EE::error( 'Failed to compress database backup. Please check disk space.' ); + } $this->fs->remove( $backup_dir . '/sql' ); } @@ -751,16 +825,17 @@ private function pre_backup_restore_checks() { $command = 'rclone listremotes'; $output = EE::launch( $command ); - $rclone_path = get_config_value( 'rclone-path', 'easyengine:easyengine' ); - $rclone_path = explode( ':', $rclone_path )[0] . ':'; + $rclone_path = get_config_value( 'rclone-path', 'easyengine:easyengine' ); + $rclone_backend = explode( ':', $rclone_path )[0]; + $rclone_path = $rclone_backend . ':'; if ( strpos( $output->stdout, $rclone_path ) === false ) { $this->capture_error( - 'rclone backend easyengine does not exist', + sprintf( 'rclone backend "%s" is not configured. Please create it using `rclone config`', $rclone_backend ), self::ERROR_TYPE_CONFIG, 2002 ); - EE::error( 'rclone backend easyengine does not exist. Please create it using `rclone config`' ); + EE::error( sprintf( 'rclone backend "%s" does not exist. Please create it using `rclone config`', $rclone_backend ) ); } $this->check_and_install( 'zip', 'zip' ); @@ -1217,7 +1292,7 @@ private function cleanup_old_backups() { // Check if we have more backups than allowed if ( count( $backups ) > ( $no_of_backups + 1 ) ) { $backups_to_delete = array_slice( $backups, $no_of_backups ); - + EE::log( sprintf( 'Cleaning up old backups. Keeping %d most recent backups.', $no_of_backups ) ); foreach ( $backups_to_delete as $backup ) { EE::log( 'Deleting old backup: ' . $backup ); @@ -1241,6 +1316,7 @@ private function cleanup_old_backups() { private function rollback_failed_backup() { if ( empty( $this->dash_new_backup_path ) ) { EE::warning( 'Cannot rollback backup: backup path not found.' ); + return; } @@ -1305,10 +1381,11 @@ private function restore_php_conf( $backup_dir ) { /** * Send success callback to EasyDash API after successful backup. * - * @param string $ed_api_url The EasyDash API URL. - * @param string $backup_id The backup ID. - * @param string $verify_token The verification token. - * @param array $backup_metadata The backup metadata. + * @param string $ed_api_url The EasyDash API URL. + * @param string $backup_id The backup ID. + * @param string $verify_token The verification token. + * @param array $backup_metadata The backup metadata. + * * @return bool True if API request succeeded, false otherwise. */ private function send_dash_success_callback( $ed_api_url, $backup_id, $verify_token, $backup_metadata ) { @@ -1365,10 +1442,10 @@ private function send_dash_failure_callback( $ed_api_url, $backup_id, $verify_to ]; EE::debug( 'Sending failure callback with error details: ' . json_encode( [ - 'error_message' => $payload['error_message'], - 'error_type' => $payload['error_type'], - 'error_code' => $payload['error_code'], - ] ) ); + 'error_message' => $payload['error_message'], + 'error_type' => $payload['error_type'], + 'error_code' => $payload['error_code'], + ] ) ); $this->send_dash_request( $endpoint, $payload ); } @@ -1377,14 +1454,15 @@ private function send_dash_failure_callback( $ed_api_url, $backup_id, $verify_to * Send HTTP request to EasyEngine Dashboard API with retry logic for 5xx errors and connection errors. * * @param string $endpoint The API endpoint URL. - * @param array $payload The request payload. + * @param array $payload The request payload. + * * @return bool True if request succeeded, false otherwise. */ private function send_dash_request( $endpoint, $payload ) { - $max_retries = 3; - $retry_delay = 300; // 5 minutes in seconds + $max_retries = 3; + $retry_delay = 300; // 5 minutes in seconds $max_attempts = $max_retries + 1; // 1 initial attempt + 3 retries = 4 total - $attempt = 1; + $attempt = 1; while ( $attempt <= $max_attempts ) { $ch = curl_init( $endpoint ); @@ -1397,9 +1475,9 @@ private function send_dash_request( $endpoint, $payload ) { ] ); curl_setopt( $ch, CURLOPT_TIMEOUT, 30 ); - $response = curl_exec( $ch ); + $response = curl_exec( $ch ); $http_code = curl_getinfo( $ch, CURLINFO_HTTP_CODE ); - $error = curl_error( $ch ); + $error = curl_error( $ch ); curl_close( $ch ); @@ -1410,13 +1488,14 @@ private function send_dash_request( $endpoint, $payload ) { if ( ! $error && $http_code >= 200 && $http_code < 300 ) { EE::log( 'EasyEngine Dashboard callback sent successfully.' ); EE::debug( 'EasyEngine Dashboard response: ' . $response_text ); + return true; // Success } // Determine if this is a retryable error - $is_5xx_error = $http_code >= 500 && $http_code < 600; + $is_5xx_error = $http_code >= 500 && $http_code < 600; $is_connection_error = ! empty( $error ) || $http_code === 0; - $should_retry = ( $is_5xx_error || $is_connection_error ) && $attempt < $max_attempts; + $should_retry = ( $is_5xx_error || $is_connection_error ) && $attempt < $max_attempts; if ( $should_retry ) { // Retry on 5xx errors or connection errors @@ -1470,6 +1549,7 @@ private function send_dash_request( $endpoint, $payload ) { // 4xx or other HTTP error codes that shouldn't be retried EE::warning( 'EasyEngine Dashboard callback returned HTTP ' . $http_code . '. Response: ' . $response_text ); } + return false; // Failure } } @@ -1481,12 +1561,14 @@ private function send_dash_request( $endpoint, $payload ) { * Sanitize count value for API payload. * * @param mixed $value The value to sanitize. + * * @return int The sanitized integer value. */ private function sanitize_count( $value ) { if ( $value === '-' || ! is_numeric( $value ) ) { return 0; } + return intval( $value ); } }