3 Commits

Author SHA1 Message Date
db9ba2bacd Release version 1.1.18 - Fix root cause of duplicate settings
Identified and fixed the actual root cause of duplicate settings page:
automatic instantiation in settings file being executed multiple times.

Root Cause Analysis:
- Settings file ended with: return new WC_TPP_Settings();
- File is in Composer's classmap for autoloading
- When autoloader loads class, it executes the entire file including instantiation
- Each include/autoload created a NEW instance despite admin singleton pattern
- Admin singleton prevented multiple admin instances but not multiple settings instances

The Fix:
- Removed automatic instantiation (return new) from settings file
- Settings file now only contains class definition
- Admin class explicitly creates instance: new WC_TPP_Settings()
- Changed from include to require_once to prevent multiple file loads
- Settings instance now created exactly once, when filter needs it

Fixes:
- Settings page rendering twice in WooCommerce backend
- Multiple WC_TPP_Settings instances being created
- Composer autoload triggering unintended instantiation

Changes:
- Removed line 145 from class-wc-tpp-settings.php (return new WC_TPP_Settings())
- Modified add_settings_page() to use require_once + explicit new
- Settings file now side-effect free, safe for autoloading

Technical Details:
- Composer classmap autoloader includes files when class is referenced
- Previous code had side effect (instantiation) on every include
- New code separates class definition from instantiation
- Instance creation now controlled explicitly by admin class
- require_once ensures file loaded once even if accessed multiple times

Updated Files:
- includes/class-wc-tpp-settings.php (removed return new line)
- includes/class-wc-tpp-admin.php (explicit instantiation)
- wc-tier-and-package-prices.php (version 1.1.18)
- composer.json (version 1.1.18)
- CHANGELOG.md (v1.1.18 section)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 23:29:35 +01:00
e46372da51 Release version 1.1.17 - Array duplicate prevention
Fixed persistent duplicate settings page by adding array-level duplicate
detection in addition to singleton pattern from v1.1.16.

Root Cause:
- WooCommerce calls woocommerce_get_settings_pages filter multiple times
- Even with singleton pattern, each filter call added settings instance to array
- Singleton prevented multiple class instances but not multiple array entries

Fixes:
- Settings page rendering twice despite singleton pattern in v1.1.16
- Filter adding same settings instance to array on repeated calls

Changes:
- Added duplicate detection loop in add_settings_page() before array append
- Uses strict comparison (===) to check if instance already in array
- Returns early if settings page already present, preventing duplicate

Technical Details:
- foreach loop iterates through existing $settings array
- Compares each element against cached self::$settings_instance
- Only appends to array if instance not found
- Complements singleton pattern with array-level protection
- Handles WooCommerce calling filter multiple times during page load

Updated Files:
- includes/class-wc-tpp-admin.php (added duplicate check in filter)
- wc-tier-and-package-prices.php (version 1.1.17)
- composer.json (version 1.1.17)
- CHANGELOG.md (v1.1.17 section)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 23:05:11 +01:00
2b2c06794b Release version 1.1.16 - Singleton pattern for settings page
Fixed persistent duplicate settings page rendering by implementing proper
singleton pattern for admin class and caching settings instance.

Fixes:
- Settings page still appearing twice despite v1.1.15 fix
- Multiple instantiation of WC_TPP_Admin class
- Duplicate creation of WC_TPP_Settings instances

Changes:
- Implemented singleton pattern for WC_TPP_Admin class
- Added private static $instance property with get_instance() method
- Made WC_TPP_Admin constructor private
- Added static $settings_instance property to cache settings page
- Modified add_settings_page() to check and reuse cached settings instance
- Changed instantiation from new WC_TPP_Admin() to WC_TPP_Admin::get_instance()

Technical Details:
- Ensures only one WC_TPP_Admin instance exists throughout plugin lifecycle
- Prevents duplicate filter registrations even if woocommerce_get_settings_pages called multiple times
- Settings page object created once and reused on subsequent filter calls
- Follows WordPress/WooCommerce best practices for singleton implementation

Updated Files:
- includes/class-wc-tpp-admin.php (singleton pattern implementation)
- wc-tier-and-package-prices.php (version 1.1.16)
- composer.json (version 1.1.16)
- CHANGELOG.md (v1.1.16 section)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 20:01:27 +01:00
5 changed files with 92 additions and 8 deletions

View File

@@ -5,6 +5,70 @@ All notable changes to WooCommerce Tier and Package Prices will be documented in
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.1.18] - 2025-12-22
### Fixed
- **ROOT CAUSE IDENTIFIED**: Settings page rendering twice due to automatic instantiation in settings file
- Settings file being included multiple times via Composer autoloader creating duplicate instances
### Changed
- Removed `return new WC_TPP_Settings();` from bottom of settings file
- Changed admin class to explicitly instantiate settings with `new WC_TPP_Settings()`
- Changed from `include` to `require_once` for settings file to prevent multiple loads
### Technical Details
- Settings file (class-wc-tpp-settings.php) was creating instance automatically on include
- File is in Composer's classmap, so when autoloaded it executed instantiation again
- Each include/autoload created new instance even with singleton pattern in admin class
- Solution: Remove automatic instantiation, use `require_once` + explicit `new` in admin class
- Now settings instance only created once, explicitly, when needed by filter
- Composer autoload can load class definition without side effects
## [1.1.17] - 2025-12-22
### Fixed
- Settings page still rendering twice despite singleton pattern in v1.1.16
- Filter adding settings instance to array multiple times when called repeatedly
### Changed
- Added duplicate detection in `add_settings_page()` filter method
- Filter now checks if settings instance already exists in array before adding
### Technical Details
- Added foreach loop to check existing settings pages in array
- Uses strict comparison (`===`) to detect if exact instance already present
- Returns early if settings instance found, preventing duplicate array entries
- Complements singleton pattern from v1.1.16 with array-level duplicate prevention
- Handles edge case where WooCommerce calls filter multiple times
## [1.1.16] - 2025-12-22
### Fixed
- Settings page still rendering twice in WooCommerce backend despite v1.1.15 fix
- Multiple instantiation of WC_TPP_Admin and WC_TPP_Settings classes
### Changed
- Implemented singleton pattern for WC_TPP_Admin class with `get_instance()` method
- Made WC_TPP_Admin constructor private to prevent direct instantiation
- Added static caching of WC_TPP_Settings instance to prevent duplicate creation
- Changed class instantiation from `new WC_TPP_Admin()` to `WC_TPP_Admin::get_instance()`
### Technical Details
- Added `private static $instance` property to WC_TPP_Admin class
- Added `private static $settings_instance` property to cache settings page instance
- Modified `add_settings_page()` to check and reuse cached settings instance
- Ensures only one instance of each class exists throughout plugin lifecycle
- Prevents duplicate filter registrations even if called multiple times
## [1.1.15] - 2025-12-22
### Fixed

View File

@@ -1,7 +1,7 @@
{
"name": "magdev/wc-tier-package-prices",
"description": "WooCommerce plugin for tier pricing and package prices with Twig templates",
"version": "1.1.15",
"version": "1.1.18",
"type": "wordpress-plugin",
"license": "GPL-2.0-or-later",
"authors": [

View File

@@ -10,7 +10,17 @@ if (!defined('ABSPATH')) {
if (!class_exists('WC_TPP_Admin')) {
class WC_TPP_Admin {
public function __construct() {
private static $instance = null;
private static $settings_instance = null;
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
add_filter('woocommerce_get_settings_pages', array($this, 'add_settings_page'));
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
}
@@ -19,7 +29,19 @@ if (!class_exists('WC_TPP_Admin')) {
* Add settings page to WooCommerce settings
*/
public function add_settings_page($settings) {
$settings[] = include WC_TPP_PLUGIN_DIR . 'includes/class-wc-tpp-settings.php';
if (null === self::$settings_instance) {
require_once WC_TPP_PLUGIN_DIR . 'includes/class-wc-tpp-settings.php';
self::$settings_instance = new WC_TPP_Settings();
}
// Check if our settings page is already in the array to prevent duplicates
foreach ($settings as $settings_page) {
if ($settings_page === self::$settings_instance) {
return $settings;
}
}
$settings[] = self::$settings_instance;
return $settings;
}
@@ -31,5 +53,5 @@ if (!class_exists('WC_TPP_Admin')) {
}
}
new WC_TPP_Admin();
WC_TPP_Admin::get_instance();
}

View File

@@ -141,5 +141,3 @@ if (!class_exists('WC_TPP_Settings')) {
}
}
}
return new WC_TPP_Settings();

View File

@@ -4,7 +4,7 @@
* Plugin Name: WooCommerce Tier and Package Prices
* Plugin URI: https://src.bundespruefstelle.ch/magdev/wc-tier-package-prices
* Description: Add tier pricing and package prices to WooCommerce products with configurable quantities at fixed prices
* Version: 1.1.15
* Version: 1.1.18
* Author: Marco Graetsch
* Author URI: https://src.bundespruefstelle.ch/magdev
* Text Domain: wc-tier-package-prices
@@ -23,7 +23,7 @@ if (!defined('ABSPATH')) {
// Define plugin constants
if (!defined('WC_TPP_VERSION')) {
define('WC_TPP_VERSION', '1.1.15');
define('WC_TPP_VERSION', '1.1.18');
}
if (!defined('WC_TPP_PLUGIN_DIR')) {
define('WC_TPP_PLUGIN_DIR', plugin_dir_path(__FILE__));