Field name to its allowed range and engine default. */ private const BOUNDS = [ 'shiftora_chunk_size' => [ 'min' => DatabaseExporter::MIN_EFFECTIVE_CHUNK_SIZE, 'max' => DatabaseExporter::MAX_CHUNK_SIZE, 'default' => DatabaseExporter::DEFAULT_CHUNK_SIZE, ], 'shiftora_sql_byte_budget' => [ 'min' => 64 * 1024, 'max' => 32 * 1024 * 1024, 'default' => DatabaseExporter::SQL_BYTE_BUDGET, ], 'shiftora_sql_encoded_budget' => [ 'min' => 64 * 1024, 'max' => 32 * 1024 * 1024, 'default' => DatabaseExporter::SQL_ENCODED_BUDGET, ], 'shiftora_request_timeout' => [ 'min' => 10, 'max' => 300, 'default' => HttpClient::INSTALLED_DEFAULT_TIMEOUT, ], ]; /** * The current values, resolved the way a migration would read them. * * @return array{chunk_size:int, sql_byte_budget:int, sql_encoded_budget:int, request_timeout:int, ssl_verify:string} */ public static function current(): array { $raw_chunk_size = (int) get_option( 'shiftora_chunk_size', DatabaseExporter::DEFAULT_CHUNK_SIZE ); return [ 'chunk_size' => self::clamp( 'shiftora_chunk_size', DatabaseExporter::effective_chunk_size( $raw_chunk_size ) ), 'sql_byte_budget' => self::clamp( 'shiftora_sql_byte_budget', (int) get_option( 'shiftora_sql_byte_budget', DatabaseExporter::SQL_BYTE_BUDGET ) ), 'sql_encoded_budget' => self::clamp( 'shiftora_sql_encoded_budget', (int) get_option( 'shiftora_sql_encoded_budget', DatabaseExporter::SQL_ENCODED_BUDGET ) ), 'request_timeout' => self::clamp( 'shiftora_request_timeout', (int) get_option( 'shiftora_request_timeout', HttpClient::INSTALLED_DEFAULT_TIMEOUT ) ), 'ssl_verify' => HttpClient::tls_mode( get_option( 'shiftora_ssl_verify', HttpClient::TLS_AUTO ) ), ]; } /** * @return array{min:int, max:int} */ public static function bounds( string $field ): array { return [ 'min' => self::BOUNDS[ $field ]['min'], 'max' => self::BOUNDS[ $field ]['max'], ]; } /** * Keeps a value inside the field's HTML min/max so a stored value from outside * the range (older version, a filter, WP-CLI) still renders as submittable. */ private static function clamp( string $field, int $value ): int { $range = self::BOUNDS[ $field ]; return max( $range['min'], min( $range['max'], $value ) ); } /** * Every request field save() reads besides the nonce; the admin-post handler passes nothing else on. * * @return string[] */ public static function field_names(): array { return [ ...array_keys( self::BOUNDS ), 'shiftora_ssl_verify' ]; } /** * Validate and store the submitted fields. * * @param array $request Submitted fields, i.e. $_POST. * * @return bool|WP_Error True when saved, otherwise why not. */ public function save( array $request ): bool|WP_Error { if ( ! current_user_can( 'manage_options' ) ) { return new WP_Error( 'shiftora_settings_forbidden', __( 'You do not have permission to change this setting.', 'shiftora' ) ); } if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( (string) ( $request['_wpnonce'] ?? '' ) ) ), self::NONCE_ACTION ) ) { return new WP_Error( 'shiftora_settings_forbidden', __( 'This request could not be verified. Please reload the page and try again.', 'shiftora' ), [ 'status' => 403 ] ); } foreach ( self::BOUNDS as $field => $range ) { $raw = $request[ $field ] ?? null; if ( null === $raw || '' === trim( (string) $raw ) || ! is_numeric( $raw ) ) { continue; // Left blank: keep whatever is already stored. } $value = max( $range['min'], min( $range['max'], (int) $raw ) ); if ( $value === $range['default'] ) { delete_option( $field ); } else { update_option( $field, $value ); } } // Auto is the default and is stored as no option; true is how "always" has been stored since the checkbox. $tls_mode = HttpClient::tls_mode( $request['shiftora_ssl_verify'] ?? null ); if ( HttpClient::TLS_ALWAYS === $tls_mode ) { update_option( 'shiftora_ssl_verify', true ); } elseif ( HttpClient::TLS_NEVER === $tls_mode ) { update_option( 'shiftora_ssl_verify', HttpClient::TLS_NEVER ); } else { delete_option( 'shiftora_ssl_verify' ); } return true; } }