: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* @param array $score_filters Array containing the score filters.
* @return array Array containing the score filters that need to be applied to the meta query.
protected function determine_score_filters( $score_filters ) {
if ( count( $score_filters ) > 1 ) {
return array_merge( [ 'relation' => 'AND' ], $score_filters );
* Retrieves the post type from the $_GET variable.
* @return string|null The sanitized current post type or null when the variable is not set in $_GET.
public function get_current_post_type() {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
if ( isset( $_GET['post_type'] ) && is_string( $_GET['post_type'] ) ) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
return sanitize_text_field( wp_unslash( $_GET['post_type'] ) );
* Retrieves the SEO filter from the $_GET variable.
* @return string|null The sanitized seo filter or null when the variable is not set in $_GET.
public function get_current_seo_filter() {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
if ( isset( $_GET['seo_filter'] ) && is_string( $_GET['seo_filter'] ) ) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
return sanitize_text_field( wp_unslash( $_GET['seo_filter'] ) );
* Retrieves the Readability filter from the $_GET variable.
* @return string|null The sanitized readability filter or null when the variable is not set in $_GET.
public function get_current_readability_filter() {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
if ( isset( $_GET['readability_filter'] ) && is_string( $_GET['readability_filter'] ) ) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
return sanitize_text_field( wp_unslash( $_GET['readability_filter'] ) );
* Retrieves the keyword filter from the $_GET variable.
* @return string|null The sanitized seo keyword filter or null when the variable is not set in $_GET.
public function get_current_keyword_filter() {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
if ( isset( $_GET['seo_kw_filter'] ) && is_string( $_GET['seo_kw_filter'] ) ) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
return sanitize_text_field( wp_unslash( $_GET['seo_kw_filter'] ) );
* Uses the vars to create a complete filter query that can later be executed to filter out posts.
* @param array $vars Array containing the variables that will be used in the meta query.
* @param array $filters Array containing the filters that we need to apply in the meta query.
* @return array Array containing the complete filter query.
protected function build_filter_query( $vars, $filters ) {
// If no filters were applied, just return everything.
if ( count( $filters ) === 0 ) {
$result = [ 'meta_query' => [] ];
$result['meta_query'] = array_merge( $result['meta_query'], [ $this->determine_score_filters( $filters ) ] );
$current_seo_filter = $this->get_current_seo_filter();
// This only applies for the SEO score filter because it can because the SEO score can be altered by the no-index option.
if ( $this->is_valid_filter( $current_seo_filter ) && ! in_array( $current_seo_filter, [ WPSEO_Rank::NO_INDEX, WPSEO_Rank::NO_FOCUS ], true ) ) {
$result['meta_query'] = array_merge( $result['meta_query'], [ $this->get_meta_robots_query_values() ] );
return array_merge( $vars, $result );
* Creates a Readability score filter.
* @param number $low The lower boundary of the score.
* @param number $high The higher boundary of the score.
* @return array The Readability Score filter.
protected function create_readability_score_filter( $low, $high ) {
'key' => WPSEO_Meta::$meta_prefix . 'content_score',
'value' => [ $low, $high ],
* Creates an SEO score filter.
* @param number $low The lower boundary of the score.
* @param number $high The higher boundary of the score.
* @return array The SEO score filter.
protected function create_seo_score_filter( $low, $high ) {
'key' => WPSEO_Meta::$meta_prefix . 'linkdex',
'value' => [ $low, $high ],
* Creates a filter to retrieve posts that were set to no-index.
* @return array Array containin the no-index filter.
protected function create_no_index_filter() {
'key' => WPSEO_Meta::$meta_prefix . 'meta-robots-noindex',
* Creates a filter to retrieve posts that have no keyword set.
* @return array Array containing the no focus keyword filter.
protected function create_no_focus_keyword_filter() {
'key' => WPSEO_Meta::$meta_prefix . 'meta-robots-noindex',
'value' => 'needs-a-value-anyway',
'compare' => 'NOT EXISTS',
'key' => WPSEO_Meta::$meta_prefix . 'linkdex',
'value' => 'needs-a-value-anyway',
'compare' => 'NOT EXISTS',
* Determines whether a particular post_id is of an indexable post type.
* @param string $post_id The post ID to check.
* @return bool Whether or not it is indexable.
protected function is_indexable( $post_id ) {
if ( ! empty( $post_id ) && ! $this->uses_default_indexing( $post_id ) ) {
return WPSEO_Meta::get_value( 'meta-robots-noindex', $post_id ) === '2';
$post = get_post( $post_id );
if ( is_object( $post ) ) {
// If the option is false, this means we want to index it.
return WPSEO_Options::get( 'noindex-' . $post->post_type, false ) === false;
* Determines whether the given post ID uses the default indexing settings.
* @param int $post_id The post ID to check.
* @return bool Whether or not the default indexing is being used for the post.
protected function uses_default_indexing( $post_id ) {
return WPSEO_Meta::get_value( 'meta-robots-noindex', $post_id ) === '0';
* Returns filters when $order_by is matched in the if-statement.
* @param string $order_by The ID of the column by which to order the posts.
* @return array Array containing the order filters.
private function filter_order_by( $order_by ) {
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Reason: Only used when user requests sorting.
'meta_key' => WPSEO_Meta::$meta_prefix . 'metadesc',
'orderby' => 'meta_value',
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Reason: Only used when user requests sorting.
'meta_key' => WPSEO_Meta::$meta_prefix . 'focuskw',
'orderby' => 'meta_value',
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Reason: Only used when user requests sorting.
'meta_key' => WPSEO_Meta::$meta_prefix . 'linkdex',
'orderby' => 'meta_value_num',
case 'wpseo-score-readability':
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Reason: Only used when user requests sorting.
'meta_key' => WPSEO_Meta::$meta_prefix . 'content_score',
'orderby' => 'meta_value_num',
* Parses the score column.
* @param int $post_id The ID of the post for which to show the score.
* @return string The HTML for the SEO score indicator.
private function parse_column_score( $post_id ) {
$meta = $this->get_meta( $post_id );
return $this->score_icon_helper->for_seo( $meta->indexable, '', __( 'Post is set to noindex.', 'wordpress-seo' ) );
* Parsing the readability score column.
* @param int $post_id The ID of the post for which to show the readability score.
* @return string The HTML for the readability score indicator.
private function parse_column_score_readability( $post_id ) {
$meta = $this->get_meta( $post_id );
return $this->score_icon_helper->for_readability( $meta->indexable->readability_score );
* Sets up the hooks for the post_types.
private function set_post_type_hooks() {
$post_types = WPSEO_Post_Type::get_accessible_post_types();
if ( ! is_array( $post_types ) || $post_types === [] ) {
foreach ( $post_types as $post_type ) {
if ( $this->display_metabox( $post_type ) === false ) {
add_filter( 'manage_' . $post_type . '_posts_columns', [ $this, 'column_heading' ], 10, 1 );
add_action( 'manage_' . $post_type . '_posts_custom_column', [ $this, 'column_content' ], 10, 2 );
add_action( 'manage_edit-' . $post_type . '_sortable_columns', [ $this, 'column_sort' ], 10, 2 );
* Wraps the WPSEO_Metabox check to determine whether the metabox should be displayed either by
* choice of the admin or because the post type is not a public post type.
* @param string|null $post_type Optional. The post type to test, defaults to the current post post_type.
* @return bool Whether or not the meta box (and associated columns etc) should be hidden.
private function display_metabox( $post_type = null ) {
$current_post_type = $this->get_current_post_type();
if ( ! isset( $post_type ) && ! empty( $current_post_type ) ) {
$post_type = $current_post_type;
return WPSEO_Utils::is_metabox_active( $post_type, 'post_type' );
* Determines whether or not filter dropdowns should be displayed.
* @return bool Whether or the current page can display the filter drop downs.
public function can_display_filter() {
if ( $GLOBALS['pagenow'] === 'upload.php' ) {
if ( $this->display_metabox() === false ) {
$screen = get_current_screen();
if ( $screen === null ) {
return WPSEO_Post_Type::is_post_type_accessible( $screen->post_type );