• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Yoast / wordpress-seo / dd6e866a9e6d253114633104d9e3858d807178ba

19 Jun 2024 10:03AM UTC coverage: 48.628% (-4.3%) from 52.936%
dd6e866a9e6d253114633104d9e3858d807178ba

push

github

web-flow
Merge pull request #21431 from Yoast/21429-update-copy-in-the-introduction-and-consent-modals

Updates the copy for the introduction and consent modals

7441 of 13454 branches covered (55.31%)

Branch coverage included in aggregate %.

0 of 3 new or added lines in 2 files covered. (0.0%)

3718 existing lines in 107 files now uncovered.

25100 of 53464 relevant lines covered (46.95%)

62392.47 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

64.23
/src/generators/breadcrumbs-generator.php
1
<?php
2

3
namespace Yoast\WP\SEO\Generators;
4

5
use Yoast\WP\SEO\Context\Meta_Tags_Context;
6
use Yoast\WP\SEO\Helpers\Current_Page_Helper;
7
use Yoast\WP\SEO\Helpers\Options_Helper;
8
use Yoast\WP\SEO\Helpers\Pagination_Helper;
9
use Yoast\WP\SEO\Helpers\Post_Type_Helper;
10
use Yoast\WP\SEO\Helpers\Url_Helper;
11
use Yoast\WP\SEO\Models\Indexable;
12
use Yoast\WP\SEO\Repositories\Indexable_Repository;
13

14
/**
15
 * Represents the generator class for the breadcrumbs.
16
 */
17
class Breadcrumbs_Generator implements Generator_Interface {
18

19
        /**
20
         * The indexable repository.
21
         *
22
         * @var Indexable_Repository
23
         */
24
        private $repository;
25

26
        /**
27
         * The options helper.
28
         *
29
         * @var Options_Helper
30
         */
31
        private $options;
32

33
        /**
34
         * The current page helper.
35
         *
36
         * @var Current_Page_Helper
37
         */
38
        private $current_page_helper;
39

40
        /**
41
         * The post type helper.
42
         *
43
         * @var Post_Type_Helper
44
         */
45
        private $post_type_helper;
46

47
        /**
48
         * The URL helper.
49
         *
50
         * @var Url_Helper
51
         */
52
        private $url_helper;
53

54
        /**
55
         * The pagination helper.
56
         *
57
         * @var Pagination_Helper
58
         */
59
        private $pagination_helper;
60

61
        /**
62
         * Breadcrumbs_Generator constructor.
63
         *
64
         * @param Indexable_Repository $repository          The repository.
65
         * @param Options_Helper       $options             The options helper.
66
         * @param Current_Page_Helper  $current_page_helper The current page helper.
67
         * @param Post_Type_Helper     $post_type_helper    The post type helper.
68
         * @param Url_Helper           $url_helper          The URL helper.
69
         * @param Pagination_Helper    $pagination_helper   The pagination helper.
70
         */
71
        public function __construct(
18✔
72
                Indexable_Repository $repository,
73
                Options_Helper $options,
74
                Current_Page_Helper $current_page_helper,
75
                Post_Type_Helper $post_type_helper,
76
                Url_Helper $url_helper,
77
                Pagination_Helper $pagination_helper
78
        ) {
9✔
79
                $this->repository          = $repository;
18✔
80
                $this->options             = $options;
18✔
81
                $this->current_page_helper = $current_page_helper;
18✔
82
                $this->post_type_helper    = $post_type_helper;
18✔
83
                $this->url_helper          = $url_helper;
18✔
84
                $this->pagination_helper   = $pagination_helper;
18✔
85
        }
9✔
86

87
        /**
88
         * Generates the breadcrumbs.
89
         *
90
         * @param Meta_Tags_Context $context The meta tags context.
91
         *
92
         * @return array<array<int,string>> An array of associative arrays that each have a 'text' and a 'url'.
93
         */
94
        public function generate( Meta_Tags_Context $context ) {
36✔
95
                $static_ancestors = [];
36✔
96
                $breadcrumbs_home = $this->options->get( 'breadcrumbs-home' );
36✔
97
                if ( $breadcrumbs_home !== '' && ! \in_array( $this->current_page_helper->get_page_type(), [ 'Home_Page', 'Static_Home_Page' ], true ) ) {
36✔
98
                        $front_page_id = $this->current_page_helper->get_front_page_id();
16✔
99
                        if ( $front_page_id === 0 ) {
16✔
100
                                $home_page_ancestor = $this->repository->find_for_home_page();
12✔
101
                                if ( \is_a( $home_page_ancestor, Indexable::class ) ) {
12✔
102
                                        $static_ancestors[] = $home_page_ancestor;
12✔
103
                                }
104
                        }
105
                        else {
106
                                $static_ancestor = $this->repository->find_by_id_and_type( $front_page_id, 'post' );
4✔
107
                                if ( \is_a( $static_ancestor, Indexable::class ) && $static_ancestor->post_status !== 'unindexed' ) {
4✔
108
                                        $static_ancestors[] = $static_ancestor;
4✔
109
                                }
110
                        }
111
                }
112
                $page_for_posts = \get_option( 'page_for_posts' );
36✔
113
                if ( $this->should_have_blog_crumb( $page_for_posts, $context ) ) {
36✔
114
                        $static_ancestor = $this->repository->find_by_id_and_type( $page_for_posts, 'post' );
4✔
115
                        if ( \is_a( $static_ancestor, Indexable::class ) && $static_ancestor->post_status !== 'unindexed' ) {
4✔
116
                                $static_ancestors[] = $static_ancestor;
4✔
117
                        }
118
                }
119
                if (
120
                        $context->indexable->object_type === 'post'
36✔
121
                        && $context->indexable->object_sub_type !== 'post'
36✔
122
                        && $context->indexable->object_sub_type !== 'page'
36✔
123
                        && $this->post_type_helper->has_archive( $context->indexable->object_sub_type )
36✔
124
                ) {
125
                        $static_ancestor = $this->repository->find_for_post_type_archive( $context->indexable->object_sub_type );
2✔
126
                        if ( \is_a( $static_ancestor, Indexable::class ) ) {
2✔
127
                                $static_ancestors[] = $static_ancestor;
2✔
128
                        }
129
                }
130
                if ( $context->indexable->object_type === 'term' ) {
36✔
131
                        $parent = $this->get_taxonomy_post_type_parent( $context->indexable->object_sub_type );
×
132
                        if ( $parent && $parent !== 'post' && $this->post_type_helper->has_archive( $parent ) ) {
×
133
                                $static_ancestor = $this->repository->find_for_post_type_archive( $parent );
×
134
                                if ( \is_a( $static_ancestor, Indexable::class ) ) {
×
135
                                        $static_ancestors[] = $static_ancestor;
×
136
                                }
137
                        }
138
                }
139
                $indexables = [];
36✔
140
                if ( ! \in_array( $this->current_page_helper->get_page_type(), [ 'Home_Page', 'Static_Home_Page' ], true ) ) {
36✔
141
                        // Get all ancestors of the indexable and append itself to get all indexables in the full crumb.
142
                        $indexables = $this->repository->get_ancestors( $context->indexable );
34✔
143
                }
144
                $indexables[] = $context->indexable;
36✔
145

146
                if ( ! empty( $static_ancestors ) ) {
36✔
147
                        \array_unshift( $indexables, ...$static_ancestors );
16✔
148
                }
149

150
                $indexables = \apply_filters( 'wpseo_breadcrumb_indexables', $indexables, $context );
36✔
151
                $indexables = \is_array( $indexables ) ? $indexables : [];
36✔
152
                $indexables = \array_filter(
36✔
153
                        $indexables,
36✔
154
                        static function ( $indexable ) {
18✔
155
                                return \is_a( $indexable, Indexable::class );
36✔
156
                        }
18✔
157
                );
18✔
158

159
                $crumbs = \array_map( [ $this, 'get_post_type_crumb' ], $indexables );
160

161
                if ( $breadcrumbs_home !== '' ) {
18✔
162
                        $crumbs[0]['text'] = $breadcrumbs_home;
163
                }
164

165
                $crumbs = $this->add_paged_crumb( $crumbs, $context->indexable );
166

167
                /**
168
                 * Filter: 'wpseo_breadcrumb_links' - Allow the developer to filter the Yoast SEO breadcrumb links, add to them, change order, etc.
169
                 *
170
                 * @param array $crumbs The crumbs array.
171
                 */
172
                $filtered_crumbs = \apply_filters( 'wpseo_breadcrumb_links', $crumbs );
173

174
                // Basic check to make sure the filtered crumbs are in an array.
175
                if ( ! \is_array( $filtered_crumbs ) ) {
×
176
                        \_doing_it_wrong(
×
177
                                'Filter: \'wpseo_breadcrumb_links\'',
×
178
                                'The `wpseo_breadcrumb_links` filter should return a multi-dimensional array.',
×
179
                                'YoastSEO v20.0'
180
                        );
181
                }
182
                else {
18✔
183
                        $crumbs = $filtered_crumbs;
184
                }
185

186
                $filter_callback = static function ( $link_info, $index ) use ( $crumbs ) {
18✔
187
                        /**
188
                         * Filter: 'wpseo_breadcrumb_single_link_info' - Allow developers to filter the Yoast SEO Breadcrumb link information.
189
                         *
190
                         * @param array $link_info The breadcrumb link information.
191
                         * @param int   $index     The index of the breadcrumb in the list.
192
                         * @param array $crumbs    The complete list of breadcrumbs.
193
                         */
194
                        return \apply_filters( 'wpseo_breadcrumb_single_link_info', $link_info, $index, $crumbs );
18✔
195
                };
36✔
196
                return \array_map( $filter_callback, $crumbs, \array_keys( $crumbs ) );
197
        }
198

199
        /**
200
         * Returns the modified post crumb.
201
         *
202
         * @param string[]  $crumb    The crumb.
203
         * @param Indexable $ancestor The indexable.
204
         *
205
         * @return array<int,string> The crumb.
206
         */
207
        private function get_post_crumb( $crumb, $ancestor ) {
208
                $crumb['id'] = $ancestor->object_id;
209

210
                return $crumb;
211
        }
212

213
        /**
214
         * Adds the correct ID to the crumb array based on the ancestor provided.
215
         *
216
         * @param Indexable $ancestor The ancestor indexable.
217
         *
218
         * @return string[]
219
         */
220
        private function get_post_type_crumb( Indexable $ancestor ) {
×
UNCOV
221
                $crumb = [
×
222
                        'url'  => $ancestor->permalink,
×
223
                        'text' => $ancestor->breadcrumb_title,
224
                ];
225

226
                switch ( $ancestor->object_type ) {
×
227
                        case 'post':
×
228
                                $crumb = $this->get_post_crumb( $crumb, $ancestor );
×
229
                                break;
×
230
                        case 'post-type-archive':
×
231
                                $crumb = $this->get_post_type_archive_crumb( $crumb, $ancestor );
×
232
                                break;
×
233
                        case 'term':
×
234
                                $crumb = $this->get_term_crumb( $crumb, $ancestor );
×
235
                                break;
×
236
                        case 'system-page':
×
237
                                $crumb = $this->get_system_page_crumb( $crumb, $ancestor );
×
238
                                break;
×
239
                        case 'user':
×
240
                                $crumb = $this->get_user_crumb( $crumb, $ancestor );
×
241
                                break;
×
242
                        case 'date-archive':
×
243
                                $crumb = $this->get_date_archive_crumb( $crumb );
×
244
                                break;
245
                        default:
246
                                // Handle unknown object types (optional).
247
                                break;
248
                }
249

250
                return $crumb;
251
        }
252

253
        /**
254
         * Returns the modified post type crumb.
255
         *
256
         * @param string[]  $crumb    The crumb.
257
         * @param Indexable $ancestor The indexable.
258
         *
259
         * @return string[] The crumb.
260
         */
261
        private function get_post_type_archive_crumb( $crumb, $ancestor ) {
262
                $crumb['ptarchive'] = $ancestor->object_sub_type;
263

264
                return $crumb;
265
        }
266

267
        /**
268
         * Returns the modified term crumb.
269
         *
270
         * @param string[]  $crumb    The crumb.
271
         * @param Indexable $ancestor The indexable.
272
         *
273
         * @return array<int,string> The crumb.
274
         */
275
        private function get_term_crumb( $crumb, $ancestor ) {
×
276
                $crumb['term_id']  = $ancestor->object_id;
×
277
                $crumb['taxonomy'] = $ancestor->object_sub_type;
278

279
                return $crumb;
280
        }
281

282
        /**
283
         * Returns the modified system page crumb.
284
         *
285
         * @param string[]  $crumb    The crumb.
286
         * @param Indexable $ancestor The indexable.
287
         *
288
         * @return string[] The crumb.
289
         */
290
        private function get_system_page_crumb( $crumb, $ancestor ) {
×
291
                if ( $ancestor->object_sub_type === 'search-result' ) {
×
292
                        $crumb['text'] = $this->options->get( 'breadcrumbs-searchprefix' ) . ' ' . \esc_html( \get_search_query() );
×
293
                        $crumb['url']  = \get_search_link();
294
                }
295
                elseif ( $ancestor->object_sub_type === '404' ) {
×
296
                        $crumb['text'] = $this->options->get( 'breadcrumbs-404crumb' );
297
                }
298

299
                return $crumb;
300
        }
301

302
        /**
303
         * Returns the modified user crumb.
304
         *
305
         * @param string[]  $crumb    The crumb.
306
         * @param Indexable $ancestor The indexable.
307
         *
308
         * @return string[] The crumb.
309
         */
310
        private function get_user_crumb( $crumb, $ancestor ) {
×
311
                $display_name  = \get_the_author_meta( 'display_name', $ancestor->object_id );
×
312
                $crumb['text'] = $this->options->get( 'breadcrumbs-archiveprefix' ) . ' ' . $display_name;
313

314
                return $crumb;
315
        }
316

317
        /**
318
         * Returns the modified date archive crumb.
319
         *
320
         * @param string[] $crumb The crumb.
321
         *
322
         * @return string[] The crumb.
323
         */
324
        protected function get_date_archive_crumb( $crumb ) {
18✔
325
                $home_url = $this->url_helper->home();
18✔
326
                $prefix   = $this->options->get( 'breadcrumbs-archiveprefix' );
327

328
                if ( \is_day() ) {
6✔
329
                        $day           = \esc_html( \get_the_date() );
6✔
330
                        $crumb['url']  = $home_url . \get_the_date( 'Y/m/d' ) . '/';
6✔
331
                        $crumb['text'] = $prefix . ' ' . $day;
332
                }
6✔
333
                elseif ( \is_month() ) {
6✔
334
                        $month         = \esc_html( \trim( \single_month_title( ' ', false ) ) );
6✔
335
                        $crumb['url']  = $home_url . \get_the_date( 'Y/m' ) . '/';
6✔
336
                        $crumb['text'] = $prefix . ' ' . $month;
337
                }
3✔
338
                elseif ( \is_year() ) {
6✔
339
                        $year          = \get_the_date( 'Y' );
6✔
340
                        $crumb['url']  = $home_url . $year . '/';
6✔
341
                        $crumb['text'] = $prefix . ' ' . $year;
342
                }
343

344
                return $crumb;
345
        }
346

347
        /**
348
         * Returns whether or not a blog crumb should be added.
349
         *
350
         * @param int               $page_for_posts The page for posts ID.
351
         * @param Meta_Tags_Context $context        The meta tags context.
352
         *
353
         * @return bool Whether or not a blog crumb should be added.
354
         */
355
        protected function should_have_blog_crumb( $page_for_posts, $context ) {
8✔
356
                // When there is no page configured as blog page.
357
                if ( \get_option( 'show_on_front' ) !== 'page' || ! $page_for_posts ) {
6✔
358
                        return false;
359
                }
360

361
                if ( $context->indexable->object_type === 'term' ) {
×
362
                        $parent = $this->get_taxonomy_post_type_parent( $context->indexable->object_sub_type );
×
363
                        return $parent === 'post';
364
                }
365

366
                if ( $this->options->get( 'breadcrumbs-display-blog-page' ) !== true ) {
×
367
                        return false;
368
                }
369

370
                // When the current page is the home page, searchpage or isn't a singular post.
371
                if ( \is_home() || \is_search() || ! \is_singular( 'post' ) ) {
8✔
372
                        return false;
373
                }
374

375
                return true;
376
        }
377

378
        /**
379
         * Returns the post type parent of a given taxonomy.
380
         *
381
         * @param string $taxonomy The taxonomy.
382
         *
383
         * @return string|false The parent if it exists, false otherwise.
384
         */
385
        protected function get_taxonomy_post_type_parent( $taxonomy ) {
×
386
                $parent = $this->options->get( 'taxonomy-' . $taxonomy . '-ptparent' );
387

388
                if ( empty( $parent ) || (string) $parent === '0' ) {
×
389
                        return false;
390
                }
391

392
                return $parent;
393
        }
394

395
        /**
396
         * Adds a crumb for the current page, if we're on an archive page or paginated post.
397
         *
398
         * @param string[]  $crumbs            The array of breadcrumbs.
399
         * @param Indexable $current_indexable The current indexable.
400
         *
401
         * @return string[] The breadcrumbs.
402
         */
403
        protected function add_paged_crumb( array $crumbs, $current_indexable ) {
12✔
404
                $is_simple_page = $this->current_page_helper->is_simple_page();
405

406
                // If we're not on a paged page do nothing.
407
                if ( ! $is_simple_page && ! $this->current_page_helper->is_paged() ) {
6✔
408
                        return $crumbs;
409
                }
410

411
                // If we're not on a paginated post do nothing.
412
                if ( $is_simple_page && $current_indexable->number_of_pages === null ) {
×
413
                        return $crumbs;
414
                }
415

416
                $current_page_number = $this->pagination_helper->get_current_page_number();
12✔
417
                if ( $current_page_number <= 1 ) {
6✔
418
                        return $crumbs;
419
                }
420

421
                $crumbs[] = [
6✔
422
                        'text' => \sprintf(
3✔
423
                                /* translators: %s expands to the current page number */
424
                                \__( 'Page %s', 'wordpress-seo' ),
6✔
425
                                $current_page_number
3✔
426
                        ),
3✔
427
                ];
3✔
428

429
                return $crumbs;
430
        }
431
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc