$bitMask[$y][$x] = ($maskFunc == 0)?1:0; } } } return $bitMask; } //---------------------------------------------------------------------- public static function serial($bitFrame) { $codeArr = array(); foreach ($bitFrame as $line) $codeArr[] = join('', $line); return gzcompress(join("\n", $codeArr), 9); } //---------------------------------------------------------------------- public static function unserial($code) { $codeArr = array(); $codeLines = explode("\n", gzuncompress($code)); foreach ($codeLines as $line) $codeArr[] = str_split($line); return $codeArr; } //---------------------------------------------------------------------- public function makeMaskNo($maskNo, $width, $s, &$d, $maskGenOnly = false) { $b = 0; $bitMask = array(); $fileName = QR_CACHE_DIR.'mask_'.$maskNo.DIRECTORY_SEPARATOR.'mask_'.$width.'_'.$maskNo.'.dat'; if (QR_CACHEABLE) { if (file_exists($fileName)) { $bitMask = self::unserial(file_get_contents($fileName)); } else { $bitMask = $this->generateMaskNo($maskNo, $width, $s, $d); if (!file_exists(QR_CACHE_DIR.'mask_'.$maskNo)) mkdir(QR_CACHE_DIR.'mask_'.$maskNo); file_put_contents($fileName, self::serial($bitMask)); } } else { $bitMask = $this->generateMaskNo($maskNo, $width, $s, $d); } if ($maskGenOnly) return; $d = $s; for($y=0; $y<$width; $y++) { for($x=0; $x<$width; $x++) { if($bitMask[$y][$x] == 1) { $d[$y][$x] = chr(ord($s[$y][$x]) ^ (int)$bitMask[$y][$x]); } $b += (int)(ord($d[$y][$x]) & 1); } } return $b; } //---------------------------------------------------------------------- public function makeMask($width, $frame, $maskNo, $level) { $masked = array_fill(0, $width, str_repeat("\0", $width)); $this->makeMaskNo($maskNo, $width, $frame, $masked); $this->writeFormatInformation($width, $masked, $maskNo, $level); return $masked; } //---------------------------------------------------------------------- public function calcN1N3($length) { $demerit = 0; for($i=0; $i<$length; $i++) { if($this->runLength[$i] >= 5) { $demerit += (N1 + ($this->runLength[$i] - 5)); } if($i & 1) { if(($i >= 3) && ($i < ($length-2)) && ($this->runLength[$i] % 3 == 0)) { $fact = (int)($this->runLength[$i] / 3); if(($this->runLength[$i-2] == $fact) && ($this->runLength[$i-1] == $fact) && ($this->runLength[$i+1] == $fact) && ($this->runLength[$i+2] == $fact)) { if(($this->runLength[$i-3] < 0) || ($this->runLength[$i-3] >= (4 * $fact))) { $demerit += N3; } else if((($i+3) >= $length) || ($this->runLength[$i+3] >= (4 * $fact))) { $demerit += N3; } } } } } return $demerit; } //---------------------------------------------------------------------- public function evaluateSymbol($width, $frame) { $head = 0; $demerit = 0; for($y=0; $y<$width; $y++) { $head = 0; $this->runLength[0] = 1; $frameY = $frame[$y]; if ($y>0) $frameYM = $frame[$y-1]; for($x=0; $x<$width; $x++) { if(($x > 0) && ($y > 0)) { $b22 = ord($frameY[$x]) & ord($frameY[$x-1]) & ord($frameYM[$x]) & ord($frameYM[$x-1]); $w22 = ord($frameY[$x]) | ord($frameY[$x-1]) | ord($frameYM[$x]) | ord($frameYM[$x-1]); if(($b22 | ($w22 ^ 1))&1) { $demerit += N2; } } if(($x == 0) && (ord($frameY[$x]) & 1)) { $this->runLength[0] = -1; $head = 1; $this->runLength[$head] = 1; } else if($x > 0) { if((ord($frameY[$x]) ^ ord($frameY[$x-1])) & 1) { $head++; $this->runLength[$head] = 1; } else { $this->runLength[$head]++; } } } $demerit += $this->calcN1N3($head+1); } for($x=0; $x<$width; $x++) { $head = 0; $this->runLength[0] = 1; for($y=0; $y<$width; $y++) { if($y == 0 && (ord($frame[$y][$x]) & 1)) { $this->runLength[0] = -1; $head = 1; $this->runLength[$head] = 1; } else if($y > 0) { if((ord($frame[$y][$x]) ^ ord($frame[$y-1][$x])) & 1) { $head++; $this->runLength[$head] = 1; } else { $this->runLength[$head]++; } } } $demerit += $this->calcN1N3($head+1); } return $demerit; } //---------------------------------------------------------------------- public function mask($width, $frame, $level) { $minDemerit = PHP_INT_MAX; $bestMaskNum = 0; $bestMask = array(); $checked_masks = array(0,1,2,3,4,5,6,7); if (QR_FIND_FROM_RANDOM !== false) { $howManuOut = 8-(QR_FIND_FROM_RANDOM % 9); for ($i = 0; $i < $howManuOut; $i++) { $remPos = rand (0, count($checked_masks)-1); unset($checked_masks[$remPos]); $checked_masks = array_values($checked_masks); } } $bestMask = $frame; foreach($checked_masks as $i) { $mask = array_fill(0, $width, str_repeat("\0", $width)); $demerit = 0; $blacks = 0; $blacks = $this->makeMaskNo($i, $width, $frame, $mask); $blacks += $this->writeFormatInformation($width, $mask, $i, $level); $blacks = (int)(100 * $blacks / ($width * $width)); $demerit = (int)((int)(abs($blacks - 50) / 5) * N4); $demerit += $this->evaluateSymbol($width, $mask); if($demerit < $minDemerit) { $minDemerit = $demerit; $bestMask = $mask; $bestMaskNum = $i; } } return $bestMask; } //---------------------------------------------------------------------- } >store_currency; } /** * Returns the currency code to be used by WooCommerce. * * @return string The code of the currency to be used. */ public function get_woocommerce_currency(): string { if ( $this->compatibility->should_return_store_currency() ) { return $this->get_store_currency()->get_code(); } if ( empty( $this->woocommerce_currency ) ) { $this->woocommerce_currency = $this->get_selected_currency_code(); } return $this->woocommerce_currency; } /** * Returns the number of decimals to be used by WooCommerce. * * @param int $decimals The original decimal count. * * @return int The number of decimals. */ public function get_price_decimals( $decimals ): int { $currency_code = $this->get_currency_code(); if ( $currency_code !== $this->get_store_currency()->get_code() ) { return absint( $this->localization_service->get_currency_format( $currency_code )['num_decimals'] ); } return $decimals; } /** * Returns the decimal separator to be used by WooCommerce. * * @param string $separator The original separator. * * @return string The decimal separator. */ public function get_price_decimal_separator( $separator ): string { $currency_code = $this->get_currency_code(); $store_currency_code = $this->get_store_currency()->get_code(); if ( $currency_code === $store_currency_code ) { $currency_code = $store_currency_code; $this->price_decimal_separators[ $currency_code ] = $separator; } if ( empty( $this->price_decimal_separators[ $currency_code ] ) ) { $this->price_decimal_separators[ $currency_code ] = $this->localization_service->get_currency_format( $currency_code )['decimal_sep']; } return $this->price_decimal_separators[ $currency_code ]; } /** * Returns the thousand separator to be used by WooCommerce. * * @param string $separator The original separator. * * @return string The thousand separator. */ public function get_price_thousand_separator( $separator ): string { $currency_code = $this->get_currency_code(); if ( $currency_code !== $this->get_store_currency()->get_code() ) { return $this->localization_service->get_currency_format( $currency_code )['thousand_sep']; } return $separator; } /** * Returns the currency format to be used by WooCommerce. * * @param string $format The original currency format. * * @return string The currency format. */ public function get_woocommerce_price_format( $format ): string { $currency_code = $this->get_currency_code(); if ( $currency_code !== $this->get_store_currency()->get_code() ) { $currency_pos = $this->localization_service->get_currency_format( $currency_code )['currency_pos']; switch ( $currency_pos ) { case 'left': return '%1$s%2$s'; case 'right': return '%2$s%1$s'; case 'left_space': return '%1$s %2$s'; case 'right_space': return '%2$s %1$s'; default: return '%1$s%2$s'; } } return $format; } /** * Adds the currency and exchange rate to the cart hash so it's recalculated properly. * * @param string $hash The cart hash. * * @return string The adjusted cart hash. */ public function add_currency_to_cart_hash( $hash ): string { $currency = $this->multi_currency->get_selected_currency(); return md5( $hash . $currency->get_code() . $currency->get_rate() ); } /** * Inits order currency code. * * @param mixed $arg Either WC_Order or the id of an order are expected, but can be empty. * * @return int|mixed The order id or what was passed as $arg. */ public function init_order_currency( $arg ) { if ( null !== $this->order_currency ) { return $arg; } // We remove these filters here because 'wc_get_order' // can trigger them, leading to an infinitely recursive call. remove_filter( 'woocommerce_price_format', [ $this, 'get_woocommerce_price_format' ], 900 ); remove_filter( 'wc_get_price_thousand_separator', [ $this, 'get_price_thousand_separator' ], 900 ); remove_filter( 'wc_get_price_decimal_separator', [ $this, 'get_price_decimal_separator' ], 900 ); remove_filter( 'wc_get_price_decimals', [ $this, 'get_price_decimals' ], 900 ); $order = ! $arg instanceof WC_Order ? wc_get_order( $arg ) : $arg; add_filter( 'wc_get_price_decimals', [ $this, 'get_price_decimals' ], 900 ); add_filter( 'wc_get_price_decimal_separator', [ $this, 'get_price_decimal_separator' ], 900 ); add_filter( 'wc_get_price_thousand_separator', [ $this, 'get_price_thousand_separator' ], 900 ); add_filter( 'woocommerce_price_format', [ $this, 'get_woocommerce_price_format' ], 900 ); if ( $order ) { $this->order_currency = $order->get_currency(); return $order->get_id(); } $this->order_currency = $this->multi_currency->get_selected_currency()->get_code(); return $arg; } /** * Gets the order id from the wp query_vars and then calls init_order_currency. * * @return void */ public function init_order_currency_from_query_vars() { global $wp; if ( ! empty( $wp->query_vars['order-pay'] ) ) { $this->init_order_currency( $wp->query_vars['order-pay'] ); } elseif ( ! empty( $wp->query_vars['order-received'] ) ) { $this->init_order_currency( $wp->query_vars['order-received'] ); } elseif ( ! empty( $wp->query_vars['view-order'] ) ) { $this->init_order_currency( $wp->query_vars['view-order'] ); } } /** * Fixes the decimals for the store currency when shipping rates are being determined. * Our `wc_get_price_decimals` filter returns the decimals for the selected currency during this calculation, which leads to incorrect results. * * @param array $args The argument array to be filtered. * @param object $method The shipping method being calculated. * * @return array */ public function fix_price_decimals_for_shipping_rates( array $args, $method ): array { $args['price_decimals'] = absint( $this->localization_service->get_currency_format( $this->get_store_currency()->get_code() )['num_decimals'] ); return $args; } /** * Returns the current value of order_currency. * * @return ?string The currency code or null. */ public function get_order_currency() { return $this->order_currency; } /** * Maybe init the order_currency when the order total is queried if we should_use_order_currency. * * This works off of filtering during WC_Abstract_Order->get_total, which states it returns a float, however, in the instances of orders with negative * amounts, such as refund orders, it will return a string. * * @param mixed $total The order total. * @param WC_Order $order The order being worked on. * * @return mixed The unmodified total. */ public function maybe_init_order_currency_from_order_total_prop( $total, $order ) { if ( $this->should_use_order_currency() ) { $this->init_order_currency( $order ); } return $total; } /** * If the order_currency is set and we should be using the order currency, clear it. * * This should only be happening on the instances tested for in should_use_order_currency. We would need to clear the order currency once the total * filter is run so that if another total comes up, like in the order list, we use the next order's currency. * * @param string $formatted_total Total to display. * @param WC_Order $order Order data. * @param string $tax_display Type of tax display. * @param bool $display_refunded If should include refunded value. * * @return string The unmodified formatted total. */ public function maybe_clear_order_currency_after_formatted_order_total( $formatted_total, $order, $tax_display, $display_refunded ): string { if ( null !== $this->order_currency && $this->should_use_order_currency() ) { $this->order_currency = null; } return $formatted_total; } /** * Gets the currency code for us to use. * * @return string|null Three letter currency code. */ private function get_currency_code() { if ( $this->should_use_order_currency() ) { $this->init_order_currency_from_query_vars(); return $this->order_currency; } $this->selected_currency_code = $this->get_selected_currency_code(); return $this->selected_currency_code; } /** * Helper function to "cache" the selected currency. * * @return string */ private function get_selected_currency_code(): string { if ( empty( $this->selected_currency_code ) ) { $this->selected_currency_code = $this->multi_currency->get_selected_currency()->get_code(); } return $this->selected_currency_code; } /** * Checks whether currency code used for formatting should be overridden. * * @return bool */ private function should_use_order_currency(): bool { $pages = [ 'my-account', 'checkout' ]; $vars = [ 'order-received', 'order-pay', 'orders', 'view-order' ]; if ( $this->utils->is_page_with_vars( $pages, $vars ) ) { return $this->utils->is_call_in_backtrace( [ 'WC_Shortcode_My_Account::view_order', 'WC_Shortcode_Checkout::order_received', 'WC_Shortcode_Checkout::order_pay', 'WC_Order->get_formatted_order_total', ] ); } return false; } }