PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 27.9
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v27.9
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 / integrations / front-end / feed-improvements.php

feed-improvements.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 27.9, at src/integrations/front-end/feed-improvements.php

183 lines 4.7 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\Integrations\Front_End;
4
5 use Yoast\WP\SEO\Conditionals\Front_End_Conditional;
6 use Yoast\WP\SEO\Helpers\Options_Helper;
7 use Yoast\WP\SEO\Integrations\Integration_Interface;
8 use Yoast\WP\SEO\Surfaces\Meta_Surface;
9
10 /**
11 * Class Feed_Improvements
12 */
13 class Feed_Improvements implements Integration_Interface {
14
15 /**
16 * Holds the options helper.
17 *
18 * @var Options_Helper
19 */
20 private $options;
21
22 /**
23 * Holds the meta helper surface.
24 *
25 * @var Meta_Surface
26 */
27 private $meta;
28
29 /**
30 * Canonical_Header constructor.
31 *
32 * @codeCoverageIgnore It only sets depedencies.
33 *
34 * @param Options_Helper $options The options helper.
35 * @param Meta_Surface $meta The meta surface.
36 */
37 public function __construct( Options_Helper $options, Meta_Surface $meta ) {
38 $this->options = $options;
39 $this->meta = $meta;
40 }
41
42 /**
43 * Returns the conditionals based in which this loadable should be active.
44 *
45 * @return array
46 */
47 public static function get_conditionals() {
48 return [ Front_End_Conditional::class ];
49 }
50
51 /**
52 * Registers hooks to WordPress.
53 *
54 * @return void
55 */
56 public function register_hooks() {
57 \add_filter( 'get_bloginfo_rss', [ $this, 'filter_bloginfo_rss' ], 10, 2 );
58 \add_filter( 'document_title_separator', [ $this, 'filter_document_title_separator' ] );
59
60 \add_action( 'do_feed_rss', [ $this, 'handle_rss_feed' ], 9 );
61 \add_action( 'do_feed_rss2', [ $this, 'send_canonical_header' ], 9 );
62 \add_action( 'do_feed_rss2', [ $this, 'add_robots_headers' ], 9 );
63 }
64
65 /**
66 * Filter `bloginfo_rss` output to give the URL for what's being shown instead of just always the homepage.
67 *
68 * @param string $show The output so far.
69 * @param string $what What is being shown.
70 *
71 * @return string
72 */
73 public function filter_bloginfo_rss( $show, $what ) {
74 if ( $what === 'url' ) {
75 return $this->get_url_for_queried_object( $show );
76 }
77
78 return $show;
79 }
80
81 /**
82 * Makes sure send canonical header always runs, because this RSS hook does not support the for_comments parameter
83 *
84 * @return void
85 */
86 public function handle_rss_feed() {
87 $this->send_canonical_header( false );
88 }
89
90 /**
91 * Adds a canonical link header to the main canonical URL for the requested feed object. If it is not a comment
92 * feed.
93 *
94 * @param bool $for_comments If the RRS feed is meant for a comment feed.
95 *
96 * @return void
97 */
98 public function send_canonical_header( $for_comments ) {
99
100 if ( $for_comments || \headers_sent() ) {
101 return;
102 }
103
104 $queried_object = \get_queried_object();
105 // Don't call get_class with null. This gives a warning.
106 $class = ( $queried_object !== null ) ? \get_class( $queried_object ) : null;
107
108 $url = $this->get_url_for_queried_object( $this->meta->for_home_page()->canonical );
109 if ( ( ! empty( $url ) && $url !== $this->meta->for_home_page()->canonical ) || $class === null ) {
110 \header( \sprintf( 'Link: <%s>; rel="canonical"', $url ), false );
111 }
112 }
113
114 /**
115 * Adds noindex, follow tag for comment feeds.
116 *
117 * @param bool $for_comments If the RSS feed is meant for a comment feed.
118 *
119 * @return void
120 */
121 public function add_robots_headers( $for_comments ) {
122 if ( $for_comments && ! \headers_sent() ) {
123 \header( 'X-Robots-Tag: noindex, follow', true );
124 }
125 }
126
127 /**
128 * Makes sure the title separator set in Yoast SEO is used for all feeds.
129 *
130 * @param string $separator The separator from WordPress.
131 *
132 * @return string The separator from Yoast SEO's settings.
133 */
134 public function filter_document_title_separator( $separator ) {
135 return \html_entity_decode( $this->options->get_title_separator() );
136 }
137
138 /**
139 * Determines the main URL for the queried object.
140 *
141 * @param string $url The URL determined so far.
142 *
143 * @return string The canonical URL for the queried object.
144 */
145 protected function get_url_for_queried_object( $url = '' ) {
146 $queried_object = \get_queried_object();
147 // Don't call get_class with null. This gives a warning.
148 $class = ( $queried_object !== null ) ? \get_class( $queried_object ) : null;
149 $meta = false;
150
151 switch ( $class ) {
152 // Post type archive feeds.
153 case 'WP_Post_Type':
154 $meta = $this->meta->for_post_type_archive( $queried_object->name );
155 break;
156 // Post comment feeds.
157 case 'WP_Post':
158 $meta = $this->meta->for_post( $queried_object->ID );
159 break;
160 // Term feeds.
161 case 'WP_Term':
162 $meta = $this->meta->for_term( $queried_object->term_id );
163 break;
164 // Author feeds.
165 case 'WP_User':
166 $meta = $this->meta->for_author( $queried_object->ID );
167 break;
168 // This would be NULL on the home page and on date archive feeds.
169 case null:
170 $meta = $this->meta->for_home_page();
171 break;
172 default:
173 break;
174 }
175
176 if ( $meta ) {
177 return $meta->canonical;
178 }
179
180 return $url;
181 }
182 }
183