Infinite template recursion in form-billing.php / form-shipping.php
-
ENVIRONMENT
- WordPress: 7.1.1
- WooCommerce: 11.1.1
- Fluid Checkout (free): 4.2.7
- PHP: 8.3 and 8.2 (reproduced on both)
- Server: LiteSpeed (Hostinger shared)
- Theme: Hello Theme by Elementor
SYMPTOM
Since updating to WooCommerce 11, the checkout page shows an endless spinner. The AJAX request?wc-ajax=update_order_reviewnever finishes: the PHP process is killed and LiteSpeed returns HTTP 503.get_refreshed_fragmentsand every other wc-ajax endpoint work normally (~0.3s).ROOT CAUSE
Your templatesfc/checkout-steps/checkout/form-billing.phpandform-shipping.phpfire the core actions for compatibility:do_action( 'woocommerce_checkout_billing', $checkout ); // form-billing.php do_action( 'woocommerce_checkout_shipping', $checkout ); // form-shipping.php, line 78WooCommerce core still has
WC_Checkout::checkout_form_billing/checkout_form_shippinghooked on those actions. Fluid Checkout is supposed to remove them, but with WooCommerce 11 theremove_action()no longer matches the registered callback, so the core method stays attached. The template fires the action → core method callswc_get_template('checkout/form-billing.php')→ Fluid Checkout’s template override is loaded again → it fires the action again → infinite loop until the process is killed.I suspect WooCommerce 11 changed how those callbacks are registered (e.g. first-class callable / Closure instead of
array( $this, 'method' )), which is why the oldremove_action( $hook, array( WC()->checkout(), 'checkout_form_billing' ) )silently fails.EVIDENCE
1) PHP 8.3 fatal (zend.max_allowed_stack_size):
PHP Fatal error: Maximum call stack size of 8339456 bytes reached during compilation …/fluid-checkout/templates/fc/checkout-steps/checkout/form-shipping.php on line 642) Backtrace logged from inside the template (each 6-frame block repeats until the process dies):
04 wc_get_template
05 WC_Checkout->checkout_form_billing
06 WP_Hook->apply_filters
07 WP_Hook->do_action
08 do_action
09 include
10 wc_get_template
11 WC_Checkout->checkout_form_billing
12 WP_Hook->apply_filters
…
Identical pattern forcheckout_form_shipping/ form-shipping.php.3) Template include counter:
checkout/form-billing.phpincluded 38,050 times in a single update_order_review request before the process was killed.4) Every nested include logs the same warnings because core passes only
$checkout, not your variables:
PHP Warning: Undefined variable $is_billing_same_as_shipping in …/form-billing.php on line 23
PHP Warning: Undefined variable $billing_same_as_shipping_fields in …/form-billing.php on line 37
PHP Warning: foreach() argument must be of type array|object, null given in …/form-billing.php on line 37
PHP Warning: Undefined variable $billing_only_fields in …/form-billing.php on line 49HOW TO REPRODUCE
- Clean WordPress install, WooCommerce 11.1, Fluid Checkout (latest), PHP 8.2 or 8.3.
- Add any product to the cart and open the checkout page.
- Open DevTools → Network. Observe
?wc-ajax=update_order_review: it never returns (503 on LiteSpeed, 500/timeout elsewhere). On PHP 8.3 the “Maximum call stack size” fatal above appears in debug.log. - To confirm the loop, drop this in wp-content/mu-plugins/ and reload the checkout — it logs the include depth of the template:
https://pastebin.com/RcfdiRE7 - Also verify the core callback is still attached after Fluid Checkout has run (it should not be):
https://pastebin.com/6nH7vtZP
On my install the core callback is still present on both hooks when the checkout renders.
WORKAROUND I AM USING
A mu-plugin that blocks re-entry of the two templates via thewc_get_templatefilter (returns an empty file when the same template is requested while already rendering). With this in place update_order_review completes in ~0.6s and the checkout works. It is a band-aid; the proper fix is to make the core callback removal work with WooCommerce 11 (match by method name across array callbacks and Closures, or remove thedo_actionfrom the templates and re-add only third-party callbacks).
You must be logged in to reply to this topic.