HEX
Server: Apache
System: Linux webm020.cluster127.gra.hosting.ovh.net 6.18.42-ovh-vps-grsec-zfs+ #1 SMP PREEMPT_DYNAMIC Wed Aug 5 15:59:48 CEST 2026 x86_64
User: bohousj (181416)
PHP: 8.4.22
Disabled: _dyuweyrj4,_dyuweyrj4r,dl
Upload Files
File: /home/b/o/h/bohousj/www/wp-content/plugins/woo-product-bundle/includes/class-product.php
<?php
defined( 'ABSPATH' ) || exit;

if ( ! class_exists( 'WC_Product_Woosb' ) && class_exists( 'WC_Product' ) ) {
	class WC_Product_Woosb extends WC_Product {
		protected $items = null;

		public function __construct( $product = 0 ) {
			$this->supports[] = 'ajax_add_to_cart';
			parent::__construct( $product );

			$this->build_items();
		}

		public function get_type() {
			return 'woosb';
		}

		public function add_to_cart_url() {
			// Cache the product ID to avoid multiple property access
			$product_id = $this->id;

			// Combine conditions into a single variable to improve readability and avoid repeated checks
			$can_add_directly = $this->is_purchasable()
			                    && $this->is_in_stock()
			                    && ! $this->has_variables()
			                    && ! $this->has_optional();

			// Use ternary operator for simpler conditional assignment
			$url = $can_add_directly
				? remove_query_arg( 'added-to-cart', add_query_arg( 'add-to-cart', $product_id ) )
				: get_permalink( $product_id );

			// Chain filters to reduce variable assignments
			return apply_filters(
				'woosb_product_add_to_cart_url',
				apply_filters( 'woocommerce_product_add_to_cart_url', $url, $this ),
				$this
			);
		}

		public function add_to_cart_text() {
			// Cache helper instance to avoid multiple method calls
			$helper = WPCleverWoosb_Helper();

			// Combine conditions and use early return pattern
			if ( ! $this->is_purchasable() || ! $this->is_in_stock() ) {
				$text = $helper->localization( 'button_read',
					esc_html__( 'Read more', 'woo-product-bundle' )
				);
			} else {
				$button_type = ( ! $this->has_variables() && ! $this->has_optional() )
					? 'button_add'
					: 'button_select';

				$default_text = ( $button_type === 'button_add' )
					? esc_html__( 'Add to cart', 'woo-product-bundle' )
					: esc_html__( 'Select options', 'woo-product-bundle' );

				$text = $helper->localization( $button_type, $default_text );
			}

			// Chain filters to reduce variable assignments
			return apply_filters(
				'woosb_product_add_to_cart_text',
				apply_filters( 'woocommerce_product_add_to_cart_text', $text, $this ),
				$this
			);
		}

		public function single_add_to_cart_text() {
			// Cache helper instance
			$helper = WPCleverWoosb_Helper();

			// Define default text as a constant or class property if used elsewhere
			$default_text = esc_html__( 'Add to cart', 'woo-product-bundle' );

			// Get localized text and apply filters in a single chain
			return apply_filters(
				'woosb_product_single_add_to_cart_text',
				apply_filters(
					'woocommerce_product_single_add_to_cart_text',
					$helper->localization( 'button_single', $default_text ),
					$this
				),
				$this
			);
		}

		public function is_on_sale( $context = 'view' ) {
			// Cache the fixed price check to avoid multiple method calls
			$is_fixed = $this->is_fixed_price();

			// Early return if fixed price is set
			if ( $is_fixed ) {
				return parent::is_on_sale( $context );
			}

			// Cache discount values to avoid multiple method calls
			$discount_amount     = $this->get_discount_amount();
			$discount_percentage = $this->get_discount_percentage();

			// Return true if either discount is set, otherwise check parent
			return $discount_amount || $discount_percentage || parent::is_on_sale( $context );
		}

		public function get_sale_price( $context = 'view' ) {
			// Early return for non-view context or fixed price
			if ( $context !== 'view' || $this->is_fixed_price() ) {
				return parent::get_sale_price( $context );
			}

			// Cache discount values
			$discount_amount     = $this->get_discount_amount();
			$discount_percentage = $this->get_discount_percentage();

			// Early return if no discount
			if ( ! $discount_amount && ! $discount_percentage ) {
				return '';
			}

			// Cache helper instance
			$helper     = WPCleverWoosb_Helper();
			$sale_price = 0;

			// Check items existence early
			if ( empty( $this->items ) ) {
				return $sale_price;
			}

			// Process items
			foreach ( $this->items as $item ) {
				// Get product once
				$_product = wc_get_product( $item['id'] );

				// Skip invalid products or woosb type
				if ( ! $_product || $_product->is_type( 'woosb' ) ) {
					continue;
				}

				// Calculate item price
				$_price = (float) $helper->get_price( $_product ) * (float) $item['qty'];

				// Apply discount percentage if applicable
				if ( $discount_percentage ) {
					$sale_price += $helper->round_price( $_price * ( 100 - $discount_percentage ) / 100 );
				} else {
					$sale_price += $_price;
				}
			}

			// Apply fixed discount amount if applicable
			return $discount_amount ? ( $sale_price - $discount_amount ) : $sale_price;
		}

		public function get_price( $context = 'view' ) {
			// Early return if not view context
			if ( $context !== 'view' ) {
				return parent::get_price( $context );
			}

			// Cache values to avoid multiple method calls
			$regular_price = (float) $this->get_regular_price();
			$parent_price  = (float) parent::get_price( $context );

			// Return '0' if either price is zero
			if ( $regular_price === 0.0 || $parent_price === 0.0 ) {
				return '0';
			}

			return parent::get_price( $context );
		}

		public function get_manage_stock( $context = 'view' ) {
			$parent_manage = parent::get_manage_stock( $context );

			// Early return if stock management is disabled globally or via filter
			if ( 'yes' !== get_option( 'woocommerce_manage_stock' ) ||
			     apply_filters( 'woosb_disable_inventory_management', false ) ) {
				return $parent_manage;
			}

			// Early return if no items or has optional items
			if ( empty( $this->items ) || $this->has_optional() ) {
				return $parent_manage;
			}

			$exclude_unpurchasable = $this->exclude_unpurchasable();
			$helper                = WPCleverWoosb_Helper();

			foreach ( $this->items as $item ) {
				$product = wc_get_product( $item['id'] );

				// Skip invalid products or those meeting exclusion criteria
				if ( ! $product ||
				     $product->is_type( 'woosb' ) ||
				     ( $exclude_unpurchasable &&
				       ( ! $product->is_purchasable() || ! $helper->is_in_stock( $product ) ) ) ) {
					continue;
				}

				// Return true if product manages stock
				if ( $product->get_manage_stock( $context ) === true ) {
					return true;
				}

				// Check parent product if this is a variation
				if ( $product->is_type( 'variation' ) ) {
					$parent_product = wc_get_product( $product->get_parent_id() );

					if ( $parent_product && $parent_product->get_manage_stock( $context ) === true ) {
						return true;
					}
				}
			}

			// Return parent manage stock setting if this product manages stock
			return $this->is_manage_stock() ? $parent_manage : false;
		}

		public function get_stock_status( $context = 'view' ) {
			$parent_status = parent::get_stock_status( $context );

			// Early return if inventory management is disabled
			if ( apply_filters( 'woosb_disable_inventory_management', false ) ) {
				return $parent_status;
			}

			// Early return if no items
			if ( empty( $this->items ) ) {
				return $parent_status;
			}

			$exclude_unpurchasable = $this->exclude_unpurchasable();
			$stock_status          = 'instock';
			$all_out_of_stock      = true;
			$helper                = WPCleverWoosb_Helper(); // Cache helper instance

			foreach ( $this->items as $item ) {
				// Skip if product doesn't exist
				$_product = wc_get_product( $item['id'] );

				if ( ! $_product || $_product->is_type( 'woosb' ) ) {
					continue;
				}

				$_qty = (float) $item['qty'];

				if ( ! empty( $item['optional'] ) ) {
					$_qty = ! empty( $item['min'] ) ? (float) $item['min'] : 0;
				}

				// Cache commonly used method results
				$is_in_stock      = $helper->is_in_stock( $_product );
				$has_enough_stock = $helper->has_enough_stock( $_product, $_qty );

				if ( $is_in_stock && $has_enough_stock ) {
					$all_out_of_stock = false;
				}

				if ( $exclude_unpurchasable && ( ! $_product->is_purchasable() || ! $is_in_stock ) ) {
					continue;
				}

				if ( $_qty && ( $_product->get_stock_status( $context ) === 'outofstock' || ! $has_enough_stock ) ) {
					return 'outofstock';
				}

				if ( $_product->get_stock_status( $context ) === 'onbackorder' ||
				     ( $_qty && ! $has_enough_stock && $_product->backorders_allowed() ) ) {
					$stock_status = 'onbackorder';
				}
			}

			if ( $all_out_of_stock ) {
				return 'outofstock';
			}

			if ( $this->is_manage_stock() ) {
				return $parent_status === 'instock' ? $stock_status : $parent_status;
			}

			return $stock_status;
		}

		public function get_stock_quantity( $context = 'view' ) {
			$parent_quantity = parent::get_stock_quantity( $context );

			// Early return if stock management is disabled
			if ( 'yes' !== get_option( 'woocommerce_manage_stock' ) ||
			     apply_filters( 'woosb_disable_inventory_management', false ) ) {
				return $parent_quantity;
			}

			$product_id            = $this->id;
			$exclude_unpurchasable = $this->exclude_unpurchasable();
			$items                 = $this->items;

			// Early return if no items or has optional items
			if ( ! $items || $this->has_optional() ) {
				update_post_meta( $product_id, '_stock', $parent_quantity );

				return $parent_quantity;
			}

			$helper        = WPCleverWoosb_Helper();
			$available_qty = [];

			foreach ( $items as $item ) {
				// Skip if quantity is not positive
				if ( $item['qty'] <= 0 ) {
					continue;
				}

				$_product = wc_get_product( $item['id'] );

				// Cache stock quantity to avoid multiple calls
				$stock_quantity = $helper->get_stock_quantity( $_product );

				// Skip invalid products or those not meeting criteria
				if ( ! $_product ||
				     $_product->is_type( 'woosb' ) ||
				     ! $_product->get_manage_stock() ||
				     $stock_quantity === null ||
				     ( $exclude_unpurchasable && ( ! $_product->is_purchasable() || ! $helper->is_in_stock( $_product ) ) ) ) {
					continue;
				}

				$available_qty[] = floor( $stock_quantity / (float) $item['qty'] );
			}

			// If no available quantities found, update and return parent quantity
			if ( empty( $available_qty ) ) {
				update_post_meta( $product_id, '_stock', $parent_quantity );

				return $parent_quantity;
			}

			// Find minimum available quantity without sorting full array
			$min_available = min( $available_qty );

			// Use parent quantity if it's lower and stock is managed
			if ( $this->is_manage_stock() && $parent_quantity < $min_available ) {
				update_post_meta( $product_id, '_stock', $parent_quantity );

				return $parent_quantity;
			}

			update_post_meta( $product_id, '_stock', $min_available );

			return $min_available;
		}

		public function get_backorders( $context = 'view' ) {
			$parent_backorders = parent::get_backorders( $context );

			// Early return if inventory management is disabled
			if ( apply_filters( 'woosb_disable_inventory_management', false ) ) {
				return $parent_backorders;
			}

			// Early return if no items or has optional items
			if ( empty( $this->items ) || $this->has_optional() ) {
				return $parent_backorders;
			}

			$exclude_unpurchasable = $this->exclude_unpurchasable();
			$backorders            = 'yes';
			$helper                = WPCleverWoosb_Helper();

			foreach ( $this->items as $item ) {
				// Calculate quantity once
				$qty = ! empty( $item['optional'] ) ?
					( ! empty( $item['min'] ) ? (float) $item['min'] : 0 ) :
					(float) $item['qty'];

				// Get product once
				$product = wc_get_product( $item['id'] );

				// Skip if product doesn't meet criteria
				if ( ! $product ||
				     $product->is_type( 'woosb' ) ||
				     ! $product->get_manage_stock() ||
				     ( $exclude_unpurchasable && ( ! $product->is_purchasable() || ! $helper->is_in_stock( $product ) ) ) ) {
					continue;
				}

				// Skip if product has enough stock
				if ( $helper->is_in_stock( $product ) && $helper->has_enough_stock( $product, $qty ) ) {
					continue;
				}

				// Check backorders status
				$product_backorders = $product->get_backorders( $context );

				if ( $product_backorders === 'no' ) {
					return 'no';
				}

				if ( $product_backorders === 'notify' ) {
					$backorders = 'notify';
				}
			}

			// Simplified return logic
			if ( $this->is_manage_stock() ) {
				return $parent_backorders === 'yes' ? $backorders : $parent_backorders;
			}

			return $backorders;
		}

		public function get_sold_individually( $context = 'view' ) {
			$parent_individually = parent::get_sold_individually( $context );

			// Early return if inventory management is disabled
			if ( apply_filters( 'woosb_disable_inventory_management', false ) ) {
				return $parent_individually;
			}

			// Early return if no items or has optional items
			if ( empty( $this->items ) || $this->has_optional() ) {
				return $parent_individually;
			}

			$exclude_unpurchasable = $this->exclude_unpurchasable();
			$helper                = WPCleverWoosb_Helper();

			foreach ( $this->items as $item ) {
				$product = wc_get_product( $item['id'] );

				// Skip invalid products or those meeting exclusion criteria
				if ( ! $product ||
				     $product->is_type( 'woosb' ) ||
				     ( $exclude_unpurchasable &&
				       ( ! $product->is_purchasable() || ! $helper->is_in_stock( $product ) ) ) ) {
					continue;
				}

				// Return true if any product is sold individually
				if ( $product->is_sold_individually() ) {
					return true;
				}
			}

			return $parent_individually;
		}


		// extra functions

		public function has_variables() {
			// Early return if no items
			if ( empty( $this->items ) ) {
				return apply_filters( 'woosb_has_variables', false, $this );
			}

			// Use array_reduce for better performance
			$has_variables = array_reduce( $this->items, function ( $carry, $item ) {
				if ( $carry ) {
					return true;
				} // Skip if we already found a variable product

				if ( $product = wc_get_product( $item['id'] ) ) {
					return $product->is_type( 'variable' ) ? true : $carry;
				}

				return $carry;
			}, false );

			return apply_filters( 'woosb_has_variables', $has_variables, $this );
		}

		public function has_optional() {
			// Early return if no items
			if ( empty( $this->items ) ) {
				return apply_filters( 'woosb_has_optional', false, $this );
			}

			// Use array_reduce for better performance
			$has_optional = array_reduce( $this->items, function ( $carry, $item ) {
				return $carry || ! empty( $item['optional'] );
			}, false );

			return apply_filters( 'woosb_has_optional', $has_optional, $this );
		}

		public function is_optional() {
			// new version 8.0
			return self::has_optional();
		}

		public function is_manage_stock() {
			return apply_filters( 'woosb_is_manage_stock', get_post_meta( $this->id, 'woosb_manage_stock', true ) === 'on', $this );
		}

		public function is_fixed_price() {
			$disable_auto_price = get_post_meta( $this->id, 'woosb_disable_auto_price', true ) ?: apply_filters( 'woosb_disable_auto_price_default', 'off' );

			return apply_filters( 'woosb_is_fixed_price', $disable_auto_price === 'on', $this );
		}

		public function exclude_unpurchasable() {
			// Get meta value once
			$exclude_unpurchasable = get_post_meta( $this->id, 'woosb_exclude_unpurchasable', true );

			// Check if we need to use default setting
			if ( ! $exclude_unpurchasable || in_array( $exclude_unpurchasable, [ 'unset', 'default' ], true ) ) {
				$exclude_unpurchasable = WPCleverWoosb_Helper()->get_setting( 'exclude_unpurchasable', 'no' );
			}

			return apply_filters( 'woosb_exclude_unpurchasable', $exclude_unpurchasable === 'yes', $this );
		}

		public function get_discount_amount() {
			// Early return if fixed price
			if ( $this->is_fixed_price() ) {
				return apply_filters( 'woosb_get_discount_amount', 0, $this );
			}

			// Get and cast discount amount in one step
			$discount_amount = (float) get_post_meta( $this->id, 'woosb_discount_amount', true );

			return apply_filters( 'woosb_get_discount_amount', $discount_amount, $this );
		}

		public function get_discount_percentage() {
			// Early returns for fixed price or if discount amount exists
			if ( $this->is_fixed_price() || $this->get_discount_amount() ) {
				return apply_filters( 'woosb_get_discount_percentage', 0, $this );
			}

			// Get discount percentage
			$discount_percentage = get_post_meta( $this->id, 'woosb_discount', true );

			// Validate discount percentage
			if ( is_numeric( $discount_percentage ) ) {
				$discount_percentage = (float) $discount_percentage;
				if ( $discount_percentage > 0 && $discount_percentage < 100 ) {
					return apply_filters( 'woosb_get_discount_percentage', $discount_percentage, $this );
				}
			}

			return apply_filters( 'woosb_get_discount_percentage', 0, $this );
		}

		public function get_discount() {
			$discount = $this->get_discount_amount() ?: $this->get_discount_percentage() . '%';

			return apply_filters( 'woosb_get_discount', $discount, $this );
		}

		public function get_ids() {
			return apply_filters( 'woosb_get_ids', get_post_meta( $this->id, 'woosb_ids', true ), $this );
		}

		public function get_ids_str() {
			$ids = $this->get_ids();

			if ( ! is_array( $ids ) ) {
				return apply_filters( 'woosb_get_ids_str', $ids, $this );
			}

			$ids_str = implode( ',', array_map(
				function ( $key, $item ) {
					return ! empty( $item['id'] ) ? "{$item['id']}/{$key}/{$item['qty']}" : null;
				},
				array_keys( $ids ),
				$ids
			) );

			return apply_filters( 'woosb_get_ids_str', $ids_str, $this );
		}

		public function build_items( $ids = null ) {
			$items = [];
			$ids   = $ids ?: $this->get_ids();

			// Early return if no IDs
			if ( empty( $ids ) ) {
				$this->items = $items;

				return;
			}

			$helper     = WPCleverWoosb_Helper();
			$product_id = $this->id;

			if ( is_array( $ids ) ) {
				// Process array format (v7.0+)
				// Cache meta values for better performance
				$optional_products      = get_post_meta( $product_id, 'woosb_optional_products', true ) === 'on';
				$limit_each_min         = get_post_meta( $product_id, 'woosb_limit_each_min', true );
				$limit_each_min_default = get_post_meta( $product_id, 'woosb_limit_each_min_default', true ) === 'on';
				$limit_each_max         = get_post_meta( $product_id, 'woosb_limit_each_max', true );
				$use_sku                = apply_filters( 'woosb_use_sku', false );

				foreach ( $ids as $key => $item ) {
					// Set default values
					$item = array_merge( [
						'id'    => 0,
						'sku'   => '',
						'qty'   => 0,
						'attrs' => []
					], $item );

					// Process SKU if enabled
					if ( $use_sku && ! empty( $item['sku'] ) ) {
						$new_id = $helper->get_product_id_from_sku( $item['sku'] );

						if ( $new_id ) {
							$item['id'] = $new_id;
						}
					}

					// Set min/max values if not set (v8.0+)
					if ( ! isset( $item['min'] ) ) {
						if ( $optional_products ) {
							$item['optional'] = "1";
						}

						$item['min'] = $limit_each_min_default ? (float) $item['qty'] : $limit_each_min;
						$item['max'] = $limit_each_max;
					}

					$items[ $key ] = $item;
				}
			} else {
				// Process string format
				$ids_arr = explode( ',', $ids );

				if ( ! empty( $ids_arr ) ) {
					foreach ( $ids_arr as $ids_item ) {
						$data = explode( '/', $ids_item );
						$id   = rawurldecode( $data[0] ?? 0 );

						// Get product ID and SKU
						if ( ! is_numeric( $id ) ) {
							// Process SKU
							$sku = $id;
							$id  = wc_get_product_id_by_sku( ltrim( $id, 'sku-' ) );
						} else {
							// Process ID
							$product = wc_get_product( $id );
							$sku     = $product ? $product->get_sku() : '';
						}

						if ( ! $id ) {
							continue;
						}

						// Get key and quantity
						$key = isset( $data[1] )
							? ( is_numeric( $data[1] ) && ! isset( $data[2] )
								? $helper->generate_key()
								: $data[1] )
							: $helper->generate_key();

						$qty = isset( $data[1] )
							? ( is_numeric( $data[1] ) && ! isset( $data[2] )
								? (float) $data[1]
								: (float) ( $data[2] ?? 1 ) )
							: 1;

						// Build item array
						$items[ $key ] = [
							'id'    => apply_filters( 'woosb_item_id', $id ),
							'sku'   => apply_filters( 'woosb_item_sku', $sku ),
							'qty'   => $qty,
							'attrs' => isset( $data[3] )
								? (array) json_decode( rawurldecode( $data[3] ) )
								: []
						];
					}
				}
			}

			$this->items = $items;
		}

		public function get_items() {
			return apply_filters( 'woosb_get_items', $this->items, $this );
		}
	}
}