PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / trunk
Yoast SEO – Advanced SEO with real-time guidance and built-in AI vtrunk
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
wordpress-seo / src / helpers / current-page-helper.php

current-page-helper.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI trunk, at src/helpers/current-page-helper.php

593 lines 16.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Yoast\WP\SEO\Helpers;
4
5 use WP_Post;
6 use Yoast\WP\SEO\Wrappers\WP_Query_Wrapper;
7
8 /**
9 * A helper object for WordPress posts.
10 */
11 class Current_Page_Helper {
12
13 /**
14 * The WP Query wrapper.
15 *
16 * @var WP_Query_Wrapper
17 */
18 private $wp_query_wrapper;
19
20 /**
21 * Current_Page_Helper constructor.
22 *
23 * @codeCoverageIgnore It only sets dependencies.
24 *
25 * @param WP_Query_Wrapper $wp_query_wrapper The wrapper for WP_Query.
26 */
27 public function __construct( WP_Query_Wrapper $wp_query_wrapper ) {
28 $this->wp_query_wrapper = $wp_query_wrapper;
29 }
30
31 /**
32 * Returns the page type for the current request.
33 *
34 * @codeCoverageIgnore It just depends on other functions for its result.
35 *
36 * @return string Page type.
37 */
38 public function get_page_type() {
39 switch ( true ) {
40 case $this->is_search_result():
41 return 'Search_Result_Page';
42 case $this->is_static_posts_page():
43 return 'Static_Posts_Page';
44 case $this->is_home_static_page():
45 return 'Static_Home_Page';
46 case $this->is_home_posts_page():
47 return 'Home_Page';
48 case $this->is_simple_page():
49 return 'Post_Type';
50 case $this->is_post_type_archive():
51 return 'Post_Type_Archive';
52 case $this->is_term_archive():
53 return 'Term_Archive';
54 case $this->is_author_archive():
55 return 'Author_Archive';
56 case $this->is_date_archive():
57 return 'Date_Archive';
58 case $this->is_404():
59 return 'Error_Page';
60 }
61
62 return 'Fallback';
63 }
64
65 /**
66 * Checks if the currently opened page is a simple page.
67 *
68 * @return bool Whether the currently opened page is a simple page.
69 */
70 public function is_simple_page() {
71 return $this->get_simple_page_id() > 0;
72 }
73
74 /**
75 * Returns the id of the currently opened page.
76 *
77 * @return int The id of the currently opened page.
78 */
79 public function get_simple_page_id() {
80 if ( \is_singular() ) {
81 return \get_the_ID();
82 }
83
84 if ( $this->is_posts_page() ) {
85 return \get_option( 'page_for_posts' );
86 }
87
88 /**
89 * Filter: Allow changing the default page id.
90 *
91 * @param int $page_id The default page id.
92 */
93 return \apply_filters( 'wpseo_frontend_page_type_simple_page_id', 0 );
94 }
95
96 /**
97 * Returns the id of the currently opened author archive.
98 *
99 * @codeCoverageIgnore It wraps WordPress functionality.
100 *
101 * @return int The id of the currently opened author archive.
102 */
103 public function get_author_id() {
104 $wp_query = $this->wp_query_wrapper->get_main_query();
105
106 return $wp_query->get( 'author' );
107 }
108
109 /**
110 * Returns the id of the front page.
111 *
112 * @return int The id of the front page. 0 if the front page is not a static page.
113 */
114 public function get_front_page_id() {
115 if ( \get_option( 'show_on_front' ) !== 'page' ) {
116 return 0;
117 }
118
119 return (int) \get_option( 'page_on_front' );
120 }
121
122 /**
123 * Returns the id of the currently opened term archive.
124 *
125 * @return int The id of the currently opened term archive.
126 */
127 public function get_term_id() {
128 $wp_query = $this->wp_query_wrapper->get_main_query();
129
130 if ( $wp_query->is_tax() || $wp_query->is_tag() || $wp_query->is_category() ) {
131 $queried_object = $wp_query->get_queried_object();
132 if ( $queried_object && ! \is_wp_error( $queried_object ) ) {
133 return $queried_object->term_id;
134 }
135 }
136
137 return 0;
138 }
139
140 /**
141 * Returns the post type of the main query.
142 *
143 * @return string The post type of the main query.
144 */
145 public function get_queried_post_type() {
146 $wp_query = $this->wp_query_wrapper->get_main_query();
147 $post_type = $wp_query->get( 'post_type' );
148 if ( \is_array( $post_type ) ) {
149 $post_type = \reset( $post_type );
150 }
151
152 return $post_type;
153 }
154
155 /**
156 * Returns the permalink of the currently opened date archive.
157 * If the permalink was cached, it returns this permalink.
158 * If not, we call another function to get the permalink through wp_query.
159 *
160 * @return string The permalink of the currently opened date archive.
161 */
162 public function get_date_archive_permalink() {
163 static $date_archive_permalink;
164
165 if ( isset( $date_archive_permalink ) ) {
166 return $date_archive_permalink;
167 }
168
169 $date_archive_permalink = $this->get_non_cached_date_archive_permalink();
170
171 return $date_archive_permalink;
172 }
173
174 /**
175 * Determine whether this is the homepage and shows posts.
176 *
177 * @return bool Whether or not the current page is the homepage that displays posts.
178 */
179 public function is_home_posts_page() {
180 $wp_query = $this->wp_query_wrapper->get_main_query();
181
182 if ( ! $wp_query->is_home() ) {
183 return false;
184 }
185
186 /*
187 * Whether the static page's `Homepage` option is actually not set to a page.
188 * Otherwise WordPress proceeds to handle the homepage as a `Your latest posts` page.
189 */
190 if ( (int) \get_option( 'page_on_front' ) === 0 ) {
191 return true;
192 }
193
194 return \get_option( 'show_on_front' ) === 'posts';
195 }
196
197 /**
198 * Determine whether this is the static frontpage.
199 *
200 * @return bool Whether or not the current page is a static frontpage.
201 */
202 public function is_home_static_page() {
203 $wp_query = $this->wp_query_wrapper->get_main_query();
204
205 if ( ! $wp_query->is_front_page() ) {
206 return false;
207 }
208
209 if ( \get_option( 'show_on_front' ) !== 'page' ) {
210 return false;
211 }
212
213 return $wp_query->is_page( \get_option( 'page_on_front' ) );
214 }
215
216 /**
217 * Determine whether this is the static posts page.
218 *
219 * @return bool Whether or not the current page is a static posts page.
220 */
221 public function is_static_posts_page() {
222 $wp_query = $this->wp_query_wrapper->get_main_query();
223 $queried_object = $wp_query->get_queried_object();
224
225 return (
226 $wp_query->is_posts_page
227 && \is_a( $queried_object, WP_Post::class )
228 && $queried_object->post_type === 'page'
229 );
230 }
231
232 /**
233 * Determine whether this is the statically set posts page, when it's not the frontpage.
234 *
235 * @return bool Whether or not it's a non-frontpage, statically set posts page.
236 */
237 public function is_posts_page() {
238 $wp_query = $this->wp_query_wrapper->get_main_query();
239
240 if ( ! $wp_query->is_home() ) {
241 return false;
242 }
243
244 return \get_option( 'show_on_front' ) === 'page';
245 }
246
247 /**
248 * Determine whether this is a post type archive.
249 *
250 * @codeCoverageIgnore It wraps WordPress functionality.
251 *
252 * @return bool Whether nor not the current page is a post type archive.
253 */
254 public function is_post_type_archive() {
255 $wp_query = $this->wp_query_wrapper->get_main_query();
256
257 return $wp_query->is_post_type_archive();
258 }
259
260 /**
261 * Determine whether this is a term archive.
262 *
263 * @codeCoverageIgnore It wraps WordPress functionality.
264 *
265 * @return bool Whether nor not the current page is a term archive.
266 */
267 public function is_term_archive() {
268 $wp_query = $this->wp_query_wrapper->get_main_query();
269
270 return $wp_query->is_tax || $wp_query->is_tag || $wp_query->is_category;
271 }
272
273 /**
274 * Determine whether this is an attachment page.
275 *
276 * @codeCoverageIgnore It wraps WordPress functionality.
277 *
278 * @return bool Whether nor not the current page is an attachment page.
279 */
280 public function is_attachment() {
281 $wp_query = $this->wp_query_wrapper->get_main_query();
282
283 return $wp_query->is_attachment;
284 }
285
286 /**
287 * Determine whether this is an author archive.
288 *
289 * @codeCoverageIgnore It wraps WordPress functionality.
290 *
291 * @return bool Whether nor not the current page is an author archive.
292 */
293 public function is_author_archive() {
294 $wp_query = $this->wp_query_wrapper->get_main_query();
295
296 return $wp_query->is_author();
297 }
298
299 /**
300 * Determine whether this is an date archive.
301 *
302 * @codeCoverageIgnore It wraps WordPress functionality.
303 *
304 * @return bool Whether nor not the current page is an date archive.
305 */
306 public function is_date_archive() {
307 $wp_query = $this->wp_query_wrapper->get_main_query();
308
309 return $wp_query->is_date();
310 }
311
312 /**
313 * Determine whether this is a search result.
314 *
315 * @codeCoverageIgnore It wraps WordPress functionality.
316 *
317 * @return bool Whether nor not the current page is a search result.
318 */
319 public function is_search_result() {
320 $wp_query = $this->wp_query_wrapper->get_main_query();
321
322 return $wp_query->is_search();
323 }
324
325 /**
326 * Determine whether this is a 404 page.
327 *
328 * @codeCoverageIgnore It wraps WordPress functionality.
329 *
330 * @return bool Whether nor not the current page is a 404 page.
331 */
332 public function is_404() {
333 $wp_query = $this->wp_query_wrapper->get_main_query();
334
335 return $wp_query->is_404();
336 }
337
338 /**
339 * Checks if the current page is the post format archive.
340 *
341 * @codeCoverageIgnore It wraps WordPress functionality.
342 *
343 * @return bool Whether or not the current page is the post format archive.
344 */
345 public function is_post_format_archive() {
346 $wp_query = $this->wp_query_wrapper->get_main_query();
347
348 return $wp_query->is_tax( 'post_format' );
349 }
350
351 /**
352 * Determine whether this page is an taxonomy archive page for multiple terms (url: /term-1,term2/).
353 *
354 * @return bool Whether or not the current page is an archive page for multiple terms.
355 */
356 public function is_multiple_terms_page() {
357 if ( ! $this->is_term_archive() ) {
358 return false;
359 }
360
361 return $this->count_queried_terms() > 1;
362 }
363
364 /**
365 * Checks whether the current page is paged.
366 *
367 * @codeCoverageIgnore This method only calls a WordPress function.
368 *
369 * @return bool Whether the current page is paged.
370 */
371 public function is_paged() {
372 return \is_paged();
373 }
374
375 /**
376 * Checks if the current page is the front page.
377 *
378 * @codeCoverageIgnore It wraps WordPress functionality.
379 *
380 * @return bool Whether or not the current page is the front page.
381 */
382 public function is_front_page() {
383 $wp_query = $this->wp_query_wrapper->get_main_query();
384
385 return $wp_query->is_front_page();
386 }
387
388 /**
389 * Retrieves the current admin page.
390 *
391 * @codeCoverageIgnore It only wraps a global WordPress variable.
392 *
393 * @return string The current page.
394 */
395 public function get_current_admin_page() {
396 global $pagenow;
397
398 return $pagenow;
399 }
400
401 /**
402 * Determines whether the current admin screen is the block editor.
403 *
404 * Deliberately uses get_current_screen() and not WP_Screen::get(): the latter re-applies
405 * the replace_editor filter on every call, which is a render hook with side effects
406 * (e.g. it breaks the WooCommerce block email editor).
407 *
408 * Only reliable after set_current_screen() has run (during admin page load, after
409 * admin_init); earlier calls return false.
410 *
411 * @return bool Whether the current screen is the block editor.
412 */
413 public function is_block_editor() {
414 if ( ! \function_exists( 'get_current_screen' ) ) {
415 return false;
416 }
417
418 $screen = \get_current_screen();
419
420 return $screen !== null && $screen->is_block_editor();
421 }
422
423 /**
424 * Check if the current opened page is a Yoast SEO page.
425 *
426 * @return bool True when current page is a yoast seo plugin page.
427 */
428 public function is_yoast_seo_page() {
429 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
430 if ( isset( $_GET['page'] ) && \is_string( $_GET['page'] ) ) {
431 // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: We are not processing form information, We are only using the variable in the strpos function.
432 $current_page = \wp_unslash( $_GET['page'] );
433 return \strpos( $current_page, 'wpseo_' ) === 0;
434 }
435 return false;
436 }
437
438 /**
439 * Returns the current Yoast SEO page.
440 * (E.g. the `page` query variable in the URL).
441 *
442 * @return string The current Yoast SEO page.
443 */
444 public function get_current_yoast_seo_page() {
445 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
446 if ( isset( $_GET['page'] ) && \is_string( $_GET['page'] ) ) {
447 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
448 return \sanitize_text_field( \wp_unslash( $_GET['page'] ) );
449 }
450
451 return '';
452 }
453
454 /**
455 * Returns whether the current admin overview (list table) shows the trash.
456 *
457 * Mirrors the check WP_Posts_List_Table itself uses to decide it is on the trash view.
458 *
459 * @return bool Whether the current admin overview shows the trash.
460 */
461 public function is_trash_overview(): bool {
462 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
463 return isset( $_REQUEST['post_status'] ) && $_REQUEST['post_status'] === 'trash';
464 }
465
466 /**
467 * Checks if the current global post is the privacy policy page.
468 *
469 * @return bool current global post is set as privacy page
470 */
471 public function current_post_is_privacy_policy() {
472 global $post;
473
474 if ( ! isset( $post->ID ) ) {
475 return false;
476 }
477
478 return (int) $post->ID === (int) \get_option( 'wp_page_for_privacy_policy', false );
479 }
480
481 /**
482 * Returns the permalink of the currently opened date archive.
483 *
484 * @return string The permalink of the currently opened date archive.
485 */
486 protected function get_non_cached_date_archive_permalink() {
487 $date_archive_permalink = '';
488 $wp_query = $this->wp_query_wrapper->get_main_query();
489
490 if ( $wp_query->is_day() ) {
491 $date_archive_permalink = \get_day_link( $wp_query->get( 'year' ), $wp_query->get( 'monthnum' ), $wp_query->get( 'day' ) );
492 }
493 if ( $wp_query->is_month() ) {
494 $date_archive_permalink = \get_month_link( $wp_query->get( 'year' ), $wp_query->get( 'monthnum' ) );
495 }
496 if ( $wp_query->is_year() ) {
497 $date_archive_permalink = \get_year_link( $wp_query->get( 'year' ) );
498 }
499
500 return $date_archive_permalink;
501 }
502
503 /**
504 * Counts the total amount of queried terms.
505 *
506 * @codeCoverageIgnore This relies too much on WordPress dependencies.
507 *
508 * @return int The amoumt of queried terms.
509 */
510 protected function count_queried_terms() {
511 $wp_query = $this->wp_query_wrapper->get_main_query();
512 $term = $wp_query->get_queried_object();
513
514 $queried_terms = $wp_query->tax_query->queried_terms;
515 if ( $term === null || empty( $queried_terms[ $term->taxonomy ]['terms'] ) ) {
516 return 0;
517 }
518
519 return \count( $queried_terms[ $term->taxonomy ]['terms'] );
520 }
521
522 /**
523 * Retrieves the current post id.
524 * Returns 0 if no post id is found.
525 *
526 * @return int The post id.
527 */
528 public function get_current_post_id(): int {
529 // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: We are not processing form information, We are casting to an integer.
530 if ( isset( $_GET['post'] ) && \is_string( $_GET['post'] ) && (int) \wp_unslash( $_GET['post'] ) > 0 ) {
531 // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: We are not processing form information, We are casting to an integer, also this is a helper function.
532 return (int) \wp_unslash( $_GET['post'] );
533 }
534 return 0;
535 }
536
537 /**
538 * Retrieves the current post type.
539 *
540 * @return string The post type.
541 */
542 public function get_current_post_type(): string {
543 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
544 if ( isset( $_GET['post_type'] ) && \is_string( $_GET['post_type'] ) ) {
545 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
546 return \sanitize_text_field( \wp_unslash( $_GET['post_type'] ) );
547 }
548
549 // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason: should be done outside the helper function.
550 if ( isset( $_POST['post_type'] ) && \is_string( $_POST['post_type'] ) ) {
551 // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason: should be done outside the helper function.
552 return \sanitize_text_field( \wp_unslash( $_POST['post_type'] ) );
553 }
554
555 $post_id = $this->get_current_post_id();
556
557 if ( $post_id ) {
558 return \get_post_type( $post_id );
559 }
560
561 return 'post';
562 }
563
564 /**
565 * Retrieves the current taxonomy.
566 *
567 * @return string The taxonomy.
568 */
569 public function get_current_taxonomy(): string {
570 if ( ! isset( $_SERVER['REQUEST_METHOD'] ) || ! \in_array( $_SERVER['REQUEST_METHOD'], [ 'GET', 'POST' ], true ) ) {
571 return '';
572 }
573
574 // phpcs:ignore WordPress.Security.NonceVerification -- Reason: We are not processing form information.
575 if ( $_SERVER['REQUEST_METHOD'] === 'POST' ) {
576 // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason: should be done outside the helper function.
577 if ( isset( $_POST['taxonomy'] ) && \is_string( $_POST['taxonomy'] ) ) {
578 // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason: should be done outside the helper function.
579 return \sanitize_text_field( \wp_unslash( $_POST['taxonomy'] ) );
580 }
581 return '';
582 }
583
584 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
585 if ( isset( $_GET['taxonomy'] ) && \is_string( $_GET['taxonomy'] ) ) {
586 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
587 return \sanitize_text_field( \wp_unslash( $_GET['taxonomy'] ) );
588 }
589
590 return '';
591 }
592 }
593