├── LICENSE ├── README.md └── WordPress-simple-URL-based-breadcrumb.php /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WordPress simple URL based breadcrumb 2 | 3 | 🍞 A non-invasive WordPress unofficial plugin, minimalist and SEO friendly. both lightweight and lightning fast, adding URL based breadcrumb support. Plug-and-play, with no required configuration. 4 | 5 | ||Version| 6 | |-|-| 7 | |Requires at least WordPress:|`5.0.0`| 8 | |Requires at least PHP:|`7.0.0`| 9 | |[Tested up to WordPress:](https://wordpress.org/download/releases/)|`6.0.2`| 10 | |[Current plugin version:](https://github.com/amarinediary/WordPress-simple-URL-based-breadcrumb/releases/tag/1.2.4)|`1.2.4`| 11 | 12 | ### Latest changelog 13 | 14 | #### `1.2.4` 15 | 16 | `1.2.4` brings further improvements conditional logic based on the PHP version and further improvements. `1.2.4` hasn't been through any testing. Feedback for `1.2.4` is apreciated and more than welcome. 17 | 18 | [`1.2.3`](https://github.com/amarinediary/WordPress-simple-URL-based-breadcrumb/releases/tag/1.2.3) remains the current most stable version. 19 | 20 | - [x] Introduced `safe_str_ends_with`. `safe_str_ends_with` uses the native PHP > 8.0.0 function when available, or falls back to the custom implementation. 21 | - [x] Introduced `safe_str_contains`. `safe_str_contains` uses the native PHP > 8.0.0 function when available, or falls back to the custom implementation. 22 | - [x] Introduced `attempt_to_retrieve_server_scheme`. 23 | - [x] `get_the_crumbs` was updated to use introduced functions. 24 | - [x] `the_bread` was updated. Improvements to how default parameters are introduced were made. The use of `extract()` saves us the trouble of writing multiple if statements. 25 | - [x] Code commenting improvements. 26 | - [x] Emphasis on [WordPress coding standards](https://developer.wordpress.org/coding-standards/wordpress-coding-standards/). 27 | - [x] [README.md](https://github.com/amarinediary/WordPress-simple-URL-based-breadcrumb/blob/main/README.md) updates. 28 | - Special thanks to [@mattmacneil](https://github.com/mattmacneil) for the feedback and kind words. 29 | 30 | We are looking for ideas on how the search breadcrumb should be handled (with/without pagination) ? [Open a new issue](https://github.com/amarinediary/WordPress-simple-URL-based-breadcrumb/issues/new/choose) to share what's on your mind. 31 | 32 | ## Give us feedback 🙏 33 | 34 | Let us know how we can improve this plugin, [Open a new issue](https://github.com/amarinediary/WordPress-simple-URL-based-breadcrumb/issues/new/choose). 35 | 36 | ## Unofficial plugin ? 37 | Open source is a key value for us. Unofficial here means Open Source. Restricting it through the WordPress plugin store would be counterproductive. Put your hands in the engine and have fun. Star-it, Fork-it and Modify-it at your convenience. 38 | 39 | ## Table of contents 40 | 41 | - [Displaying the bread, a formatted crumbs list](https://github.com/amarinediary/WordPress-simple-URL-based-breadcrumb#displaying-the-bread-a-formatted-crumbs-list) 42 | - [Parameters](https://github.com/amarinediary/WordPress-simple-URL-based-breadcrumb#parameters) 43 | - [Example: The bread with a custom separator](https://github.com/amarinediary/WordPress-simple-URL-based-breadcrumb#example-the-bread-with-a-custom-separator) 44 | - [Example: Displaying the last 3 crumbs](https://github.com/amarinediary/WordPress-simple-URL-based-breadcrumb#example-displaying-the-last-3-crumbs) 45 | - [Example: The bread with a root crumb](https://github.com/amarinediary/WordPress-simple-URL-based-breadcrumb#example-the-bread-with-a-root-crumb) 46 | - [Example: Intercepting the crumbs array](https://github.com/amarinediary/WordPress-simple-URL-based-breadcrumb#example-intercepting-the-crumbs-array) 47 | - [HTML5 structure output](https://github.com/amarinediary/WordPress-simple-URL-based-breadcrumb#html5-structure-output) 48 | - [Styling](https://github.com/amarinediary/WordPress-simple-URL-based-breadcrumb#styling) 49 | - [Minimal css boilerplate (Optional)](https://github.com/amarinediary/WordPress-simple-URL-based-breadcrumb#minimal-css-boilerplate-optional) 50 | - [Retrieving the crumbs](https://github.com/amarinediary/WordPress-simple-URL-based-breadcrumb#retrieving-the-crumbs) 51 | - [Example: Ouputing the crumbs object](https://github.com/amarinediary/WordPress-simple-URL-based-breadcrumb#example-ouputing-the-crumbs-object) 52 | - [Breadcrumb behaviour and taxonomies](https://github.com/amarinediary/WordPress-simple-URL-based-breadcrumb#breadcrumb-behaviour-and-taxonomies) 53 | - [Discrepancies between Google Schema Validation tools and the Google Search Console Enhancement Reports and Performance Reports.](https://github.com/amarinediary/WordPress-simple-URL-based-breadcrumb#discrepancies-between-google-schema-validation-tools-and-the-google-search-console-enhancement-reports-and-performance-reports) 54 | - [Localhost development](https://github.com/amarinediary/WordPress-simple-URL-based-breadcrumb#localhost-development) 55 | 56 | ## Displaying the bread, a formatted crumbs list. 57 | 58 | ```php 59 | '→', 84 | ); 85 | 86 | the_bread( $ingredients ); 87 | ``` 88 | 89 | ### Example: Displaying the last 3 crumbs 90 | 91 | ```php 92 | -3, 96 | 'length' => 3, 97 | ); 98 | 99 | the_bread( $ingredients ); 100 | ``` 101 | 102 | ### Example: The bread with a root crumb 103 | 104 | ```php 105 | array( 109 | 'slug' => 'home', 110 | 'url' => get_home_url(), 111 | ), 112 | ); 113 | 114 | the_bread( $ingredients ); 115 | ``` 116 | 117 | ### Example: Intercepting the crumbs array 118 | 119 | ```php 120 | 'search', 130 | 'url' => 'https://.../search/', 131 | ) 132 | ); 133 | 134 | //And intercepting a specific crumb to modify it... 135 | array_walk( $crumbs, function( &$value, $key ) { 136 | 137 | if ( 'something' == $value['slug'] ) { 138 | 139 | $value['slug'] = 'somethingelse'; 140 | 141 | }; 142 | 143 | } ); 144 | 145 | //And use it with our bread... 146 | $ingredients = array( 147 | 'crumbs' => $crumbs, 148 | ); 149 | 150 | the_bread( $ingredients ); 151 | ``` 152 | 153 | ### HTML5 structure output 154 | 155 | ```html 156 |
    157 |
  1. 158 | 159 | Where 160 | 161 | 162 |
  2. 163 | > 164 |
  3. 165 | 166 | Is 167 | 168 | 169 |
  4. 170 | > 171 |
  5. 172 | 173 | My 174 | 175 | 176 |
  6. 177 | > 178 |
  7. 179 | 180 | Bread 181 | 182 | 183 |
  8. 184 |
185 | ``` 186 | 187 | ### Styling 188 | 189 | By default Where-Is-My-Bread has no associated stylesheet, but has two associated css classes: 190 | 191 | - The `
    ` tag comes with the css class, `.🍞` or `.bread` *(fallback)*. 192 | - Each `
  1. ` tag comes with the class, `.crumb`. 193 | 194 | ### Minimal css boilerplate (Optional) 195 | 196 | ``` 197 | .🍞, 198 | .bread { 199 | list-style-type: none; 200 | margin:0; 201 | padding:0; 202 | } 203 | 204 | .🍞 li, 205 | .bread li { 206 | display:inline-block; 207 | } 208 | 209 | .🍞 li.crumb:last-child a, 210 | .bread li.crumb:last-child a { 211 | text-decoration: none; 212 | pointer-events: none; 213 | color: inherit; 214 | } 215 | ``` 216 | 217 | ## Retrieving the crumbs 218 | 219 | Even tho we recommend you to use `the_bread()` function to display and build your own breadcrumb, you can use `get_the_crumbs()` to retrieve the crumbs object. 220 | 221 | ### Example: Outputting the crumbs object 222 | 223 | ```php 224 | Search Console has changed the way that it evaluates and reports errors in Breadcrumbs and HowTo structured data. As a result, you may see changes in the number of Breadcrumbs > and HowTo entities and issues reported for your property, as well as a change in severity of some issues from errors to warnings. 241 | 242 | - Source @ [Data anomalies in Search Console](https://support.google.com/webmasters/answer/6211453?hl=en#zippy=%2Crich-result-reports) 243 | 244 | > [...] Our team has investigated a couple of instances where this search verifies that the markup is there, despite the disagreement with Search Console and has found a solution, where the errors decline steadily over the course of a few days [...] 245 | 246 | - Source @ [How To Resolve Misattributed Errors In The New Google Search Console](https://www.schemaapp.com/schema-markup/how-to-resolve-misattributed-errors-in-the-new-google-search-console/) 247 | - Source @ [Discrepancies in Google Search Console: Enhancement Reports vs. Performance Reports](https://support.schemaapp.com/support/solutions/articles/33000267425-discrepancies-in-google-search-console-enhancement-reports-vs-performance-reports) 248 | 249 | It would seems that the issue is coming from the Google Bot validation itelf and not the structured data of the plugin. We will continue to monitor the situation as 2022 unfold. 250 | 251 | ## Localhost development 252 | 253 | As we are reading and parsing the url, the crumbs on a localhost environement will reflect the development folder architecture. This will not be reflected on a live site. eg: `Home > Www > Wordpress > My awesome post` on a local branch, `Home > My awesome post` on a live branch. 254 | 255 | ## Watch it, Star-it, Fork-it ! 256 | 257 | We made your day? Give us a star! 258 | -------------------------------------------------------------------------------- /WordPress-simple-URL-based-breadcrumb.php: -------------------------------------------------------------------------------- 1 | 8.0.0 function when available, or falls back to the custom implementation. 61 | * 62 | * @param string $haystack The string to search within. 63 | * @param string $needle The string to search for. 64 | * 65 | * @return Bool True if $haystack ends with $needle, false otherwise. 66 | * 67 | * @since 1.2.4 68 | */ 69 | function safe_str_ends_with( $haystack, $needle ) { 70 | 71 | if ( function_exists( 'str_ends_with' ) ) { 72 | 73 | return str_ends_with( $haystack, $needle ); 74 | 75 | } 76 | 77 | return backward_compatibility_str_ends_with( $haystack, $needle ); 78 | 79 | } 80 | 81 | /** 82 | * Wrapper function that safely uses the str_contains function 83 | * It uses the native PHP > 8.0.0 function when available, or falls back to the custom implementation. 84 | * 85 | * @param string $haystack The string to search within. 86 | * @param string $needle The string to search for. 87 | * 88 | * @return Bool True if $haystack contains $needle, false otherwise. 89 | * 90 | * @since 1.2.4 91 | */ 92 | function safe_str_contains( $haystack, $needle ) { 93 | 94 | if ( function_exists( 'str_contains' ) ) { 95 | 96 | return str_contains( $haystack, $needle ); 97 | 98 | } 99 | 100 | return backward_compatibility_str_contains( $haystack, $needle ); 101 | 102 | } 103 | 104 | /** 105 | * Attempts to determine the server scheme (http or https) of the current request based on various server variables. 106 | * 107 | * This function checks multiple server variables like $_SERVER['HTTPS'], $_SERVER['SERVER_PORT'], 108 | * $_SERVER['HTTP_X_FORWARDED_PROTO'], and $_SERVER['HTTP_X_FORWARDED_SSL'] to accurately 109 | * determine whether the current request is made over https or http. 110 | * 111 | * $_SERVER["REQUEST_SCHEME"] seems to be UNRELIABLE. 112 | * 113 | * Article "Is $_SERVER['REQUEST_SCHEME'] reliable?". 114 | * @see https://stackoverflow.com/a/18008178/3645650 115 | * 116 | * $_SERVER['REQUEST_SCHEME'] is a native variable of Apache web server since its version 2.4. 117 | * Naturally, if a variable is not set by the server, PHP will not include it in its global array $_SERVER. 118 | * 119 | * An alternative to $_SERVER['REQUEST_SCHEME'] is $_SERVER['HTTPS'] which set to a non-empty value if the script was queried through the HTTPS protocol. 120 | * 121 | * Article "How to find out if you're using HTTPS without $_SERVER['HTTPS']". 122 | * @see https://stackoverflow.com/a/16076965/3645650 123 | * 124 | * @return String $server_scheme The server scheme, either 'http' or 'https'. 125 | * 126 | * @since 1.0.0 127 | */ 128 | if ( ! function_exists( 'attempt_to_retrieve_server_scheme' ) ) { 129 | 130 | function attempt_to_retrieve_server_scheme() { 131 | 132 | if ( isset( $_SERVER['HTTPS'] ) && safe_str_contains( $_SERVER['HTTPS'], 'on' ) ) { 133 | 134 | $server_scheme = 'https'; 135 | 136 | } elseif ( isset($_SERVER['SERVER_PORT'] ) && $_SERVER['SERVER_PORT'] == '443' ) { 137 | 138 | $server_scheme = 'https'; 139 | 140 | } elseif ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && safe_str_contains( $_SERVER['HTTP_X_FORWARDED_PROTO'], 'https' ) ) { 141 | 142 | $server_scheme = 'https'; 143 | 144 | } elseif ( isset( $_SERVER['HTTP_X_FORWARDED_SSL'] ) && safe_str_contains( $_SERVER['HTTP_X_FORWARDED_SSL'], 'on' ) ) { 145 | 146 | $server_scheme = 'https'; 147 | 148 | } else { 149 | 150 | $server_scheme = 'http'; 151 | 152 | } 153 | 154 | } 155 | 156 | } 157 | 158 | /** 159 | * Retrieve the crumbs. 160 | * 161 | * @return Array Crumbs array. 162 | * 163 | * @since 1.0.0 164 | */ 165 | 166 | if ( ! function_exists( 'get_the_crumbs' ) ) { 167 | 168 | function get_the_crumbs() { 169 | 170 | // Retrieve the server scheme ('http' or 'https') 171 | $server_scheme = attempt_to_retrieve_server_scheme(); 172 | 173 | /** 174 | * $_SERVER["REQUEST_URI"] seems to be RELIABLE. 175 | * $_SERVER['REQUEST_URI'] will not be empty in WordPress, because it is filled in wp_fix_server_vars() (file wp-includes/load.php). 176 | * 177 | * Article "Is it safe to use $_SERVER['REQUEST_URI']?". 178 | * @see https://wordpress.stackexchange.com/a/110541/190376 179 | */ 180 | $server_uri = $_SERVER['REQUEST_URI']; 181 | 182 | /** 183 | * $_SERVER["HTTP_HOST"] seems to be RELIABLE. 184 | * 185 | * Article "How reliable is HTTP_HOST?". 186 | * @see https://stackoverflow.com/a/4096246/3645650 187 | */ 188 | $server_host = $_SERVER["HTTP_HOST"]; 189 | 190 | // Remove query string if present 191 | if ( safe_str_contains( $server_uri, '?' ) ) { 192 | 193 | $server_uri = substr( $server_uri, 0, strpos( $server_uri, '?' ) ); 194 | 195 | } 196 | 197 | // Remove trailing slash if present 198 | if ( safe_str_ends_with( $server_uri, '/' ) ) { 199 | 200 | $server_uri = explode( '/', substr( $server_uri, 1, -1 ) ); 201 | 202 | } else { 203 | 204 | $server_uri = explode( '/', substr( $server_uri, 1 ) ); 205 | 206 | } 207 | 208 | // Initialize crumbs array 209 | $crumbs = array(); 210 | 211 | // Populate crumbs array 212 | foreach ( $server_uri as $crumb ) { 213 | 214 | $slug = esc_html( urldecode( $crumb ) ); 215 | 216 | $taxonomies = get_taxonomies( 217 | array( 218 | 'public' => true, 219 | ), 220 | 'objects' 221 | ); 222 | 223 | // Iterate through all the taxonomies. 224 | foreach ( $taxonomies as $taxonomy ) { 225 | 226 | // Check if there's a term with the given slug in the current taxonomy. 227 | if ( $term = get_term_by( 'slug', $crumb, $taxonomy->name ) ) { 228 | 229 | // If a matching term is found, update the slug with the actual term name. 230 | $slug = $term->name; 231 | 232 | // Break the loop since a match has been found. 233 | break; 234 | 235 | } 236 | 237 | } 238 | 239 | $url = esc_url( $server_scheme . '://' . $server_host . '/' . substr( implode( '/', $server_uri ), 0, strpos( implode( '/', $server_uri ), $crumb ) ) . $crumb. '/' ); 240 | 241 | array_push( $crumbs, 242 | array( 243 | 'slug' => $slug, 244 | 'url' => $url, 245 | ) 246 | ); 247 | 248 | }; 249 | 250 | /** 251 | * WordPress, by default, doesn't generate a taxonomy index, meaning https://.../taxonomy will redirect to a 404. 252 | * Any request needs to be made against a term. eg: https://.../taxonomy/term will redirect to taxonomy.php. 253 | * Therefore we need to remove the taxonomy slug from the crumbs array to avoid displaying a link to a 404. 254 | * 255 | * We round up all taxonomies through get_taxonomies(). 256 | * @see https://developer.wordpress.org/reference/functions/get_taxonomies/ 257 | * 258 | * Through array_filter we filter-out any matching crumbs. 259 | * @see https://www.php.net/manual/en/function.array-filter.php 260 | */ 261 | $banned_slugs = array(); 262 | 263 | $taxonomies = get_taxonomies( 264 | array( 265 | 'public' => true, 266 | ), 267 | 'objects' 268 | ); 269 | 270 | foreach ( $taxonomies as $taxonomy ) { 271 | 272 | array_push( $banned_slugs, $taxonomy->name ); 273 | 274 | if ( isset( $taxonomy->rewrite['slug'] ) ) { 275 | 276 | array_push( $banned_slugs, $taxonomy->rewrite['slug'] ); 277 | 278 | }; 279 | 280 | }; 281 | 282 | $banned_crumbs = array(); 283 | 284 | foreach ( $banned_slugs as $banned_slug ) { 285 | 286 | $slug = esc_html( $banned_slug ); 287 | 288 | $url = esc_url( $server_scheme . '://' . $server_host . '/' . substr( implode( '/', $server_uri ), 0, strpos( implode( '/', $server_uri ), $banned_slug ) ) . $banned_slug. '/' ); 289 | 290 | array_push( $banned_crumbs, 291 | array( 292 | 'slug' => $slug, 293 | 'url' => $url, 294 | ) 295 | ); 296 | 297 | }; 298 | 299 | $crumbs = array_filter( $crumbs, function( $crumb ) use ( $banned_slugs ) { 300 | 301 | if ( ! in_array( $crumb['slug'], $banned_slugs ) && ! in_array( $crumb['url'], $banned_slugs ) ) { 302 | 303 | return ! in_array( $crumb['slug'], $banned_slugs ); 304 | 305 | }; 306 | 307 | } ); 308 | 309 | return $crumbs; 310 | 311 | }; 312 | 313 | }; 314 | 315 | /** 316 | * Display the breadcrumb, a formatted crumbs list. 317 | * 318 | * @param Array $ingredients The bread arguments. 319 | * 320 | * @return Void 321 | * 322 | * @since 1.0.0 323 | */ 324 | if ( ! function_exists( 'the_bread' ) ) { 325 | 326 | function the_bread( $ingredients = array() ) { 327 | 328 | // Default values using array destructuring 329 | $defaults = [ 330 | 'crumbs' => get_the_crumbs(), 331 | 'root' => null, 332 | 'separator' => '', 333 | 'offset' => 0, 334 | 'length' => null 335 | ]; 336 | 337 | // Merge provided ingredients with defaults 338 | $ingredients = array_merge( $defaults, $ingredients ); 339 | 340 | // Extract variables from the ingredients array 341 | extract( $ingredients ); 342 | 343 | // Handle the root crumb case 344 | if ( $root ) { 345 | 346 | array_unshift( $crumbs, $root ); 347 | 348 | } 349 | 350 | // Handle the length case 351 | $crumbs = array_slice( $crumbs, $offset, $length ); 352 | 353 | if ( $crumbs ) { 354 | 355 | echo ''; 407 | 408 | } 409 | 410 | } 411 | 412 | } --------------------------------------------------------------------------------