6 Commits

Author SHA1 Message Date
9b7638a7e2 Release version 1.1.19 - Enhanced duplicate detection
Fixed persistent duplicate settings page by improving duplicate detection
to check by class type and ID instead of strict instance comparison.

Issue:
- Strict instance comparison (===) failed to detect duplicates
- Different object instances of same class not caught by previous check
- Settings page still appearing twice despite singleton and instance caching

Solution:
- Changed duplicate detection from instance comparison to type/ID checks
- Added instanceof WC_TPP_Settings check
- Added get_id() method check for ID 'tier_package_prices'
- Added direct id property check as fallback
- Multiple layers of detection to catch duplicates regardless of how created

Fixes:
- Settings page rendering twice in WooCommerce backend
- Duplicate detection failing for different instances of same class

Changes:
- Enhanced add_settings_page() with multi-layer duplicate detection
- Checks: instanceof, get_id(), and id property
- Returns early if any existing page matches criteria

Technical Details:
- Strict comparison only works if exact same object instance
- Different instances (even of same class/ID) fail === check
- instanceof checks class type regardless of which instance
- ID checks ensure no duplicate pages with same identifier
- Fallback checks handle different WooCommerce versions/implementations

Updated Files:
- includes/class-wc-tpp-admin.php (enhanced duplicate detection)
- wc-tier-and-package-prices.php (version 1.1.19)
- composer.json (version 1.1.19)
- CHANGELOG.md (v1.1.19 section)

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 23:33:12 +01:00
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
959229b9d8 Release version 1.1.15 - Fix duplicate settings page
Fixed settings page appearing twice in WooCommerce settings due to double instantiation of WC_TPP_Settings class.

**Issue:**
- Settings page rendered twice on same page
- WC_TPP_Settings class instantiated twice: once automatically in settings file, once via admin class include

**Fix:**
- Removed conditional wrapper `if (class_exists('WC_TPP_Settings'))` from settings return statement
- Settings class now only instantiated when admin class includes the file via `return new WC_TPP_Settings();`
- Restored v1.1.2 pattern for settings file

**Files Modified:**
- includes/class-wc-tpp-settings.php (simplified return statement)

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 19:39:20 +01:00
f0ab2ff755 Release version 1.1.14 - Restore plugin functionality
**CRITICAL FIX:** Restored plugin to working state after v1.1.8-1.1.13 were completely non-functional.

**Root Cause:**
- v1.1.8 moved class instantiation from individual files to init_classes() method
- v1.1.13 wrapped classes in class_exists() guards
- Combination prevented any classes from being instantiated
- Result: No settings, no frontend, no backend functionality

**Solution:**
- Reverted to v1.1.2 pattern (last working version)
- Each class file now instantiates itself with `new ClassName();`
- Removed init_classes() method and woocommerce_loaded hook
- All class_exists() guards remain for redeclaration protection

**What Now Works:**
 WooCommerce Settings → Tier & Package Prices tab
 Product edit pages show tier/package pricing meta boxes
 Frontend displays pricing tables on product pages
 Cart applies tier/package pricing correctly
 All plugin functionality fully operational

**Files Modified:**
- includes/class-wc-tpp-admin.php
- includes/class-wc-tpp-product-meta.php
- includes/class-wc-tpp-frontend.php
- includes/class-wc-tpp-cart.php
- wc-tier-and-package-prices.php (removed init_classes)

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 19:32:47 +01:00
11 changed files with 178 additions and 20 deletions

View File

@@ -5,6 +5,134 @@ 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.19] - 2025-12-22
### Fixed
- Settings page still appearing twice despite instance caching
- Duplicate detection using strict instance comparison failing for different object instances
### Changed
- Enhanced duplicate detection to check by class type and ID instead of instance
- Added `instanceof WC_TPP_Settings` check
- Added ID-based duplicate detection via `get_id()` method and direct property access
- Multiple fallback checks to catch duplicates regardless of instance identity
### Technical Details
- Previous strict comparison (`===`) failed when different instances of same class existed
- New approach checks: instanceof, get_id() method, and id property
- Returns early if any settings page with ID 'tier_package_prices' found
- Prevents duplicates even if settings instance recreated or serialized
## [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
- Settings page rendering twice in WooCommerce settings
- Duplicate instantiation of WC_TPP_Settings class causing double rendering
### Technical Details
- Removed conditional `if (class_exists('WC_TPP_Settings'))` wrapper from settings return statement
- Settings class now only instantiated via `return new WC_TPP_Settings();` when included by admin class
- Matches v1.1.2 pattern where settings file returns instance without automatic instantiation
- Prevents double registration in WooCommerce settings pages array
## [1.1.14] - 2025-12-22
### Fixed
- **CRITICAL:** Plugin completely non-functional in v1.1.8-1.1.13 - no settings, no frontend, no backend
- Classes never instantiated due to incorrect initialization pattern introduced in v1.1.8
- Restored v1.1.2 pattern: classes auto-instantiate when files are included
- All plugin functionality now working: settings page, product meta boxes, frontend display, cart integration
### Changed
- Reverted to direct class instantiation pattern from v1.1.2 (last known working version)
- Removed `init_classes()` method and `woocommerce_loaded` hook approach from v1.1.8
- Each class file now instantiates itself with `new ClassName()` at end of file
- Simplified plugin initialization for better reliability
### Technical Details
- Restored class instantiation in all 5 component files:
- `class-wc-tpp-admin.php`: Added `new WC_TPP_Admin();` after class declaration
- `class-wc-tpp-product-meta.php`: Added `new WC_TPP_Product_Meta();` after class declaration
- `class-wc-tpp-frontend.php`: Added `new WC_TPP_Frontend();` after class declaration
- `class-wc-tpp-cart.php`: Added `new WC_TPP_Cart();` after class declaration
- `class-wc-tpp-settings.php`: Already has instantiation via return statement
- Removed `init_classes()` method from main plugin class
- Removed `woocommerce_loaded` hook that delayed class instantiation
- Classes now instantiate immediately when `require_once` loads them
- All `class_exists()` guards remain in place for redeclaration protection
## [1.1.13] - 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.13",
"version": "1.1.19",
"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,32 @@ 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
// Check by class type and ID instead of instance comparison
foreach ($settings as $settings_page) {
if ($settings_page instanceof WC_TPP_Settings) {
return $settings;
}
// Also check by ID property if it's a WC_Settings_Page
if (is_object($settings_page) &&
method_exists($settings_page, 'get_id') &&
$settings_page->get_id() === 'tier_package_prices') {
return $settings;
}
// Check id property directly
if (is_object($settings_page) &&
isset($settings_page->id) &&
$settings_page->id === 'tier_package_prices') {
return $settings;
}
}
$settings[] = self::$settings_instance;
return $settings;
}
@@ -30,4 +65,6 @@ if (!class_exists('WC_TPP_Admin')) {
}
}
}
WC_TPP_Admin::get_instance();
}

View File

@@ -248,4 +248,6 @@ if (!class_exists('WC_TPP_Cart')) {
return $editable;
}
}
new WC_TPP_Cart();
}

View File

@@ -183,4 +183,6 @@ if (!class_exists('WC_TPP_Frontend')) {
return $new_html;
}
}
new WC_TPP_Frontend();
}

View File

@@ -168,4 +168,6 @@ if (!class_exists('WC_TPP_Product_Meta')) {
update_post_meta($post_id, '_wc_tpp_restrict_to_packages', $restrict_to_packages);
}
}
new WC_TPP_Product_Meta();
}

View File

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

Binary file not shown.

View File

@@ -0,0 +1 @@
e0cc51d1493ed35ab254220d9f46997b wc-tier-and-package-prices-1.1.14.zip

View File

@@ -0,0 +1 @@
8a2ce7438ee49baffdcaaf323b6426d73dd1cf704bea94a80fcce27a42c097ad wc-tier-and-package-prices-1.1.14.zip

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.13
* Version: 1.1.19
* 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.13');
define('WC_TPP_VERSION', '1.1.19');
}
if (!defined('WC_TPP_PLUGIN_DIR')) {
define('WC_TPP_PLUGIN_DIR', plugin_dir_path(__FILE__));
@@ -92,16 +92,6 @@ if (!class_exists('WC_Tier_Package_Prices')) {
require_once WC_TPP_PLUGIN_DIR . 'includes/class-wc-tpp-product-meta.php';
require_once WC_TPP_PLUGIN_DIR . 'includes/class-wc-tpp-frontend.php';
require_once WC_TPP_PLUGIN_DIR . 'includes/class-wc-tpp-cart.php';
// Instantiate classes after WooCommerce is loaded
add_action('woocommerce_loaded', array($this, 'init_classes'));
}
public function init_classes() {
new WC_TPP_Admin();
new WC_TPP_Product_Meta();
new WC_TPP_Frontend();
new WC_TPP_Cart();
}
public function declare_hpos_compatibility() {