: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
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.
* Holds the meta helper surface.
* 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(
$this->options = $options;
* Returns the conditionals based in which this loadable should be active.
public static function get_conditionals() {
return [ Front_End_Conditional::class ];
* Registers hooks to WordPress.
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.
public function filter_bloginfo_rss( $show, $what ) {
return $this->get_url_for_queried_object( $show );
* Makes sure send canonical header always runs, because this RSS hook does not support the for_comments parameter
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
* @param bool $for_comments If the RRS feed is meant for a comment feed.
public function send_canonical_header( $for_comments ) {
if ( $for_comments || \headers_sent() ) {
$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;
$url = $this->get_url_for_queried_object( $this->meta->for_home_page()->canonical );
if ( ( ! empty( $url ) && $url !== $this->meta->for_home_page()->canonical ) || $class === null ) {
\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.
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;
// Post type archive feeds.
$meta = $this->meta->for_post_type_archive( $queried_object->name );
$meta = $this->meta->for_post( $queried_object->ID );
$meta = $this->meta->for_term( $queried_object->term_id );
$meta = $this->meta->for_author( $queried_object->ID );
// This would be NULL on the home page and on date archive feeds.
$meta = $this->meta->for_home_page();