Automattic / woocommerce-subscriptions-core

Subscriptions core package for WooCommerce
Other
80 stars 29 forks source link

PHP Fatal error in cron when $subscription_ids is an empty array. #568

Closed redundans closed 2 months ago

redundans commented 4 months ago

I ran into an issue when updating an old subscription where the $subscription_ids variable turned out to contain an empty array. Which triggers a PHP Fatal error that prevents wp cron from finishing the rest of the queue of subscriptions.

PHP Fatal error: Uncaught TypeError: rsort(): Argument #1 ($array) must be of type array, bool given in /mnt/persist/www/arbetaren.se/release/web/app/plugins/woocommerce-subscriptions/vendor/woocommerce/subscriptions-core/includes/data-stores/class-wcs-customer-store-cached-cpt.php:117

Perhaps error handling with empty would be in order here to ensure wp cron doesn't abort?

if ( ! empty( $subscription_ids ) ) {
    rsort( $subscription_ids );
}

https://github.com/Automattic/woocommerce-subscriptions-core/blob/19d4f2acf246c7f8275438c9356eb79a141bdf4d/includes/data-stores/class-wcs-customer-store-cached-cpt.php#L117

mmca101 commented 4 months ago

Im getting a similar error on'

PHP Fatal error: Uncaught TypeError: array_sum(): Argument #1 ($array) must be of type array, null given in /wp-content/plugins/woocommerce-subscriptions/vendor/woocommerce/subscriptions-core/includes/class-wcs-cart-renewal.php:449

Adding an extra check in the "get_cart_item_from_session" function (@class-wcs-cart-renewal.php) to see if the line item ID exists in the subscription items array seems to fix this too. Checks added are commented with HOTFIX:


    /**
     * Restore renewal flag when cart is reset and modify Product object with renewal order related info
     *
     * @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
     */

     public function get_cart_item_from_session( $cart_item_session_data, $cart_item, $key ) {

        if ( $this->should_honor_subscription_prices( $cart_item ) ) {
            $cart_item_session_data[ $this->cart_item_key ] = $cart_item[ $this->cart_item_key ];

            $_product = $cart_item_session_data['data'];

            // Need to get the original subscription or order price, not the current price
            $subscription = $this->get_order( $cart_item );

            if ( $subscription ) {
                $subscription_items = $subscription->get_items();

                // HOTFIX - Check if the line item ID exists in the subscription items array to prevent a fatal error from wp core
                if ( isset( $cart_item_session_data[ $this->cart_item_key ]['line_item_id'] ) && isset( $subscription_items[ $cart_item_session_data[ $this->cart_item_key ]['line_item_id'] ] ) ) {
                    $item_to_renew = $subscription_items[ $cart_item_session_data[ $this->cart_item_key ]['line_item_id'] ];

                    $price = $item_to_renew['line_subtotal'];

                    if ( $_product->is_taxable() && $subscription->get_prices_include_tax() ) {

                        // If this item's subtracted tax data hasn't been repaired, do that now.
                        if ( isset( $item_to_renew['_subtracted_base_location_tax'] ) ) {
                            WC_Subscriptions_Upgrader::repair_subtracted_base_taxes( $item_to_renew->get_id() );

                            // The item has been updated so get a refreshed version of the item.
                            $item_to_renew = WC_Order_Factory::get_order_item( $item_to_renew->get_id() );
                        }

                        if ( isset( $item_to_renew['_subtracted_base_location_taxes'] ) ) {
                            $price += array_sum( $item_to_renew['_subtracted_base_location_taxes'] ) * $item_to_renew['qty'];
                        } else {
                            // HOTFIX - Check if the taxes array and subtotal key exist before accessing them to prevent fatal errors from wp core
                            if ( isset( $item_to_renew['taxes']['subtotal'] ) ) {
                                $price += array_sum( $item_to_renew['taxes']['subtotal'] ); // Use the taxes array items here as they contain taxes to a more accurate number of decimals.
                            }
                        }
                    }

                    // In rare cases quantity can be zero. Check first to prevent triggering a fatal error in php8+
                    if ( isset( $item_to_renew['qty'] ) && 0 !== (int) $item_to_renew['qty'] ) {
                        $_product->set_price( $price / $item_to_renew['qty'] );
                    }

                    // Don't carry over any sign up fee
                    wcs_set_objects_property( $_product, 'subscription_sign_up_fee', 0, 'set_prop_only' );

                    // Allow plugins to add additional strings to the product name for renewals
                    $line_item_name = is_callable( $item_to_renew, 'get_name' ) ? $item_to_renew->get_name() : $item_to_renew['name'];
                    wcs_set_objects_property( $_product, 'name', apply_filters( 'woocommerce_subscriptions_renewal_product_title', $line_item_name, $_product ), 'set_prop_only' );

                    // Make sure the same quantity is renewed
                    $cart_item_session_data['quantity'] = $item_to_renew['qty'];
                }
            }
        }

        return $cart_item_session_data;
    }