Create New Item
Item Type
File
Folder
Item Name
Search file in folder and subfolders...
Are you sure want to rename?
betrayful
/
wp
/
wp-content
/
plugins
/
woocommerce
/
src
/
Blocks
:
CoreBreadcrumbsCompatibility.php
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
<?php declare(strict_types=1); namespace Automattic\WooCommerce\Blocks; /** * Adds WooCommerce compatibility behavior to the Core Breadcrumbs block. * * @internal */ final class CoreBreadcrumbsCompatibility { /* * Compatibility methods. */ /** * Set the preferred taxonomy and term for the breadcrumbs block on the product post type. * * This method mimics the behavior of WC_Breadcrumb::add_crumbs_single() to ensure * consistent breadcrumb term selection between WooCommerce's legacy breadcrumbs * and the Core breadcrumbs block. * * @internal * * @param array $settings The settings for the breadcrumbs block. * @param string $post_type The post type. * @param int $post_id The current post ID. * @return array The settings for the breadcrumbs block. */ public function set_product_breadcrumbs_preferred_taxonomy( $settings, $post_type, $post_id = 0 ) { if ( ! is_array( $settings ) || 'product' !== $post_type ) { return $settings; } $settings['taxonomy'] = 'product_cat'; $post_id = (int) $post_id; if ( ! $post_id ) { return $settings; } $terms = wc_get_product_terms( $post_id, 'product_cat', /** * Filters the arguments used to fetch product terms for breadcrumbs. * * @since 9.5.0 * * @param array $args Array of arguments for `wc_get_product_terms()`. */ apply_filters( 'woocommerce_breadcrumb_product_terms_args', array( 'orderby' => 'parent', 'order' => 'DESC', ) ) ); if ( empty( $terms ) || is_wp_error( $terms ) ) { return $settings; } /** * Filters the main term used in product breadcrumbs. * * @since 9.5.0 * * @param \WP_Term $main_term The main term to be used in breadcrumbs. * @param \WP_Term[] $terms Array of all product category terms. */ $main_term = apply_filters( 'woocommerce_breadcrumb_main_term', $terms[0], $terms ); if ( $main_term instanceof \WP_Term ) { $settings['term'] = $main_term->slug; } return $settings; } /** * Apply WooCommerce breadcrumb filters to Core breadcrumbs block items. * * This bridges the Core breadcrumbs block with WooCommerce's legacy breadcrumb filters, * ensuring backward compatibility for sites that have customized breadcrumbs using * the `woocommerce_get_breadcrumb` filter. * * @internal * * @param array $items Array of breadcrumb items from Core. * @return array Modified breadcrumb items. */ public function apply_woocommerce_breadcrumb_filters( $items ) { if ( ! is_array( $items ) ) { return $items; } $items = $this->apply_woocommerce_core_breadcrumb_adjustments( $items ); if ( ! has_filter( 'woocommerce_get_breadcrumb' ) ) { return $items; } // Convert Core format to WooCommerce format. // Core: array( 'url' => '...', 'label' => '...' ) // Woo: array( 'label', 'url' ). $wc_crumbs = array_map( function ( $item ) { return array( $item['label'] ?? '', $item['url'] ?? '', ); }, $items ); /** * Filters the breadcrumb trail array. * * @since 2.3.0 * * @param array $crumbs The breadcrumb trail. * @param \WC_Breadcrumb|null $breadcrumb The breadcrumb object (null when called from Core block). */ $wc_crumbs = apply_filters( 'woocommerce_get_breadcrumb', $wc_crumbs, null ); $core_items = array(); foreach ( $wc_crumbs as $index => $crumb ) { $item = isset( $items[ $index ] ) && is_array( $items[ $index ] ) ? $items[ $index ] : array(); $label = is_array( $crumb ) ? ( $crumb[0] ?? '' ) : ''; $url = is_array( $crumb ) ? ( $crumb[1] ?? '' ) : ''; $item['label'] = $label; if ( $url ) { $item['url'] = $url; } else { unset( $item['url'] ); } $core_items[] = $item; } return $core_items; } /** * Apply WooCommerce breadcrumb behavior to Core breadcrumbs. * * @param array $items Array of breadcrumb items from Core. * @return array Modified breadcrumb items. */ private function apply_woocommerce_core_breadcrumb_adjustments( $items ) { if ( ! is_array( $items ) ) { return $items; } $items = $this->replace_product_archive_breadcrumb_label( $items ); $items = $this->prepend_shop_page_to_product_taxonomy_breadcrumbs( $items ); $items = $this->prepend_taxonomy_label_to_product_taxonomy_breadcrumbs( $items ); $items = $this->prepend_shop_page_to_product_search_breadcrumbs( $items ); $items = $this->replace_product_tag_breadcrumb_label( $items ); $items = $this->replace_search_breadcrumb_label( $items ); $items = $this->prepend_my_account_page_to_endpoint_breadcrumbs( $items ); $items = $this->apply_home_breadcrumb_url_filter( $items ); return $items; } /** * Apply WooCommerce's Home breadcrumb URL filter. * * @param array $items Array of breadcrumb items from Core. * @return array Modified breadcrumb items. */ private function apply_home_breadcrumb_url_filter( $items ) { if ( ! has_filter( 'woocommerce_breadcrumb_home_url' ) ) { return $items; } $home_index = $this->get_breadcrumb_item_index_by_url( $items, home_url( '/' ) ); if ( null === $home_index ) { return $items; } $default_home_url = home_url(); /** * Filters the Home breadcrumb URL. * * @param string $url The Home breadcrumb URL. * * @since 2.3.0 */ $home_url = apply_filters( 'woocommerce_breadcrumb_home_url', $default_home_url ); $items[ $home_index ]['url'] = is_string( $home_url ) ? $home_url : $default_home_url; return $items; } /** * Replace product archive breadcrumb labels with the WooCommerce shop page title. * * @param array $items Array of breadcrumb items from Core. * @return array Modified breadcrumb items. */ private function replace_product_archive_breadcrumb_label( $items ) { $shop_url = get_post_type_archive_link( 'product' ); if ( ! $shop_url || null === $this->get_breadcrumb_item_index_by_url( $items, $shop_url ) ) { return $items; } $shop_page_item = $this->get_shop_page_breadcrumb_item( $shop_url ); if ( empty( $shop_page_item ) ) { return $items; } foreach ( $items as $index => $item ) { if ( self::are_breadcrumb_urls_equal( $item['url'] ?? '', $shop_page_item['url'] ) ) { $items[ $index ]['label'] = $shop_page_item['label']; } } return $items; } /** * Prepend the shop page to product taxonomy breadcrumbs. * * @param array $items Array of breadcrumb items from Core. * @return array Modified breadcrumb items. */ private function prepend_shop_page_to_product_taxonomy_breadcrumbs( $items ) { if ( ! ( is_product_category() || is_product_tag() ) ) { return $items; } $permalinks = wc_get_permalink_structure(); $shop_page = $this->get_woocommerce_page_post( 'shop' ); if ( ! $shop_page || ! isset( $permalinks['product_base'] ) || ! strstr( $permalinks['product_base'], '/' . $shop_page->post_name ) || intval( get_option( 'page_on_front' ) ) === $shop_page->ID ) { return $items; } return $this->prepend_shop_page_to_breadcrumbs( $items ); } /** * Prepend taxonomy labels to product taxonomy breadcrumbs. * * @param array $items Array of breadcrumb items from Core. * @return array Modified breadcrumb items. */ private function prepend_taxonomy_label_to_product_taxonomy_breadcrumbs( $items ) { if ( ! is_product_taxonomy() || empty( $items ) ) { return $items; } $current_term = $this->get_queried_term(); if ( ! $current_term || in_array( $current_term->taxonomy, array( 'product_brand', 'product_cat', 'product_tag' ), true ) ) { return $items; } $taxonomy = get_taxonomy( $current_term->taxonomy ); if ( ! $taxonomy || ! $taxonomy->labels->name ) { return $items; } $insert_index = $this->get_first_breadcrumb_insert_index( $items ); if ( isset( $items[ $insert_index ]['label'] ) && $taxonomy->labels->name === $items[ $insert_index ]['label'] ) { return $items; } return $this->insert_parent_breadcrumb_item( $items, array( 'label' => $taxonomy->labels->name, ) ); } /** * Prepend the shop page to product search breadcrumbs. * * @param array $items Array of breadcrumb items from Core. * @return array Modified breadcrumb items. */ private function prepend_shop_page_to_product_search_breadcrumbs( $items ) { if ( ! $this->is_product_search() || intval( get_option( 'page_on_front' ) ) === wc_get_page_id( 'shop' ) ) { return $items; } return $this->prepend_shop_page_to_breadcrumbs( $items ); } /** * Replace Core's search breadcrumb label with WooCommerce's search label. * * @param array $items Array of breadcrumb items from Core. * @return array Modified breadcrumb items. */ private function replace_search_breadcrumb_label( $items ) { if ( ! is_search() || empty( $items ) ) { return $items; } $search_url = (int) get_query_var( 'paged' ) > 1 ? get_pagenum_link( 1 ) : ''; /* translators: %s: search term */ return $this->replace_current_archive_breadcrumb_label( $items, sprintf( __( 'Search results for “%s”', 'woocommerce' ), get_search_query() ), $search_url ); } /** * Replace product tag breadcrumb labels with WooCommerce's tag archive label. * * @param array $items Array of breadcrumb items from Core. * @return array Modified breadcrumb items. */ private function replace_product_tag_breadcrumb_label( $items ) { if ( ! is_product_tag() || empty( $items ) ) { return $items; } $current_term = $this->get_queried_term(); if ( ! $current_term ) { return $items; } $tag_link = get_term_link( $current_term, 'product_tag' ); if ( is_wp_error( $tag_link ) ) { $tag_link = ''; } /* translators: %s: product tag */ return $this->replace_current_archive_breadcrumb_label( $items, sprintf( __( 'Products tagged “%s”', 'woocommerce' ), $current_term->name ), $tag_link ); } /** * Prepend the My Account page to account endpoint breadcrumbs. * * @param array $items Array of breadcrumb items from Core. * @return array Modified breadcrumb items. */ private function prepend_my_account_page_to_endpoint_breadcrumbs( $items ) { if ( ! is_wc_endpoint_url() || ! is_account_page() ) { return $items; } $my_account_page = $this->get_woocommerce_page_post( 'myaccount' ); $my_account_url = $my_account_page ? get_permalink( $my_account_page ) : ''; if ( ! $my_account_page || ! $my_account_url ) { return $items; } $woocommerce = WC(); if ( $woocommerce->query instanceof \WC_Query ) { $endpoint = $woocommerce->query->get_current_endpoint(); $action = isset( $_GET['action'] ) ? sanitize_text_field( wp_unslash( $_GET['action'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Used only to select the breadcrumb label. $endpoint_title = $endpoint ? $woocommerce->query->get_endpoint_title( $endpoint, $action ) : ''; if ( $endpoint_title ) { $items = $this->replace_current_archive_breadcrumb_label( $items, $endpoint_title ); } } return $this->insert_parent_breadcrumb_item_if_missing_url( $items, array( 'label' => $my_account_page->post_title, 'url' => $my_account_url, ) ); } /* * Utility methods. */ /** * Check whether the current request is a product search. * * @return bool Whether the current request is a product search. */ private function is_product_search(): bool { if ( ! is_search() ) { return false; } $post_type = get_query_var( 'post_type' ); if ( is_array( $post_type ) ) { return in_array( 'product', $post_type, true ); } return 'product' === $post_type || is_shop(); } /** * Prepend the shop page to breadcrumb items. * * @param array $items Array of breadcrumb items from Core. * @return array Modified breadcrumb items. */ private function prepend_shop_page_to_breadcrumbs( $items ) { $shop_page_item = $this->get_shop_page_breadcrumb_item(); if ( empty( $shop_page_item ) ) { return $items; } $shop_page_index = $this->get_breadcrumb_item_index_by_url( $items, $shop_page_item['url'] ); if ( null !== $shop_page_index ) { return $this->replace_breadcrumb_label_at_index( $items, $shop_page_index, $shop_page_item['label'] ); } return $this->insert_parent_breadcrumb_item_if_missing_url( $items, $shop_page_item ); } /** * Get the shop page breadcrumb item. * * @param string $shop_url Shop archive URL. * @return array|null Shop page breadcrumb item. */ private function get_shop_page_breadcrumb_item( $shop_url = '' ) { $shop_page = $this->get_woocommerce_page_post( 'shop' ); $shop_url = $shop_url ? $shop_url : ( $shop_page ? get_permalink( $shop_page ) : get_post_type_archive_link( 'product' ) ); $shop_label = $shop_page ? get_the_title( $shop_page ) : ''; if ( ! $shop_label ) { $product_post_type = get_post_type_object( 'product' ); $shop_label = $product_post_type ? $product_post_type->labels->name : ''; } if ( ! $shop_url || ! $shop_label ) { return null; } return array( 'label' => $shop_label, 'url' => $shop_url, ); } /** * Get a WooCommerce page post. * * @param string $page_name WooCommerce page name. * @return \WP_Post|null WooCommerce page post. */ private function get_woocommerce_page_post( string $page_name ) { $page_id = wc_get_page_id( $page_name ); return $page_id > 0 ? get_post( $page_id ) : null; } /** * Get the queried term. * * @return \WP_Term|null Queried term. */ private function get_queried_term() { $queried_object = $GLOBALS['wp_query']->get_queried_object(); return $queried_object instanceof \WP_Term ? $queried_object : null; } /** * Replace the current archive breadcrumb label. * * @param array $items Array of breadcrumb items from Core. * @param string $label Replacement label. * @param string $archive_url Archive URL. * @return array Modified breadcrumb items. */ private function replace_current_archive_breadcrumb_label( $items, $label, $archive_url = '' ) { $item_index = $this->get_breadcrumb_item_index_by_url( $items, $archive_url ); if ( null === $item_index ) { $item_keys = array_keys( $items ); if ( empty( $item_keys ) ) { return $items; } $item_index = end( $item_keys ); $paged = (int) get_query_var( 'paged' ); if ( $paged > 1 && count( $item_keys ) > 1 && isset( $items[ $item_index ]['label'] ) ) { $pagination_label = sprintf( /* translators: %s: page number */ __( 'Page %s', 'default' ), // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch -- Match the Core Breadcrumbs block pagination label. number_format_i18n( $paged ) ); if ( $pagination_label === (string) $items[ $item_index ]['label'] ) { $item_index = $item_keys[ count( $item_keys ) - 2 ]; } } if ( ! empty( $items[ $item_index ]['url'] ) ) { return $items; } } return $this->replace_breadcrumb_label_at_index( $items, $item_index, $label ); } /** * Replace a breadcrumb label at an index. * * @param array $items Array of breadcrumb items from Core. * @param int|string|null $index Breadcrumb item index. * @param string $label Replacement label. * @return array Modified breadcrumb items. */ private function replace_breadcrumb_label_at_index( $items, $index, $label ) { if ( null === $index || ! isset( $items[ $index ] ) ) { return $items; } $items[ $index ]['label'] = $label; return $items; } /** * Insert a parent breadcrumb item if its URL is not already present. * * @param array $items Array of breadcrumb items from Core. * @param array $item Breadcrumb item to insert. * @return array Modified breadcrumb items. */ private function insert_parent_breadcrumb_item_if_missing_url( $items, $item ) { if ( empty( $item['url'] ) || null !== $this->get_breadcrumb_item_index_by_url( $items, $item['url'] ) ) { return $items; } return $this->insert_parent_breadcrumb_item( $items, $item ); } /** * Insert a parent breadcrumb item after the home item. * * @param array $items Array of breadcrumb items from Core. * @param array $item Breadcrumb item to insert. * @return array Modified breadcrumb items. */ private function insert_parent_breadcrumb_item( $items, $item ) { array_splice( $items, $this->get_first_breadcrumb_insert_index( $items ), 0, array( $item ) ); return $items; } /** * Get the first breadcrumb item index matching a URL. * * @param array $items Array of breadcrumb items from Core. * @param string $url URL to find. * @return int|string|null Breadcrumb item index. */ private function get_breadcrumb_item_index_by_url( $items, $url ) { if ( ! $url ) { return null; } foreach ( $items as $index => $item ) { if ( self::are_breadcrumb_urls_equal( $item['url'] ?? '', $url ) ) { return $index; } } return null; } /** * Get the index where WooCommerce should insert parent breadcrumb items. * * @param array $items Array of breadcrumb items from Core. * @return int Breadcrumb insertion index. */ private function get_first_breadcrumb_insert_index( $items ) { if ( empty( $items ) ) { return 0; } $first_item = reset( $items ); return self::are_breadcrumb_urls_equal( $first_item['url'] ?? '', home_url( '/' ) ) ? 1 : 0; } /** * Check whether two breadcrumb URLs are equivalent. * * @param string $first_url First URL. * @param string $second_url Second URL. * @return bool Whether the URLs are equivalent. */ private static function are_breadcrumb_urls_equal( $first_url, $second_url ) { return untrailingslashit( (string) $first_url ) === untrailingslashit( (string) $second_url ); } }