*/ const PRESETS = array( 'small' => 10, 'medium' => 50, 'fast' => 100, ); /** * Default preset key. */ public static function default_preset(): string { return 'medium'; } /** * Resolve the number of work units to process per tick for an operation. * * @param string $operation Operation slug (export|backup|find_replace). */ public static function batch_size( string $operation = '' ): int { $settings = Helpers::get_settings(); $preset = (string) ( $settings['batch_preset'] ?? self::default_preset() ); if ( 'custom' === $preset ) { $size = max( 1, absint( $settings['batch_size_custom'] ?? self::PRESETS['medium'] ) ); } else { $size = self::PRESETS[ $preset ] ?? self::PRESETS['medium']; } /** * Filter the per-tick batch size. * * @param int $size Items per tick. * @param string $operation Operation slug. */ $size = (int) apply_filters( 'spinxqm_batch_size', $size, $operation ); return max( 1, $size ); } /** * Bytes of output to accumulate per step request before yielding. This is the * primary work limiter for the browser-driven step loop (a "max request * size"): each step writes ~this many bytes of SQL, then returns * so the browser can immediately fire the next request. Larger = fewer, beefier * requests = faster on capable hosts; smaller = lower memory/peak. * * @param string $operation Operation slug. */ public static function chunk_bytes( string $operation = '' ): int { $settings = Helpers::get_settings(); $bytes = absint( $settings['max_chunk_bytes'] ?? 0 ); if ( $bytes < 1 ) { $bytes = 6 * MB_IN_BYTES; } /** * Filter the per-step output byte budget. * * @param int $bytes Bytes per step. * @param string $operation Operation slug. */ $bytes = (int) apply_filters( 'spinxqm_chunk_bytes', $bytes, $operation ); // Clamp to a sane window (256 KB – 128 MB). return max( 256 * KB_IN_BYTES, min( 128 * MB_IN_BYTES, $bytes ) ); } /** * Maximum bytes of row data per INSERT statement in a SQL dump. Keeps every * statement comfortably under MySQL's max_allowed_packet on import (a single * 2000-row extended INSERT of large rows can otherwise blow the packet limit and * the importer's buffer). Default 1 MB; filterable. */ public static function max_insert_bytes(): int { $bytes = (int) apply_filters( 'spinxqm_max_insert_bytes', (int) MB_IN_BYTES ); return max( 64 * KB_IN_BYTES, $bytes ); } /** * Gzip compression level for SQL output. Default 6 (the standard zlib balance); * 1 = fastest/largest, 9 = slowest/smallest. */ public static function gzip_level(): int { $settings = Helpers::get_settings(); $level = (int) ( $settings['gzip_level'] ?? 6 ); /** * Filter the gzip compression level. * * @param int $level Compression level 1–9. */ $level = (int) apply_filters( 'spinxqm_gzip_level', $level ); return max( 1, min( 9, $level ) ); } /** * Soft time budget (seconds) for a single step request. The batch loop yields * once this elapses even if the byte budget has not been reached, so a slow * chunk never blows past the request limit. No set_time_limit() is used. * * @param string $operation Operation slug. */ public static function time_budget( string $operation = '' ): int { /** * Filter the per-tick soft time budget in seconds. * * @param int $seconds Default budget. * @param string $operation Operation slug. */ $seconds = (int) apply_filters( 'spinxqm_process_timeout', 20, $operation ); return max( 5, $seconds ); } /** * Raise the memory ceiling for a processing tick using the WordPress * wrapper (never ini_set). Developers can override via the filter. */ public static function maybe_raise_memory(): void { $custom = (string) apply_filters( 'spinxqm_process_memory_limit', '' ); if ( '' !== $custom ) { add_filter( 'admin_memory_limit', static function () use ( $custom ) { return $custom; } ); } wp_raise_memory_limit( 'admin' ); } /** * Whether a preset key is valid (named preset or "custom"). * * @param string $preset Preset key to check. * @return bool */ public static function is_valid_preset( string $preset ): bool { return 'custom' === $preset || isset( self::PRESETS[ $preset ] ); } }