Title: TypeError "string + int" in AtumCalculatedProps line 486 with a persistent object cache (PHP 8)
Reporting a reproducible fatal in ATUM 2.0.3.2 on PHP 8.3. It hits any store running a persistent object cache (Redis/Memcached) that has variable products with variations that had zero sales in the lookback window.
The error:
Uncaught TypeError: Unsupported operand types: string + int in classes/Components/AtumCalculatedProps.php:486
Call path: maybe_update_variable_calc_prop() <- update_atum_sales_calc_props() (line 179) <- the deferred hook <- AtumQueues::handle_async_hooks(). Because it runs on the async queue, it retries the same data and re-fatals repeatedly (hundreds of times in our logs).
Root cause, as far as I can trace it:
At line 486, maybe_update_variable_calc_prop() sums each variation's calc prop into the parent:
$variable_value += call_user_func( array( $variation_product, "get_$prop" ) );
The value comes from Helpers::get_sold_last_days(). For a variation with no sales in the window, the underlying get_var() returns NULL, and that NULL gets cached. On the next request a persistent object cache returns the cached NULL as an empty string ('') instead of NULL. The getter then returns '', and '' + int is a hard TypeError on PHP 8.
This seems to only happen with a persistent object cache active. WordPress's default in-memory cache keeps NULL within the request, so the round-trip corruption never occurs there — which may be why it isn't universal. Our database rows are clean (no NULL/empty values on disk); the bad value only appears after the cache round-trip.
Suggested fix: cast to a number before the addition at line 486, e.g.
$variable_value += (float) call_user_func( array( $variation_product, "get_$prop" ) );
and initialise the accumulator with $variable_value = (float) $value;. Normalising get_sold_last_days() to return 0 instead of NULL before caching would also cover the other call sites.
Workaround for anyone hitting this before a patch: mark ATUM's cache group non-persistent so its entries stop round-tripping through the object cache. In a small mu-plugin or snippet:
add_action( 'init', function () {
if ( function_exists( 'wp_cache_add_non_persistent_groups' ) ) {
wp_cache_add_non_persistent_groups( array( 'atum-stock-manager-for-woocommerce' ) );
}
} );
That stopped the fatal for us immediately, with ATUM's in-request caching still intact. The numeric cast at line 486 is the real fix though.
Happy to provide more detail. Thanks for the plugin.