HEX
Server: Apache
System: Linux c265a.dattaweb.com 4.18.0-553.97.1.el8_10.x86_64 #1 SMP Mon Jan 26 05:26:16 EST 2026 x86_64
User: c2650654 (20026)
PHP: 8.3.31
Disabled: system, shell, exec, system_exec, shell_exec, mysql_pconnect, passthru, popen, proc_open, proc_close, proc_nice, proc_terminate, proc_get_status, escapeshellarg, escapeshellcmd, eval, dl, imap_mail, libvirt_connect, gnupg_init, unsetenv, apache_setenv, pcntl_exec, pcntl_alarm, pcntl_fork, pcntl_waitpid, pcntl_wait, pcntl_wifexited, pcntl_wifstopped, pcntl_wifsignaled, pcntl_wifcontinued, pcntl_wexitstatus, pcntl_wtermsig, pcntl_wstopsig, pcntl_signal, pcntl_signal_get_handler, pcntl_signal_dispatch, pcntl_get_last_error, pcntl_strerror, pcntl_sigprocmask, pcntl_sigwaitinfo, pcntl_sigtimedwait, pcntl_getpriority, pcntl_setpriority, pcntl_async_signals, opcache_get_status, opcache_reset, opcache_get_configuration
Upload Files
File: /home/c2650654/public_html/wp-content/plugins/tier-pricing-table/src/PricingTable.php
<?php namespace TierPricingTable;

use TierPricingTable\Admin\ProductPage\AdvanceOptionsForVariableProduct;
use TierPricingTable\Admin\ProductPage\TieredPricingTab;
use TierPricingTable\Core\ServiceContainerTrait;
use TierPricingTable\Settings\Sections\GeneralSection\GeneralSection;
use WC_Product;
use WC_Product_Data_Store_CPT;
use WC_Product_Variable;
use WC_Product_Variation;

class PricingTable {
	
	use ServiceContainerTrait;
	
	private static $instance;
	
	private function __construct() {}
	
	public static function getInstance(): self {
		if ( is_null( self::$instance ) ) {
			self::$instance = new self();
		}
		
		return self::$instance;
	}
	
	/**
	 * Main function for rendering pricing table for product
	 *
	 * @param  int  $parentProductID
	 * @param  int  $variationID
	 * @param  array  $settings
	 */
	public function renderPricingTable( $parentProductID = 0, $variationID = null, array $settings = array() ) {
		
		if ( apply_filters( 'tiered_pricing_table/should_render_pricing_table', true, $parentProductID, $variationID,
				$settings ) === false ) {
			return;
		}
		
		$parentProduct = wc_get_product( $parentProductID );
		
		if ( ! $parentProduct ) {
			return;
		}
		
		if ( ! $this->productHasPricingRules( $parentProduct ) ) {
			return;
		}
		
		$settings = wp_parse_args( $settings, $this->getDefaultSettings( $parentProductID ) );
		
		// If the product is variable, but no specific variation is passed - check for default
		if ( ! $variationID && TierPricingTablePlugin::isVariableProductSupported( $parentProduct ) ) {
			$variationID = $this->getDefaultVariation( $parentProduct );
		}
		
		$ajaxVariationsThreshold = apply_filters( 'tiered_pricing_table/ajax_variations_threshold', 10 );
		
		// Use variation if exists, otherwise the parent product is used
		$productId = $variationID ? $variationID : $parentProduct->get_id();
		$product   = wc_get_product( $productId );
		
		$supportedTypes = array_merge( TierPricingTablePlugin::getSupportedSimpleProductTypes(),
			TierPricingTablePlugin::getSupportedVariableProductTypes() );
		
		// Exit if product is not valid
		if ( ! $product || ! in_array( $parentProduct->get_type(), $supportedTypes ) ) {
			return;
		}
		
		$settings = apply_filters( 'tiered_pricing_table/display_settings', $settings, $productId );
		
		if ( 'tooltip' === $settings['display_type'] ) {
			wp_enqueue_script( 'jquery-ui-tooltip' );
		}
		
		$hidden = ( 'tooltip' === $settings['display_type'] || ! $settings['display'] );
		
		do_action( 'tiered_pricing_table/before_rendering_tiered_pricing', $parentProduct, $variationID, $settings );
		
		$loadVariationByAjax = true;
		
		?>
		<div class="clear"></div>
		<div class="tpt__tiered-pricing <?php echo esc_attr( $hidden ? 'tpt__hidden' : '' ); ?>"
			 data-settings="<?php echo esc_attr( json_encode( $settings ) ); ?>"
			 data-display-context="<?php echo esc_attr( $settings['display_context'] ); ?>"
			 data-display-type="<?php echo esc_attr( $settings['display_type'] ); ?>"
			 data-product-id="<?php echo esc_attr( $parentProduct->get_id() ); ?>"
			 data-product-type="<?php echo esc_attr( $parentProduct->get_type() ); ?>"
			
			<?php if ( TierPricingTablePlugin::isVariableProductSupported( $parentProduct ) ): ?>
				<?php
				$loadVariationByAjax       = count( $parentProduct->get_children() ) > $ajaxVariationsThreshold;
				$variableProductSamePrices = AdvanceOptionsForVariableProduct::isVariableProductSamePrices( $parentProduct->get_id() );
				?>
				data-load-variation-by-ajax="<?php echo esc_attr( wc_bool_to_string( $loadVariationByAjax ) ); ?>"
				data-variable-product-same-prices="<?php echo esc_attr( wc_bool_to_string( $variableProductSamePrices ) ); ?>"
			<?php endif; ?>>
			
			<?php $this->renderPricingTableHTML( $parentProduct, $product, $settings ); ?>
		</div>
		
		<?php if ( TierPricingTablePlugin::isVariableProductSupported( $parentProduct ) && ! $loadVariationByAjax ): ?>
			<div class="tpt__tiered-pricing-preloaded-variations" style="display:none">
				<?php foreach ( $parentProduct->get_available_variations( 'objects' ) as $childProduct ): ?>
					<div class="tiered-pricing-preloaded-variation"
						 data-variation-id="<?php echo esc_attr( $childProduct->get_id() ); ?>"
						 data-display-context="<?php echo esc_attr( $settings['display_context'] ); ?>">
						<?php $this->renderPricingTableHTML( $parentProduct, $childProduct, $settings ); ?>
					</div>
				<?php endforeach; ?>
			</div>
		<?php endif; ?>
		<?php
	}
	
	/**
	 * Render pricing table without wrapper
	 *
	 * @param  WC_Product  $parentProduct
	 * @param  WC_Product  $product
	 * @param  array  $settings
	 */
	public function renderPricingTableHTML(
		WC_Product $parentProduct,
		WC_Product $product,
		array $settings = array()
	) {
		
		$settings = wp_parse_args( $settings, $this->getDefaultSettings( $parentProduct->get_id() ) );
		
		// Don't get rules from variable product when variation is empty
		if ( in_array( $parentProduct->get_type(),
				TierPricingTablePlugin::getSupportedVariableProductTypes() ) && ! ( $product instanceof WC_Product_Variation ) ) {
			// Empty rule
			$priceRule = new PricingRule( $product->get_id() );
		} else {
			$priceRule = PriceManager::getPricingRule( $product->get_id() );
		}
		
		// If rules are not empty
		if ( ! empty( $priceRule->getRules() ) ) {
			
			$displayType        = $settings['display_type'];
			$availableTemplates = TierPricingTablePlugin::getAvailablePricingLayouts();
			
			$templateType = array_key_exists( $settings['display_type'], $availableTemplates ) ? $displayType : 'table';
			
			if ( 'tooltip' === $displayType ) {
				$templateType = 'table';
			}
			
			if ( 'blocks' === $displayType ) {
				$style = $settings['blocks_style'];
				
				if ( $style !== 'default' ) {
					$templateType = 'blocks-' . $style;
				} else {
					$templateType = 'blocks';
				}
			}
			
			$template = "tiered-pricing-$templateType.php";
			
			do_action( 'tiered_pricing_table/before_rendering_tiered_pricing/inner', $priceRule, $product, $settings );
			
			$this->checkForDuplicateQuantities( $priceRule );
			
			$this->getContainer()->getFileManager()->includeTemplate( 'frontend/' . $template, array(
				'pricing_rule' => $priceRule,
				'price_rules'  => $priceRule->getRules(),
				'real_price'   => $product->get_price(),
				'product_name' => $product->get_name(),
				'product_id'   => $product->get_id(),
				'product'      => $product,
				'minimum'      => $priceRule->getMinimum( true ),
				'settings'     => $settings,
				'pricing_type' => $priceRule->getType(),
				'id'           => $this->getUniqueTieredPricingId(),
			) );
		}
	}
	
	protected function getDefaultSettings( $productId = false ): array {
		$settings = array(
			'display_context'       => 'product-page',
			'display'               => $this->getContainer()->getSettings()->get( 'display', 'yes' ) === 'yes',
			'display_type'          => $this->getContainer()->getSettings()->get( 'display_type', 'table' ),
			'title'                 => $this->getContainer()->getSettings()->get( 'table_title', '' ),
			'table_class'           => $this->getContainer()->getSettings()->get( 'table_css_class', '' ),
			'quantity_column_title' => $this->getContainer()->getSettings()->get( 'head_quantity_text',
				__( 'Quantity', 'tier-pricing-table' ) ),
			'price_column_title'    => $this->getContainer()->getSettings()->get( 'head_price_text',
				__( 'Price', 'tier-pricing-table' ) ),
			'discount_column_title' => $this->getContainer()->getSettings()->get( 'head_discount_text',
				__( 'Discount (%)', 'tier-pricing-table' ) ),
			'quantity_type'         => $this->getContainer()->getSettings()->get( 'quantity_type', 'range' ),
			'show_discount_column'  => $this->getContainer()->getSettings()->get( 'show_discount_column',
					'yes' ) === 'yes',
			'clickable_rows'        => $this->getContainer()->getSettings()->get( 'clickable_table_rows',
					'yes' ) === 'yes',
			'active_tier_color'     => $this->getContainer()->getSettings()->get( 'selected_quantity_color',
				'#96598A' ),
			'tooltip_border'        => $this->getContainer()->getSettings()->get( 'tooltip_border', 'yes' ) === 'yes',
			
			'blocks_style' => GeneralSection::getPricingBlocksStyle(),
			
			'options_show_total'                  => GeneralSection::isShowOptionTotal(),
			'options_show_original_product_price' => GeneralSection::isShowOriginalProductPrice(),
			'options_show_default_option'         => GeneralSection::isDefaultOptionEnabled(),
			
			'options_default_option_text' => GeneralSection::getDefaultOptionText(),
			'options_option_text'         => GeneralSection::getOptionText(),
			
			'plain_text_show_default_option' => GeneralSection::isPlainTextFirstTierEnabled(),
			'plain_text_option_text'         => GeneralSection::getPlainTextTemplate(),
			'plain_text_default_option_text' => GeneralSection::getPlainTextFirstTierTemplate(),
			
			'update_price_on_product_page'  => $this->getContainer()->getSettings()->get( 'update_price_on_product_page',
					'yes' ) === 'yes',
			'show_tiered_price_as_discount' => $this->getContainer()->getSettings()->get( 'show_tiered_price_as_discount',
					'yes' ) === 'yes',
			'show_total_price'              => $this->getContainer()->getSettings()->get( 'show_total_price',
					'no' ) === 'yes',
		);
		
		$default_quantity_measurement = array(
			'singular' => '',
			'plural'   => '',
		);
		
		$quantity_measurement = $default_quantity_measurement;
		
		if ( in_array( $settings['display_type'], array( 'table', 'horizontal-table', 'tooltip' ) ) ) {
			$quantity_measurement = $this->getContainer()->getSettings()->get( 'table_quantity_measurement',
				$default_quantity_measurement );
		}
		
		if ( 'blocks' === $settings['display_type'] ) {
			$quantity_measurement = $this->getContainer()->getSettings()->get( 'blocks_quantity_measurement', array(
				'singular' => _n( 'piece', 'pieces', 1, 'tier-pricing-table' ),
				'plural'   => _n( 'piece', 'pieces', 2, 'tier-pricing-table' ),
			) );
		}
		
		$settings['quantity_measurement_singular'] = $quantity_measurement['singular'];
		$settings['quantity_measurement_plural']   = $quantity_measurement['plural'];
		
		if ( $productId ) {
			$template = TieredPricingTab::getProductTemplate( $productId );
			
			if ( 'default' !== $template ) {
				$settings['display_type'] = $template;
			}
			
			$baseUnitName = TieredPricingTab::getProductBaseUnitName( $productId );
			
			if ( $baseUnitName['singular'] && $baseUnitName['plural'] ) {
				$settings['quantity_measurement_singular'] = $baseUnitName['singular'];
				$settings['quantity_measurement_plural']   = $baseUnitName['plural'];
			}
		}
		
		return $settings;
	}
	
	protected function getUniqueTieredPricingId() {
		return preg_replace( '/[0-9]+/', '', strtolower( wp_generate_password( 20, false ) ) );
	}
	
	protected function getDefaultVariation( WC_Product_Variable $product ): ?int {
		
		$defaultVariation = AdvanceOptionsForVariableProduct::getDefaultVariation( $product->get_id() );
		
		if ( $defaultVariation ) {
			return $defaultVariation->get_id();
		}
		
		$defaultAttributes = $product->get_default_attributes();
		
		if ( ! empty( $defaultAttributes ) ) {
			
			$defaultAttributesKeys = array_map( function ( $attribute ) {
				return 'attribute_' . $attribute;
			}, array_keys( $defaultAttributes ) );
			
			$defaultAttributes = array_combine( $defaultAttributesKeys, $defaultAttributes );
			
			return ( new WC_Product_Data_Store_CPT() )->find_matching_product_variation( $product, $defaultAttributes );
		}
		
		return null;
	}
	
	public function productHasPricingRules( WC_Product $product ): bool {
		
		if ( TierPricingTablePlugin::isSimpleProductSupported( $product ) ) {
			$pricingRule = PriceManager::getPricingRule( $product->get_id() );
			
			return ! empty( $pricingRule->getRules() );
		}
		
		if ( TierPricingTablePlugin::isVariableProductSupported( $product ) && $product instanceof WC_Product_Variable ) {
			
			// Do not check if product has tiered pricing for variable product by default.
			// If a variable product has many variations, it can take many resources to check every variation for rules existing.
			// The downside is that the plugin will send an AJAX request for each variant selection on the product page even if a product does not have any tiered pricing
			if ( ! apply_filters( 'tiered_pricing_table/check_if_variable_product_has_rules', false, $product ) ) {
				return true;
			}
			
			$hasTieredPricing = $this->getContainer()->getCache()->getProductData( $product, 'product_has_rules' );
			
			if ( 'no' === $hasTieredPricing ) {
				return false;
			}
			
			if ( 'yes' === $hasTieredPricing ) {
				return true;
			}
			
			foreach ( $product->get_available_variations() as $productVariation ) {
				$pricingRule = PriceManager::getPricingRule( $productVariation['variation_id'] );
				
				if ( ! empty( $pricingRule->getRules() ) ) {
					$this->getContainer()->getCache()->setProductData( $product, 'product_has_rules', 'yes' );
					
					return true;
				}
			}
			
			$this->getContainer()->getCache()->setProductData( $product, 'product_has_rules', 'no' );
		}
		
		return false;
	}
	
	public function checkForDuplicateQuantities( PricingRule $pricingRule ) {
		// Show notice only for admin users
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}
		$minimum           = $pricingRule->getMinimum( true );
		$pricingRules      = $pricingRule->getRules();
		$firstRuleQuantity = ! empty( $pricingRules ) ? array_keys( $pricingRules )[0] : false;
		
		if ( $firstRuleQuantity && $firstRuleQuantity <= $minimum ) {
			?>

			<div style="font-size: .8em;">
				<div class="woocommerce-error" role="alert" style="margin-bottom: 0">
					Duplicated quantities detected. Minimum order quantity is equal or higher than the first tiered
					pricing.
				</div>
				<div style="color: #555;
	padding: 10px 12px;
	border: 1px solid #b32c2e;
	background: #fff5f5;">
					<p style="padding: 0; margin: 0">
						The minimum order quantity must be less than the first tiered pricing rule quantity.
					</p>
					<p style="padding: 0; margin: 0"> The first tier always uses woocommerce price and is applied to
						quantities between minimum order
						quantity and
						the first tiered pricing rule quantity.
					</p>
					<br>
					<b>This notice shown only for administrators.</b>
				</div>
			</div>
			<?php
		}
	}
}