init_hooks();
}
/**
* Initialize hooks.
*/
private function init_hooks() {
// Load admin components only if needed.
if ( is_admin() ) {
require_once QUEUE_OPTIMIZER_PLUGIN_DIR . 'admin/class-settings-page.php';
Queue_Optimizer_Settings_Page::get_instance();
require_once QUEUE_OPTIMIZER_PLUGIN_DIR . 'admin/class-dashboard-widget.php';
Queue_Optimizer_Dashboard_Widget::get_instance();
}
// Core ActionScheduler optimization filters.
add_filter( 'action_scheduler_queue_runner_time_limit', array( $this, 'set_time_limit' ) );
add_filter( 'action_scheduler_queue_runner_concurrent_batches', array( $this, 'set_concurrent_batches' ) );
add_filter( 'action_scheduler_queue_runner_batch_size', array( $this, 'set_batch_size' ) );
add_filter( 'action_scheduler_retention_period', array( $this, 'set_retention_period' ) );
// Image editor priority.
add_filter( 'wp_image_editors', array( $this, 'set_image_editor' ) );
// Raise memory limit for image processing.
add_filter( 'image_memory_limit', array( $this, 'set_image_memory_limit' ) );
// Plugin activation hook.
register_activation_hook( QUEUE_OPTIMIZER_PLUGIN_DIR . '365i-queue-optimizer.php', array( __CLASS__, 'activate' ) );
}
/**
* Display WordPress version compatibility notice.
*/
public function display_version_notice() {
echo '
' .
sprintf(
/* translators: %s: minimum required WordPress version */
esc_html__( '365i Queue Optimizer requires WordPress version %s or higher.', '365i-queue-optimizer' ),
esc_html( QUEUE_OPTIMIZER_MIN_WP_VERSION )
) .
'
';
}
/**
* Set ActionScheduler time limit.
*
* @param int $time_limit The default time limit.
* @return int Modified time limit.
*/
public function set_time_limit( $time_limit ) {
$value = get_option( 'queue_optimizer_time_limit', 60 );
$validated = $this->validate_int_option( $value, 10, 300, 60 );
return apply_filters( 'queue_optimizer_time_limit', $validated, $value, $time_limit );
}
/**
* Set ActionScheduler concurrent batches.
*
* @param int $batches The default number of concurrent batches.
* @return int Modified number of concurrent batches.
*/
public function set_concurrent_batches( $batches ) {
$value = get_option( 'queue_optimizer_concurrent_batches', 4 );
$validated = $this->validate_int_option( $value, 1, 10, 4 );
return apply_filters( 'queue_optimizer_concurrent_batches', $validated, $value, $batches );
}
/**
* Set ActionScheduler batch size.
*
* @since 1.4.0
* @param int $batch_size The default batch size.
* @return int Modified batch size.
*/
public function set_batch_size( $batch_size ) {
$value = get_option( 'queue_optimizer_batch_size', 50 );
$validated = $this->validate_int_option( $value, 25, 200, 50 );
return apply_filters( 'queue_optimizer_batch_size', $validated, $value, $batch_size );
}
/**
* Set ActionScheduler data retention period.
*
* @since 1.4.0
* @param int $period The default retention period in seconds.
* @return int Modified retention period.
*/
public function set_retention_period( $period ) {
$days = get_option( 'queue_optimizer_retention_days', 7 );
$validated = $this->validate_int_option( $days, 1, 30, 7 );
$seconds = $validated * DAY_IN_SECONDS;
return apply_filters( 'queue_optimizer_retention_period', $seconds, $validated, $period );
}
/**
* Set image editor priority.
*
* @param array $editors Array of image editor class names.
* @return array Modified array with preferred editor prioritized.
*/
public function set_image_editor( $editors ) {
$allowed = array( 'WP_Image_Editor_Imagick', 'WP_Image_Editor_GD' );
$preferred = get_option( 'queue_optimizer_image_engine', 'WP_Image_Editor_Imagick' );
if ( ! in_array( $preferred, $allowed, true ) ) {
$preferred = 'WP_Image_Editor_Imagick';
}
$result = array_merge( array( $preferred ), array_diff( $editors, array( $preferred ) ) );
return apply_filters( 'queue_optimizer_image_editors', $result, $preferred, $editors );
}
/**
* Set image memory limit for processing.
*
* @since 1.4.0
* @param int $limit Current memory limit.
* @return int Optimized memory limit.
*/
public function set_image_memory_limit( $limit ) {
// Use WordPress's recommended image memory limit.
$wp_limit = wp_convert_hr_to_bytes( WP_MAX_MEMORY_LIMIT );
// Return the higher of current or WP max.
$optimized = max( $limit, $wp_limit );
return apply_filters( 'queue_optimizer_image_memory_limit', $optimized, $limit );
}
/**
* Validate integer option within range.
*
* @param mixed $value The value to validate.
* @param int $min Minimum allowed value.
* @param int $max Maximum allowed value.
* @param int $default Default value if validation fails.
* @return int Validated value.
*/
private function validate_int_option( $value, $min, $max, $default ) {
$value = intval( $value );
return ( $value < $min || $value > $max ) ? $default : $value;
}
/**
* Get server environment information.
*
* @since 1.4.0
* @return array Server environment data.
*/
public static function get_server_environment() {
$env = array();
// PHP settings.
$env['php_version'] = PHP_VERSION;
$env['max_execution_time'] = (int) ini_get( 'max_execution_time' );
$env['memory_limit'] = ini_get( 'memory_limit' );
$env['memory_limit_bytes'] = wp_convert_hr_to_bytes( ini_get( 'memory_limit' ) );
// WordPress settings.
$env['wp_version'] = $GLOBALS['wp_version'];
$env['wp_memory_limit'] = WP_MEMORY_LIMIT;
$env['wp_max_memory_limit'] = WP_MAX_MEMORY_LIMIT;
// ImageMagick detection.
$env['imagick_available'] = extension_loaded( 'imagick' ) && class_exists( 'Imagick' );
$env['imagick_version'] = '';
if ( $env['imagick_available'] ) {
$imagick = new Imagick();
$version = $imagick->getVersion();
if ( isset( $version['versionString'] ) ) {
// Extract version number from string like "ImageMagick 7.1.0-62 Q16..."
preg_match( '/ImageMagick\s+([\d.]+)/', $version['versionString'], $matches );
$env['imagick_version'] = isset( $matches[1] ) ? $matches[1] : '';
}
}
// GD detection.
$env['gd_available'] = extension_loaded( 'gd' ) && function_exists( 'gd_info' );
$env['gd_version'] = '';
if ( $env['gd_available'] ) {
$gd_info = gd_info();
$env['gd_version'] = isset( $gd_info['GD Version'] ) ? $gd_info['GD Version'] : '';
}
// WebP/AVIF support.
$env['webp_support'] = false;
$env['avif_support'] = false;
if ( $env['imagick_available'] ) {
$formats = Imagick::queryFormats();
$env['webp_support'] = in_array( 'WEBP', $formats, true );
$env['avif_support'] = in_array( 'AVIF', $formats, true );
} elseif ( $env['gd_available'] ) {
$gd_info = gd_info();
$env['webp_support'] = ! empty( $gd_info['WebP Support'] );
$env['avif_support'] = ! empty( $gd_info['AVIF Support'] );
}
// ActionScheduler status.
$env['actionscheduler_active'] = class_exists( 'ActionScheduler' );
$env['actionscheduler_version'] = '';
$env['pending_actions'] = 0;
if ( $env['actionscheduler_active'] && class_exists( 'ActionScheduler_Versions' ) ) {
$env['actionscheduler_version'] = ActionScheduler_Versions::instance()->latest_version();
if ( function_exists( 'as_get_scheduled_actions' ) ) {
$pending = as_get_scheduled_actions( array(
'status' => ActionScheduler_Store::STATUS_PENDING,
'per_page' => 0,
), 'ids' );
$env['pending_actions'] = count( $pending );
}
}
// Hosting environment detection.
$env['server_type'] = self::detect_server_type();
return $env;
}
/**
* Detect server type for recommended settings.
*
* @since 1.4.0
* @since 1.5.0 Added manual override and more conservative auto-detection.
* @return string Server type: 'shared', 'vps', or 'dedicated'.
*/
private static function detect_server_type() {
// First check for manual override.
$manual = get_option( 'queue_optimizer_server_type_override', '' );
if ( in_array( $manual, array( 'shared', 'vps', 'dedicated' ), true ) ) {
return $manual;
}
$memory_bytes = wp_convert_hr_to_bytes( ini_get( 'memory_limit' ) );
$execution_time = (int) ini_get( 'max_execution_time' );
// Very high thresholds for "dedicated" - be conservative.
// Many shared hosts now offer high PHP limits but share CPU/DB resources.
if ( $memory_bytes >= 1024 * MB_IN_BYTES && ( $execution_time >= 300 || 0 === $execution_time ) ) {
return 'dedicated';
}
// Higher thresholds for "vps".
if ( $memory_bytes >= 512 * MB_IN_BYTES && $execution_time >= 120 ) {
return 'vps';
}
// Default: assume shared hosting (safest).
return 'shared';
}
/**
* Get all recommendations for all server types.
*
* @since 1.6.0
* @return array All server type recommendations.
*/
public static function get_all_recommendations() {
return array(
'shared' => array(
'time_limit' => 30,
'concurrent_batches' => 1,
'batch_size' => 25,
'retention_days' => 3,
),
'vps' => array(
'time_limit' => 45,
'concurrent_batches' => 2,
'batch_size' => 35,
'retention_days' => 5,
),
'dedicated' => array(
'time_limit' => 60,
'concurrent_batches' => 4,
'batch_size' => 50,
'retention_days' => 7,
),
);
}
/**
* Get recommended settings based on server environment.
*
* @since 1.4.0
* @since 1.5.0 More conservative default values.
* @return array Recommended settings.
*/
public static function get_recommended_settings() {
$server_type = self::detect_server_type();
$recommendations = self::get_all_recommendations();
return isset( $recommendations[ $server_type ] )
? $recommendations[ $server_type ]
: $recommendations['shared'];
}
/**
* Plugin activation.
*/
public static function activate() {
// Set default options.
add_option( 'queue_optimizer_time_limit', 60 );
add_option( 'queue_optimizer_concurrent_batches', 4 );
add_option( 'queue_optimizer_batch_size', 50 );
add_option( 'queue_optimizer_retention_days', 7 );
add_option( 'queue_optimizer_image_engine', 'WP_Image_Editor_Imagick' );
add_option( 'queue_optimizer_activated', time() );
// Clear any caches.
wp_cache_flush();
}
}