PHP8/8.1下Hyyan Polylang for WooCommerce变量商品报错求助
问题背景
长期使用Hyyan Polylang for WooCommerce插件,将主机PHP版本从7.4升级到8/8.1后,可变商品出现崩溃(无按钮、部分组件缺失),但简单商品正常。
报错信息
调试时触发致命错误:
Fatal error: Uncaught TypeError: NumberFormatter::format(): Argument #1 ($num) must be of type int|float, string given in /home/t80469/public_html/wp-content/plugins/woo-poly-integration/src/Hyyan/WPI/LocaleNumbers.php:97 Stack trace: #0 /home/t80469/public_html/wp-content/plugins/woo-poly-integration/src/Hyyan/WPI/LocaleNumbers.php(97): NumberFormatter->format('', 3) #1 /home/t80469/public_html/wp-includes/class-wp-hook.php(308): Hyyan\WPI\LocaleNumbers->getLocalizedDecimal('', '') #2 /home/t80469/public_html/wp-includes/plugin.php(205): WP_Hook->apply_filters('', Array) #3 /home/t80469/public_html/wp-content/plugins/woocommerce/includes/wc-formatting-functions.php(358): apply_filters('woocommerce_for...', '', '') #4 [internal function]: wc_format_localized_decimal('') #5 /home/t80469/public_html/wp-content/plugins/woocommerce/includes/wc-formatting-functions.php(1330): array_map('wc_format_local...', Array) #6 /home/t80469/public_html/wp-content/plugins/woocommerce/includes/class-wc-product-variable.php(370): wc_format_dimensions(Array) #7 /home/t80469/public_html/wp-content/plugins/woocommerce/includes/class-wc-product-variable.php(312): WC_Product_Variable->get_available_variation(Object(WC_Product_Variation)) #8 /home/t80469/public_html/wp-content/plugins/woocommerce/includes/wc-template-functions.php(1749): WC_Product_Variable->get_available_variations() #9 /home/t80469/public_html/wp-includes/class-wp-hook.php(308): woocommerce_variable_add_to_cart('') #10 /home/t80469/public_html/wp-includes/class-wp-hook.php(332): WP_Hook->apply_filters('', Array) #11 /home/t80469/public_html/wp-includes/plugin.php(517): WP_Hook->do_action(Array) #12 /home/t80469/public_html/wp-content/plugins/woocommerce/includes/wc-template-functions.php(1697): do_action('woocommerce_var...') #13 /home/t80469/public_html/wp-includes/class-wp-hook.php(308): woocommerce_template_single_add_to_cart('') #14 /home/t80469/public_html/wp-includes/class-wp-hook.php(332): WP_Hook->apply_filters(NULL, Array) #15 /home/t80469/public_html/wp-includes/plugin.php(517): WP_Hook->do_action(Array) #16 /home/t80469/public_html/wp-content/plugins/woocommerce/templates/content-single-product.php(60): do_action('woocommerce_sin...') #17 /home/t80469/public_html/wp-includes/template.php(785): require('/home/t80469/pu...') #18 /home/t80469/public_html/wp-content/plugins/woocommerce/includes/wc-core-functions.php(284): load_template('/home/t80469/pu...', false) #19 /home/t80469/public_html/wp-content/plugins/woocommerce/templates/single-product.php(37): wc_get_template_part('content', 'single-product') #20 /home/t80469/public_html/wp-includes/template-loader.php(106): include('/home/t80469/pu...') #21 /home/t80469/public_html/wp-blog-header.php(19): require_once('/home/t80469/pu...') #22 /home/t80469/public_html/index.php(17): require('/home/t80469/pu...') #23 {main} thrown in /home/t80469/public_html/wp-content/plugins/woo-poly-integration/src/Hyyan/WPI/LocaleNumbers.php on line 97
临时解决方法
找到插件目录下的/plugins/woo-poly-integration/src/Hyyan/WPI/LocaleNumbers.php文件,注释掉原本被作者注释后又启用的代码:
//add_filter('woocommerce_format_localized_decimal', array($this, 'getLocalizedDecimal'), 10, 2);
完整上下文:
/** * Hook relevant WooCommerce filters to apply localisation according to Polylang locale. */ public function __construct() { if ( class_exists('\NumberFormatter') && 'on' === Settings::getOption('localenumbers', Features::getID(), 'on') ) { //localise standard price formatting arguments add_filter('wc_get_price_decimal_separator', array($this, 'getLocaleDecimalSeparator'), 10, 1); add_filter('wc_get_price_thousand_separator', array($this, 'getLocaleThousandSeparator'), 10, 1); add_filter('wc_price_args', array($this, 'filterPriceArgs'), 10, 1); //WooCommerce 3.1 unreleased checkin https://github.com/woocommerce/woocommerce/pull/15628 //add_filter('woocommerce_format_localized_decimal', array($this, 'getLocalizedDecimal'), 10, 2); //no additional override on finished price format as no currency paramber available //add_filter('woocommerce_format_localized_price', array($this, 'getLocalizedPrice'), 10, 2); } }
注释后调试无报错,可变商品按钮恢复正常,但希望了解:
getLocalizedDecimal函数的重要性- PHP8+中NumberFormatter的变化
- 如何正确替代该函数
解答
1. getLocalizedDecimal函数的重要性
这个函数的作用是根据Polylang设置的当前语言区域,格式化十进制数值,比如把小数点分隔符、千位分隔符转换成对应语言的格式(比如法语用逗号做小数点,英语用点)。它挂钩woocommerce_format_localized_decimal过滤器,会在WooCommerce处理价格、尺寸、重量等带小数的数值时触发,确保数值显示符合当前语言的本地化规范。
不过从代码上下文看,作者原本就注释了这个钩子,说明它是针对WooCommerce某个未发布版本(3.1)的兼容代码,核心的本地化逻辑已经通过wc_get_price_decimal_separator、wc_get_price_thousand_separator和wc_price_args这几个钩子实现了,所以禁用它不会影响核心的价格本地化功能。
2. PHP8+中NumberFormatter的变化
PHP8.0开始,对类型检查变得严格:
- PHP7.x中,
NumberFormatter::format()方法接收字符串类型的数值时会自动隐性转换; - PHP8.0+中,该方法的第一个参数
$num明确要求是int|float类型,传入字符串会直接抛出TypeError,这就是你升级后报错的根本原因——代码中传入了空字符串''给这个方法,PHP8+不再自动转换,直接触发致命错误。
3. 正确替代/修复方案
如果需要保留该函数的功能,不需要完全注释掉钩子,而是修改getLocalizedDecimal函数的实现,在调用NumberFormatter::format()前先做类型校验和转换:
打开LocaleNumbers.php找到getLocalizedDecimal方法(大概在97行附近),修改为:
public function getLocalizedDecimal($num, $locale = '') { // 先处理空值或非数值情况 if (empty($num) || !is_numeric($num)) { return $num; } // 转换为数值类型 $num = is_string($num) ? floatval($num) : $num; $formatter = $this->getFormatter($locale); return $formatter->format($num, NumberFormatter::TYPE_DECIMAL); }
这样修改后:
- 遇到空字符串或非数值时直接返回原内容,避免触发类型错误;
- 把字符串类型的数值转换为float后再传给
format()方法,符合PHP8+的类型要求; - 同时保留了原有的本地化格式化功能。
另外,也可以检查商品的尺寸数据,确保可变商品的所有变体都正确填写了尺寸(报错栈显示是在处理wc_format_dimensions时触发,可能存在某个变体尺寸为空的情况),从数据源上避免空值传入。
内容的提问来源于stack exchange,提问作者libertarian

