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