$arguments UCP tool arguments. * @return array */ public static function search_catalog( array $arguments ): array { if ( ! function_exists( 'wc_get_products' ) ) { return self::catalog_response( array() ); } $catalog = isset( $arguments['catalog'] ) && is_array( $arguments['catalog'] ) ? $arguments['catalog'] : $arguments; $query = isset( $catalog['query'] ) ? sanitize_text_field( (string) $catalog['query'] ) : ''; $pagination = isset( $catalog['pagination'] ) && is_array( $catalog['pagination'] ) ? $catalog['pagination'] : array(); $limit = isset( $pagination['limit'] ) ? max( 1, min( 50, (int) $pagination['limit'] ) ) : 20; if ( self::is_full_catalog_query( $query ) ) { $query = ''; } if ( $query !== '' ) { $found = self::query_published_products_by_search( $query, $limit ); $products = $found['products']; $total = $found['total']; } else { $found = self::query_all_published_products( $limit ); $products = $found['products']; $total = $found['total']; } $payload = array(); foreach ( $products as $product ) { if ( ! $product instanceof WC_Product ) { continue; } $formatted = self::format_product( $product ); if ( ! $formatted && $query !== '' ) { $formatted = self::format_unavailable_product_stub( $product ); } if ( ! $formatted ) { continue; } // Browse/full-catalog responses list only products visitors can buy now. if ( $query === '' && ! self::is_catalog_available( $product ) ) { continue; } $payload[] = $formatted; } return self::catalog_response( $payload, $total, $limit ); } /** * Paginated compact catalog for Agentmatica inventory-map generation. * * @param array $arguments Tool arguments. * @return array */ public static function export_catalog_snapshot( array $arguments ): array { $page = isset( $arguments['page'] ) ? max( 1, (int) $arguments['page'] ) : 1; $page_size = isset( $arguments['page_size'] ) ? max( 1, min( 200, (int) $arguments['page_size'] ) ) : 100; if ( ! function_exists( 'wc_get_product' ) ) { return array( 'products' => array(), 'pagination' => array( 'page' => $page, 'page_size' => $page_size, 'total_count' => 0, 'has_next_page' => false, ), ); } $query = new WP_Query( array( 'post_type' => 'product', 'post_status' => 'publish', 'posts_per_page' => $page_size, 'paged' => $page, 'orderby' => 'ID', 'order' => 'ASC', 'fields' => 'ids', ) ); $products = array(); foreach ( $query->posts as $product_id ) { $product = wc_get_product( (int) $product_id ); if ( ! $product instanceof WC_Product ) { continue; } $formatted = self::format_snapshot_product( $product ); if ( $formatted ) { $products[] = $formatted; } } return array( 'products' => $products, 'pagination' => array( 'page' => $page, 'page_size' => $page_size, 'total_count' => (int) $query->found_posts, 'has_next_page' => $page < (int) $query->max_num_pages, ), ); } /** * @param array $arguments UCP tool arguments. * @return array */ public static function lookup_catalog( array $arguments ): array { $catalog = isset( $arguments['catalog'] ) && is_array( $arguments['catalog'] ) ? $arguments['catalog'] : $arguments; $ids = isset( $catalog['ids'] ) && is_array( $catalog['ids'] ) ? $catalog['ids'] : array(); $products = array(); foreach ( $ids as $raw_id ) { $product = self::load_product_by_identifier( (string) $raw_id ); if ( $product ) { $formatted = self::format_product( $product ); if ( $formatted ) { $products[] = $formatted; } } } return self::catalog_response( $products ); } /** * @param array $arguments UCP tool arguments. * @return array */ public static function get_product( array $arguments ): array { $catalog = isset( $arguments['catalog'] ) && is_array( $arguments['catalog'] ) ? $arguments['catalog'] : $arguments; $raw_id = isset( $catalog['id'] ) ? (string) $catalog['id'] : ''; if ( $raw_id === '' && isset( $arguments['id'] ) ) { $raw_id = (string) $arguments['id']; } $product = self::load_product_by_identifier( $raw_id ); if ( ! $product ) { $name = ''; if ( isset( $catalog['name'] ) ) { $name = sanitize_text_field( (string) $catalog['name'] ); } elseif ( isset( $arguments['name'] ) ) { $name = sanitize_text_field( (string) $arguments['name'] ); } elseif ( isset( $catalog['query'] ) ) { $name = sanitize_text_field( (string) $catalog['query'] ); } if ( $name !== '' ) { return self::search_catalog( array( 'query' => $name, 'pagination' => array( 'limit' => 5 ), ) ); } return self::catalog_response( array() ); } $formatted = self::format_product( $product ); if ( ! $formatted ) { return self::catalog_response( array() ); } return self::catalog_response( array( $formatted ) ); } private static function load_product_by_identifier( string $raw_id ): ?WC_Product { $raw_id = trim( $raw_id ); if ( $raw_id === '' ) { return null; } if ( preg_match( '/gid:\/\/woocommerce\/ProductVariation\/(\d+)/', $raw_id, $matches ) ) { $variation = wc_get_product( (int) $matches[1] ); if ( $variation instanceof WC_Product_Variation ) { $parent = wc_get_product( $variation->get_parent_id() ); return $parent instanceof WC_Product ? $parent : null; } return null; } if ( preg_match( '/gid:\/\/woocommerce\/Product\/(\d+)/', $raw_id, $matches ) ) { $product = wc_get_product( (int) $matches[1] ); return $product instanceof WC_Product ? $product : null; } if ( preg_match( '/^\d+$/', $raw_id ) ) { $product = wc_get_product( (int) $raw_id ); return $product instanceof WC_Product ? $product : null; } return null; } /** * @param list> $products * @return array */ /** * @param list> $products */ private static function catalog_response( array $products, ?int $total_count = null, ?int $limit = null, ): array { $page_count = count( $products ); $total_count = $total_count ?? $page_count; $limit = $limit ?? $page_count; return array( 'ucp' => array( 'version' => Agentmatica_Ucp_Profile::UCP_VERSION, 'capabilities' => array( 'dev.ucp.shopping.catalog.search' => array( array( 'version' => Agentmatica_Ucp_Profile::UCP_VERSION ), ), ), ), 'products' => $products, 'pagination' => array( 'has_next_page' => $total_count > $page_count, 'total_count' => $total_count, 'limit' => $limit, 'returned_count' => $page_count, ), ); } private static function is_full_catalog_query( string $query ): bool { $normalized = strtolower( trim( $query ) ); if ( $normalized === '' ) { return true; } if ( preg_match( '/\b(all|every|full|entire|whole)\s+(products?|items?|catalog|inventory|stock)\b/', $normalized ) ) { return true; } if ( preg_match( '/\b(how many|number of|count of|total)\s+(products?|items?)\b/', $normalized ) ) { return true; } return false; } /** * Published product count from post counts (no product hydration). */ private static function count_published_products(): int { $counts = wp_count_posts( 'product' ); if ( ! is_object( $counts ) || ! isset( $counts->publish ) ) { return 0; } return (int) $counts->publish; } /** * Products the visitor can browse and buy right now (in stock + purchasable). */ private static function is_catalog_available( WC_Product $product ): bool { if ( $product->get_status() !== 'publish' ) { return false; } if ( ! $product->is_purchasable() ) { return false; } if ( $product->is_type( 'variable' ) ) { foreach ( $product->get_children() as $child_id ) { $variation = wc_get_product( (int) $child_id ); if ( $variation instanceof WC_Product_Variation && $variation->is_in_stock() && $variation->is_purchasable() ) { return true; } } return false; } return $product->is_in_stock(); } /** * Load published products for catalog browse (available to buy only). * * @return array{products: list, total: int} */ private static function query_all_published_products( int $limit ): array { $query = new WP_Query( array( 'post_type' => 'product', 'post_status' => 'publish', 'posts_per_page' => $limit, 'paged' => 1, 'orderby' => 'date', 'order' => 'DESC', 'fields' => 'ids', 'no_found_rows' => false, 'ignore_sticky_posts' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, 'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- instock browse only, paged. array( 'key' => '_stock_status', 'value' => 'instock', ), ), ) ); $products = array(); foreach ( $query->posts as $product_id ) { $product = wc_get_product( (int) $product_id ); if ( ! $product instanceof WC_Product ) { continue; } if ( ! self::is_catalog_available( $product ) ) { continue; } $products[] = $product; } $total = (int) $query->found_posts; if ( $total < 1 ) { $total = self::count_published_products(); } return array( 'products' => $products, 'total' => $total, ); } /** * Named search — SQL/Woo search plus a capped PHP re-rank. Never hydrates the full catalogue. * * @return array{products: list, total: int} */ private static function query_published_products_by_search( string $search, int $limit ): array { $normalized = strtolower( trim( preg_replace( '/\s+/', ' ', $search ) ) ); if ( $normalized === '' ) { return array( 'products' => array(), 'total' => 0, ); } $candidate_limit = min( 100, max( $limit * 5, 40 ) ); $found = self::search_published_product_ids( $normalized, $candidate_limit ); $ids = $found['ids']; $total = $found['total']; $scored = array(); foreach ( $ids as $product_id ) { $product = wc_get_product( $product_id ); if ( ! $product instanceof WC_Product ) { continue; } $score = Agentmatica_Catalog_Search::product_match_score( $product, $search ); if ( $score <= 0 ) { $score = 10; } $scored[] = array( 'score' => $score, 'id' => $product_id, 'product' => $product, ); } usort( $scored, static function ( array $left, array $right ): int { $cmp = $right['score'] <=> $left['score']; if ( 0 !== $cmp ) { return $cmp; } return $left['id'] <=> $right['id']; } ); $products = array(); foreach ( $scored as $entry ) { $products[] = $entry['product']; if ( count( $products ) >= $limit ) { break; } } if ( $total < count( $products ) ) { $total = count( $products ); } return array( 'products' => $products, 'total' => $total, ); } /** * @return array{ids: list, total: int} */ private static function search_published_product_ids( string $search, int $limit ): array { $ids = array(); $total = 0; $sku_id = function_exists( 'wc_get_product_id_by_sku' ) ? wc_get_product_id_by_sku( $search ) : 0; if ( $sku_id ) { $sku_product = wc_get_product( $sku_id ); if ( $sku_product instanceof WC_Product_Variation ) { $sku_id = (int) $sku_product->get_parent_id(); } // wc_get_product_id_by_sku() matches drafts and private products too. if ( $sku_id > 0 && get_post_status( $sku_id ) === 'publish' ) { $ids[] = $sku_id; } } $query = new WP_Query( array( 'post_type' => 'product', 'post_status' => 'publish', 'posts_per_page' => $limit, 's' => $search, 'fields' => 'ids', 'no_found_rows' => false, 'ignore_sticky_posts' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, ) ); foreach ( $query->posts as $product_id ) { $ids[] = (int) $product_id; } $total = max( $total, (int) $query->found_posts, count( $ids ) ); $title_found = self::search_product_ids_by_title_tokens( $search, $limit ); $ids = array_merge( $title_found['ids'], $ids ); $total = max( $total, $title_found['total'] ); $ids = array_values( array_unique( array_filter( array_map( 'intval', $ids ) ) ) ); return array( 'ids' => $ids, 'total' => $total, ); } /** * Title AND-match for all query tokens (older fixtures WP `s=` ranking can drop). * * @return array{ids: list, total: int} */ private static function search_product_ids_by_title_tokens( string $search, int $limit ): array { global $wpdb; $words = preg_split( '/\s+/', $search ); $tokens = array(); foreach ( false === $words ? array() : $words as $word ) { $word = trim( (string) $word ); if ( strlen( $word ) < 3 ) { continue; } $tokens[] = $word; } $tokens = array_values( array_unique( $tokens ) ); if ( $tokens === array() ) { return array( 'ids' => array(), 'total' => 0, ); } $posts = $wpdb->posts; $conditions = array( "post_type = 'product'", "post_status = 'publish'" ); $values = array(); foreach ( $tokens as $token ) { $variants = Agentmatica_Catalog_Search::expand_word_variants( $token ); if ( $variants === array() ) { $variants = array( $token ); } $like_parts = array(); foreach ( $variants as $variant ) { $like_parts[] = 'post_title LIKE %s'; $values[] = '%' . $wpdb->esc_like( $variant ) . '%'; } $conditions[] = '(' . implode( ' OR ', $like_parts ) . ')'; } $where = implode( ' AND ', $conditions ); $values[] = $limit; // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- `$posts` is `$wpdb->posts`; `$where` is LIKE %s placeholders. $ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM {$posts} WHERE {$where} ORDER BY ID ASC LIMIT %d", ...$values ) ); $count_values = array_slice( $values, 0, -1 ); $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM {$posts} WHERE {$where}", ...$count_values ) ); // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare return array( 'ids' => array_map( 'intval', is_array( $ids ) ? $ids : array() ), 'total' => (int) $count, ); } /** * @return array|null */ public static function format_product( WC_Product $product ): ?array { $currency = get_woocommerce_currency(); $variants = self::build_variants( $product, $currency ); if ( empty( $variants ) ) { return null; } $price_range = self::price_range_from_variants( $variants, $currency ); $media = self::build_media( $product ); $description = wp_strip_all_tags( $product->get_short_description() ); if ( $description === '' ) { $description = wp_strip_all_tags( $product->get_description() ); } return array( 'id' => self::product_gid( (int) $product->get_id() ), 'handle' => $product->get_slug(), 'title' => $product->get_name(), 'description' => array( 'plain' => $description, ), 'url' => $product->get_permalink(), 'price_range' => $price_range, 'media' => $media, 'options' => self::build_options( $product ), 'variants' => $variants, 'catalog_configuration' => self::build_catalog_configuration( $product, $variants ), ); } /** * @return list> */ private static function build_media( WC_Product $product ): array { $image_id = $product->get_image_id(); if ( ! $image_id ) { return array(); } $url = wp_get_attachment_image_url( $image_id, 'large' ); if ( ! $url ) { return array(); } return array( array( 'type' => 'image', 'url' => $url, 'alt_text' => (string) get_post_meta( $image_id, '_wp_attachment_image_alt', true ), ), ); } /** * Explain when WooCommerce attributes exist but chat cannot offer variant choice. * * @param list> $variants * @return array|null */ private static function build_catalog_configuration( WC_Product $product, array $variants ): ?array { $attributes = $product->get_attributes(); $attribute_labels = self::list_product_attribute_labels( $product ); if ( empty( $attributes ) ) { return null; } $variation_attribute_count = 0; foreach ( $attributes as $attribute ) { if ( $attribute->get_variation() ) { ++$variation_attribute_count; } } $purchasable_variant_count = count( $variants ); $selectable = $product->is_type( 'variable' ) && $variation_attribute_count > 0 && $purchasable_variant_count > 1; if ( $selectable ) { return array( 'selectable_variants' => true, 'attribute_labels' => $attribute_labels, ); } $message = __( 'This product lists attributes in WooCommerce but chat checkout has a single purchasable SKU.', 'agentmatica-mcp-connector' ); if ( $product->is_type( 'simple' ) && ! empty( $attribute_labels ) ) { $message = __( 'Simple product with Size/Color-style attributes that are not WooCommerce variations. Use Product data → Variable product, mark attributes “Used for variations”, then generate variations.', 'agentmatica-mcp-connector' ); } elseif ( $product->is_type( 'variable' ) && $purchasable_variant_count <= 1 ) { if ( $variation_attribute_count > 0 ) { $message = __( 'Variable product attributes are configured but no variations were generated. In WooCommerce, open Variations → Generate variations for each size/color combination.', 'agentmatica-mcp-connector' ); } else { $message = __( 'Variable product without generated variations. In WooCommerce, mark attributes “Used for variations” and create variations for each size/color combination.', 'agentmatica-mcp-connector' ); } } return array( 'selectable_variants' => false, 'attribute_labels' => $attribute_labels, 'message' => $message, ); } /** * @return list */ private static function list_product_attribute_labels( WC_Product $product ): array { $labels = array(); foreach ( $product->get_attributes() as $attribute ) { $name = wc_attribute_label( $attribute->get_name() ); if ( $name !== '' ) { $labels[] = (string) $name; } } return array_values( array_unique( $labels ) ); } /** * @return list> */ private static function build_options( WC_Product $product ): array { if ( ! $product->is_type( 'variable' ) ) { return array(); } $options = array(); foreach ( $product->get_attributes() as $attribute ) { if ( ! $attribute->get_variation() ) { continue; } $labels = array(); if ( $attribute->is_taxonomy() ) { $terms = wc_get_product_terms( $product->get_id(), $attribute->get_name(), array( 'fields' => 'names' ) ); foreach ( $terms as $term_name ) { $labels[] = array( 'label' => (string) $term_name ); } } else { foreach ( $attribute->get_options() as $option ) { $labels[] = array( 'label' => (string) $option ); } } if ( ! empty( $labels ) ) { $options[] = array( 'name' => wc_attribute_label( $attribute->get_name() ), 'values' => $labels, ); } } return $options; } /** * @return list> */ private static function build_variants( WC_Product $product, string $currency ): array { if ( $product->is_type( 'variable' ) ) { $variants = array(); foreach ( $product->get_children() as $child_id ) { $variation = wc_get_product( $child_id ); if ( ! $variation instanceof WC_Product_Variation ) { continue; } $formatted = self::format_variation( $variation, $currency ); if ( $formatted ) { $variants[] = $formatted; } } if ( ! empty( $variants ) ) { return $variants; } if ( ! empty( self::build_options( $product ) ) ) { return self::build_variable_parent_placeholder_variants( $product, $currency ); } return array(); } $price = self::minor_amount( self::resolve_product_price( $product ) ); if ( $price === null ) { return array(); } return array( array( 'id' => self::product_gid( (int) $product->get_id() ), 'sku' => $product->get_sku(), 'title' => $product->get_name(), 'description' => array( 'plain' => $product->get_name() ), 'price' => array( 'amount' => $price, 'currency' => $currency, ), 'availability' => array( 'available' => $product->is_in_stock() && $product->is_purchasable(), ), 'options' => array(), ), ); } /** * Variable product with attributes configured but no child variations generated yet. * * @return list> */ private static function build_variable_parent_placeholder_variants( WC_Product $product, string $currency ): array { $price = self::minor_amount( self::resolve_product_price( $product ) ); if ( $price === null ) { $price = 0; } return array( array( 'id' => self::product_gid( (int) $product->get_id() ), 'sku' => $product->get_sku(), 'title' => $product->get_name(), 'description' => array( 'plain' => $product->get_name() ), 'price' => array( 'amount' => $price, 'currency' => $currency, ), 'availability' => array( 'available' => false, ), 'options' => array(), ), ); } /** * Minimal UCP payload for named search when a product exists but is not purchasable now. * * @return array|null */ private static function format_unavailable_product_stub( WC_Product $product ): ?array { if ( $product->get_status() !== 'publish' ) { return null; } $currency = get_woocommerce_currency(); $price = self::minor_amount( self::resolve_product_price( $product ) ) ?? 0; $media = self::build_media( $product ); return array( 'id' => self::product_gid( (int) $product->get_id() ), 'handle' => $product->get_slug(), 'title' => $product->get_name(), 'description' => array( 'plain' => wp_strip_all_tags( $product->get_name() ), ), 'url' => $product->get_permalink(), 'price_range' => array( 'min' => array( 'amount' => $price, 'currency' => $currency, ), 'max' => array( 'amount' => $price, 'currency' => $currency, ), ), 'media' => $media, 'options' => self::build_options( $product ), 'variants' => array( array( 'id' => self::product_gid( (int) $product->get_id() ), 'title' => $product->get_name(), 'description' => array( 'plain' => $product->get_name() ), 'price' => array( 'amount' => $price, 'currency' => $currency, ), 'availability' => array( 'available' => self::is_catalog_available( $product ), ), 'options' => array(), ), ), 'catalog_configuration' => self::build_catalog_configuration( $product, array( array( 'id' => self::product_gid( (int) $product->get_id() ), ), ) ), ); } /** * Compact product row for inventory-map export (major-unit prices). * * @return array|null */ private static function format_snapshot_product( WC_Product $product ): ?array { $currency = get_woocommerce_currency(); $prices = array(); if ( $product->is_type( 'variable' ) ) { foreach ( $product->get_children() as $child_id ) { $variation = wc_get_product( (int) $child_id ); if ( ! $variation instanceof WC_Product_Variation ) { continue; } $price = self::resolve_product_price( $variation ); if ( $price > 0 ) { $prices[] = $price; } } } else { $price = self::resolve_product_price( $product ); if ( $price > 0 ) { $prices[] = $price; } } if ( empty( $prices ) ) { $prices = array( 0.0 ); } $dimensions = array(); foreach ( self::build_options( $product ) as $option ) { $values = array(); if ( isset( $option['values'] ) && is_array( $option['values'] ) ) { foreach ( $option['values'] as $entry ) { if ( is_array( $entry ) && isset( $entry['label'] ) ) { $values[] = (string) $entry['label']; } } } if ( ! empty( $values ) && isset( $option['name'] ) ) { $dimensions[] = array( 'name' => (string) $option['name'], 'values' => $values, ); } } $category_path = self::product_category_path( $product ); $variant_count = $product->is_type( 'variable' ) ? max( count( $product->get_children() ), 1 ) : 1; return array( 'id' => self::product_gid( (int) $product->get_id() ), 'title' => $product->get_name(), 'product_type' => $category_path ? $category_path : $product->get_type(), 'category_path' => $category_path, 'price_min' => min( $prices ), 'price_max' => max( $prices ), 'currency' => $currency, 'variant_count' => $variant_count, 'option_dimensions' => $dimensions, ); } private static function product_category_path( WC_Product $product ): ?string { $ids = $product->get_category_ids(); if ( empty( $ids ) ) { return null; } $deepest = null; $deepest_depth = -1; foreach ( $ids as $term_id ) { $term = get_term( (int) $term_id, 'product_cat' ); if ( ! $term || is_wp_error( $term ) ) { continue; } $depth = count( get_ancestors( (int) $term->term_id, 'product_cat' ) ); if ( $depth > $deepest_depth ) { $deepest = $term; $deepest_depth = $depth; } } if ( ! $deepest ) { return null; } $names = array(); foreach ( array_reverse( get_ancestors( (int) $deepest->term_id, 'product_cat' ) ) as $ancestor_id ) { $ancestor = get_term( (int) $ancestor_id, 'product_cat' ); if ( $ancestor && ! is_wp_error( $ancestor ) ) { $names[] = $ancestor->name; } } $names[] = $deepest->name; return implode( ' > ', $names ); } private static function resolve_product_price( WC_Product $product ): float { $price = $product->get_price(); if ( $price !== '' && $price !== null ) { return (float) $price; } $regular = $product->get_regular_price(); if ( $regular !== '' && $regular !== null ) { return (float) $regular; } return 0.0; } /** * @return array|null */ private static function format_variation( WC_Product_Variation $variation, string $currency ): ?array { $price = self::minor_amount( self::resolve_product_price( $variation ) ); if ( $price === null ) { return null; } $options = array(); foreach ( $variation->get_attributes() as $name => $value ) { $label = $value; if ( taxonomy_exists( $name ) ) { $term = get_term_by( 'slug', $value, $name ); if ( $term && ! is_wp_error( $term ) ) { $label = $term->name; } } $options[] = array( 'name' => wc_attribute_label( $name ), 'label' => (string) $label, ); } return array( 'id' => self::variant_gid( (int) $variation->get_id() ), 'sku' => $variation->get_sku(), 'title' => $variation->get_name(), 'description' => array( 'plain' => $variation->get_name() ), 'price' => array( 'amount' => $price, 'currency' => $currency, ), 'availability' => array( 'available' => $variation->is_in_stock() && $variation->is_purchasable(), ), 'options' => $options, ); } /** * @param list> $variants * @return array */ private static function price_range_from_variants( array $variants, string $currency ): array { $amounts = array(); foreach ( $variants as $variant ) { if ( isset( $variant['price']['amount'] ) ) { $amounts[] = (int) $variant['price']['amount']; } } if ( empty( $amounts ) ) { return array( 'min' => array( 'amount' => 0, 'currency' => $currency, ), 'max' => array( 'amount' => 0, 'currency' => $currency, ), ); } $min = min( $amounts ); $max = max( $amounts ); return array( 'min' => array( 'amount' => $min, 'currency' => $currency, ), 'max' => array( 'amount' => $max, 'currency' => $currency, ), ); } private static function minor_amount( float $price ): ?int { if ( $price < 0 ) { return null; } $decimals = wc_get_price_decimals(); return (int) round( $price * pow( 10, $decimals ) ); } }