BlockHooksTrait.php
4 weeks ago
BlockTemplateUtils.php
2 months ago
BlocksSharedState.php
3 months ago
BlocksWpQuery.php
2 years ago
CartCheckoutUtils.php
4 weeks ago
MiniCartUtils.php
1 year ago
ProductAvailabilityUtils.php
11 months ago
ProductDataUtils.php
1 year ago
ProductGalleryUtils.php
4 weeks ago
StyleAttributesUtils.php
1 year ago
Utils.php
2 years ago
CartCheckoutUtils.php
568 lines
| 1 | <?php // phpcs:ignore Generic.PHP.RequireStrictTypes.MissingDeclaration |
| 2 | namespace Automattic\WooCommerce\Blocks\Utils; |
| 3 | |
| 4 | use Automattic\Block_Scanner; |
| 5 | |
| 6 | /** |
| 7 | * Class containing utility methods for dealing with the Cart and Checkout blocks. |
| 8 | */ |
| 9 | class CartCheckoutUtils { |
| 10 | /** |
| 11 | * Caches if we're on the cart page. |
| 12 | * |
| 13 | * @var bool |
| 14 | */ |
| 15 | private static $is_cart_page = null; |
| 16 | |
| 17 | /** |
| 18 | * Caches if we're on the checkout page. |
| 19 | * |
| 20 | * @var bool |
| 21 | */ |
| 22 | private static $is_checkout_page = null; |
| 23 | |
| 24 | /** |
| 25 | * Returns true if the current page is a specific page type (cart or checkout). |
| 26 | * |
| 27 | * This is determined by looking at the global $post object and comparing it to the post ID defined in settings, |
| 28 | * or checking the page contents for a block or shortcode. |
| 29 | * |
| 30 | * This function cannot be used accurately before the `wp` action has been run. |
| 31 | * |
| 32 | * @param string $page_type The page type to check for. |
| 33 | * @return bool|null |
| 34 | */ |
| 35 | private static function is_page_type( string $page_type ): ?bool { |
| 36 | if ( ! did_action( 'wp' ) ) { |
| 37 | return null; |
| 38 | } |
| 39 | |
| 40 | $page_id = wc_get_page_id( $page_type ); |
| 41 | |
| 42 | if ( $page_id && is_page( $page_id ) ) { |
| 43 | return true; |
| 44 | } |
| 45 | |
| 46 | // If the is_page check returned false, check the page contents for a cart block or shortcode. |
| 47 | global $post; |
| 48 | |
| 49 | if ( null === $post ) { |
| 50 | return null; |
| 51 | } |
| 52 | |
| 53 | if ( $post instanceof \WP_Post ) { |
| 54 | return wc_post_content_has_shortcode( 'cart' === $page_type ? 'woocommerce_cart' : 'woocommerce_checkout' ) || self::has_block_variation( 'woocommerce/classic-shortcode', 'shortcode', $page_type, $post->post_content ); |
| 55 | } |
| 56 | |
| 57 | return false; |
| 58 | } |
| 59 | |
| 60 | /** |
| 61 | * Returns true on the cart page. |
| 62 | * |
| 63 | * @return bool |
| 64 | */ |
| 65 | public static function is_cart_page(): bool { |
| 66 | if ( null === self::$is_cart_page ) { |
| 67 | self::$is_cart_page = self::is_page_type( 'cart' ); |
| 68 | } |
| 69 | return true === self::$is_cart_page; |
| 70 | } |
| 71 | |
| 72 | /** |
| 73 | * Returns true on the checkout page. |
| 74 | * |
| 75 | * @return bool |
| 76 | */ |
| 77 | public static function is_checkout_page(): bool { |
| 78 | if ( null === self::$is_checkout_page ) { |
| 79 | self::$is_checkout_page = self::is_page_type( 'checkout' ); |
| 80 | } |
| 81 | return true === self::$is_checkout_page; |
| 82 | } |
| 83 | |
| 84 | /** |
| 85 | * Returns true if shipping methods exist in the store. Excludes local pickup and only counts enabled shipping methods. |
| 86 | * |
| 87 | * @return bool true if shipping methods exist. |
| 88 | */ |
| 89 | public static function shipping_methods_exist() { |
| 90 | // Local pickup is included with legacy shipping methods since they do not support shipping zones. |
| 91 | $local_pickup_count = count( |
| 92 | array_filter( |
| 93 | WC()->shipping()->get_shipping_methods(), |
| 94 | function ( $method ) { |
| 95 | return isset( $method->enabled ) && 'yes' === $method->enabled && ! $method->supports( 'shipping-zones' ) && $method->supports( 'local-pickup' ); |
| 96 | } |
| 97 | ) |
| 98 | ); |
| 99 | |
| 100 | $shipping_methods_count = wc_get_shipping_method_count( true, true ) - $local_pickup_count; |
| 101 | return $shipping_methods_count > 0; |
| 102 | } |
| 103 | |
| 104 | /** |
| 105 | * Check if the post content contains a block with a specific attribute value. |
| 106 | * |
| 107 | * @param string $block_id The block ID to check for. |
| 108 | * @param string $attribute The attribute to check. |
| 109 | * @param string $value The value to check for. |
| 110 | * @param string $post_content The post content to check. |
| 111 | * @return boolean |
| 112 | */ |
| 113 | public static function has_block_variation( $block_id, $attribute, $value, $post_content ) { |
| 114 | if ( ! $post_content ) { |
| 115 | return false; |
| 116 | } |
| 117 | |
| 118 | $scanner = Block_Scanner::create( $post_content ); |
| 119 | if ( ! $scanner ) { |
| 120 | return false; |
| 121 | } |
| 122 | |
| 123 | while ( $scanner->next_delimiter() ) { |
| 124 | if ( ! $scanner->opens_block( $block_id ) ) { |
| 125 | continue; |
| 126 | } |
| 127 | |
| 128 | $attrs = $scanner->allocate_and_return_parsed_attributes(); |
| 129 | |
| 130 | if ( isset( $attrs[ $attribute ] ) && $value === $attrs[ $attribute ] ) { |
| 131 | return true; |
| 132 | } |
| 133 | |
| 134 | // `Cart` is default for `woocommerce/classic-shortcode` so it will be empty in the block attributes. |
| 135 | if ( 'woocommerce/classic-shortcode' === $block_id && |
| 136 | 'shortcode' === $attribute && |
| 137 | 'cart' === $value && |
| 138 | ! isset( $attrs['shortcode'] ) ) { |
| 139 | return true; |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | return false; |
| 144 | } |
| 145 | |
| 146 | /** |
| 147 | * Checks if the default cart page is using the Cart block. |
| 148 | * |
| 149 | * @return bool true if the WC cart page is using the Cart block. |
| 150 | */ |
| 151 | public static function is_cart_block_default() { |
| 152 | if ( wp_is_block_theme() ) { |
| 153 | // Ignore the pages and check the templates. |
| 154 | $templates_from_db = BlockTemplateUtils::get_block_templates_from_db( array( 'cart' ), 'wp_template' ); |
| 155 | foreach ( $templates_from_db as $template ) { |
| 156 | if ( has_block( 'woocommerce/cart', $template->content ) ) { |
| 157 | return true; |
| 158 | } |
| 159 | } |
| 160 | } |
| 161 | $cart_page_id = wc_get_page_id( 'cart' ); |
| 162 | return $cart_page_id && has_block( 'woocommerce/cart', $cart_page_id ); |
| 163 | } |
| 164 | |
| 165 | /** |
| 166 | * Checks if the default checkout page is using the Checkout block. |
| 167 | * |
| 168 | * @return bool true if the WC checkout page is using the Checkout block. |
| 169 | */ |
| 170 | public static function is_checkout_block_default() { |
| 171 | if ( wp_is_block_theme() ) { |
| 172 | // Ignore the pages and check the templates. |
| 173 | $templates_from_db = BlockTemplateUtils::get_block_templates_from_db( array( 'checkout' ), 'wp_template' ); |
| 174 | foreach ( $templates_from_db as $template ) { |
| 175 | if ( has_block( 'woocommerce/checkout', $template->content ) ) { |
| 176 | return true; |
| 177 | } |
| 178 | } |
| 179 | } |
| 180 | $checkout_page_id = wc_get_page_id( 'checkout' ); |
| 181 | return $checkout_page_id && has_block( 'woocommerce/checkout', $checkout_page_id ); |
| 182 | } |
| 183 | |
| 184 | /** |
| 185 | * Migrate checkout block field visibility attributes to settings when using the checkout block. |
| 186 | * |
| 187 | * This migration routine is called if the options (woocommerce_checkout_phone_field, woocommerce_checkout_company_field, |
| 188 | * woocommerce_checkout_address_2_field) are not set. They are not set by default; they were originally set by the |
| 189 | * customizer interface of the legacy shortcode based checkout. |
| 190 | * |
| 191 | * Once migration is initiated, the settings will be updated and will not trigger this routine again. |
| 192 | * |
| 193 | * Note: The block only stores non-default attributes. Not all attributes will be present. |
| 194 | * |
| 195 | * e.g. `{"showCompanyField":true,"requireCompanyField":true,"showApartmentField":false,"className":"wc-block-checkout"}` |
| 196 | * |
| 197 | * If the attributes are missing, we assume default values are needed. |
| 198 | */ |
| 199 | protected static function migrate_checkout_block_field_visibility_attributes() { |
| 200 | // Before migrating attributes, migrate the "default" options checkout block uses into the settings. |
| 201 | update_option( 'woocommerce_checkout_phone_field', 'optional' ); |
| 202 | update_option( 'woocommerce_checkout_company_field', 'hidden' ); |
| 203 | update_option( 'woocommerce_checkout_address_2_field', 'optional' ); |
| 204 | |
| 205 | // Parse the block from the checkout page. |
| 206 | $checkout_blocks = \WC_Blocks_Utils::get_blocks_from_page( 'woocommerce/checkout', 'checkout' ); |
| 207 | |
| 208 | if ( empty( $checkout_blocks ) || ! isset( $checkout_blocks[0]['attrs'] ) ) { |
| 209 | return; |
| 210 | } |
| 211 | |
| 212 | // Combine actual attributes with default values. |
| 213 | $block_attributes = wp_parse_args( |
| 214 | $checkout_blocks[0]['attrs'], |
| 215 | array( |
| 216 | 'showPhoneField' => true, |
| 217 | 'requirePhoneField' => false, |
| 218 | 'showCompanyField' => false, |
| 219 | 'requireCompanyField' => false, |
| 220 | 'showApartmentField' => true, |
| 221 | 'requireApartmentField' => false, |
| 222 | ) |
| 223 | ); |
| 224 | |
| 225 | if ( $block_attributes['showPhoneField'] ) { |
| 226 | update_option( 'woocommerce_checkout_phone_field', $block_attributes['requirePhoneField'] ? 'required' : 'optional' ); |
| 227 | } else { |
| 228 | update_option( 'woocommerce_checkout_phone_field', 'hidden' ); |
| 229 | } |
| 230 | |
| 231 | if ( $block_attributes['showCompanyField'] ) { |
| 232 | update_option( 'woocommerce_checkout_company_field', $block_attributes['requireCompanyField'] ? 'required' : 'optional' ); |
| 233 | } else { |
| 234 | update_option( 'woocommerce_checkout_company_field', 'hidden' ); |
| 235 | } |
| 236 | |
| 237 | if ( $block_attributes['showApartmentField'] ) { |
| 238 | update_option( 'woocommerce_checkout_address_2_field', $block_attributes['requireApartmentField'] ? 'required' : 'optional' ); |
| 239 | } else { |
| 240 | update_option( 'woocommerce_checkout_address_2_field', 'hidden' ); |
| 241 | } |
| 242 | } |
| 243 | |
| 244 | /** |
| 245 | * Get the default visibility for the address_2 field. |
| 246 | * |
| 247 | * @return string |
| 248 | */ |
| 249 | public static function get_company_field_visibility() { |
| 250 | $option_value = get_option( 'woocommerce_checkout_company_field' ); |
| 251 | |
| 252 | if ( $option_value ) { |
| 253 | return $option_value; |
| 254 | } |
| 255 | |
| 256 | if ( self::is_checkout_block_default() ) { |
| 257 | self::migrate_checkout_block_field_visibility_attributes(); |
| 258 | return get_option( 'woocommerce_checkout_company_field', 'hidden' ); |
| 259 | } |
| 260 | |
| 261 | return 'optional'; |
| 262 | } |
| 263 | |
| 264 | /** |
| 265 | * Get the default visibility for the address_2 field. |
| 266 | * |
| 267 | * @return string |
| 268 | */ |
| 269 | public static function get_address_2_field_visibility() { |
| 270 | $option_value = get_option( 'woocommerce_checkout_address_2_field' ); |
| 271 | |
| 272 | if ( $option_value ) { |
| 273 | return $option_value; |
| 274 | } |
| 275 | |
| 276 | if ( self::is_checkout_block_default() ) { |
| 277 | self::migrate_checkout_block_field_visibility_attributes(); |
| 278 | return get_option( 'woocommerce_checkout_address_2_field', 'optional' ); |
| 279 | } |
| 280 | |
| 281 | return 'optional'; |
| 282 | } |
| 283 | |
| 284 | /** |
| 285 | * Get the default visibility for the address_2 field. |
| 286 | * |
| 287 | * @return string |
| 288 | */ |
| 289 | public static function get_phone_field_visibility() { |
| 290 | $option_value = get_option( 'woocommerce_checkout_phone_field' ); |
| 291 | |
| 292 | if ( $option_value ) { |
| 293 | return $option_value; |
| 294 | } |
| 295 | |
| 296 | if ( self::is_checkout_block_default() ) { |
| 297 | self::migrate_checkout_block_field_visibility_attributes(); |
| 298 | return get_option( 'woocommerce_checkout_phone_field', 'optional' ); |
| 299 | } |
| 300 | |
| 301 | return 'required'; |
| 302 | } |
| 303 | |
| 304 | /** |
| 305 | * Checks if the template overriding the page loads the page content or not. |
| 306 | * Templates by default load the page content, but if that block is deleted the content can get out of sync with the one presented in the page editor. |
| 307 | * |
| 308 | * @since 10.9.0 |
| 309 | * |
| 310 | * @param string $block The block to check. |
| 311 | * |
| 312 | * @return bool true if the template has out of sync content. |
| 313 | */ |
| 314 | public static function is_overridden_by_custom_template_content( string $block ): bool { |
| 315 | |
| 316 | $block = str_replace( 'woocommerce/', '', $block ); |
| 317 | |
| 318 | if ( wp_is_block_theme() ) { |
| 319 | $templates_from_db = BlockTemplateUtils::get_block_templates_from_db( array( 'page-' . $block ) ); |
| 320 | foreach ( $templates_from_db as $template ) { |
| 321 | if ( ! has_block( 'woocommerce/page-content-wrapper', $template->content ) ) { |
| 322 | // Return true if the template does not load the page content via the woocommerce/page-content-wrapper block. |
| 323 | return true; |
| 324 | } |
| 325 | } |
| 326 | } |
| 327 | |
| 328 | return false; |
| 329 | } |
| 330 | |
| 331 | /** |
| 332 | * Checks if the template overriding the page loads the page content or not. |
| 333 | * Templates by default load the page content, but if that block is deleted the content can get out of sync with the one presented in the page editor. |
| 334 | * |
| 335 | * @deprecated 10.9.0 Use is_overridden_by_custom_template_content() instead. |
| 336 | * |
| 337 | * @param string $block The block to check. |
| 338 | * |
| 339 | * @return bool true if the template has out of sync content. |
| 340 | */ |
| 341 | public static function is_overriden_by_custom_template_content( string $block ): bool { |
| 342 | wc_deprecated_function( __METHOD__, '10.9.0', 'is_overridden_by_custom_template_content' ); |
| 343 | return self::is_overridden_by_custom_template_content( $block ); |
| 344 | } |
| 345 | |
| 346 | /** |
| 347 | * Gets country codes, names, states, and locale information. |
| 348 | * |
| 349 | * @return array |
| 350 | */ |
| 351 | public static function get_country_data() { |
| 352 | $billing_countries = WC()->countries->get_allowed_countries(); |
| 353 | $shipping_countries = WC()->countries->get_shipping_countries(); |
| 354 | $country_states = wc()->countries->get_states(); |
| 355 | $all_countries = self::deep_sort_with_accents( array_unique( array_merge( $billing_countries, $shipping_countries ) ) ); |
| 356 | $country_locales = array_map( |
| 357 | function ( $locale ) { |
| 358 | foreach ( $locale as $field => $field_data ) { |
| 359 | if ( isset( $field_data['priority'] ) ) { |
| 360 | $locale[ $field ]['index'] = $field_data['priority']; |
| 361 | unset( $locale[ $field ]['priority'] ); |
| 362 | } |
| 363 | if ( isset( $field_data['class'] ) ) { |
| 364 | unset( $locale[ $field ]['class'] ); |
| 365 | } |
| 366 | } |
| 367 | return $locale; |
| 368 | }, |
| 369 | WC()->countries->get_country_locale() |
| 370 | ); |
| 371 | |
| 372 | $country_data = array(); |
| 373 | |
| 374 | foreach ( array_keys( $all_countries ) as $country_code ) { |
| 375 | $country_data[ $country_code ] = array( |
| 376 | 'allowBilling' => isset( $billing_countries[ $country_code ] ), |
| 377 | 'allowShipping' => isset( $shipping_countries[ $country_code ] ), |
| 378 | 'states' => $country_states[ $country_code ] ?? array(), |
| 379 | 'locale' => $country_locales[ $country_code ] ?? array(), |
| 380 | ); |
| 381 | } |
| 382 | |
| 383 | return $country_data; |
| 384 | } |
| 385 | |
| 386 | /** |
| 387 | * Removes accents from an array of values, sorts by the values, then returns the original array values sorted. |
| 388 | * |
| 389 | * @param array $sort_array Array of values to sort. |
| 390 | * @return array Sorted array. |
| 391 | */ |
| 392 | protected static function deep_sort_with_accents( $sort_array ) { |
| 393 | if ( ! is_array( $sort_array ) || empty( $sort_array ) ) { |
| 394 | return $sort_array; |
| 395 | } |
| 396 | |
| 397 | $array_without_accents = array_map( |
| 398 | function ( $value ) { |
| 399 | return is_array( $value ) |
| 400 | ? self::deep_sort_with_accents( $value ) |
| 401 | : remove_accents( wc_strtolower( html_entity_decode( $value ) ) ); |
| 402 | }, |
| 403 | $sort_array |
| 404 | ); |
| 405 | |
| 406 | asort( $array_without_accents ); |
| 407 | return array_replace( $array_without_accents, $sort_array ); |
| 408 | } |
| 409 | |
| 410 | /** |
| 411 | * Retrieves formatted shipping zones from WooCommerce. |
| 412 | * |
| 413 | * @return array An array of formatted shipping zones. |
| 414 | */ |
| 415 | public static function get_shipping_zones() { |
| 416 | $shipping_zones = \WC_Shipping_Zones::get_zones(); |
| 417 | $formatted_shipping_zones = array_reduce( |
| 418 | $shipping_zones, |
| 419 | function ( $acc, $zone ) { |
| 420 | $acc[] = array( |
| 421 | 'id' => $zone['id'], |
| 422 | 'title' => $zone['zone_name'], |
| 423 | 'description' => $zone['formatted_zone_location'], |
| 424 | ); |
| 425 | return $acc; |
| 426 | }, |
| 427 | array() |
| 428 | ); |
| 429 | $formatted_shipping_zones[] = array( |
| 430 | 'id' => 0, |
| 431 | 'title' => __( 'International', 'woocommerce' ), |
| 432 | 'description' => __( 'Locations outside all other zones', 'woocommerce' ), |
| 433 | ); |
| 434 | return $formatted_shipping_zones; |
| 435 | } |
| 436 | |
| 437 | /** |
| 438 | * Recursively search the checkout block to find the express checkout block and |
| 439 | * get the button style attributes using the parse_blocks function. |
| 440 | * |
| 441 | * @param array $blocks Blocks to search. |
| 442 | * @param string $cart_or_checkout The block type to check. |
| 443 | * |
| 444 | * @return array Block attributes. |
| 445 | */ |
| 446 | public static function find_express_checkout_attributes_in_parsed_blocks( $blocks, $cart_or_checkout ) { |
| 447 | $express_block_name = 'woocommerce/' . $cart_or_checkout . '-express-payment-block'; |
| 448 | foreach ( $blocks as $block ) { |
| 449 | if ( ! empty( $block['blockName'] ) && $express_block_name === $block['blockName'] && ! empty( $block['attrs'] ) ) { |
| 450 | return $block['attrs']; |
| 451 | } |
| 452 | |
| 453 | if ( ! empty( $block['innerBlocks'] ) ) { |
| 454 | $answer = self::find_express_checkout_attributes_in_parsed_blocks( $block['innerBlocks'], $cart_or_checkout ); |
| 455 | if ( $answer ) { |
| 456 | return $answer; |
| 457 | } |
| 458 | } |
| 459 | } |
| 460 | } |
| 461 | |
| 462 | /** |
| 463 | * Recursively search the checkout block to find the express checkout block and |
| 464 | * get the button style attributes |
| 465 | * |
| 466 | * @param string|array $post_content The post content. |
| 467 | * @param string $cart_or_checkout The block type to check. |
| 468 | * |
| 469 | * @return array|null Block attributes, if present and valid, otherwise `null`. |
| 470 | */ |
| 471 | public static function find_express_checkout_attributes( $post_content, $cart_or_checkout ) { |
| 472 | if ( is_array( $post_content ) ) { |
| 473 | // If an array is passed, assume it's already been parsed with parse_blocks, |
| 474 | // use the old method, and show a deprecation warning. |
| 475 | wc_deprecated_argument( |
| 476 | 'post_content', |
| 477 | '10.3.0', |
| 478 | 'Passing parsed blocks as an array in $post_content is deprecated. Please pass the post content as a string.' |
| 479 | ); |
| 480 | return self::find_express_checkout_attributes_in_parsed_blocks( $post_content, $cart_or_checkout ); |
| 481 | } |
| 482 | |
| 483 | $express_block_name = 'woocommerce/' . $cart_or_checkout . '-express-payment-block'; |
| 484 | |
| 485 | $scanner = Block_Scanner::create( $post_content ); |
| 486 | |
| 487 | while ( $scanner->next_delimiter() ) { |
| 488 | if ( $scanner->opens_block( $express_block_name ) ) { |
| 489 | return $scanner->allocate_and_return_parsed_attributes(); |
| 490 | } |
| 491 | } |
| 492 | |
| 493 | return null; |
| 494 | } |
| 495 | |
| 496 | /** |
| 497 | * Given an array of blocks, find the express payment block and update its attributes. |
| 498 | * |
| 499 | * @param array $blocks Blocks to search. |
| 500 | * @param string $cart_or_checkout The block type to check. |
| 501 | * @param array $updated_attrs The new attributes to set. |
| 502 | */ |
| 503 | public static function update_blocks_with_new_attrs( &$blocks, $cart_or_checkout, $updated_attrs ) { |
| 504 | $express_block_name = 'woocommerce/' . $cart_or_checkout . '-express-payment-block'; |
| 505 | foreach ( $blocks as $key => &$block ) { |
| 506 | if ( ! empty( $block['blockName'] ) && $express_block_name === $block['blockName'] ) { |
| 507 | $blocks[ $key ]['attrs'] = $updated_attrs; |
| 508 | } |
| 509 | |
| 510 | if ( ! empty( $block['innerBlocks'] ) ) { |
| 511 | self::update_blocks_with_new_attrs( $block['innerBlocks'], $cart_or_checkout, $updated_attrs ); |
| 512 | } |
| 513 | } |
| 514 | } |
| 515 | |
| 516 | /** |
| 517 | * Check if the cart page is defined. |
| 518 | * |
| 519 | * @return bool True if the cart page is defined, false otherwise. |
| 520 | */ |
| 521 | public static function has_cart_page() { |
| 522 | return wc_get_page_permalink( 'cart', -1 ) !== -1; |
| 523 | } |
| 524 | |
| 525 | /** |
| 526 | * Get product IDs from a user's persistent cart. |
| 527 | * |
| 528 | * This method retrieves product IDs stored in the user's persistent cart meta. |
| 529 | * It can be used for abandoned cart emails, cart-based product collections, |
| 530 | * and other scenarios where cart products need to be retrieved for a user. |
| 531 | * |
| 532 | * @param int|null $user_id The user ID. If not provided, will attempt to look up by email. |
| 533 | * @param string|null $user_email The user email. Used to lookup user if ID not provided. |
| 534 | * @return array<int> Array of product IDs from the user's cart, or empty array if none found. |
| 535 | */ |
| 536 | public static function get_cart_product_ids_for_user( ?int $user_id, ?string $user_email ) { |
| 537 | if ( empty( $user_id ) && ! empty( $user_email ) ) { |
| 538 | $user = get_user_by( 'email', $user_email ); |
| 539 | if ( $user ) { |
| 540 | $user_id = $user->ID; |
| 541 | } |
| 542 | } |
| 543 | |
| 544 | if ( empty( $user_id ) ) { |
| 545 | return array(); |
| 546 | } |
| 547 | |
| 548 | $cart_meta = get_user_meta( $user_id, '_woocommerce_persistent_cart_' . get_current_blog_id(), true ); |
| 549 | |
| 550 | if ( empty( $cart_meta ) || ! is_array( $cart_meta ) || empty( $cart_meta['cart'] ) ) { |
| 551 | return array(); |
| 552 | } |
| 553 | |
| 554 | return array_values( |
| 555 | array_unique( |
| 556 | array_filter( |
| 557 | array_map( |
| 558 | function ( $cart_item ) { |
| 559 | return isset( $cart_item['product_id'] ) ? intval( $cart_item['product_id'] ) : 0; |
| 560 | }, |
| 561 | $cart_meta['cart'] |
| 562 | ) |
| 563 | ) |
| 564 | ) |
| 565 | ); |
| 566 | } |
| 567 | } |
| 568 |