馃敶 HIGH PRIORITY TASKS (Function Logic Improvements)
LOGIC-001: Fix Unsafe Option Access Pattern
File: admin/class-infinite-loader-for-woocommerce-admin.php
Current Issue: Direct array access without validation
// Line 388-389 - UNSAFE
$infinite_loader_lm_custom_class = isset( $infinite_loader_button_setting['custom_class'] ) ? $infinite_loader_button_setting['custom_class'] : '';
$infinite_loader_lm_button_text = isset( $infinite_loader_button_setting['button_text'] ) ? $infinite_loader_button_setting['button_text'] : '';
Replace with Safe Helper Method:
/**
* Safely get option value with default fallback
*
* @param array $options Option array
* @param string $key Option key
* @param mixed $default Default value
* @return mixed Option value or default
*/
private function infinite_loader_get_option_value( $options, $key, $default = '' ) {
if ( ! is_array( $options ) ) {
return $default;
}
return isset( $options[ $key ] ) && ! empty( $options[ $key ] ) ? $options[ $key ] : $default;
}
// Usage throughout the class:
$infinite_loader_lm_custom_class = $this->infinite_loader_get_option_value( $infinite_loader_button_setting, 'custom_class' );
$infinite_loader_lm_button_text = $this->infinite_loader_get_option_value( $infinite_loader_button_setting, 'button_text', 'Load More' );
LOGIC-002: Fix Empty Settings Check Logic
File: admin/class-infinite-loader-for-woocommerce-admin.php
Lines 407-409 - Current Logic Issue:
public function infinite_loader_for_woocommerce_button_style() {
$infinite_loader_load_more_button_style = '';
$infinite_loader_button_setting = get_option( 'infinite_loader_admin_button_option', array() );
if( empty( $infinite_loader_button_setting ) ) {
return;
}
// Missing return statement causes undefined behavior
Fix Return Logic:
public function infinite_loader_for_woocommerce_button_style() {
$infinite_loader_load_more_button_style = '';
$infinite_loader_button_setting = get_option( 'infinite_loader_admin_button_option', array() );
if ( empty( $infinite_loader_button_setting ) ) {
return ''; // Return empty string instead of void
}
// Validate required fields exist
$required_fields = array( 'text_font_size', 'text_color', 'background_color' );
foreach ( $required_fields as $field ) {
if ( ! isset( $infinite_loader_button_setting[ $field ] ) ) {
error_log( "Infinite Loader: Missing required button setting: {$field}" );
return '';
}
}
// Continue with existing logic...
}
LOGIC-003: Fix Inconsistent Default Value Handling
File: admin/class-infinite-loader-for-woocommerce-admin.php
Lines 410-420 - Inconsistent defaults:
// Current inconsistent logic
$infinite_loader_lm_button_margin_top = ( isset( $infinite_loader_button_setting['margin_top'] ) && ! empty( $infinite_loader_button_setting['margin_top'] ) ) ? $infinite_loader_button_setting['margin_top'] : '0';
$infinite_loader_lm_button_margin_right = ( isset( $infinite_loader_button_setting['margin_right'] ) && ! empty( $infinite_loader_button_setting['margin_right'] ) ) ? $infinite_loader_button_setting['margin_right'] : '0';
Replace with Consistent Helper:
/**
* Get button dimension value with validation
*
* @param array $settings Button settings
* @param string $key Setting key
* @param string $default Default value
* @return string Validated dimension value
*/
private function infinite_loader_get_dimension_value( $settings, $key, $default = '0' ) {
$value = $this->infinite_loader_get_option_value( $settings, $key, $default );
// Ensure numeric value
$numeric_value = absint( $value );
// Limit reasonable range (0-999px)
return (string) max( 0, min( 999, $numeric_value ) );
}
// Replace all margin/padding/border assignments:
$infinite_loader_lm_button_margin_top = $this->infinite_loader_get_dimension_value( $infinite_loader_button_setting, 'margin_top' );
$infinite_loader_lm_button_margin_right = $this->infinite_loader_get_dimension_value( $infinite_loader_button_setting, 'margin_right' );
$infinite_loader_lm_button_margin_bottom = $this->infinite_loader_get_dimension_value( $infinite_loader_button_setting, 'margin_bottom' );
$infinite_loader_lm_button_margin_left = $this->infinite_loader_get_dimension_value( $infinite_loader_button_setting, 'margin_left' );
LOGIC-004: Fix Style Generation Logic Issues
File: admin/class-infinite-loader-for-woocommerce-admin.php
Lines 424-445 - Refactor style building:
/**
* Build CSS style string for button
*
* @param array $infinite_loader_button_setting Button settings
* @return string Generated CSS styles
*/
private function infinite_loader_build_button_styles( $infinite_loader_button_setting ) {
$styles = array();
// Text styles
$styles[] = sprintf(
'font-size: %spx;',
$this->infinite_loader_get_dimension_value( $infinite_loader_button_setting, 'text_font_size', '16' )
);
// Color styles with validation
$text_color = $this->infinite_loader_get_option_value( $infinite_loader_button_setting, 'text_color', '#ffffff' );
$bg_color = $this->infinite_loader_get_option_value( $infinite_loader_button_setting, 'background_color', '#1d76da' );
if ( $this->infinite_loader_is_valid_color( $text_color ) ) {
$styles[] = sprintf( 'color: %s;', sanitize_hex_color( $text_color ) );
}
if ( $this->infinite_loader_is_valid_color( $bg_color ) ) {
$styles[] = sprintf( 'background-color: %s;', sanitize_hex_color( $bg_color ) );
}
// Spacing styles
$styles[] = $this->infinite_loader_build_spacing_styles( $infinite_loader_button_setting );
// Border styles
$styles[] = $this->infinite_loader_build_border_styles( $infinite_loader_button_setting );
return implode( ' ', array_filter( $styles ) );
}
/**
* Validate color format
*
* @param string $color Color value
* @return bool True if valid color
*/
private function infinite_loader_is_valid_color( $color ) {
return ! empty( $color ) && (
sanitize_hex_color( $color ) ||
preg_match( '/^rgb\(/', $color ) ||
preg_match( '/^rgba\(/', $color )
);
}
/**
* Build spacing styles (padding/margin)
*
* @param array $settings Button settings
* @return string CSS spacing styles
*/
private function infinite_loader_build_spacing_styles( $settings ) {
$spacing_styles = array();
$properties = array( 'padding', 'margin' );
$directions = array( 'top', 'right', 'bottom', 'left' );
foreach ( $properties as $property ) {
foreach ( $directions as $direction ) {
$key = $property . '_' . $direction;
$value = $this->infinite_loader_get_dimension_value( $settings, $key );
$spacing_styles[] = sprintf( '%s-%s: %spx;', $property, $direction, $value );
}
}
return implode( ' ', $spacing_styles );
}
LOGIC-005: Fix JavaScript DOM Performance Issues
File: public/js/infinite_loader_products.js
Lines 15-30 - Fix DOM cache initialization:
// Replace existing domCache with performance improvements
var infinite_loader_dom_cache = {
products: null,
pagination: null,
result_count: null,
window: null,
document: null,
body: null,
cache_timestamp: 0,
cache_duration: 5000, // 5 seconds cache
infinite_loader_init_cache: function() {
const now = Date.now();
if (now - this.cache_timestamp < this.cache_duration && this.products) {
return; // Cache still valid
}
this.products = $(infinite_loader_product_data.products);
this.pagination = $(infinite_loader_product_data.pagination);
this.result_count = $('.woocommerce-result-count');
this.window = $(window);
this.document = $(document);
this.body = $('body');
this.cache_timestamp = now;
},
infinite_loader_get_products: function() {
this.infinite_loader_init_cache();
return this.products;
},
infinite_loader_get_pagination: function() {
this.infinite_loader_init_cache();
return this.pagination;
},
infinite_loader_refresh_cache: function() {
this.cache_timestamp = 0;
this.infinite_loader_init_cache();
}
};
// Replace all domCache references with infinite_loader_dom_cache
LOGIC-006: Fix AJAX Error Handling Logic
File: public/js/infinite_loader_products.js
Lines 150+ - Improve error handling:
// Replace existing AJAX call
infinite_loader_ajax_instance = $.ajax({
method: "GET",
url: next_page,
timeout: 30000, // Add timeout
beforeSend: function (xhr) {
xhr.setRequestHeader('X-Braapfdisable', '1');
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
},
success: function (data) {
infinite_loader_process_ajax_response(data, next_page, replace);
},
error: function(xhr, status, error) {
infinite_loader_handle_ajax_error(xhr, status, error, next_page, replace);
},
complete: function() {
infinite_loader_ajax_instance = false;
}
});
/**
* Handle AJAX errors with retry mechanism
*/
function infinite_loader_handle_ajax_error(xhr, status, error, next_page, replace) {
console.error('Infinite Loader AJAX Error:', {
status: xhr.status,
statusText: xhr.statusText,
error: error,
url: next_page
});
// Determine error type
let error_message = infinite_loader_product_data.error_message || 'Unable to load more products.';
let retry_available = true;
if (xhr.status === 404) {
error_message = infinite_loader_product_data.not_found_message || 'No more products available.';
retry_available = false;
} else if (xhr.status === 0 && status === 'timeout') {
error_message = infinite_loader_product_data.timeout_message || 'Request timed out. Please check your connection.';
}
// Show error UI
infinite_loader_show_error_message(error_message, retry_available, next_page, replace);
// Clean up loading state
infinite_loader_end_ajax_loading();
}
/**
* Show user-friendly error message
*/
function infinite_loader_show_error_message(message, show_retry, next_page, replace) {
let error_html = '<div class="infinite-loader-error" role="alert">' +
'<p>' + message + '</p>';
if (show_retry) {
error_html += '<button class="infinite-loader-retry button" data-url="' + next_page + '" data-replace="' + replace + '">' +
(infinite_loader_product_data.retry_text || 'Retry') + '</button>';
}
error_html += '</div>';
infinite_loader_dom_cache.infinite_loader_get_products().after(error_html);
// Auto-remove error after 10 seconds
setTimeout(function() {
$('.infinite-loader-error').fadeOut(500, function() {
$(this).remove();
});
}, 10000);
}
// Add retry click handler
$(document).on('click', '.infinite-loader-retry', function() {
const $button = $(this);
const url = $button.data('url');
const replace = $button.data('replace');
$('.infinite-loader-error').remove();
infinite_loader_load_next_page(replace, url);
});
LOGIC-007: Fix Pagination Logic Issues
File: public/js/infinite_loader_products.js
Lines 200+ - Fix pagination replacement logic:
/**
* Replace pagination with improved error handling
*/
function infinite_loader_pagination_replace_partial($data, replace) {
try {
const $pagination = infinite_loader_dom_cache.infinite_loader_get_pagination();
const $new_pagination = $data.find(infinite_loader_product_data.pagination);
if (!$pagination.length) {
console.warn('Infinite Loader: Original pagination not found');
return false;
}
if (!$new_pagination.length) {
console.warn('Infinite Loader: New pagination not found in response');
return false;
}
const $prev_page = infinite_loader_jquery_get_prev_page();
const $new_prev_page = infinite_loader_jquery_get_prev_page($data);
const $next_page = infinite_loader_jquery_get_next_page();
const $new_next_page = infinite_loader_jquery_get_next_page($data);
let pagination_updated = false;
// Handle complex pagination structures
if (($pagination.find(infinite_loader_product_data.next_page).length ||
$pagination.find(infinite_loader_product_data.prev_page).length) &&
$pagination.find('.current').length) {
pagination_updated = infinite_loader_handle_complex_pagination($pagination, $new_pagination, replace);
}
// Fallback to simple replacement
if (!pagination_updated) {
infinite_loader_handle_simple_pagination($pagination, $new_pagination, $prev_page, $new_prev_page, $next_page, $new_next_page, replace);
}
return true;
} catch (error) {
console.error('Infinite Loader: Pagination replacement failed:', error);
return false;
}
}
/**
* Handle complex pagination with current page indicators
*/
function infinite_loader_handle_complex_pagination($pagination, $new_pagination, replace) {
try {
const $current = replace == 2 ? $pagination.find('.current').last() : $pagination.find('.current').first();
const $new_current = replace == 2 ? $new_pagination.find('.current').last() : $new_pagination.find('.current').first();
if (!$current.length || !$new_current.length) {
return false;
}
const $current_block = infinite_loader_get_current_page_last_block($current);
const $new_current_block = infinite_loader_get_current_page_last_block($new_current);
if ($current_block && $new_current_block) {
if (replace == 2) {
$current_block.prevAll().remove();
$current_block.before($new_current_block.prevAll().clone());
$current_block.before($new_current_block.clone());
} else {
$current_block.nextAll().remove();
$current_block.after($new_current_block.clone());
$current_block.after($new_current_block.nextAll().clone());
}
return true;
}
return false;
} catch (error) {
console.error('Infinite Loader: Complex pagination handling failed:', error);
return false;
}
}
LOGIC-008: Fix Settings Initialization Race Condition
File: includes/class-infinite-loader-for-woocommerce-activator.php
Lines 25+ - Fix activation logic:
public static function activate() {
// Check if WooCommerce is active before setting defaults
if ( ! class_exists( 'WooCommerce' ) ) {
deactivate_plugins( plugin_basename( __FILE__ ) );
wp_die(
esc_html__( 'Infinite Loader for WooCommerce requires WooCommerce to be installed and activated.', 'infinite-loader-for-woocommerce' ),
esc_html__( 'Plugin Activation Error', 'infinite-loader-for-woocommerce' ),
array( 'back_link' => true )
);
}
// Use atomic operations for option creation
$infinite_loader_default_options = self::infinite_loader_get_default_options();
foreach ( $infinite_loader_default_options as $option_name => $default_values ) {
$existing_option = get_option( $option_name );
if ( false === $existing_option ) {
// Option doesn't exist, create it
add_option( $option_name, $default_values );
} else {
// Option exists, merge with defaults for new keys
$merged_options = array_merge( $default_values, $existing_option );
update_option( $option_name, $merged_options );
}
}
// Set activation flag for welcome redirect
set_transient( 'infinite_loader_activation_redirect', true, 30 );
}
/**
* Get default option values
*
* @return array Default options
*/
private static function infinite_loader_get_default_options() {
return array(
'infinite_loader_admin_general_option' => array(
'product_loading_type' => 'pagination',
'product_per_page' => '8',
'loading_image' => 'fa-spinner',
'rotate_image' => 'yes',
'do_not_update_url' => 'no',
'enable_font_awesome' => 'yes',
),
'infinite_loader_admin_button_option' => array(
'button_text' => 'Load More',
'background_color' => '#1d76da',
'background_color_mouse_hover' => '#0e4da0',
'border_color' => '#1d76da',
'text_color' => '#ffffff',
'text_color_mouse_hover' => '#ffffff',
'text_font_size' => '16',
'padding_top' => '13',
'padding_right' => '30',
'padding_bottom' => '13',
'padding_left' => '30',
'border_radius_top' => '50',
'border_radius_right' => '50',
'border_radius_bottom' => '50',
'border_radius_left' => '50',
'border_top' => '1',
'border_right' => '1',
'border_bottom' => '1',
'border_left' => '1',
),
'infinite_loader_admin_previous_button_option' => array(
'button_text' => 'Load Previous',
'background_color' => '#1d76da',
'background_color_mouse_hover' => '#0e4da0',
'border_color' => '#1d76da',
'text_color' => '#ffffff',
'text_color_mouse_hover' => '#ffffff',
'text_font_size' => '16',
'padding_top' => '13',
'padding_right' => '30',
'padding_bottom' => '13',
'padding_left' => '30',
'margin_bottom' => '20',
'border_radius_top' => '50',
'border_radius_right' => '50',
'border_radius_bottom' => '50',
'border_radius_left' => '50',
'border_top' => '1',
'border_right' => '1',
'border_bottom' => '1',
'border_left' => '1',
),
);
}
LOGIC-009: Fix Product Count Logic Issues
File: public/class-infinite-loader-for-woocommerce-public.php
Lines 250+ - Fix result count logic:
/**
* Display Woocommerce count with improved logic
*
* @param string $text Original text
* @return string Modified text
*/
public function infinite_loader_products_count_additional( $text ) {
remove_filter( 'ngettext', array( $this, 'infinite_loader_products_count_additional' ), 1, 9999 );
remove_filter( 'ngettext_with_context', array( $this, 'infinite_loader_products_count_additional' ), 1, 9999 );
// Get product count data with fallback
$infinite_loader_count_data = $this->infinite_loader_get_product_count_data();
if ( ! $infinite_loader_count_data ) {
return $text;
}
extract( $infinite_loader_count_data );
echo '<span class="infinite_loader_product_count" style="display: none;" data-text="';
// Use proper WordPress internationalization
if ( 1 === $total ) {
echo esc_html__( 'Showing the single result', 'infinite-loader-for-woocommerce' );
} elseif ( $total <= $per_page || -1 === $per_page ) {
printf(
/* translators: %d: total number of results */
esc_html( _n( 'Showing %d result', 'Showing %d results', $total, 'infinite-loader-for-woocommerce' ) ),
number_format_i18n( $total )
);
} else {
printf(
/* translators: 1: first result number 2: last result number 3: total results */
esc_html__( 'Showing %1$s–%2$s of %3$s results', 'infinite-loader-for-woocommerce' ),
number_format_i18n( $first ),
number_format_i18n( $last ),
number_format_i18n( $total )
);
}
printf(
'" data-start="%d" data-end="%d" data-laststart="%d" data-lastend="%d"></span>',
esc_attr( $first ),
esc_attr( $last ),
esc_attr( $first ),
esc_attr( $last )
);
return $text;
}
/**
* Get product count data with proper error handling
*
* @return array|false Product count data or false on error
*/
private function infinite_loader_get_product_count_data() {
try {
if ( class_exists( 'WC_Query' ) && method_exists( 'WC_Query', 'product_query' ) && function_exists( 'wc_get_loop_prop' ) ) {
$total = wc_get_loop_prop( 'total' );
$per_page = wc_get_loop_prop( 'per_page' );
$paged = wc_get_loop_prop( 'current_page' );
// Validate WooCommerce data
if ( ! is_numeric( $total ) || ! is_numeric( $per_page ) || ! is_numeric( $paged ) ) {
throw new Exception( 'Invalid WooCommerce loop properties' );
}
} else {
global $wp_query;
if ( ! isset( $wp_query ) || ! is_object( $wp_query ) ) {
throw new Exception( 'WP_Query not available' );
}
$paged = max( 1, $wp_query->get( 'paged' ) );
$per_page = $wp_query->get( 'posts_per_page' );
$total = $wp_query->found_posts;
}
// Calculate first and last with bounds checking
$first = max( 1, ( $per_page * $paged ) - $per_page + 1 );
$last = min( $total, $per_page * $paged );
// Ensure logical consistency
if ( $first > $last || $last > $total ) {
throw new Exception( 'Inconsistent pagination calculation' );
}
return compact( 'total', 'per_page', 'paged', 'first', 'last' );
} catch ( Exception $e ) {
error_log( 'Infinite Loader: Product count calculation failed - ' . $e->getMessage() );
return false;
}
}
LOGIC-010: Fix Asset Loading Logic
File: public/class-infinite-loader-for-woocommerce-public.php
Lines 50+ - Improve conditional loading:
/**
* Register the stylesheets for the public-facing side of the site.
*
* @since 1.0.0
*/
public function enqueue_styles() {
// Only load on relevant pages
if ( ! $this->infinite_loader_should_load_assets() ) {
return;
}
$infinite_loader_asset_config = $this->infinite_loader_get_asset_config();
wp_enqueue_style(
$this->plugin_name,
plugin_dir_url( __FILE__ ) . 'css' . $infinite_loader_asset_config['css_path'] . '/infinite-loader-for-woocommerce-public' . $infinite_loader_asset_config['css_extension'],
array(),
$this->version,
'all'
);
}
/**
* Register the JavaScript for the public-facing side of the site.
*
* @since 1.0.0
*/
public function enqueue_scripts() {
if ( ! $this->infinite_loader_should_load_assets() ) {
return;
}
$infinite_loader_asset_config = $this->infinite_loader_get_asset_config();
wp_enqueue_script(
'infinite_loader_products',
plugin_dir_url( __FILE__ ) . 'js' . $infinite_loader_asset_config['js_path'] . '/infinite-loader-for-woocommerce-public' . $infinite_loader_asset_config['js_extension'],
array( 'jquery' ),
$this->version,
true // Load in footer for better performance
);
}
/**
* Check if assets should be loaded on current page
*
* @return bool True if assets should be loaded
*/
private function infinite_loader_should_load_assets() {
// Load on shop pages and product archives
return is_shop() || is_product_category() || is_product_tag() || is_product_taxonomy();
}
/**
* Get asset configuration for current environment
*
* @return array Asset paths and extensions
*/
private function infinite_loader_get_asset_config() {
$is_debug = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG;
$is_rtl = is_rtl();
return array(
'css_extension' => $is_debug ? ( $is_rtl ? '.rtl.css' : '.css' ) : ( $is_rtl ? '.rtl.css' : '.min.css' ),
'css_path' => $is_debug ? ( $is_rtl ? '/rtl' : '' ) : ( $is_rtl ? '/rtl' : '/min' ),
'js_extension' => $is_debug ? '.js' : '.min.js',
'js_path' => $is_debug ? '' : '/min',
);
}
These improvements maintain all existing function prefixes while fixing critical logic issues, improving error handling, and enhancing performance without breaking existing functionality.
馃敶 HIGH PRIORITY TASKS (Function Logic Improvements)
LOGIC-001: Fix Unsafe Option Access Pattern
File:
admin/class-infinite-loader-for-woocommerce-admin.phpCurrent Issue: Direct array access without validation
Replace with Safe Helper Method:
LOGIC-002: Fix Empty Settings Check Logic
File:
admin/class-infinite-loader-for-woocommerce-admin.phpLines 407-409 - Current Logic Issue:
Fix Return Logic:
LOGIC-003: Fix Inconsistent Default Value Handling
File:
admin/class-infinite-loader-for-woocommerce-admin.phpLines 410-420 - Inconsistent defaults:
Replace with Consistent Helper:
LOGIC-004: Fix Style Generation Logic Issues
File:
admin/class-infinite-loader-for-woocommerce-admin.phpLines 424-445 - Refactor style building:
LOGIC-005: Fix JavaScript DOM Performance Issues
File:
public/js/infinite_loader_products.jsLines 15-30 - Fix DOM cache initialization:
LOGIC-006: Fix AJAX Error Handling Logic
File:
public/js/infinite_loader_products.jsLines 150+ - Improve error handling:
LOGIC-007: Fix Pagination Logic Issues
File:
public/js/infinite_loader_products.jsLines 200+ - Fix pagination replacement logic:
LOGIC-008: Fix Settings Initialization Race Condition
File:
includes/class-infinite-loader-for-woocommerce-activator.phpLines 25+ - Fix activation logic:
LOGIC-009: Fix Product Count Logic Issues
File:
public/class-infinite-loader-for-woocommerce-public.phpLines 250+ - Fix result count logic:
LOGIC-010: Fix Asset Loading Logic
File:
public/class-infinite-loader-for-woocommerce-public.phpLines 50+ - Improve conditional loading:
These improvements maintain all existing function prefixes while fixing critical logic issues, improving error handling, and enhancing performance without breaking existing functionality.