HEX
Server: Apache/2.4.65 (Debian)
System: Linux kubikelcreative 5.10.0-35-amd64 #1 SMP Debian 5.10.237-1 (2025-05-19) x86_64
User: www-data (33)
PHP: 8.4.13
Disabled: NONE
Upload Files
File: /var/www/indoadvisory/wp/wp-content/plugins/polylang-wc/frontend/frontend-cart.php
<?php
/**
 * @package Polylang-WC
 */

/**
 * Manages the translation of the cart.
 *
 * @since 1.0
 */
class PLLWC_Frontend_Cart {
	/**
	 * Controls if the cart translation if enabled.
	 *
	 * @var bool
	 */
	protected $enable_cart_translation;

	/**
	 * Product language data store.
	 *
	 * @var PLLWC_Product_Language_CPT
	 */
	protected $data_store;

	/**
	 * Constructor.
	 * Setups filters and actions.
	 *
	 * @since 1.0
	 */
	public function __construct() {
		/**
		 * Filters if the cart translation is enabled.
		 *
		 * It can be useful to disable the translation of the products in the cart in case
		 * a third party plugin adds information to the cart that we are not able to translate.
		 *
		 * @since 1.6
		 *
		 * @param bool $enable True if the cart translation is enabled, false otherwise.
		 */
		$this->enable_cart_translation = apply_filters( 'pllwc_enable_cart_translation', true );

		if ( did_action( 'pll_language_defined' ) ) {
			$this->init();
		} else {
			add_action( 'pll_language_defined', array( $this, 'init' ), 1 );
			add_filter( 'option_woocommerce_cart_page_id', array( $this, 'translate_add_to_cart_page_id' ) );
		}

		if ( $this->enable_cart_translation ) {
			$this->data_store = PLLWC_Data_Store::load( 'product_language' );

			add_filter( 'pll_set_language_from_query', array( $this, 'pll_set_language_from_query' ), 5 ); // Before Polylang.

			add_filter( 'woocommerce_cart_hash', array( $this, 'cart_hash' ), 10, 2 ); // Hash should be language independent.
			add_filter( 'woocommerce_cart_item_data_to_validate', array( $this, 'cart_item_data_to_validate' ), 10, 2 ); // Since WC 3.4.
		}
	}

	/**
	 * Setups actions and filters once the language is defined.
	 *
	 * @since 1.0
	 *
	 * @return void
	 */
	public function init() {
		/*
		 * Resets the cart when switching the language, even when the cart translation is disabled,
		 * to translate the mini cart the theme.
		 */
		if ( isset( $_COOKIE[ PLL_COOKIE ] ) && pll_current_language() !== $_COOKIE[ PLL_COOKIE ] ) {
			add_action( 'wp_enqueue_scripts', array( $this, 'wp_enqueue_scripts' ) ); // After WooCommerce load_scripts().
		}

		if ( $this->enable_cart_translation ) {
			// Translates the products in the cart.
			add_action( 'woocommerce_cart_loaded_from_session', array( $this, 'woocommerce_cart_loaded_from_session' ) );
		}
	}

	/**
	 * Reloads the cart when the language is set from the content.
	 *
	 * @since 0.3.2
	 *
	 * @param PLL_Language|false $lang False or language object.
	 * @return PLL_Language|false
	 */
	public function pll_set_language_from_query( $lang ) {
		if ( ! PLL()->options['force_lang'] ) {
			if ( did_action( 'pll_language_defined' ) ) {
				/*
				 * Handle a specific case for the Site home (when the language code is hidden for the default language).
				 * Done here and not in the 'pll_language_defined' action to avoid a notice with WooCommerce Dynamic pricing which calls is_shop().
				 */
				WC()->cart->get_cart_from_session();
			} else {
				add_action( 'pll_language_defined', array( WC()->cart, 'get_cart_from_session' ) );
			}
		}

		return $lang;
	}

	/**
	 * Resets the cached data when switching the language.
	 *
	 * @since 0.1
	 *
	 * @return void
	 */
	public function wp_enqueue_scripts() {
		// Reset shipping methods (required since WC 2.6).
		WC()->shipping()->calculate_shipping( WC()->cart->get_shipping_packages() );

		$cart_hash_key = apply_filters( 'woocommerce_cart_hash_key', 'wc_cart_hash_' . md5( get_current_blog_id() . '_' . get_site_url( get_current_blog_id(), '/' ) . get_template() ) );
		$fragment_name = apply_filters( 'woocommerce_cart_fragment_name', 'wc_fragments_' . md5( get_current_blog_id() . '_' . get_site_url( get_current_blog_id(), '/' ) . get_template() ) );

		// Add js to reset the cart.
		wp_add_inline_script( // Since WP 4.5.
			'wc-cart-fragments',
			sprintf(
				'(function( $ ){
						sessionStorage.removeItem( "%s" );
						sessionStorage.removeItem( "%s" );
					}
				)();',
				esc_js( $cart_hash_key ),
				esc_js( $fragment_name )
			),
			'before'
		);
	}

	/**
	 * Translates the product attributes in the cart.
	 *
	 * @since 1.1
	 *
	 * @param string[] $attributes Selected attributes.
	 * @param string   $lang       Target language.
	 * @param string   $orig_lang  Source language.
	 * @return string[]
	 */
	public function translate_attributes_in_cart( $attributes, $lang, $orig_lang ) {
		foreach ( $attributes as $name => $value ) {
			if ( '' === $value ) {
				continue;
			}

			$taxonomy = wc_attribute_taxonomy_name( str_replace( 'attribute_pa_', '', urldecode( $name ) ) );

			if ( taxonomy_exists( $taxonomy ) ) {
				// Don't use get_term_by( 'slug' ) which is filtered in the current language by Polylang Pro.
				$terms = get_terms( array( 'taxonomy' => $taxonomy, 'slug' => $value, 'lang' => $orig_lang ) );

				if ( ! empty( $terms ) && is_array( $terms ) ) {
					$term = reset( $terms );
					if ( $term instanceof WP_Term && $term_id = pll_get_term( $term->term_id, $lang ) ) {
						$term = get_term( $term_id, $taxonomy );
						if ( $term instanceof WP_Term ) {
							$attributes[ $name ] = $term->slug;
						}
					}
				}
			}
		}

		return $attributes;
	}

	/**
	 * Translates the products in the cart.
	 *
	 * @since 0.3.5
	 *
	 * @param array  $item Cart item.
	 * @param string $lang Language code.
	 * @return array
	 */
	protected function translate_cart_item( $item, $lang ) {
		$orig_lang = $this->data_store->get_language( $item['product_id'] );

		if ( ! $orig_lang ) {
			return $item;
		}

		$item['product_id'] = $this->data_store->get( $item['product_id'], $lang );

		// Variable product.
		if ( $item['variation_id'] && $tr_id = $this->data_store->get( $item['variation_id'], $lang ) ) {
			$item['variation_id'] = $tr_id;
			if ( ! empty( $item['data'] ) ) {
				$item['data'] = wc_get_product( $item['variation_id'] );
			}

			// Variations attributes.
			if ( ! empty( $item['variation'] ) ) {
				$item['variation'] = $this->translate_attributes_in_cart( $item['variation'], $lang, $orig_lang );
			}
		} elseif ( ! empty( $item['data'] ) ) {
			// Simple product.
			$item['data'] = wc_get_product( $item['product_id'] );
		}

		/**
		 * Filters a cart item when it is translated.
		 *
		 * @since 0.6
		 *
		 * @param array  $item Cart item.
		 * @param string $lang Language code.
		 */
		$item = apply_filters( 'pllwc_translate_cart_item', $item, $lang );

		/**
		 * Filters the cart item data.
		 * This filters aims to replace the filter 'woocommerce_add_cart_item_data',
		 * which can't be used here as it conflicts with WooCommerce Bookings,
		 * which uses the filter to create new bookings and not only to filter the cart item data.
		 *
		 * @since 0.7.4
		 *
		 * @param array $cart_item_data Cart item data.
		 * @param array $item           Cart item.
		 */
		$cart_item_data = (array) apply_filters( 'pllwc_add_cart_item_data', array(), $item );
		$item['key'] = WC()->cart->generate_cart_id( $item['product_id'], $item['variation_id'], $item['variation'], $cart_item_data );

		return $item;
	}

	/**
	 * Translates the cart contents.
	 *
	 * @since 0.3.5
	 *
	 * @param array  $contents Cart contents.
	 * @param string $lang     Language code.
	 * @return array
	 */
	protected function translate_cart_contents( $contents, $lang = '' ) {
		if ( empty( $lang ) ) {
			$lang = pll_current_language();
		}

		foreach ( $contents as $key => $item ) {
			if ( $item['product_id'] && ( $tr_id = $this->data_store->get( $item['product_id'], $lang ) ) && $tr_id !== $item['product_id'] ) {
				unset( $contents[ $key ] );
				$item = $this->translate_cart_item( $item, $lang );
				$contents[ $item['key'] ] = $item;

				/**
				 * Fires after a cart item has been translated.
				 *
				 * @since 1.1
				 *
				 * @param array  $item Cart item.
				 * @param string $key  Previous cart item key. The new key can be found in $item['key'].
				 */
				do_action( 'pllwc_translated_cart_item', $item, $key );
			}
		}

		/**
		 * Filters the cart contents after all cart items have been translated.
		 *
		 * @since 1.1
		 *
		 * @param array  $contents Cart contents.
		 * @param string $lang     Language code.
		 */
		$contents = apply_filters( 'pllwc_translate_cart_contents', $contents, $lang );

		return $contents;
	}

	/**
	 * Translates the products and removed products in the cart.
	 *
	 * @since 0.3.5
	 *
	 * @return void
	 */
	public function woocommerce_cart_loaded_from_session() {
		WC()->cart->cart_contents = $this->translate_cart_contents( WC()->cart->cart_contents );
		WC()->cart->removed_cart_contents = $this->translate_cart_contents( WC()->cart->removed_cart_contents );
	}

	/**
	 * Makes the cart hash language independent by relying on products in default language.
	 *
	 * @since 0.9.4
	 *
	 * @param string $hash         Cart hash.
	 * @param array  $cart_session Cart session.
	 * @return string Modified cart hash.
	 */
	public function cart_hash( $hash, $cart_session ) {
		if ( ! empty( $cart_session ) ) {
			$cart_session = $this->translate_cart_contents( $cart_session, pll_default_language() );
			$hash = md5( wp_json_encode( $cart_session ) . WC()->cart->get_total( 'edit' ) );
		}
		return $hash;
	}

	/**
	 * Makes the cart item hash language independent by relying on attributes in default language
	 *
	 * @since 1.0
	 *
	 * @param array      $data    Data to validate in the hash.
	 * @param WC_Product $product Product in the cart item.
	 * @return array
	 */
	public function cart_item_data_to_validate( $data, $product ) {
		if ( ! empty( $data['attributes'] ) ) {
			$tr_product_id = $this->data_store->get( $product->get_id(), pll_default_language() );
			$tr_product = wc_get_product( $tr_product_id );
			if ( $tr_product && method_exists( $tr_product, 'get_variation_attributes' ) ) {
				$data['attributes'] = $tr_product->get_variation_attributes();
			}
		}
		return $data;
	}

	/**
	 * Translates the cart page id in the Add to cart action.
	 *
	 * @since 1.6
	 *
	 * @param int $page_id Cart page id.
	 * @return int
	 */
	public function translate_add_to_cart_page_id( $page_id ) {
		if ( empty( $_REQUEST['add-to-cart'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			return $page_id;
		}

		$product_id = absint( wp_unslash( $_REQUEST['add-to-cart'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$language = $this->data_store->get_language( $product_id );

		if ( ! empty( $language ) ) {
			$page_id = pll_get_post( $page_id, $language );
		}
		return $page_id ? $page_id : 0;
	}
}