diff --git a/admin/class-admin-user-profile.php b/admin/class-admin-user-profile.php
index 4579e462f3e..dc6f7b577a2 100644
--- a/admin/class-admin-user-profile.php
+++ b/admin/class-admin-user-profile.php
@@ -19,23 +19,6 @@ public function __construct() {
add_action( 'edit_user_profile', [ $this, 'user_profile' ] );
add_action( 'personal_options_update', [ $this, 'process_user_option_update' ] );
add_action( 'edit_user_profile_update', [ $this, 'process_user_option_update' ] );
-
- add_action( 'update_user_meta', [ $this, 'clear_author_sitemap_cache' ], 10, 3 );
- }
-
- /**
- * Clear author sitemap cache when settings are changed.
- *
- * @since 3.1
- *
- * @param int $meta_id The ID of the meta option changed.
- * @param int $object_id The ID of the user.
- * @param string $meta_key The key of the meta field changed.
- */
- public function clear_author_sitemap_cache( $meta_id, $object_id, $meta_key ) {
- if ( $meta_key === '_yoast_wpseo_profile_updated' ) {
- WPSEO_Sitemaps_Cache::clear( [ 'author' ] );
- }
}
/**
diff --git a/admin/class-admin.php b/admin/class-admin.php
index 8036678a054..67c6364f98a 100644
--- a/admin/class-admin.php
+++ b/admin/class-admin.php
@@ -70,9 +70,6 @@ public function __construct() {
add_action( 'admin_init', [ $this, 'map_manage_options_cap' ] );
- WPSEO_Sitemaps_Cache::register_clear_on_option_update( 'wpseo' );
- WPSEO_Sitemaps_Cache::register_clear_on_option_update( 'home' );
-
if ( YoastSEO()->helpers->current_page->is_yoast_seo_page() ) {
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
}
diff --git a/composer.json b/composer.json
index 8c80b81bdb6..a54453be962 100644
--- a/composer.json
+++ b/composer.json
@@ -78,8 +78,8 @@
"Yoast\\WP\\SEO\\Composer\\Actions::check_coding_standards"
],
"check-cs-thresholds": [
- "@putenv YOASTCS_THRESHOLD_ERRORS=253",
- "@putenv YOASTCS_THRESHOLD_WARNINGS=220",
+ "@putenv YOASTCS_THRESHOLD_ERRORS=244",
+ "@putenv YOASTCS_THRESHOLD_WARNINGS=198",
"Yoast\\WP\\SEO\\Composer\\Actions::check_cs_thresholds"
],
"check-cs": [
diff --git a/inc/class-upgrade.php b/inc/class-upgrade.php
index 7460711b34a..dd2f5e3da6c 100644
--- a/inc/class-upgrade.php
+++ b/inc/class-upgrade.php
@@ -144,9 +144,6 @@ protected function finish_up( $previous_version = null ) {
// Just flush rewrites, always, to at least make them work after an upgrade.
add_action( 'shutdown', 'flush_rewrite_rules' );
- // Flush the sitemap cache.
- WPSEO_Sitemaps_Cache::clear();
-
// Make sure all our options always exist - issue #1245.
WPSEO_Options::ensure_options_exist();
}
@@ -570,9 +567,6 @@ private function upgrade_772() {
private function upgrade_90() {
global $wpdb;
- // Invalidate all sitemap cache transients.
- WPSEO_Sitemaps_Cache_Validator::cleanup_database();
-
// Removes all scheduled tasks for hitting the sitemap index.
wp_clear_scheduled_hook( 'wpseo_hit_sitemap_index' );
diff --git a/inc/sitemaps/abstract-class-indexable-sitemap-provider.php b/inc/sitemaps/abstract-class-indexable-sitemap-provider.php
new file mode 100644
index 00000000000..eb8ede16b42
--- /dev/null
+++ b/inc/sitemaps/abstract-class-indexable-sitemap-provider.php
@@ -0,0 +1,133 @@
+repository = YoastSEO()->classes->get( Indexable_Repository::class );
+ $this->xml_sitemap_helper = YoastSEO()->helpers->xml_sitemap;
+ }
+
+ /**
+ * Retrieves the links for the sitemap.
+ *
+ * @param int $max_entries Entries per sitemap.
+ *
+ * @return array
+ */
+ public function get_index_links( $max_entries ) {
+ global $wpdb;
+
+ $query = $this->repository
+ ->query_where_noindex( false, $this->get_object_type() )
+ ->select( 'id' )
+ ->where( 'is_publicly_viewable', true )
+ ->order_by_asc( 'object_sub_type' )
+ ->order_by_asc( 'object_last_modified' );
+
+ $excluded_object_ids = $this->get_excluded_object_ids();
+ if ( count( $excluded_object_ids ) > 0 ) {
+ $query->where_not_in( 'object_id', $excluded_object_ids );
+ }
+
+ $table_name = Model::get_table_name( 'Indexable' );
+ // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Query is prepared by our ORM.
+ $raw_query = $wpdb->prepare( $query->get_sql(), $query->get_values() );
+
+ // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Complex query is not possible without a direct query.
+ $last_object_per_page = $wpdb->get_results(
+ // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Variables are secure.
+ $wpdb->prepare(
+ // This query pulls only every Nth last_modified from the database, resetting row counts when the sub type changes.
+ "
+ SELECT i.object_sub_type, i.object_last_modified
+ FROM $table_name AS i
+ INNER JOIN (
+ SELECT id
+ FROM (
+ SELECT IF( @previous_sub_type = object_sub_type, @row:=@row+1, @row:=0) AS rownum, @previous_sub_type:=object_sub_type AS previous_sub_type, id
+ FROM ( $raw_query ) AS sorted, ( SELECT @row:=-1, @previous_sub_type:=null ) AS init
+ ) AS ranked
+ WHERE rownum MOD %d = 0
+ ) AS subset
+ ON subset.id = i.id
+ ",
+ $max_entries
+ )
+ // phpcs:enable
+ );
+
+ $index = [];
+ $page = 1;
+ foreach ( $last_object_per_page as $index => $object ) {
+ if ( $this->should_exclude_object_sub_type( $object->object_sub_type ) ) {
+ continue;
+ }
+
+ $next_object_is_not_same_sub_type = ! ( isset( $last_object_per_page[ ( $index + 1 ) ] ) && $last_object_per_page[ ( $index + 1 ) ] === $object->object_sub_type );
+ if ( $page === 1 && $next_object_is_not_same_sub_type ) {
+ $page = '';
+ }
+
+ $index[] = [
+ 'loc' => WPSEO_Sitemaps_Router::get_base_url( $object->object_sub_type . '-sitemap' . $page . '.xml' ),
+ 'lastmod' => $object->object_last_modified,
+ ];
+
+ if ( $next_object_is_not_same_sub_type ) {
+ $page = 1;
+ }
+ }
+
+ return $index;
+ }
+
+ /**
+ * Returns the object type for this sitemap.
+ *
+ * @return string The object type.
+ */
+ abstract protected function get_object_type();
+
+ /**
+ * Returns a list of all object IDs that should be excluded.
+ *
+ * @return int[]
+ */
+ abstract protected function get_excluded_object_ids();
+
+ /**
+ * Whether or not a specific object sub type should be excluded.
+ *
+ * @param string $object_sub_type The object sub type.
+ *
+ * @return boolean Whether or not it should be excluded.
+ */
+ abstract protected function should_exclude_object_sub_type( $object_sub_type );
+}
diff --git a/inc/sitemaps/class-author-sitemap-provider.php b/inc/sitemaps/class-author-sitemap-provider.php
index 815fb0f3cb3..fa8fe18a6ed 100644
--- a/inc/sitemaps/class-author-sitemap-provider.php
+++ b/inc/sitemaps/class-author-sitemap-provider.php
@@ -5,14 +5,37 @@
* @package WPSEO\XML_Sitemaps
*/
-use Yoast\WP\SEO\Helpers\Author_Archive_Helper;
-use Yoast\WP\SEO\Helpers\Wordpress_Helper;
+use Yoast\WP\Lib\Model;
+use Yoast\WP\SEO\Helpers\XML_Sitemap_Helper;
+use Yoast\WP\SEO\Repositories\Indexable_Repository;
/**
* Sitemap provider for author archives.
*/
class WPSEO_Author_Sitemap_Provider implements WPSEO_Sitemap_Provider {
+ /**
+ * The indexable repository.
+ *
+ * @var Indexable_Repository
+ */
+ private $repository;
+
+ /**
+ * The XML sitemap helper.
+ *
+ * @var XML_Sitemap_Helper
+ */
+ private $xml_sitemap_helper;
+
+ /**
+ * Set up object properties for data reuse.
+ */
+ public function __construct() {
+ $this->repository = YoastSEO()->classes->get( Indexable_Repository::class );
+ $this->xml_sitemap_helper = YoastSEO()->helpers->xml_sitemap;
+ }
+
/**
* Check if provider supports given item type.
*
@@ -37,110 +60,67 @@ public function handles_type( $type ) {
* @return array
*/
public function get_index_links( $max_entries ) {
+ global $wpdb;
if ( ! $this->handles_type( 'author' ) ) {
return [];
}
- // @todo Consider doing this less often / when necessary. R.
- $this->update_user_meta();
-
- $has_exclude_filter = has_filter( 'wpseo_sitemap_exclude_author' );
-
- $query_arguments = [];
-
- if ( ! $has_exclude_filter ) { // We only need full users if legacy filter(s) hooked to exclusion logic. R.
- $query_arguments['fields'] = 'ID';
- }
-
- $users = $this->get_users( $query_arguments );
-
- if ( $has_exclude_filter ) {
- $users = $this->exclude_users( $users );
- $users = wp_list_pluck( $users, 'ID' );
- }
+ $query = $this->repository
+ ->query_where_noindex( false, 'user' )
+ ->select( 'id' )
+ ->order_by_asc( 'object_last_modified' );
- if ( empty( $users ) ) {
- return [];
+ $users_to_exclude = $this->exclude_users();
+ if ( is_array( $users_to_exclude ) && count( $users_to_exclude ) > 0 ) {
+ $query->where_not_in( 'object_id', $users_to_exclude );
}
- $index = [];
- $page = 1;
- $user_pages = array_chunk( $users, $max_entries );
-
- if ( count( $user_pages ) === 1 ) {
+ $table_name = Model::get_table_name( 'Indexable' );
+ // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Query is prepared by our ORM.
+ $raw_query = $wpdb->prepare( $query->get_sql(), $query->get_values() );
+
+ // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Complex query is not possible without a direct query.
+ $last_modified_per_page = $wpdb->get_col(
+ // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Variables are secure.
+ $wpdb->prepare(
+ // This query pulls only every Nth last_modified from the database.
+ "
+ SELECT i.object_last_modified
+ FROM $table_name AS i
+ INNER JOIN (
+ SELECT id
+ FROM (
+ SELECT @row:=@row+1 AS rownum, id
+ FROM ( $raw_query ) AS sorted, ( SELECT @row:=-1 ) AS init
+ ) AS ranked
+ WHERE rownum MOD %d = 0
+ ) AS subset
+ ON subset.id = i.id
+ ",
+ $max_entries
+ )
+ // phpcs:enable
+ );
+
+ $page = 1;
+ if ( count( $last_modified_per_page ) === 1 ) {
$page = '';
}
- foreach ( $user_pages as $users_page ) {
-
- $user_id = array_shift( $users_page ); // Time descending, first user on page is most recently updated.
- $user = get_user_by( 'id', $user_id );
- $index[] = [
+ $index_links = [];
+ foreach ( $last_modified_per_page as $last_modified ) {
+ $index_links[] = [
'loc' => WPSEO_Sitemaps_Router::get_base_url( 'author-sitemap' . $page . '.xml' ),
- 'lastmod' => ( $user->_yoast_wpseo_profile_updated ) ? YoastSEO()->helpers->date->format_timestamp( $user->_yoast_wpseo_profile_updated ) : null,
+ 'lastmod' => $last_modified,
];
- ++$page;
- }
-
- return $index;
- }
-
- /**
- * Retrieve users, taking account of all necessary exclusions.
- *
- * @param array $arguments Arguments to add.
- *
- * @return array
- */
- protected function get_users( $arguments = [] ) {
-
- global $wpdb;
-
- $defaults = [
- 'capability' => [ 'edit_posts' ],
- 'meta_key' => '_yoast_wpseo_profile_updated',
- 'orderby' => 'meta_value_num',
- 'order' => 'DESC',
- 'meta_query' => [
- 'relation' => 'AND',
- [
- 'key' => $wpdb->get_blog_prefix() . 'user_level',
- 'value' => '0',
- 'compare' => '!=',
- ],
- [
- 'relation' => 'OR',
- [
- 'key' => 'wpseo_noindex_author',
- 'value' => 'on',
- 'compare' => '!=',
- ],
- [
- 'key' => 'wpseo_noindex_author',
- 'compare' => 'NOT EXISTS',
- ],
- ],
- ],
- ];
-
- $wordpress_helper = new Wordpress_Helper();
- $wordpress_version = $wordpress_helper->get_wordpress_version();
-
- // Capability queries were only introduced in WP 5.9.
- if ( version_compare( $wordpress_version, '5.8.99', '<' ) ) {
- $defaults['who'] = 'authors';
- unset( $defaults['capability'] );
- }
-
- if ( WPSEO_Options::get( 'noindex-author-noposts-wpseo', true ) ) {
- unset( $defaults['who'], $defaults['capability'] ); // Otherwise it cancels out next argument.
- $author_archive = new Author_Archive_Helper();
- $defaults['has_published_posts'] = $author_archive->get_author_archive_post_types();
+ if ( is_int( $page ) ) {
+ ++$page;
+ }
}
- return get_users( array_merge( $defaults, $arguments ) );
+ return $index_links;
}
/**
@@ -156,116 +136,45 @@ protected function get_users( $arguments = [] ) {
*/
public function get_sitemap_links( $type, $max_entries, $current_page ) {
- $links = [];
-
if ( ! $this->handles_type( 'author' ) ) {
- return $links;
- }
-
- $user_criteria = [
- 'offset' => ( ( $current_page - 1 ) * $max_entries ),
- 'number' => $max_entries,
- ];
-
- $users = $this->get_users( $user_criteria );
-
- // Throw an exception when there are no users in the sitemap.
- if ( count( $users ) === 0 ) {
- throw new OutOfBoundsException( 'Invalid sitemap page requested' );
- }
-
- $users = $this->exclude_users( $users );
- if ( empty( $users ) ) {
- $users = [];
- }
-
- $time = time();
-
- foreach ( $users as $user ) {
-
- $author_link = get_author_posts_url( $user->ID );
-
- if ( empty( $author_link ) ) {
- continue;
- }
-
- $mod = $time;
-
- if ( isset( $user->_yoast_wpseo_profile_updated ) ) {
- $mod = $user->_yoast_wpseo_profile_updated;
- }
-
- $url = [
- 'loc' => $author_link,
- 'mod' => date( DATE_W3C, $mod ),
-
- // Deprecated, kept for backwards data compat. R.
- 'chf' => 'daily',
- 'pri' => 1,
- ];
-
- /** This filter is documented at inc/sitemaps/class-post-type-sitemap-provider.php */
- $url = apply_filters( 'wpseo_sitemap_entry', $url, 'user', $user );
-
- if ( ! empty( $url ) ) {
- $links[] = $url;
- }
+ return [];
}
- return $links;
- }
+ $offset = ( ( $current_page - 1 ) * $max_entries );
- /**
- * Update any users that don't have last profile update timestamp.
- *
- * @return int Count of users updated.
- */
- protected function update_user_meta() {
-
- $user_criteria = [
- 'capability' => [ 'edit_posts' ],
- 'meta_query' => [
- [
- 'key' => '_yoast_wpseo_profile_updated',
- 'compare' => 'NOT EXISTS',
- ],
- ],
- ];
+ $query = $this->repository
+ ->query_where_noindex( false, 'user' )
+ ->select_many( 'id', 'object_id', 'permalink', 'object_last_modified' )
+ ->order_by_asc( 'object_last_modified' )
+ ->offset( $offset )
+ ->limit( $max_entries );
- $wordpress_helper = new Wordpress_Helper();
- $wordpress_version = $wordpress_helper->get_wordpress_version();
-
- // Capability queries were only introduced in WP 5.9.
- if ( version_compare( $wordpress_version, '5.8.99', '<' ) ) {
- $user_criteria['who'] = 'authors';
- unset( $user_criteria['capability'] );
+ $users_to_exclude = $this->exclude_users();
+ if ( count( $users_to_exclude ) > 0 ) {
+ $query->where_not_in( 'object_id', $users_to_exclude );
}
- $users = get_users( $user_criteria );
-
- $time = time();
+ $indexables = $query->find_many();
- foreach ( $users as $user ) {
- update_user_meta( $user->ID, '_yoast_wpseo_profile_updated', $time );
+ // Throw an exception when there are no users in the sitemap.
+ if ( count( $indexables ) === 0 ) {
+ throw new OutOfBoundsException( 'Invalid sitemap page requested' );
}
- return count( $users );
+ return $this->xml_sitemap_helper->convert_indexables_to_sitemap_links( $indexables, 'user' );
}
/**
* Wrap legacy filter to deduplicate calls.
*
- * @param array $users Array of user objects to filter.
- *
* @return array
*/
- protected function exclude_users( $users ) {
-
+ protected function exclude_users() {
/**
* Filter the authors, included in XML sitemap.
*
* @param array $users Array of user objects to filter.
*/
- return apply_filters( 'wpseo_sitemap_exclude_author', $users );
+ return apply_filters( 'wpseo_sitemap_exclude_author', [ 0 ] );
}
}
diff --git a/inc/sitemaps/class-post-type-sitemap-provider.php b/inc/sitemaps/class-post-type-sitemap-provider.php
index a552e83d9e9..0ec14e32a88 100644
--- a/inc/sitemaps/class-post-type-sitemap-provider.php
+++ b/inc/sitemaps/class-post-type-sitemap-provider.php
@@ -5,73 +5,10 @@
* @package WPSEO\XML_Sitemaps
*/
-use Yoast\WP\SEO\Models\SEO_Links;
-
/**
* Sitemap provider for author archives.
*/
-class WPSEO_Post_Type_Sitemap_Provider implements WPSEO_Sitemap_Provider {
-
- /**
- * Holds image parser instance.
- *
- * @var WPSEO_Sitemap_Image_Parser
- */
- protected static $image_parser;
-
- /**
- * Holds the parsed home url.
- *
- * @var array
- */
- protected static $parsed_home_url;
-
- /**
- * Determines whether images should be included in the XML sitemap.
- *
- * @var bool
- */
- private $include_images;
-
- /**
- * Set up object properties for data reuse.
- */
- public function __construct() {
- add_filter( 'save_post', [ $this, 'save_post' ] );
-
- /**
- * Filter - Allows excluding images from the XML sitemap.
- *
- * @param bool $include True to include, false to exclude.
- */
- $this->include_images = apply_filters( 'wpseo_xml_sitemap_include_images', true );
- }
-
- /**
- * Get the Image Parser.
- *
- * @return WPSEO_Sitemap_Image_Parser
- */
- protected function get_image_parser() {
- if ( ! isset( self::$image_parser ) ) {
- self::$image_parser = new WPSEO_Sitemap_Image_Parser();
- }
-
- return self::$image_parser;
- }
-
- /**
- * Gets the parsed home url.
- *
- * @return array The home url, as parsed by wp_parse_url.
- */
- protected function get_parsed_home_url() {
- if ( ! isset( self::$parsed_home_url ) ) {
- self::$parsed_home_url = wp_parse_url( home_url() );
- }
-
- return self::$parsed_home_url;
- }
+class WPSEO_Post_Type_Sitemap_Provider extends WPSEO_Indexable_Sitemap_Provider implements WPSEO_Sitemap_Provider {
/**
* Check if provider supports given item type.
@@ -81,75 +18,41 @@ protected function get_parsed_home_url() {
* @return bool
*/
public function handles_type( $type ) {
-
return post_type_exists( $type );
}
/**
- * Retrieves the sitemap links.
+ * Returns the object type for this sitemap.
*
- * @param int $max_entries Entries per sitemap.
- *
- * @return array
+ * @return string The object type.
*/
- public function get_index_links( $max_entries ) {
- global $wpdb;
-
- $post_types = WPSEO_Post_Type::get_accessible_post_types();
- $post_types = array_filter( $post_types, [ $this, 'is_valid_post_type' ] );
- $last_modified_times = WPSEO_Sitemaps::get_last_modified_gmt( $post_types, true );
- $index = [];
-
- foreach ( $post_types as $post_type ) {
-
- $total_count = $this->get_post_type_count( $post_type );
-
- $max_pages = 1;
- if ( $total_count > $max_entries ) {
- $max_pages = (int) ceil( $total_count / $max_entries );
- }
-
- $all_dates = [];
-
- if ( $max_pages > 1 ) {
- $post_statuses = array_map( 'esc_sql', WPSEO_Sitemaps::get_post_statuses( $post_type ) );
-
- $sql = "
- SELECT post_modified_gmt
- FROM ( SELECT @rownum:=0 ) init
- JOIN {$wpdb->posts} USE INDEX( type_status_date )
- WHERE post_status IN ('" . implode( "','", $post_statuses ) . "')
- AND post_type = %s
- AND ( @rownum:=@rownum+1 ) %% %d = 0
- ORDER BY post_modified_gmt ASC
- ";
-
- $all_dates = $wpdb->get_col( $wpdb->prepare( $sql, $post_type, $max_entries ) );
- }
-
- for ( $page_counter = 0; $page_counter < $max_pages; $page_counter++ ) {
-
- $current_page = ( $max_pages > 1 ) ? ( $page_counter + 1 ) : '';
- $date = false;
-
- if ( empty( $current_page ) || $current_page === $max_pages ) {
+ protected function get_object_type() {
+ return 'post';
+ }
- if ( ! empty( $last_modified_times[ $post_type ] ) ) {
- $date = $last_modified_times[ $post_type ];
- }
- }
- else {
- $date = $all_dates[ $page_counter ];
- }
+ /**
+ * Whether or not a specific object sub type should be excluded.
+ *
+ * @param string $object_sub_type The object sub type.
+ *
+ * @return boolean Whether or not it should be excluded.
+ */
+ protected function should_exclude_object_sub_type( $object_sub_type ) {
+ /**
+ * Filter decision if post type is excluded from the XML sitemap.
+ *
+ * @param bool $exclude Default false.
+ * @param string $post_type Post type name.
+ */
+ if ( apply_filters( 'wpseo_sitemap_exclude_post_type', false, $object_sub_type ) ) {
+ return true;
+ }
- $index[] = [
- 'loc' => WPSEO_Sitemaps_Router::get_base_url( $post_type . '-sitemap' . $current_page . '.xml' ),
- 'lastmod' => $date,
- ];
- }
+ if ( ! WPSEO_Post_Type::is_post_type_accessible( $object_sub_type ) || ! WPSEO_Post_Type::is_post_type_indexable( $object_sub_type ) ) {
+ return true;
}
- return $index;
+ return false;
}
/**
@@ -159,100 +62,64 @@ public function get_index_links( $max_entries ) {
* @param int $max_entries Entries per sitemap.
* @param int $current_page Current page of the sitemap.
*
- * @return array
+ * @return array The links.
*
* @throws OutOfBoundsException When an invalid page is requested.
*/
public function get_sitemap_links( $type, $max_entries, $current_page ) {
-
- $links = [];
- $post_type = $type;
-
- if ( ! $this->is_valid_post_type( $post_type ) ) {
+ if ( ! $this->is_valid_post_type( $type ) ) {
throw new OutOfBoundsException( 'Invalid sitemap page requested' );
}
- $steps = min( 100, $max_entries );
$offset = ( $current_page > 1 ) ? ( ( $current_page - 1 ) * $max_entries ) : 0;
- $total = ( $offset + $max_entries );
-
- $post_type_entries = $this->get_post_type_count( $post_type );
-
- if ( $total > $post_type_entries ) {
- $total = $post_type_entries;
- }
-
- if ( $current_page === 1 ) {
- $links = array_merge( $links, $this->get_first_links( $post_type ) );
- }
-
- // If total post type count is lower than the offset, an invalid page is requested.
- if ( $post_type_entries < $offset ) {
- throw new OutOfBoundsException( 'Invalid sitemap page requested' );
- }
- if ( $post_type_entries === 0 ) {
- return $links;
- }
+ $query = $this->repository
+ ->query_where_noindex( false, 'post', $type )
+ ->select_many( 'id', 'object_id', 'permalink', 'object_last_modified' )
+ ->where( 'is_publicly_viewable', true )
+ ->order_by_asc( 'object_last_modified' )
+ ->offset( $offset )
+ ->limit( $max_entries );
$posts_to_exclude = $this->get_excluded_posts( $type );
+ if ( count( $posts_to_exclude ) > 0 ) {
+ $query->where_not_in( 'object_id', $posts_to_exclude );
+ }
- while ( $total > $offset ) {
-
- $posts = $this->get_posts( $post_type, $steps, $offset );
-
- $offset += $steps;
-
- if ( empty( $posts ) ) {
- continue;
- }
-
- foreach ( $posts as $post ) {
-
- if ( in_array( $post->ID, $posts_to_exclude, true ) ) {
- continue;
- }
-
- if ( WPSEO_Meta::get_value( 'meta-robots-noindex', $post->ID ) === '1' ) {
- continue;
- }
-
- $url = $this->get_url( $post );
+ $indexables = $query->find_many();
- if ( ! isset( $url['loc'] ) ) {
- continue;
+ // Add the home page or archive to the first page.
+ if ( $current_page === 1 ) {
+ if ( $type === 'page' ) {
+ $home_page = $this->repository
+ ->query_where_noindex( false, 'home-page' )
+ ->select_many( 'id', 'object_id', 'permalink', 'object_last_modified' )
+ ->where( 'is_publicly_viewable', true )
+ ->find_one();
+ if ( $home_page ) {
+ // Prepend homepage.
+ array_unshift( $indexables, $home_page );
}
-
- /**
- * Filter URL entry before it gets added to the sitemap.
- *
- * @param array $url Array of URL parts.
- * @param string $type URL type.
- * @param object $post Data object for the URL.
- */
- $url = apply_filters( 'wpseo_sitemap_entry', $url, 'post', $post );
-
- if ( ! empty( $url ) ) {
- $links[] = $url;
+ }
+ else {
+ $archive_page = $this->repository
+ ->query_where_noindex( false, 'post-type-archive', $type )
+ ->select_many( 'id', 'object_id', 'permalink', 'object_last_modified' )
+ ->where( 'is_publicly_viewable', true )
+ ->find_one();
+ if ( $archive_page ) {
+ // Prepend archive.
+ array_unshift( $indexables, $archive_page );
}
}
-
- unset( $post, $url );
}
- return $links;
- }
-
- /**
- * Check for relevant post type before invalidation.
- *
- * @param int $post_id Post ID to possibly invalidate for.
- */
- public function save_post( $post_id ) {
-
- if ( $this->is_valid_post_type( get_post_type( $post_id ) ) ) {
- WPSEO_Sitemaps_Cache::invalidate_post( $post_id );
+ // If total post type count is lower than the offset, an invalid page is requested.
+ if ( count( $indexables ) === 0 ) {
+ throw new OutOfBoundsException( 'Invalid sitemap page requested' );
}
+
+ return $this->xml_sitemap_helper->convert_indexables_to_sitemap_links( $indexables, 'post' );
}
/**
@@ -283,18 +150,11 @@ public function is_valid_post_type( $post_type ) {
/**
* Retrieves a list with the excluded post ids.
*
- * @param string $post_type Post type.
- *
* @return array Array with post ids to exclude.
*/
- protected function get_excluded_posts( $post_type ) {
+ protected function get_excluded_object_ids() {
$excluded_posts_ids = [];
- $page_on_front_id = ( $post_type === 'page' ) ? (int) get_option( 'page_on_front' ) : 0;
- if ( $page_on_front_id > 0 ) {
- $excluded_posts_ids[] = $page_on_front_id;
- }
-
/**
* Filter: 'wpseo_exclude_from_sitemap_by_post_ids' - Allow extending and modifying the posts to exclude.
*
@@ -307,351 +167,6 @@ protected function get_excluded_posts( $post_type ) {
$excluded_posts_ids = array_map( 'intval', $excluded_posts_ids );
- $page_for_posts_id = ( $post_type === 'page' ) ? (int) get_option( 'page_for_posts' ) : 0;
- if ( $page_for_posts_id > 0 ) {
- $excluded_posts_ids[] = $page_for_posts_id;
- }
-
return array_unique( $excluded_posts_ids );
}
-
- /**
- * Get count of posts for post type.
- *
- * @param string $post_type Post type to retrieve count for.
- *
- * @return int
- */
- protected function get_post_type_count( $post_type ) {
-
- global $wpdb;
-
- /**
- * Filter JOIN query part for type count of post type.
- *
- * @param string $join SQL part, defaults to empty string.
- * @param string $post_type Post type name.
- */
- $join_filter = apply_filters( 'wpseo_typecount_join', '', $post_type );
-
- /**
- * Filter WHERE query part for type count of post type.
- *
- * @param string $where SQL part, defaults to empty string.
- * @param string $post_type Post type name.
- */
- $where_filter = apply_filters( 'wpseo_typecount_where', '', $post_type );
-
- $where = $this->get_sql_where_clause( $post_type );
-
- $sql = "
- SELECT COUNT({$wpdb->posts}.ID)
- FROM {$wpdb->posts}
- {$join_filter}
- {$where}
- {$where_filter}
- ";
-
- return (int) $wpdb->get_var( $sql );
- }
-
- /**
- * Produces set of links to prepend at start of first sitemap page.
- *
- * @param string $post_type Post type to produce links for.
- *
- * @return array
- */
- protected function get_first_links( $post_type ) {
-
- $links = [];
- $archive_url = false;
-
- if ( $post_type === 'page' ) {
-
- $page_on_front_id = (int) get_option( 'page_on_front' );
- if ( $page_on_front_id > 0 ) {
- $front_page = $this->get_url(
- get_post( $page_on_front_id )
- );
- }
-
- if ( empty( $front_page ) ) {
- $front_page = [
- 'loc' => YoastSEO()->helpers->url->home(),
- ];
- }
-
- // Deprecated, kept for backwards data compat. R.
- $front_page['chf'] = 'daily';
- $front_page['pri'] = 1;
-
- $links[] = $front_page;
- }
- elseif ( $post_type !== 'page' ) {
- /**
- * Filter the URL Yoast SEO uses in the XML sitemap for this post type archive.
- *
- * @param string $archive_url The URL of this archive
- * @param string $post_type The post type this archive is for.
- */
- $archive_url = apply_filters(
- 'wpseo_sitemap_post_type_archive_link',
- $this->get_post_type_archive_link( $post_type ),
- $post_type
- );
- }
-
- if ( $archive_url ) {
-
- $links[] = [
- 'loc' => $archive_url,
- 'mod' => WPSEO_Sitemaps::get_last_modified_gmt( $post_type ),
-
- // Deprecated, kept for backwards data compat. R.
- 'chf' => 'daily',
- 'pri' => 1,
- ];
- }
-
- return $links;
- }
-
- /**
- * Get URL for a post type archive.
- *
- * @since 5.3
- *
- * @param string $post_type Post type.
- *
- * @return string|bool URL or false if it should be excluded.
- */
- protected function get_post_type_archive_link( $post_type ) {
-
- $pt_archive_page_id = -1;
-
- if ( $post_type === 'post' ) {
-
- if ( get_option( 'show_on_front' ) === 'posts' ) {
- return YoastSEO()->helpers->url->home();
- }
-
- $pt_archive_page_id = (int) get_option( 'page_for_posts' );
-
- // Post archive should be excluded if posts page isn't set.
- if ( $pt_archive_page_id <= 0 ) {
- return false;
- }
- }
-
- if ( ! $this->is_post_type_archive_indexable( $post_type, $pt_archive_page_id ) ) {
- return false;
- }
-
- return get_post_type_archive_link( $post_type );
- }
-
- /**
- * Determines whether a post type archive is indexable.
- *
- * @since 11.5
- *
- * @param string $post_type Post type.
- * @param int $archive_page_id The page id.
- *
- * @return bool True when post type archive is indexable.
- */
- protected function is_post_type_archive_indexable( $post_type, $archive_page_id = -1 ) {
-
- if ( WPSEO_Options::get( 'noindex-ptarchive-' . $post_type, false ) ) {
- return false;
- }
-
- /**
- * Filter the page which is dedicated to this post type archive.
- *
- * @since 9.3
- *
- * @param string $archive_page_id The post_id of the page.
- * @param string $post_type The post type this archive is for.
- */
- $archive_page_id = (int) apply_filters( 'wpseo_sitemap_page_for_post_type_archive', $archive_page_id, $post_type );
-
- if ( $archive_page_id > 0 && WPSEO_Meta::get_value( 'meta-robots-noindex', $archive_page_id ) === '1' ) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Retrieve set of posts with optimized query routine.
- *
- * @param string $post_type Post type to retrieve.
- * @param int $count Count of posts to retrieve.
- * @param int $offset Starting offset.
- *
- * @return object[]
- */
- protected function get_posts( $post_type, $count, $offset ) {
-
- global $wpdb;
-
- static $filters = [];
-
- if ( ! isset( $filters[ $post_type ] ) ) {
- // Make sure you're wpdb->preparing everything you throw into this!!
- $filters[ $post_type ] = [
- /**
- * Filter JOIN query part for the post type.
- *
- * @param string $join SQL part, defaults to false.
- * @param string $post_type Post type name.
- */
- 'join' => apply_filters( 'wpseo_posts_join', false, $post_type ),
-
- /**
- * Filter WHERE query part for the post type.
- *
- * @param string $where SQL part, defaults to false.
- * @param string $post_type Post type name.
- */
- 'where' => apply_filters( 'wpseo_posts_where', false, $post_type ),
- ];
- }
-
- $join_filter = $filters[ $post_type ]['join'];
- $where_filter = $filters[ $post_type ]['where'];
- $where = $this->get_sql_where_clause( $post_type );
-
- /*
- * Optimized query per this thread:
- * {@link http://wordpress.org/support/topic/plugin-wordpress-seo-by-yoast-performance-suggestion}.
- * Also see {@link http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/}.
- */
- $sql = "
- SELECT l.ID, post_title, post_content, post_name, post_parent, post_author, post_status, post_modified_gmt, post_date, post_date_gmt
- FROM (
- SELECT {$wpdb->posts}.ID
- FROM {$wpdb->posts}
- {$join_filter}
- {$where}
- {$where_filter}
- ORDER BY {$wpdb->posts}.post_modified ASC LIMIT %d OFFSET %d
- )
- o JOIN {$wpdb->posts} l ON l.ID = o.ID
- ";
-
- $posts = $wpdb->get_results( $wpdb->prepare( $sql, $count, $offset ) );
-
- $post_ids = [];
-
- foreach ( $posts as $post_index => $post ) {
- $post->post_type = $post_type;
- $sanitized_post = sanitize_post( $post, 'raw' );
- $posts[ $post_index ] = new WP_Post( $sanitized_post );
-
- $post_ids[] = $sanitized_post->ID;
- }
-
- update_meta_cache( 'post', $post_ids );
-
- return $posts;
- }
-
- /**
- * Constructs an SQL where clause for a given post type.
- *
- * @param string $post_type Post type slug.
- *
- * @return string
- */
- protected function get_sql_where_clause( $post_type ) {
-
- global $wpdb;
-
- $join = '';
- $post_statuses = array_map( 'esc_sql', WPSEO_Sitemaps::get_post_statuses( $post_type ) );
- $status_where = "{$wpdb->posts}.post_status IN ('" . implode( "','", $post_statuses ) . "')";
-
- // Based on WP_Query->get_posts(). R.
- if ( $post_type === 'attachment' ) {
- $join = " LEFT JOIN {$wpdb->posts} AS p2 ON ({$wpdb->posts}.post_parent = p2.ID) ";
- $parent_statuses = array_diff( $post_statuses, [ 'inherit' ] );
- $status_where = "p2.post_status IN ('" . implode( "','", $parent_statuses ) . "') AND p2.post_password = ''";
- }
-
- $where_clause = "
- {$join}
- WHERE {$status_where}
- AND {$wpdb->posts}.post_type = %s
- AND {$wpdb->posts}.post_password = ''
- AND {$wpdb->posts}.post_date != '0000-00-00 00:00:00'
- ";
-
- return $wpdb->prepare( $where_clause, $post_type );
- }
-
- /**
- * Produce array of URL parts for given post object.
- *
- * @param object $post Post object to get URL parts for.
- *
- * @return array|bool
- */
- protected function get_url( $post ) {
-
- $url = [];
-
- /**
- * Filter the URL Yoast SEO uses in the XML sitemap.
- *
- * Note that only absolute local URLs are allowed as the check after this removes external URLs.
- *
- * @param string $url URL to use in the XML sitemap
- * @param object $post Post object for the URL.
- */
- $url['loc'] = apply_filters( 'wpseo_xml_sitemap_post_url', get_permalink( $post ), $post );
- $link_type = YoastSEO()->helpers->url->get_link_type(
- wp_parse_url( $url['loc'] ),
- $this->get_parsed_home_url()
- );
-
- /*
- * Do not include external URLs.
- *
- * {@link https://wordpress.org/plugins/page-links-to/} can rewrite permalinks to external URLs.
- */
- if ( $link_type === SEO_Links::TYPE_EXTERNAL ) {
- return false;
- }
-
- $modified = max( $post->post_modified_gmt, $post->post_date_gmt );
-
- if ( $modified !== '0000-00-00 00:00:00' ) {
- $url['mod'] = $modified;
- }
-
- $url['chf'] = 'daily'; // Deprecated, kept for backwards data compat. R.
-
- $canonical = WPSEO_Meta::get_value( 'canonical', $post->ID );
-
- if ( $canonical !== '' && $canonical !== $url['loc'] ) {
- /*
- * Let's assume that if a canonical is set for this page and it's different from
- * the URL of this post, that page is either already in the XML sitemap OR is on
- * an external site, either way, we shouldn't include it here.
- */
- return false;
- }
- unset( $canonical );
-
- $url['pri'] = 1; // Deprecated, kept for backwards data compat. R.
-
- if ( $this->include_images ) {
- $url['images'] = $this->get_image_parser()->get_images( $post );
- }
-
- return $url;
- }
}
diff --git a/inc/sitemaps/class-sitemap-cache-data.php b/inc/sitemaps/class-sitemap-cache-data.php
deleted file mode 100644
index 5490e0b7863..00000000000
--- a/inc/sitemaps/class-sitemap-cache-data.php
+++ /dev/null
@@ -1,198 +0,0 @@
-sitemap = $sitemap;
-
- /*
- * Empty sitemap is not usable.
- */
- if ( ! empty( $sitemap ) ) {
- $this->set_status( self::OK );
- }
- else {
- $this->set_status( self::ERROR );
- }
- }
-
- /**
- * Set the status of the sitemap, is it usable.
- *
- * @param bool|string $valid Is the sitemap valid or not.
- *
- * @return void
- */
- public function set_status( $valid ) {
-
- if ( $valid === self::OK ) {
- $this->status = self::OK;
-
- return;
- }
-
- if ( $valid === self::ERROR ) {
- $this->status = self::ERROR;
- $this->sitemap = '';
-
- return;
- }
-
- $this->status = self::UNKNOWN;
- }
-
- /**
- * Is the sitemap usable.
- *
- * @return bool True if usable, False if bad or unknown.
- */
- public function is_usable() {
-
- return $this->status === self::OK;
- }
-
- /**
- * Get the XML content of the sitemap.
- *
- * @return string The content of the sitemap.
- */
- public function get_sitemap() {
-
- return $this->sitemap;
- }
-
- /**
- * Get the status of the sitemap.
- *
- * @return string Status of the sitemap, 'ok'/'error'/'unknown'.
- */
- public function get_status() {
-
- return $this->status;
- }
-
- /**
- * String representation of object.
- *
- * {@internal This magic method is only "magic" as of PHP 7.4 in which the magic method was introduced.}
- *
- * @link https://www.php.net/language.oop5.magic#object.serialize
- * @link https://wiki.php.net/rfc/custom_object_serialization
- *
- * @since 17.8.0
- *
- * @return array The data to be serialized.
- */
- public function __serialize() { // phpcs:ignore PHPCompatibility.FunctionNameRestrictions.NewMagicMethods.__serializeFound
-
- $data = [
- 'status' => $this->status,
- 'xml' => $this->sitemap,
- ];
-
- return $data;
- }
-
- /**
- * Constructs the object.
- *
- * {@internal This magic method is only "magic" as of PHP 7.4 in which the magic method was introduced.}
- *
- * @link https://www.php.net/language.oop5.magic#object.serialize
- * @link https://wiki.php.net/rfc/custom_object_serialization
- *
- * @since 17.8.0
- *
- * @param array $data The unserialized data to use to (re)construct the object.
- *
- * @return void
- */
- public function __unserialize( $data ) { // phpcs:ignore PHPCompatibility.FunctionNameRestrictions.NewMagicMethods.__unserializeFound
-
- $this->set_sitemap( $data['xml'] );
- $this->set_status( $data['status'] );
- }
-
- /**
- * String representation of object.
- *
- * {@internal The magic methods take precedence over the Serializable interface.
- * This means that in practice, this method will now only be called on PHP < 7.4.
- * For PHP 7.4 and higher, the magic methods will be used instead.}
- *
- * {@internal The Serializable interface is being phased out, in favour of the magic methods.
- * This method should be deprecated and removed and the class should no longer
- * implement the `Serializable` interface.
- * This change, however, can't be made until the minimum PHP version goes up to PHP 7.4 or higher.}
- *
- * @link http://php.net/manual/en/serializable.serialize.php
- * @link https://wiki.php.net/rfc/phase_out_serializable
- *
- * @since 5.1.0
- *
- * @return string The string representation of the object or null in C-format.
- */
- public function serialize() {
-
- return serialize( $this->__serialize() );
- }
-
- /**
- * Constructs the object.
- *
- * {@internal The magic methods take precedence over the Serializable interface.
- * This means that in practice, this method will now only be called on PHP < 7.4.
- * For PHP 7.4 and higher, the magic methods will be used instead.}
- *
- * {@internal The Serializable interface is being phased out, in favour of the magic methods.
- * This method should be deprecated and removed and the class should no longer
- * implement the `Serializable` interface.
- * This change, however, can't be made until the minimum PHP version goes up to PHP 7.4 or higher.}
- *
- * @link http://php.net/manual/en/serializable.unserialize.php
- * @link https://wiki.php.net/rfc/phase_out_serializable
- *
- * @since 5.1.0
- *
- * @param string $data The string representation of the object in C or O-format.
- *
- * @return void
- */
- public function unserialize( $data ) {
-
- $data = unserialize( $data );
- $this->__unserialize( $data );
- }
-}
diff --git a/inc/sitemaps/class-sitemap-image-parser.php b/inc/sitemaps/class-sitemap-image-parser.php
deleted file mode 100644
index 4ae581b9bcc..00000000000
--- a/inc/sitemaps/class-sitemap-image-parser.php
+++ /dev/null
@@ -1,510 +0,0 @@
-home_url = home_url();
- $parsed_home = wp_parse_url( $this->home_url );
-
- if ( ! empty( $parsed_home['host'] ) ) {
- $this->host = str_replace( 'www.', '', $parsed_home['host'] );
- }
-
- if ( ! empty( $parsed_home['scheme'] ) ) {
- $this->scheme = $parsed_home['scheme'];
- }
-
- $this->charset = esc_attr( get_bloginfo( 'charset' ) );
- }
-
- /**
- * Get set of image data sets for the given post.
- *
- * @param object $post Post object to get images for.
- *
- * @return array
- */
- public function get_images( $post ) {
-
- $images = [];
-
- if ( ! is_object( $post ) ) {
- return $images;
- }
-
- $thumbnail_id = get_post_thumbnail_id( $post->ID );
-
- if ( $thumbnail_id ) {
-
- $src = $this->get_absolute_url( $this->image_url( $thumbnail_id ) );
- $alt = WPSEO_Image_Utils::get_alt_tag( $thumbnail_id );
- $title = get_post_field( 'post_title', $thumbnail_id );
- $images[] = $this->get_image_item( $post, $src, $title, $alt );
- }
-
- /**
- * Filter: 'wpseo_sitemap_content_before_parse_html_images' - Filters the post content
- * before it is parsed for images.
- *
- * @param string $content The raw/unprocessed post content.
- */
- $content = apply_filters( 'wpseo_sitemap_content_before_parse_html_images', $post->post_content );
-
- $unfiltered_images = $this->parse_html_images( $content );
-
- foreach ( $unfiltered_images as $image ) {
- $images[] = $this->get_image_item( $post, $image['src'], $image['title'], $image['alt'] );
- }
-
- foreach ( $this->parse_galleries( $content, $post->ID ) as $attachment ) {
-
- $src = $this->get_absolute_url( $this->image_url( $attachment->ID ) );
- $alt = WPSEO_Image_Utils::get_alt_tag( $attachment->ID );
-
- $images[] = $this->get_image_item( $post, $src, $attachment->post_title, $alt );
- }
-
- if ( $post->post_type === 'attachment' && wp_attachment_is_image( $post ) ) {
-
- $src = $this->get_absolute_url( $this->image_url( $post->ID ) );
- $alt = WPSEO_Image_Utils::get_alt_tag( $post->ID );
-
- $images[] = $this->get_image_item( $post, $src, $post->post_title, $alt );
- }
-
- foreach ( $images as $key => $image ) {
-
- if ( empty( $image['src'] ) ) {
- unset( $images[ $key ] );
- }
- }
-
- /**
- * Filter images to be included for the post in XML sitemap.
- *
- * @param array $images Array of image items.
- * @param int $post_id ID of the post.
- */
- $images = apply_filters( 'wpseo_sitemap_urlimages', $images, $post->ID );
-
- return $images;
- }
-
- /**
- * Get the images in the term description.
- *
- * @param object $term Term to get images from description for.
- *
- * @return array
- */
- public function get_term_images( $term ) {
-
- $images = $this->parse_html_images( $term->description );
-
- foreach ( $this->parse_galleries( $term->description ) as $attachment ) {
-
- $images[] = [
- 'src' => $this->get_absolute_url( $this->image_url( $attachment->ID ) ),
- 'title' => $attachment->post_title,
- 'alt' => WPSEO_Image_Utils::get_alt_tag( $attachment->ID ),
- ];
- }
-
- return $images;
- }
-
- /**
- * Parse `
` tags in content.
- *
- * @param string $content Content string to parse.
- *
- * @return array
- */
- private function parse_html_images( $content ) {
-
- $images = [];
-
- if ( ! class_exists( 'DOMDocument' ) ) {
- return $images;
- }
-
- if ( empty( $content ) ) {
- return $images;
- }
-
- // Prevent DOMDocument from bubbling warnings about invalid HTML.
- libxml_use_internal_errors( true );
-
- $post_dom = new DOMDocument();
- $post_dom->loadHTML( 'charset . '">' . $content );
-
- // Clear the errors, so they don't get kept in memory.
- libxml_clear_errors();
-
- /** @var DOMElement $img */
- foreach ( $post_dom->getElementsByTagName( 'img' ) as $img ) {
-
- $src = $img->getAttribute( 'src' );
-
- if ( empty( $src ) ) {
- continue;
- }
-
- $class = $img->getAttribute( 'class' );
-
- if ( // This detects WP-inserted images, which we need to upsize. R.
- ! empty( $class )
- && ( strpos( $class, 'size-full' ) === false )
- && preg_match( '|wp-image-(?P\d+)|', $class, $matches )
- && get_post_status( $matches['id'] )
- ) {
- $src = $this->image_url( $matches['id'] );
- }
-
- $src = $this->get_absolute_url( $src );
-
- if ( strpos( $src, $this->host ) === false ) {
- continue;
- }
-
- if ( $src !== esc_url( $src ) ) {
- continue;
- }
-
- $images[] = [
- 'src' => $src,
- 'title' => $img->getAttribute( 'title' ),
- 'alt' => $img->getAttribute( 'alt' ),
- ];
- }
-
- return $images;
- }
-
- /**
- * Parse gallery shortcodes in a given content.
- *
- * @param string $content Content string.
- * @param int $post_id Optional. ID of post being parsed.
- *
- * @return array Set of attachment objects.
- */
- protected function parse_galleries( $content, $post_id = 0 ) {
-
- $attachments = [];
- $galleries = $this->get_content_galleries( $content );
-
- foreach ( $galleries as $gallery ) {
-
- $id = $post_id;
-
- if ( ! empty( $gallery['id'] ) ) {
- $id = intval( $gallery['id'] );
- }
-
- // Forked from core gallery_shortcode() to have exact same logic. R.
- if ( ! empty( $gallery['ids'] ) ) {
- $gallery['include'] = $gallery['ids'];
- }
-
- $gallery_attachments = $this->get_gallery_attachments( $id, $gallery );
-
- $attachments = array_merge( $attachments, $gallery_attachments );
- }
-
- return array_unique( $attachments, SORT_REGULAR );
- }
-
- /**
- * Retrieves galleries from the passed content.
- *
- * Forked from core to skip executing shortcodes for performance.
- *
- * @param string $content Content to parse for shortcodes.
- *
- * @return array A list of arrays, each containing gallery data.
- */
- protected function get_content_galleries( $content ) {
-
- $galleries = [];
-
- if ( ! preg_match_all( '/' . get_shortcode_regex( [ 'gallery' ] ) . '/s', $content, $matches, PREG_SET_ORDER ) ) {
- return $galleries;
- }
-
- foreach ( $matches as $shortcode ) {
-
- $attributes = shortcode_parse_atts( $shortcode[3] );
-
- if ( $attributes === '' ) { // Valid shortcode without any attributes. R.
- $attributes = [];
- }
-
- $galleries[] = $attributes;
- }
-
- return $galleries;
- }
-
- /**
- * Get image item array with filters applied.
- *
- * @param WP_Post $post Post object for the context.
- * @param string $src Image URL.
- * @param string $title Optional image title.
- * @param string $alt Optional image alt text.
- *
- * @return array
- */
- protected function get_image_item( $post, $src, $title = '', $alt = '' ) {
-
- $image = [];
-
- /**
- * Filter image URL to be included in XML sitemap for the post.
- *
- * @param string $src Image URL.
- * @param object $post Post object.
- */
- $image['src'] = apply_filters( 'wpseo_xml_sitemap_img_src', $src, $post );
-
- if ( ! empty( $title ) ) {
- $image['title'] = $title;
- }
-
- if ( ! empty( $alt ) ) {
- $image['alt'] = $alt;
- }
-
- /**
- * Filter image data to be included in XML sitemap for the post.
- *
- * @param array $image {
- * Array of image data.
- *
- * @type string $src Image URL.
- * @type string $title Image title attribute (optional).
- * @type string $alt Image alt attribute (optional).
- * }
- *
- * @param object $post Post object.
- */
- return apply_filters( 'wpseo_xml_sitemap_img', $image, $post );
- }
-
- /**
- * Get attached image URL with filters applied. Adapted from core for speed.
- *
- * @param int $post_id ID of the post.
- *
- * @return string
- */
- private function image_url( $post_id ) {
-
- static $uploads;
-
- if ( empty( $uploads ) ) {
- $uploads = wp_upload_dir();
- }
-
- if ( $uploads['error'] !== false ) {
- return '';
- }
-
- $file = get_post_meta( $post_id, '_wp_attached_file', true );
-
- if ( empty( $file ) ) {
- return '';
- }
-
- // Check that the upload base exists in the file location.
- if ( strpos( $file, $uploads['basedir'] ) === 0 ) {
- $src = str_replace( $uploads['basedir'], $uploads['baseurl'], $file );
- }
- elseif ( strpos( $file, 'wp-content/uploads' ) !== false ) {
- $src = $uploads['baseurl'] . substr( $file, ( strpos( $file, 'wp-content/uploads' ) + 18 ) );
- }
- else {
- // It's a newly uploaded file, therefore $file is relative to the baseurl.
- $src = $uploads['baseurl'] . '/' . $file;
- }
-
- return apply_filters( 'wp_get_attachment_url', $src, $post_id );
- }
-
- /**
- * Make absolute URL for domain or protocol-relative one.
- *
- * @param string $src URL to process.
- *
- * @return string
- */
- protected function get_absolute_url( $src ) {
-
- if ( empty( $src ) || ! is_string( $src ) ) {
- return $src;
- }
-
- if ( YoastSEO()->helpers->url->is_relative( $src ) === true ) {
-
- if ( $src[0] !== '/' ) {
- return $src;
- }
-
- // The URL is relative, we'll have to make it absolute.
- return $this->home_url . $src;
- }
-
- if ( strpos( $src, 'http' ) !== 0 ) {
- // Protocol relative URL, we add the scheme as the standard requires a protocol.
- return $this->scheme . ':' . $src;
- }
-
- return $src;
- }
-
- /**
- * Returns the attachments for a gallery.
- *
- * @param int $id The post ID.
- * @param array $gallery The gallery config.
- *
- * @return array The selected attachments.
- */
- protected function get_gallery_attachments( $id, $gallery ) {
-
- // When there are attachments to include.
- if ( ! empty( $gallery['include'] ) ) {
- return $this->get_gallery_attachments_for_included( $gallery['include'] );
- }
-
- // When $id is empty, just return empty array.
- if ( empty( $id ) ) {
- return [];
- }
-
- return $this->get_gallery_attachments_for_parent( $id, $gallery );
- }
-
- /**
- * Returns the attachments for the given ID.
- *
- * @param int $id The post ID.
- * @param array $gallery The gallery config.
- *
- * @return array The selected attachments.
- */
- protected function get_gallery_attachments_for_parent( $id, $gallery ) {
- $query = [
- 'posts_per_page' => -1,
- 'post_parent' => $id,
- ];
-
- // When there are posts that should be excluded from result set.
- if ( ! empty( $gallery['exclude'] ) ) {
- $query['post__not_in'] = wp_parse_id_list( $gallery['exclude'] );
- }
-
- return $this->get_attachments( $query );
- }
-
- /**
- * Returns an array with attachments for the post IDs that will be included.
- *
- * @param array $included_ids Array with IDs to include.
- *
- * @return array The found attachments.
- */
- protected function get_gallery_attachments_for_included( $included_ids ) {
- $ids_to_include = wp_parse_id_list( $included_ids );
- $attachments = $this->get_attachments(
- [
- 'posts_per_page' => count( $ids_to_include ),
- 'post__in' => $ids_to_include,
- ]
- );
-
- $gallery_attachments = [];
- foreach ( $attachments as $key => $val ) {
- $gallery_attachments[ $val->ID ] = $val;
- }
-
- return $gallery_attachments;
- }
-
- /**
- * Returns the attachments.
- *
- * @param array $args Array with query args.
- *
- * @return array The found attachments.
- */
- protected function get_attachments( $args ) {
- $default_args = [
- 'post_status' => 'inherit',
- 'post_type' => 'attachment',
- 'post_mime_type' => 'image',
-
- // Defaults taken from function get_posts.
- 'orderby' => 'date',
- 'order' => 'DESC',
- 'meta_key' => '',
- 'meta_value' => '',
- 'suppress_filters' => true,
- 'ignore_sticky_posts' => true,
- 'no_found_rows' => true,
- ];
-
- $args = wp_parse_args( $args, $default_args );
-
- $get_attachments = new WP_Query();
- return $get_attachments->query( $args );
- }
-}
diff --git a/inc/sitemaps/class-sitemaps-admin.php b/inc/sitemaps/class-sitemaps-admin.php
index 1b6e2433099..cd42f7f54f9 100644
--- a/inc/sitemaps/class-sitemaps-admin.php
+++ b/inc/sitemaps/class-sitemaps-admin.php
@@ -23,9 +23,6 @@ class WPSEO_Sitemaps_Admin {
public function __construct() {
add_action( 'transition_post_status', [ $this, 'status_transition' ], 10, 3 );
add_action( 'admin_footer', [ $this, 'status_transition_bulk_finished' ] );
-
- WPSEO_Sitemaps_Cache::register_clear_on_option_update( 'wpseo_titles', '' );
- WPSEO_Sitemaps_Cache::register_clear_on_option_update( 'wpseo', '' );
}
/**
@@ -50,8 +47,6 @@ public function status_transition( $new_status, $old_status, $post ) {
$post_type = get_post_type( $post );
- wp_cache_delete( 'lastpostmodified:gmt:' . $post_type, 'timeinfo' ); // #17455.
-
// Not something we're interested in.
if ( $post_type === 'nav_menu_item' ) {
return;
@@ -113,9 +108,7 @@ public function status_transition_bulk_finished() {
$ping_search_engines = false;
foreach ( $this->importing_post_types as $post_type ) {
- wp_cache_delete( 'lastpostmodified:gmt:' . $post_type, 'timeinfo' ); // #17455.
-
- // Just have the cache deleted for nav_menu_item.
+ // Don't ping for nav_menu_items.
if ( $post_type === 'nav_menu_item' ) {
continue;
}
@@ -130,10 +123,6 @@ public function status_transition_bulk_finished() {
return;
}
- if ( WP_CACHE ) {
- do_action( 'wpseo_hit_sitemap_index' );
- }
-
WPSEO_Sitemaps::ping_search_engines();
}
}
diff --git a/inc/sitemaps/class-sitemaps-cache-validator.php b/inc/sitemaps/class-sitemaps-cache-validator.php
deleted file mode 100644
index acb56c56af3..00000000000
--- a/inc/sitemaps/class-sitemaps-cache-validator.php
+++ /dev/null
@@ -1,319 +0,0 @@
- $max_length ) {
-
- if ( $max_length < 15 ) {
- /*
- * If this happens the most likely cause is a page number that is too high.
- *
- * So this would not happen unintentionally.
- * Either by trying to cause a high server load, finding backdoors or misconfiguration.
- */
- throw new OutOfRangeException(
- __(
- 'Trying to build the sitemap cache key, but the postfix and prefix combination leaves too little room to do this. You are probably requesting a page that is way out of the expected range.',
- 'wordpress-seo'
- )
- );
- }
-
- $half = ( $max_length / 2 );
-
- $first_part = substr( $type, 0, ( ceil( $half ) - 1 ) );
- $last_part = substr( $type, ( 1 - floor( $half ) ) );
-
- $type = $first_part . '..' . $last_part;
- }
-
- return $type;
- }
-
- /**
- * Invalidate sitemap cache.
- *
- * @since 3.2
- *
- * @param string|null $type The type to get the key for. Null for all caches.
- *
- * @return void
- */
- public static function invalidate_storage( $type = null ) {
-
- // Global validator gets cleared when no type is provided.
- $old_validator = null;
-
- // Get the current type validator.
- if ( ! is_null( $type ) ) {
- $old_validator = self::get_validator( $type );
- }
-
- // Refresh validator.
- self::create_validator( $type );
-
- if ( ! wp_using_ext_object_cache() ) {
- // Clean up current cache from the database.
- self::cleanup_database( $type, $old_validator );
- }
-
- // External object cache pushes old and unretrieved items out by itself so we don't have to do anything for that.
- }
-
- /**
- * Cleanup invalidated database cache.
- *
- * @since 3.2
- *
- * @param string|null $type The type of sitemap to clear cache for.
- * @param string|null $validator The validator to clear cache of.
- *
- * @return void
- */
- public static function cleanup_database( $type = null, $validator = null ) {
-
- global $wpdb;
-
- if ( is_null( $type ) ) {
- // Clear all cache if no type is provided.
- $like = sprintf( '%s%%', self::STORAGE_KEY_PREFIX );
- }
- else {
- // Clear type cache for all type keys.
- $like = sprintf( '%1$s%2$s_%%', self::STORAGE_KEY_PREFIX, $type );
- }
-
- /*
- * Add slashes to the LIKE "_" single character wildcard.
- *
- * We can't use `esc_like` here because we need the % in the query.
- */
- $where = [];
- $where[] = sprintf( "option_name LIKE '%s'", addcslashes( '_transient_' . $like, '_' ) );
- $where[] = sprintf( "option_name LIKE '%s'", addcslashes( '_transient_timeout_' . $like, '_' ) );
-
- // Delete transients.
- $query = sprintf( 'DELETE FROM %1$s WHERE %2$s', $wpdb->options, implode( ' OR ', $where ) );
- $wpdb->query( $query );
-
- wp_cache_delete( 'alloptions', 'options' );
- }
-
- /**
- * Get the current cache validator.
- *
- * Without the type the global validator is returned.
- * This can invalidate -all- keys in cache at once.
- *
- * With the type parameter the validator for that specific type can be invalidated.
- *
- * @since 3.2
- *
- * @param string $type Provide a type for a specific type validator, empty for global validator.
- *
- * @return string|null The validator for the supplied type.
- */
- public static function get_validator( $type = '' ) {
-
- $key = self::get_validator_key( $type );
-
- $current = get_option( $key, null );
- if ( ! is_null( $current ) ) {
- return $current;
- }
-
- if ( self::create_validator( $type ) ) {
- return self::get_validator( $type );
- }
-
- return null;
- }
-
- /**
- * Get the cache validator option key for the specified type.
- *
- * @since 3.2
- *
- * @param string $type Provide a type for a specific type validator, empty for global validator.
- *
- * @return string Validator to be used to generate the cache key.
- */
- public static function get_validator_key( $type = '' ) {
-
- if ( empty( $type ) ) {
- return self::VALIDATION_GLOBAL_KEY;
- }
-
- return sprintf( self::VALIDATION_TYPE_KEY_FORMAT, $type );
- }
-
- /**
- * Refresh the cache validator value.
- *
- * @since 3.2
- *
- * @param string $type Provide a type for a specific type validator, empty for global validator.
- *
- * @return bool True if validator key has been saved as option.
- */
- public static function create_validator( $type = '' ) {
-
- $key = self::get_validator_key( $type );
-
- // Generate new validator.
- $microtime = microtime();
-
- // Remove space.
- list( $milliseconds, $seconds ) = explode( ' ', $microtime );
-
- // Transients are purged every 24h.
- $seconds = ( $seconds % DAY_IN_SECONDS );
- $milliseconds = intval( substr( $milliseconds, 2, 3 ), 10 );
-
- // Combine seconds and milliseconds and convert to integer.
- $validator = intval( $seconds . '' . $milliseconds, 10 );
-
- // Apply base 61 encoding.
- $compressed = self::convert_base10_to_base61( $validator );
-
- return update_option( $key, $compressed, false );
- }
-
- /**
- * Encode to base61 format.
- *
- * This is base64 (numeric + alpha + alpha upper case) without the 0.
- *
- * @since 3.2
- *
- * @param int $base10 The number that has to be converted to base 61.
- *
- * @return string Base 61 converted string.
- *
- * @throws InvalidArgumentException When the input is not an integer.
- */
- public static function convert_base10_to_base61( $base10 ) {
-
- if ( ! is_int( $base10 ) ) {
- throw new InvalidArgumentException( __( 'Expected an integer as input.', 'wordpress-seo' ) );
- }
-
- // Characters that will be used in the conversion.
- $characters = '123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
- $length = strlen( $characters );
-
- $remainder = $base10;
- $output = '';
-
- do {
- // Building from right to left in the result.
- $index = ( $remainder % $length );
-
- // Prepend the character to the output.
- $output = $characters[ $index ] . $output;
-
- // Determine the remainder after removing the applied number.
- $remainder = floor( $remainder / $length );
-
- // Keep doing it until we have no remainder left.
- } while ( $remainder );
-
- return $output;
- }
-}
diff --git a/inc/sitemaps/class-sitemaps-cache.php b/inc/sitemaps/class-sitemaps-cache.php
deleted file mode 100644
index 1b09f4dea9a..00000000000
--- a/inc/sitemaps/class-sitemaps-cache.php
+++ /dev/null
@@ -1,353 +0,0 @@
-is_enabled();
- }
-
- /**
- * If cache is enabled.
- *
- * @since 3.2
- *
- * @return bool
- */
- public function is_enabled() {
-
- /**
- * Filter if XML sitemap transient cache is enabled.
- *
- * @param bool $unsigned Enable cache or not, defaults to true.
- */
- return apply_filters( 'wpseo_enable_xml_sitemap_transient_caching', false );
- }
-
- /**
- * Retrieve the sitemap page from cache.
- *
- * @since 3.2
- *
- * @param string $type Sitemap type.
- * @param int $page Page number to retrieve.
- *
- * @return string|bool
- */
- public function get_sitemap( $type, $page ) {
-
- $transient_key = WPSEO_Sitemaps_Cache_Validator::get_storage_key( $type, $page );
- if ( $transient_key === false ) {
- return false;
- }
-
- return get_transient( $transient_key );
- }
-
- /**
- * Get the sitemap that is cached.
- *
- * @param string $type Sitemap type.
- * @param int $page Page number to retrieve.
- *
- * @return WPSEO_Sitemap_Cache_Data|null Null on no cache found otherwise object containing sitemap and meta data.
- */
- public function get_sitemap_data( $type, $page ) {
-
- $sitemap = $this->get_sitemap( $type, $page );
-
- if ( empty( $sitemap ) ) {
- return null;
- }
-
- /*
- * Unserialize Cache Data object as is_serialized() doesn't recognize classes in C format.
- * This work-around should no longer be needed once the minimum PHP version has gone up to PHP 7.4,
- * as the `WPSEO_Sitemap_Cache_Data` class uses O format serialization in PHP 7.4 and higher.
- *
- * @link https://wiki.php.net/rfc/custom_object_serialization
- */
- if ( is_string( $sitemap ) && strpos( $sitemap, 'C:24:"WPSEO_Sitemap_Cache_Data"' ) === 0 ) {
- // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize -- Can't be avoided due to how WP stores options.
- $sitemap = unserialize( $sitemap );
- }
-
- // What we expect it to be if it is set.
- if ( $sitemap instanceof WPSEO_Sitemap_Cache_Data_Interface ) {
- return $sitemap;
- }
-
- return null;
- }
-
- /**
- * Store the sitemap page from cache.
- *
- * @since 3.2
- *
- * @param string $type Sitemap type.
- * @param int $page Page number to store.
- * @param string $sitemap Sitemap body to store.
- * @param bool $usable Is this a valid sitemap or a cache of an invalid sitemap.
- *
- * @return bool
- */
- public function store_sitemap( $type, $page, $sitemap, $usable = true ) {
-
- $transient_key = WPSEO_Sitemaps_Cache_Validator::get_storage_key( $type, $page );
-
- if ( $transient_key === false ) {
- return false;
- }
-
- $status = ( $usable ) ? WPSEO_Sitemap_Cache_Data::OK : WPSEO_Sitemap_Cache_Data::ERROR;
-
- $sitemap_data = new WPSEO_Sitemap_Cache_Data();
- $sitemap_data->set_sitemap( $sitemap );
- $sitemap_data->set_status( $status );
-
- return set_transient( $transient_key, $sitemap_data, DAY_IN_SECONDS );
- }
-
- /**
- * Delete cache transients for index and specific type.
- *
- * Always deletes the main index sitemaps cache, as that's always invalidated by any other change.
- *
- * @since 1.5.4
- * @since 3.2 Changed from function wpseo_invalidate_sitemap_cache() to method in this class.
- *
- * @param string $type Sitemap type to invalidate.
- *
- * @return void
- */
- public static function invalidate( $type ) {
-
- self::clear( [ $type ] );
- }
-
- /**
- * Helper to invalidate in hooks where type is passed as second argument.
- *
- * @since 3.2
- *
- * @param int $unused Unused term ID value.
- * @param string $type Taxonomy to invalidate.
- *
- * @return void
- */
- public static function invalidate_helper( $unused, $type ) {
-
- if (
- WPSEO_Options::get( 'noindex-' . $type ) === false
- || WPSEO_Options::get( 'noindex-tax-' . $type ) === false
- ) {
- self::invalidate( $type );
- }
- }
-
- /**
- * Invalidate sitemap cache for authors.
- *
- * @param int $user_id User ID.
- *
- * @return bool True if the sitemap was properly invalidated. False otherwise.
- */
- public static function invalidate_author( $user_id ) {
-
- $user = get_user_by( 'id', $user_id );
-
- if ( $user === false ) {
- return false;
- }
-
- if ( current_action() === 'user_register' ) {
- update_user_meta( $user_id, '_yoast_wpseo_profile_updated', time() );
- }
-
- if ( empty( $user->roles ) || in_array( 'subscriber', $user->roles, true ) ) {
- return false;
- }
-
- self::invalidate( 'author' );
-
- return true;
- }
-
- /**
- * Invalidate sitemap cache for the post type of a post.
- *
- * Don't invalidate for revisions.
- *
- * @since 1.5.4
- * @since 3.2 Changed from function wpseo_invalidate_sitemap_cache_on_save_post() to method in this class.
- *
- * @param int $post_id Post ID to invalidate type for.
- *
- * @return void
- */
- public static function invalidate_post( $post_id ) {
-
- if ( wp_is_post_revision( $post_id ) ) {
- return;
- }
-
- self::invalidate( get_post_type( $post_id ) );
- }
-
- /**
- * Delete cache transients for given sitemaps types or all by default.
- *
- * @since 1.8.0
- * @since 3.2 Moved from WPSEO_Utils to this class.
- *
- * @param array $types Set of sitemap types to delete cache transients for.
- *
- * @return void
- */
- public static function clear( $types = [] ) {
-
- if ( ! self::$is_enabled ) {
- return;
- }
-
- // No types provided, clear all.
- if ( empty( $types ) ) {
- self::$clear_all = true;
-
- return;
- }
-
- // Always invalidate the index sitemap as well.
- if ( ! in_array( WPSEO_Sitemaps::SITEMAP_INDEX_TYPE, $types, true ) ) {
- array_unshift( $types, WPSEO_Sitemaps::SITEMAP_INDEX_TYPE );
- }
-
- foreach ( $types as $type ) {
- if ( ! in_array( $type, self::$clear_types, true ) ) {
- self::$clear_types[] = $type;
- }
- }
- }
-
- /**
- * Invalidate storage for cache types queued to clear.
- */
- public static function clear_queued() {
-
- if ( self::$clear_all ) {
-
- WPSEO_Sitemaps_Cache_Validator::invalidate_storage();
- self::$clear_all = false;
- self::$clear_types = [];
-
- return;
- }
-
- foreach ( self::$clear_types as $type ) {
- WPSEO_Sitemaps_Cache_Validator::invalidate_storage( $type );
- }
-
- self::$clear_types = [];
- }
-
- /**
- * Adds a hook that when given option is updated, the cache is cleared.
- *
- * @since 3.2
- *
- * @param string $option Option name.
- * @param string $type Sitemap type.
- */
- public static function register_clear_on_option_update( $option, $type = '' ) {
-
- self::$cache_clear[ $option ] = $type;
- }
-
- /**
- * Clears the transient cache when a given option is updated, if that option has been registered before.
- *
- * @since 3.2
- *
- * @param string $option The option name that's being updated.
- *
- * @return void
- */
- public static function clear_on_option_update( $option ) {
-
- if ( array_key_exists( $option, self::$cache_clear ) ) {
-
- if ( empty( self::$cache_clear[ $option ] ) ) {
- // Clear all caches.
- self::clear();
- }
- else {
- // Clear specific provided type(s).
- $types = (array) self::$cache_clear[ $option ];
- self::clear( $types );
- }
- }
- }
-}
diff --git a/inc/sitemaps/class-sitemaps-renderer.php b/inc/sitemaps/class-sitemaps-renderer.php
index 95ee3297e60..ce0bfab4874 100644
--- a/inc/sitemaps/class-sitemaps-renderer.php
+++ b/inc/sitemaps/class-sitemaps-renderer.php
@@ -130,7 +130,7 @@ public function get_sitemap( $links, $type, $current_page ) {
/**
* Produce final XML output with debug information.
*
- * @param string $sitemap Sitemap XML.
+ * @param string $sitemap Sitemap XML.
*
* @return string
*/
@@ -267,9 +267,9 @@ public function sitemap_url( $url ) {
/**
* Filters the output for the sitemap URL tag.
*
- * @api string $output The output for the sitemap url tag.
- *
* @param array $url The sitemap URL array on which the output is based.
+ *
+ * @api string $output The output for the sitemap url tag.
*/
return apply_filters( 'wpseo_sitemap_url', $output, $url );
}
diff --git a/inc/sitemaps/class-sitemaps.php b/inc/sitemaps/class-sitemaps.php
index 41d7fc4b499..5eb48918d65 100644
--- a/inc/sitemaps/class-sitemaps.php
+++ b/inc/sitemaps/class-sitemaps.php
@@ -74,15 +74,6 @@ class WPSEO_Sitemaps {
*/
public $renderer;
- /**
- * The sitemap cache.
- *
- * @since 3.2
- *
- * @var WPSEO_Sitemaps_Cache
- */
- public $cache;
-
/**
* The sitemap providers.
*
@@ -100,12 +91,10 @@ public function __construct() {
add_action( 'after_setup_theme', [ $this, 'init_sitemaps_providers' ] );
add_action( 'after_setup_theme', [ $this, 'reduce_query_load' ], 99 );
add_action( 'pre_get_posts', [ $this, 'redirect' ], 1 );
- add_action( 'wpseo_hit_sitemap_index', [ $this, 'hit_sitemap_index' ] );
add_action( 'wpseo_ping_search_engines', [ __CLASS__, 'ping_search_engines' ] );
$this->router = new WPSEO_Sitemaps_Router();
$this->renderer = new WPSEO_Sitemaps_Renderer();
- $this->cache = new WPSEO_Sitemaps_Cache();
if ( ! empty( $_SERVER['SERVER_PROTOCOL'] ) ) {
$this->http_protocol = sanitize_text_field( wp_unslash( $_SERVER['SERVER_PROTOCOL'] ) );
@@ -253,9 +242,7 @@ public function redirect( $query ) {
$this->set_n( get_query_var( 'sitemap_n' ) );
- if ( ! $this->get_sitemap_from_cache( $type, $this->current_page ) ) {
- $this->build_sitemap( $type );
- }
+ $this->build_sitemap( $type );
if ( $this->bad_sitemap ) {
$query->set_404();
@@ -268,60 +255,6 @@ public function redirect( $query ) {
$this->sitemap_close();
}
- /**
- * Try to get the sitemap from cache.
- *
- * @param string $type Sitemap type.
- * @param int $page_number The page number to retrieve.
- *
- * @return bool If the sitemap has been retrieved from cache.
- */
- private function get_sitemap_from_cache( $type, $page_number ) {
-
- $this->transient = false;
-
- if ( $this->cache->is_enabled() !== true ) {
- return false;
- }
-
- /**
- * Fires before the attempt to retrieve XML sitemap from the transient cache.
- *
- * @param WPSEO_Sitemaps $sitemaps Sitemaps object.
- */
- do_action( 'wpseo_sitemap_stylesheet_cache_' . $type, $this );
-
- $sitemap_cache_data = $this->cache->get_sitemap_data( $type, $page_number );
-
- // No cache was found, refresh it because cache is enabled.
- if ( empty( $sitemap_cache_data ) ) {
- return $this->refresh_sitemap_cache( $type, $page_number );
- }
-
- // Cache object was found, parse information.
- $this->transient = true;
-
- $this->sitemap = $sitemap_cache_data->get_sitemap();
- $this->bad_sitemap = ! $sitemap_cache_data->is_usable();
-
- return true;
- }
-
- /**
- * Build and save sitemap to cache.
- *
- * @param string $type Sitemap type.
- * @param int $page_number The page number to save to.
- *
- * @return bool
- */
- private function refresh_sitemap_cache( $type, $page_number ) {
- $this->set_n( $page_number );
- $this->build_sitemap( $type );
-
- return $this->cache->store_sitemap( $type, $page_number, $this->sitemap, ! $this->bad_sitemap );
- }
-
/**
* Attempts to build the requested sitemap.
*
@@ -448,93 +381,6 @@ public function output() {
echo $this->renderer->get_output( $this->sitemap );
}
- /**
- * Makes a request to the sitemap index to cache it before the arrival of the search engines.
- *
- * @return void
- */
- public function hit_sitemap_index() {
- if ( ! $this->cache->is_enabled() ) {
- return;
- }
-
- wp_remote_get( WPSEO_Sitemaps_Router::get_base_url( 'sitemap_index.xml' ) );
- }
-
- /**
- * Get the GMT modification date for the last modified post in the post type.
- *
- * @since 3.2
- *
- * @param string|array $post_types Post type or array of types.
- * @param bool $return_all Flag to return array of values.
- *
- * @return string|array|false
- */
- public static function get_last_modified_gmt( $post_types, $return_all = false ) {
-
- global $wpdb;
-
- static $post_type_dates = null;
-
- if ( ! is_array( $post_types ) ) {
- $post_types = [ $post_types ];
- }
-
- foreach ( $post_types as $post_type ) {
- if ( ! isset( $post_type_dates[ $post_type ] ) ) { // If we hadn't seen post type before. R.
- $post_type_dates = null;
- break;
- }
- }
-
- if ( is_null( $post_type_dates ) ) {
-
- $post_type_dates = [];
- $post_type_names = WPSEO_Post_Type::get_accessible_post_types();
-
- if ( ! empty( $post_type_names ) ) {
- $post_statuses = array_map( 'esc_sql', self::get_post_statuses() );
-
- $sql = "
- SELECT post_type, MAX(post_modified_gmt) AS date
- FROM $wpdb->posts
- WHERE post_status IN ('" . implode( "','", $post_statuses ) . "')
- AND post_type IN ('" . implode( "','", $post_type_names ) . "')
- GROUP BY post_type
- ORDER BY date DESC
- ";
-
- foreach ( $wpdb->get_results( $sql ) as $obj ) {
- $post_type_dates[ $obj->post_type ] = $obj->date;
- }
- }
- }
-
- $dates = array_intersect_key( $post_type_dates, array_flip( $post_types ) );
-
- if ( count( $dates ) > 0 ) {
- if ( $return_all ) {
- return $dates;
- }
-
- return max( $dates );
- }
-
- return false;
- }
-
- /**
- * Get the modification date for the last modified post in the post type.
- *
- * @param array $post_types Post types to get the last modification date for.
- *
- * @return string
- */
- public function get_last_modified( $post_types ) {
- return YoastSEO()->helpers->date->format( self::get_last_modified_gmt( $post_types ) );
- }
-
/**
* Notify search engines of the updated sitemap.
*
@@ -578,7 +424,7 @@ protected function get_entries_per_page() {
*
* @param int $entries The maximum number of entries per XML sitemap.
*/
- $entries = (int) apply_filters( 'wpseo_sitemap_entries_per_page', 1000 );
+ $entries = (int) apply_filters( 'Yoast\WP\SEO\xml_sitemaps_entries_per_sitemap', 5000 );
return $entries;
}
diff --git a/inc/sitemaps/class-taxonomy-sitemap-provider.php b/inc/sitemaps/class-taxonomy-sitemap-provider.php
index 8d8b394fc07..e8ec3a76fde 100644
--- a/inc/sitemaps/class-taxonomy-sitemap-provider.php
+++ b/inc/sitemaps/class-taxonomy-sitemap-provider.php
@@ -8,33 +8,7 @@
/**
* Sitemap provider for author archives.
*/
-class WPSEO_Taxonomy_Sitemap_Provider implements WPSEO_Sitemap_Provider {
-
- /**
- * Holds image parser instance.
- *
- * @var WPSEO_Sitemap_Image_Parser
- */
- protected static $image_parser;
-
- /**
- * Determines whether images should be included in the XML sitemap.
- *
- * @var bool
- */
- private $include_images;
-
- /**
- * Set up object properties for data reuse.
- */
- public function __construct() {
- /**
- * Filter - Allows excluding images from the XML sitemap.
- *
- * @param bool $include True to include, false to exclude.
- */
- $this->include_images = apply_filters( 'wpseo_xml_sitemap_include_images', true );
- }
+class WPSEO_Taxonomy_Sitemap_Provider extends WPSEO_Indexable_Sitemap_Provider implements WPSEO_Sitemap_Provider {
/**
* Check if provider supports given item type.
@@ -55,115 +29,55 @@ public function handles_type( $type ) {
}
/**
- * Retrieves the links for the sitemap.
- *
- * @param int $max_entries Entries per sitemap.
+ * Returns the object type for this sitemap.
*
- * @return array
+ * @return string The object type.
*/
- public function get_index_links( $max_entries ) {
-
- $taxonomies = get_taxonomies( [ 'public' => true ], 'objects' );
+ protected function get_object_type() {
+ return 'term';
+ }
- if ( empty( $taxonomies ) ) {
- return [];
+ /**
+ * Whether or not a specific object sub type should be excluded.
+ *
+ * @param string $object_sub_type The object sub type.
+ *
+ * @return boolean Whether or not it should be excluded.
+ */
+ protected function should_exclude_object_sub_type( $object_sub_type ) {
+ /**
+ * Filter to exclude the taxonomy from the XML sitemap.
+ *
+ * @param bool $exclude Defaults to false.
+ * @param string $taxonomy_name Name of the taxonomy to exclude..
+ */
+ if ( apply_filters( 'wpseo_sitemap_exclude_taxonomy', false, $object_sub_type ) ) {
+ return true;
}
- $taxonomy_names = array_filter( array_keys( $taxonomies ), [ $this, 'is_valid_taxonomy' ] );
- $taxonomies = array_intersect_key( $taxonomies, array_flip( $taxonomy_names ) );
-
- // Retrieve all the taxonomies and their terms so we can do a proper count on them.
+ return false;
+ }
+ /**
+ * Returns a list of all object IDs that should be excluded.
+ *
+ * @return int[]
+ */
+ protected function get_excluded_object_ids() {
/**
- * Filter the setting of excluding empty terms from the XML sitemap.
+ * Filter: 'wpseo_exclude_from_sitemap_by_term_ids' - Allow excluding terms by ID.
*
- * @param bool $exclude Defaults to true.
- * @param array $taxonomy_names Array of names for the taxonomies being processed.
+ * @api array $terms_to_exclude The terms to exclude.
*/
- $hide_empty = apply_filters( 'wpseo_sitemap_exclude_empty_terms', true, $taxonomy_names );
-
- $all_taxonomies = [];
-
- foreach ( $taxonomy_names as $taxonomy_name ) {
- /**
- * Filter the setting of excluding empty terms from the XML sitemap for a specific taxonomy.
- *
- * @param bool $exclude Defaults to the sitewide setting.
- * @param string $taxonomy_name The name of the taxonomy being processed.
- */
- $hide_empty_tax = apply_filters( 'wpseo_sitemap_exclude_empty_terms_taxonomy', $hide_empty, $taxonomy_name );
+ $excluded_term_ids = apply_filters( 'wpseo_exclude_from_sitemap_by_term_ids', [] );
- $term_args = [
- 'hide_empty' => $hide_empty_tax,
- 'fields' => 'ids',
- ];
- $taxonomy_terms = get_terms( $taxonomy_name, $term_args );
-
- if ( count( $taxonomy_terms ) > 0 ) {
- $all_taxonomies[ $taxonomy_name ] = $taxonomy_terms;
- }
+ if ( ! is_array( $excluded_term_ids ) ) {
+ return [];
}
- $index = [];
-
- foreach ( $taxonomies as $tax_name => $tax ) {
-
- if ( ! isset( $all_taxonomies[ $tax_name ] ) ) { // No eligible terms found.
- continue;
- }
-
- $total_count = ( isset( $all_taxonomies[ $tax_name ] ) ) ? count( $all_taxonomies[ $tax_name ] ) : 1;
- $max_pages = 1;
+ $excluded_term_ids = array_map( 'intval', $excluded_term_ids );
- if ( $total_count > $max_entries ) {
- $max_pages = (int) ceil( $total_count / $max_entries );
- }
-
- $last_modified_gmt = WPSEO_Sitemaps::get_last_modified_gmt( $tax->object_type );
-
- for ( $page_counter = 0; $page_counter < $max_pages; $page_counter++ ) {
-
- $current_page = ( $max_pages > 1 ) ? ( $page_counter + 1 ) : '';
-
- if ( ! is_array( $tax->object_type ) || count( $tax->object_type ) === 0 ) {
- continue;
- }
-
- $terms = array_splice( $all_taxonomies[ $tax_name ], 0, $max_entries );
-
- if ( ! $terms ) {
- continue;
- }
-
- $args = [
- 'post_type' => $tax->object_type,
- 'tax_query' => [
- [
- 'taxonomy' => $tax_name,
- 'terms' => $terms,
- ],
- ],
- 'orderby' => 'modified',
- 'order' => 'DESC',
- 'posts_per_page' => 1,
- ];
- $query = new WP_Query( $args );
-
- if ( $query->have_posts() ) {
- $date = $query->posts[0]->post_modified_gmt;
- }
- else {
- $date = $last_modified_gmt;
- }
-
- $index[] = [
- 'loc' => WPSEO_Sitemaps_Router::get_base_url( $tax_name . '-sitemap' . $current_page . '.xml' ),
- 'lastmod' => $date,
- ];
- }
- }
-
- return $index;
+ return array_unique( $excluded_term_ids );
}
/**
@@ -178,99 +92,29 @@ public function get_index_links( $max_entries ) {
* @throws OutOfBoundsException When an invalid page is requested.
*/
public function get_sitemap_links( $type, $max_entries, $current_page ) {
- global $wpdb;
-
- $links = [];
if ( ! $this->handles_type( $type ) ) {
- return $links;
+ return [];
}
- $taxonomy = get_taxonomy( $type );
-
$steps = $max_entries;
$offset = ( $current_page > 1 ) ? ( ( $current_page - 1 ) * $max_entries ) : 0;
- /** This filter is documented in inc/sitemaps/class-taxonomy-sitemap-provider.php */
- $hide_empty = apply_filters( 'wpseo_sitemap_exclude_empty_terms', true, [ $taxonomy->name ] );
- /** This filter is documented in inc/sitemaps/class-taxonomy-sitemap-provider.php */
- $hide_empty_tax = apply_filters( 'wpseo_sitemap_exclude_empty_terms_taxonomy', $hide_empty, $taxonomy->name );
- $terms = get_terms(
- [
- 'taxonomy' => $taxonomy->name,
- 'hide_empty' => $hide_empty_tax,
- 'update_term_meta_cache' => false,
- 'offset' => $offset,
- 'number' => $steps,
- ]
- );
-
- // If there are no terms fetched for this range, we are on an invalid page.
- if ( empty( $terms ) ) {
- throw new OutOfBoundsException( 'Invalid sitemap page requested' );
+ $query = $this->repository
+ ->query_where_noindex( false, 'term', $type )
+ ->select_many( 'id', 'object_id', 'permalink', 'object_last_modified' )
+ ->where( 'is_publicly_viewable', true )
+ ->order_by_asc( 'object_last_modified' )
+ ->offset( $offset )
+ ->limit( $steps );
+
+ $terms_to_exclude = $this->get_excluded_object_ids();
+ if ( is_array( $terms_to_exclude ) && count( $terms_to_exclude ) > 0 ) {
+ $query->where_not_in( 'object_id', $terms_to_exclude );
}
- $post_statuses = array_map( 'esc_sql', WPSEO_Sitemaps::get_post_statuses() );
-
- // Grab last modified date.
- $sql = "
- SELECT MAX(p.post_modified_gmt) AS lastmod
- FROM $wpdb->posts AS p
- INNER JOIN $wpdb->term_relationships AS term_rel
- ON term_rel.object_id = p.ID
- INNER JOIN $wpdb->term_taxonomy AS term_tax
- ON term_tax.term_taxonomy_id = term_rel.term_taxonomy_id
- AND term_tax.taxonomy = %s
- AND term_tax.term_id = %d
- WHERE p.post_status IN ('" . implode( "','", $post_statuses ) . "')
- AND p.post_password = ''
- ";
-
- /**
- * Filter: 'wpseo_exclude_from_sitemap_by_term_ids' - Allow excluding terms by ID.
- *
- * @api array $terms_to_exclude The terms to exclude.
- */
- $terms_to_exclude = apply_filters( 'wpseo_exclude_from_sitemap_by_term_ids', [] );
-
- foreach ( $terms as $term ) {
-
- if ( in_array( $term->term_id, $terms_to_exclude, true ) ) {
- continue;
- }
-
- $url = [];
-
- $tax_noindex = WPSEO_Taxonomy_Meta::get_term_meta( $term, $term->taxonomy, 'noindex' );
-
- if ( $tax_noindex === 'noindex' ) {
- continue;
- }
-
- $url['loc'] = WPSEO_Taxonomy_Meta::get_term_meta( $term, $term->taxonomy, 'canonical' );
-
- if ( ! is_string( $url['loc'] ) || $url['loc'] === '' ) {
- $url['loc'] = get_term_link( $term, $term->taxonomy );
- }
-
- $url['mod'] = $wpdb->get_var( $wpdb->prepare( $sql, $term->taxonomy, $term->term_id ) );
-
- if ( $this->include_images ) {
- $url['images'] = $this->get_image_parser()->get_term_images( $term );
- }
-
- // Deprecated, kept for backwards data compat. R.
- $url['chf'] = 'daily';
- $url['pri'] = 1;
+ $indexables = $query->find_many();
- /** This filter is documented at inc/sitemaps/class-post-type-sitemap-provider.php */
- $url = apply_filters( 'wpseo_sitemap_entry', $url, 'term', $term );
-
- if ( ! empty( $url ) ) {
- $links[] = $url;
- }
- }
-
- return $links;
+ return $this->xml_sitemap_helper->convert_indexables_to_sitemap_links( $indexables, 'term' );
}
/**
@@ -298,7 +142,7 @@ public function is_valid_taxonomy( $taxonomy_name ) {
* Filter to exclude the taxonomy from the XML sitemap.
*
* @param bool $exclude Defaults to false.
- * @param string $taxonomy_name Name of the taxonomy to exclude..
+ * @param string $taxonomy_name Name of the taxonomy to exclude.
*/
if ( apply_filters( 'wpseo_sitemap_exclude_taxonomy', false, $taxonomy_name ) ) {
return false;
@@ -306,17 +150,4 @@ public function is_valid_taxonomy( $taxonomy_name ) {
return true;
}
-
- /**
- * Get the Image Parser.
- *
- * @return WPSEO_Sitemap_Image_Parser
- */
- protected function get_image_parser() {
- if ( ! isset( self::$image_parser ) ) {
- self::$image_parser = new WPSEO_Sitemap_Image_Parser();
- }
-
- return self::$image_parser;
- }
}
diff --git a/inc/sitemaps/interface-sitemap-cache-data.php b/inc/sitemaps/interface-sitemap-cache-data.php
deleted file mode 100644
index 136f6d65cc6..00000000000
--- a/inc/sitemaps/interface-sitemap-cache-data.php
+++ /dev/null
@@ -1,72 +0,0 @@
-build_update();
}
+ /**
+ * Returns the values bound to this query.
+ *
+ * @return array The values.
+ */
+ public function get_values() {
+ return $this->values;
+ }
+
/**
* Executes an aggregate query on the current connection.
*
diff --git a/src/config/migrations/20211108133106_AddIsPubliclyViewableToIndexables.php b/src/config/migrations/20211108133106_AddIsPubliclyViewableToIndexables.php
index 901a1db4fca..19319c1606e 100644
--- a/src/config/migrations/20211108133106_AddIsPubliclyViewableToIndexables.php
+++ b/src/config/migrations/20211108133106_AddIsPubliclyViewableToIndexables.php
@@ -34,6 +34,8 @@ public function up() {
'default' => null,
]
);
+
+ $this->query( "UPDATE $table_name SET is_publicly_viewable = is_public" );
}
/**
@@ -60,6 +62,3 @@ protected function get_table_name() {
return Model::get_table_name( 'Indexable' );
}
}
-
-
-
diff --git a/src/helpers/xml-sitemap-helper.php b/src/helpers/xml-sitemap-helper.php
new file mode 100644
index 00000000000..0c6620f0cf5
--- /dev/null
+++ b/src/helpers/xml-sitemap-helper.php
@@ -0,0 +1,123 @@
+links_repository = $links_repository;
+ }
+
+ /**
+ * Find images for a given set of indexables.
+ *
+ * @param Indexable[] $indexables Array of indexables.
+ * @param string $type The type of link to retrieve, defaults to image internal.
+ *
+ * @return array $images_by_id Array of images for the indexable, in XML sitemap format.
+ */
+ public function find_images_for_indexables( $indexables, $type = 'image-in' ) {
+ $images_by_id = [];
+ $indexable_ids = [];
+
+ foreach ( $indexables as $indexable ) {
+ $indexable_ids[] = $indexable->id;
+ }
+
+ if ( $indexable_ids === [] ) {
+ return [];
+ }
+
+ $images = $this->links_repository->query()
+ ->select_many( 'indexable_id', 'url' )
+ ->where( 'type', $type )
+ ->where_in( 'indexable_id', $indexable_ids )
+ ->find_many();
+
+ foreach ( $images as $image ) {
+ if ( ! is_array( $images_by_id[ $image->indexable_id ] ) ) {
+ $images_by_id[ $image->indexable_id ] = [];
+ }
+ $images_by_id[ $image->indexable_id ][] = [
+ 'src' => $image->url,
+ ];
+ }
+
+ return $images_by_id;
+ }
+
+ /**
+ * Convert an array of indexables to an array that can be used by the XML sitemap renderer.
+ *
+ * @param Indexable[] $indexables Array of indexables.
+ * @param string $object_type The Indexable object type.
+ *
+ * @return array Array to be used by the XML sitemap renderer.
+ */
+ public function convert_indexables_to_sitemap_links( $indexables, $object_type ) {
+ /**
+ * Filter - Allows excluding images from the XML sitemap.
+ *
+ * @param bool $include True to include, false to exclude.
+ */
+ $include_images = apply_filters( 'wpseo_xml_sitemap_include_images', true );
+
+ if ( $include_images ) {
+ $images_by_id = $this->find_images_for_indexables( $indexables );
+ }
+
+ $links = [];
+ foreach ( $indexables as $indexable ) {
+ $images = isset( $images_by_id[ $indexable->id ] ) ? $images_by_id[ $indexable->id ] : [];
+
+ if ( $indexable->object_type === 'post' ) {
+ /**
+ * Filter images to be included for the post in XML sitemap.
+ *
+ * @param array $images Array of image items.
+ * @param int $post_id ID of the post.
+ */
+ $images = \apply_filters( 'wpseo_sitemap_urlimages', $images, $indexable->object_id );
+ }
+
+ $url = [
+ 'loc' => $indexable->permalink,
+ 'mod' => $indexable->object_last_modified,
+ 'images' => $images,
+ ];
+
+ /**
+ * Filter URL entry before it gets added to the sitemap.
+ *
+ * @param array $url Array of URL parts.
+ * @param string $type URL type.
+ * @param int $object_id WordPress ID of the object.
+ */
+ $url = apply_filters( 'wpseo_sitemap_entry', $url, $object_type, $indexable->object_id );
+
+ $links[] = $url;
+ }
+
+ return $links;
+ }
+}
diff --git a/src/surfaces/helpers-surface.php b/src/surfaces/helpers-surface.php
index c0e7c063b96..e298665b74f 100644
--- a/src/surfaces/helpers-surface.php
+++ b/src/surfaces/helpers-surface.php
@@ -44,6 +44,7 @@
* @property Helpers\User_Helper $user
* @property Helpers\Woocommerce_Helper $woocommerce
* @property Helpers\Wordpress_Helper $wordpress
+ * @property Helpers\XML_Sitemap_Helper $xml_sitemap
*/
class Helpers_Surface {
diff --git a/tests/integration/sitemaps/test-class-wpseo-post-type-sitemap-provider.php b/tests/integration/sitemaps/test-class-wpseo-post-type-sitemap-provider.php
index 6654e7bf4e7..626523f460a 100644
--- a/tests/integration/sitemaps/test-class-wpseo-post-type-sitemap-provider.php
+++ b/tests/integration/sitemaps/test-class-wpseo-post-type-sitemap-provider.php
@@ -256,31 +256,6 @@ public function filter_with_invalid_output( $excluded_post_ids ) {
return '1,2,3,4';
}
- /**
- * Tests if external URLs are not being included in the sitemap.
- *
- * @covers WPSEO_Post_Type_Sitemap_Provider::get_url
- */
- public function test_get_url() {
- $current_home = get_option( 'home' );
- $sitemap_provider = new WPSEO_Post_Type_Sitemap_Provider_Double();
-
- $post_object = $this->factory()->post->create_and_get();
- $post_url = $sitemap_provider->get_url( $post_object );
-
- $this->assertStringContainsString( $current_home, $post_url['loc'] );
-
- // Change home URL.
- update_option( 'home', 'http://example.com' );
- wp_cache_delete( 'alloptions', 'options' );
-
- $this->assertFalse( $sitemap_provider->get_url( $post_object ) );
-
- // Revert original home URL.
- update_option( 'home', $current_home );
- wp_cache_delete( 'alloptions', 'options' );
- }
-
/**
* Tests a regular post is added to the sitemap.
*
diff --git a/tests/integration/sitemaps/test-class-wpseo-sitemap-image-parser.php b/tests/integration/sitemaps/test-class-wpseo-sitemap-image-parser.php
deleted file mode 100644
index a47000a1e58..00000000000
--- a/tests/integration/sitemaps/test-class-wpseo-sitemap-image-parser.php
+++ /dev/null
@@ -1,92 +0,0 @@
-factory->post->create(
- [ 'post_content' => "
" ]
- );
-
- $images = self::$class_instance->get_images( get_post( $post_id ) );
- $this->assertNotEmpty( $images[0] );
- $content_image = $images[0];
- $this->assertEquals( $content_src, $content_image['src'] );
- $this->assertEquals( $content_title, $content_image['title'] );
- $this->assertEquals( $content_alt, $content_image['alt'] );
- }
-
- /**
- * Tests the get_gallery_attachments function.
- *
- * @covers WPSEO_Sitemap_Image_Parser::get_gallery_attachments
- *
- * @link https://github.com/Yoast/wordpress-seo/issues/8634
- */
- public function test_parse_galleries() {
- /**
- * The test instance.
- *
- * @var WPSEO_Sitemap_Image_Parser_Double $image_parser
- */
- $image_parser = $this->getMockBuilder( 'WPSEO_Sitemap_Image_Parser_Double' )
- ->setMethods( [ 'get_content_galleries', 'get_gallery_attachments' ] )
- ->getMock();
-
- $image_parser
- ->expects( $this->once() )
- ->method( 'get_content_galleries' )
- ->will( $this->returnValue( [ [ 'id' => 1 ] ] ) );
-
- $a = (object) [ 'a', 'b' ];
- $b = (object) 1234;
- $c = (object) 'some string';
-
- $attachments = [ $a, $b, $c, $a, $c ];
-
- $image_parser
- ->expects( $this->once() )
- ->method( 'get_gallery_attachments' )
- ->will( $this->returnValue( $attachments ) );
-
- $attachments = $image_parser->parse_galleries( '' );
-
- $this->assertContains( $a, $attachments );
- $this->assertContains( $b, $attachments );
- $this->assertContains( $c, $attachments );
- }
-}
diff --git a/tests/integration/sitemaps/test-class-wpseo-sitemaps-cache-data.php b/tests/integration/sitemaps/test-class-wpseo-sitemaps-cache-data.php
deleted file mode 100644
index 37f6a2ea370..00000000000
--- a/tests/integration/sitemaps/test-class-wpseo-sitemaps-cache-data.php
+++ /dev/null
@@ -1,167 +0,0 @@
-subject = new WPSEO_Sitemap_Cache_Data();
- }
-
- /**
- * Test getting/setting sitemap.
- *
- * @covers WPSEO_Sitemap_Cache_Data::set_sitemap
- * @covers WPSEO_Sitemap_Cache_Data::get_sitemap
- */
- public function test_get_set_sitemap() {
- $sitemap = 'this is a sitemap';
-
- $this->subject->set_sitemap( $sitemap );
- $this->assertSame( $sitemap, $this->subject->get_sitemap() );
- }
-
- /**
- * Setting a sitemap that is not a string.
- *
- * @covers WPSEO_Sitemap_Cache_Data::get_sitemap
- * @covers WPSEO_Sitemap_Cache_Data::is_usable
- */
- public function test_set_sitemap_not_string() {
- $sitemap = new stdClass();
- $sitemap->doesnt = 'matter';
-
- $this->subject->set_sitemap( $sitemap );
- $this->assertSame( '', $this->subject->get_sitemap() );
- $this->assertFalse( $this->subject->is_usable() );
- }
-
- /**
- * Test with invalid status.
- *
- * @covers WPSEO_Sitemap_Cache_Data::get_status
- */
- public function test_set_invalid_status() {
- $status = 'invalid';
-
- $this->subject->set_status( $status );
- $this->assertSame( WPSEO_Sitemap_Cache_Data_Interface::UNKNOWN, $this->subject->get_status() );
- $this->assertFalse( $this->subject->is_usable() );
- }
-
- /**
- * Test status of sitemap without setting anything.
- *
- * @covers WPSEO_Sitemap_Cache_Data::get_status
- */
- public function test_sitemap_status_unset() {
- $this->assertSame( WPSEO_Sitemap_Cache_Data::UNKNOWN, $this->subject->get_status() );
- }
-
- /**
- * Test setting empty sitemap - status.
- *
- * @covers WPSEO_Sitemap_Cache_Data::set_sitemap
- * @covers WPSEO_Sitemap_Cache_Data::get_status
- */
- public function test_set_empty_sitemap_status() {
- $sitemap = '';
-
- $this->subject->set_sitemap( $sitemap );
- $this->assertSame( WPSEO_Sitemap_Cache_Data::ERROR, $this->subject->get_status() );
- }
-
- /**
- * Test is_usable with status.
- *
- * @covers WPSEO_Sitemap_Cache_Data::get_status
- * @covers WPSEO_Sitemap_Cache_Data::is_usable
- */
- public function test_set_status_is_usable() {
- $this->subject->set_status( WPSEO_Sitemap_Cache_Data::OK );
- $this->assertTrue( $this->subject->is_usable() );
-
- $this->subject->set_status( WPSEO_Sitemap_Cache_Data::ERROR );
- $this->assertFalse( $this->subject->is_usable() );
- }
-
- /**
- * Test setting status string/constant.
- *
- * @covers WPSEO_Sitemap_Cache_Data::set_status
- * @covers WPSEO_Sitemap_Cache_Data::get_status
- *
- * @dataProvider data_set_status_string
- *
- * @param string $input Input to pass to set_status().
- * @param string $expected Expected get_status() function output.
- */
- public function test_set_status_string( $input, $expected ) {
- $this->subject->set_status( $input );
- $this->assertSame( $expected, $this->subject->get_status() );
- }
-
- /**
- * Data provider.
- *
- * @return array
- */
- public function data_set_status_string() {
- return [
- 'Ok using interface constant' => [
- 'input' => WPSEO_Sitemap_Cache_Data::OK,
- 'expected' => WPSEO_Sitemap_Cache_Data::OK,
- ],
- 'Ok using hard coded string' => [
- 'input' => 'ok',
- 'expected' => WPSEO_Sitemap_Cache_Data::OK,
- ],
- 'Error using hard coded string' => [
- 'input' => 'error',
- 'expected' => WPSEO_Sitemap_Cache_Data::ERROR,
- ],
- ];
- }
-
- /**
- * Test serializing/unserializing.
- *
- * Tests if the class is serializable.
- *
- * @covers WPSEO_Sitemap_Cache_Data::__serialize
- * @covers WPSEO_Sitemap_Cache_Data::__unserialize
- * @covers WPSEO_Sitemap_Cache_Data::serialize
- * @covers WPSEO_Sitemap_Cache_Data::unserialize
- */
- public function test_serialize_unserialize() {
- $sitemap = 'this is a sitemap';
-
- $this->subject->set_sitemap( $sitemap );
-
- // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize -- Reason: There's no way for user input to get in between serialize and unserialize.
- $tmp = serialize( $this->subject );
- // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize -- Reason: There's no way for user input to get in between serialize and unserialize.
- $test = unserialize( $tmp );
-
- $this->assertEquals( $this->subject, $test );
- }
-}
diff --git a/tests/integration/sitemaps/test-class-wpseo-sitemaps-cache-validator.php b/tests/integration/sitemaps/test-class-wpseo-sitemaps-cache-validator.php
deleted file mode 100644
index 3d2e9eb8fac..00000000000
--- a/tests/integration/sitemaps/test-class-wpseo-sitemaps-cache-validator.php
+++ /dev/null
@@ -1,125 +0,0 @@
-assertEquals( WPSEO_Sitemaps_Cache_Validator::VALIDATION_GLOBAL_KEY, $result );
- }
-
- /**
- * Test the building of cache keys.
- *
- * @covers ::get_validator_key
- */
- public function test_get_validator_key_type() {
-
- $type = 'blabla';
- $expected = sprintf( WPSEO_Sitemaps_Cache_Validator::VALIDATION_TYPE_KEY_FORMAT, $type );
-
- $result = WPSEO_Sitemaps_Cache_Validator::get_validator_key( $type );
-
- $this->assertEquals( $expected, $result );
- }
-
- /**
- * Normal cache key retrieval.
- *
- * @covers ::get_storage_key
- */
- public function test_get_storage_key() {
-
- $page = 1;
- $type = 'page';
- $global_validator = 'global';
- $type_validator = 'type';
-
- $global_validator_key = WPSEO_Sitemaps_Cache_Validator::get_validator_key();
- update_option( $global_validator_key, $global_validator );
-
- $type_validator_key = WPSEO_Sitemaps_Cache_Validator::get_validator_key( $type );
- update_option( $type_validator_key, $type_validator );
-
- $prefix = WPSEO_Sitemaps_Cache_Validator::STORAGE_KEY_PREFIX;
- $postfix = '_1:global_type';
-
- $expected = $prefix . $type . $postfix;
-
- // Act.
- $result = WPSEO_Sitemaps_Cache_Validator::get_storage_key( $type, $page );
-
- // Assert.
- $this->assertEquals( $expected, $result );
- }
-
- /**
- * Key length should never be over 45 characters.
- *
- * This would be 53 if we don't use a timeout, but we can't because all sitemaps would
- * be autoloaded every request.
- *
- * @covers ::get_storage_key
- */
- public function test_get_storage_key_very_long_type() {
-
- $page = 1;
- $type = str_repeat( 'a', 60 );
-
- // Act.
- $result = WPSEO_Sitemaps_Cache_Validator::get_storage_key( $type, $page );
-
- // Assert.
- $this->assertEquals( 45, strlen( $result ) );
- }
-
- /**
- * Test base 10 to base 61 converter.
- *
- * @covers ::convert_base10_to_base61
- */
- public function test_base_10_to_base_61() {
-
- // Because of not using 0, everything has an offset.
- $this->assertEquals( '1', WPSEO_Sitemaps_Cache_Validator::convert_base10_to_base61( 0 ) );
- $this->assertEquals( '2', WPSEO_Sitemaps_Cache_Validator::convert_base10_to_base61( 1 ) );
- $this->assertEquals( 'Z', WPSEO_Sitemaps_Cache_Validator::convert_base10_to_base61( 60 ) );
-
- // Not using 10, because 0 offsets all positions -> 1+1=2, 0+1=1, makes 21 (this is a string not a number!).
- $this->assertEquals( '21', WPSEO_Sitemaps_Cache_Validator::convert_base10_to_base61( 61 ) );
- $this->assertEquals( '22', WPSEO_Sitemaps_Cache_Validator::convert_base10_to_base61( 62 ) );
- $this->assertEquals( '32', WPSEO_Sitemaps_Cache_Validator::convert_base10_to_base61( 123 ) );
-
- // Check against PHP_INT_MAX on 32-bit systems.
- $this->assertEquals( '3y75pX', WPSEO_Sitemaps_Cache_Validator::convert_base10_to_base61( 2147483647 ) );
- }
-
- /**
- * Tests whether an exception is thrown when a non numeric value is passed.
- *
- * @covers ::convert_base10_to_base61
- */
- public function test_base_10_to_base_61_non_integer() {
- $this->expectException( InvalidArgumentException::class );
-
- WPSEO_Sitemaps_Cache_Validator::convert_base10_to_base61( 'ab' );
- }
-}
diff --git a/tests/integration/sitemaps/test-class-wpseo-sitemaps-cache.php b/tests/integration/sitemaps/test-class-wpseo-sitemaps-cache.php
deleted file mode 100644
index 0f7dbe38657..00000000000
--- a/tests/integration/sitemaps/test-class-wpseo-sitemaps-cache.php
+++ /dev/null
@@ -1,390 +0,0 @@
-get_sitemap_data( 'post', 1 );
-
- $this->assertNull( $result );
- }
-
- /**
- * Test if the transient cache is set as a cache data object.
- *
- * @covers WPSEO_Sitemaps_Cache::store_sitemap
- * @covers WPSEO_Sitemaps_Cache::get_sitemap
- * @covers WPSEO_Sitemaps_Cache::get_sitemap_data
- */
- public function test_transient_cache_data_object() {
-
- $sitemap = 'this_is_a_sitemap';
- $type = 'post';
- $page = 1;
-
- $test = new WPSEO_Sitemap_Cache_Data();
- $test->set_sitemap( $sitemap );
-
- $cache = new WPSEO_Sitemaps_Cache();
- $this->assertTrue( $cache->store_sitemap( $type, $page, $sitemap, true ) );
-
- $result = $cache->get_sitemap( $type, $page );
-
- /*
- * In PHP < 7.4 the "old" serialization mechanism via the Serializable interface is used,
- * which combined with the WP logic doesn't automatically unserialize, which is why we need
- * to do so ourselves.
- * As of PHP 7.4, the new serialization using magic methods is used.
- */
- if ( PHP_VERSION_ID < 70400 ) {
- // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize -- Reason: There's no security risk, because users don't interact with tests.
- $result = unserialize( $result );
- }
-
- $this->assertEquals( $test, $result );
-
- $this->assertEquals( $test, $cache->get_sitemap_data( $type, $page ) );
- }
-
- /**
- * Test sitemap cache XML set as string not being validated.
- *
- * @covers WPSEO_Sitemaps_Cache::get_sitemap_data
- * @covers WPSEO_Sitemap_Cache_Data::set_sitemap
- * @covers WPSEO_Sitemap_Cache_Data::is_usable
- */
- public function test_transient_string_to_cache_data() {
-
- $sitemap = 'this is not a wpseo_sitemap_cache_data object';
- $type = 'post';
- $page = 1;
-
- $transient_key = WPSEO_Sitemaps_Cache_Validator::get_storage_key( $type, $page );
-
- set_transient( $transient_key, $sitemap, DAY_IN_SECONDS );
-
- $cache = new WPSEO_Sitemaps_Cache();
- $result = $cache->get_sitemap_data( $type, $page );
-
- $this->assertEquals( $sitemap, get_transient( $transient_key ) );
- $this->assertNull( $result );
- }
-
- /**
- * Test that a sitemap cache originally stored when WP was running on PHP < 7.4 can be retrieved and used in all PHP versions.
- *
- * @covers WPSEO_Sitemaps_Cache::get_sitemap_data
- * @covers WPSEO_Sitemap_Cache_Data::__serialize
- * @covers WPSEO_Sitemap_Cache_Data::__unserialize
- * @covers WPSEO_Sitemap_Cache_Data::serialize
- * @covers WPSEO_Sitemap_Cache_Data::unserialize
- */
- public function test_retrieving_transient_stored_in_php_lt_74() {
-
- $sitemap = 'this is a wpseo_sitemap_cache_data object stored in PHP < 7.4';
- $type = 'post';
- $page = 1;
-
- $transient_key = WPSEO_Sitemaps_Cache_Validator::get_storage_key( $type, $page );
-
- /*
- * Using this filter, we effectively mock the get_transient() and
- * the get_option() WP functions, including the call to maybe_unserialize() in get_option().
- * These functions are used in the get_sitemap[_data]() method to retrieve the transient.
- * This filter short-circuits those function calls to return the specific value we need for this test.
- */
- add_filter(
- "pre_transient_{$transient_key}",
- static function ( $pre_transient ) {
- $pre_transient = 'C:24:"WPSEO_Sitemap_Cache_Data":107:{a:2:{s:6:"status";s:2:"ok";s:3:"xml";s:61:"this is a wpseo_sitemap_cache_data object stored in PHP < 7.4";}}';
- return maybe_unserialize( $pre_transient );
- }
- );
-
- $cache = new WPSEO_Sitemaps_Cache();
- $result = $cache->get_sitemap_data( $type, $page );
-
- $this->assertInstanceOf( 'WPSEO_Sitemap_Cache_Data', $result );
- $this->assertSame( $sitemap, $result->get_sitemap() );
- $this->assertSame( 'ok', $result->get_status() );
- }
-
- /**
- * Test that a sitemap cache originally stored when WP was running on PHP >= 7.4 can be retrieved and used
- * without problems on PHP >= 7.4.
- *
- * This test also documents that when the cache was stored in PHP >= 7.4, but the PHP version on which WP
- * is being run was subsequently downgraded to PHP < 7.4, the cache will be disregarded and the sitemap will
- * need to be rebuild.
- *
- * For that particular scenario, this test also safeguards that the code under test doesn't yield any PHP
- * errors when the unusable sitemap cache data is encountered.
- *
- * @covers WPSEO_Sitemaps_Cache::get_sitemap_data
- * @covers WPSEO_Sitemap_Cache_Data::__serialize
- * @covers WPSEO_Sitemap_Cache_Data::__unserialize
- * @covers WPSEO_Sitemap_Cache_Data::serialize
- * @covers WPSEO_Sitemap_Cache_Data::unserialize
- */
- public function test_retrieving_transient_stored_in_php_gte_74() {
-
- $sitemap = 'this is a wpseo_sitemap_cache_data object stored in PHP >= 7.4';
- $type = 'post';
- $page = 1;
-
- $transient_key = WPSEO_Sitemaps_Cache_Validator::get_storage_key( $type, $page );
-
- /*
- * Using this filter, we effectively mock the get_transient() and
- * the get_option() WP functions, including the call to maybe_unserialize() in get_option().
- * These functions are used in the get_sitemap[_data]() method to retrieve the transient.
- * This filter short-circuits those function calls to return the specific value we need for this test.
- */
- add_filter(
- "pre_transient_{$transient_key}",
- static function ( $pre_transient ) {
- $pre_transient = 'O:24:"WPSEO_Sitemap_Cache_Data":2:{s:6:"status";s:2:"ok";s:3:"xml";s:62:"this is a wpseo_sitemap_cache_data object stored in PHP >= 7.4";}';
- return maybe_unserialize( $pre_transient );
- }
- );
-
- $cache = new WPSEO_Sitemaps_Cache();
- $result = $cache->get_sitemap_data( $type, $page );
-
- if ( PHP_VERSION_ID >= 70400 ) {
- // PHP 7.4+.
- $this->assertInstanceOf( 'WPSEO_Sitemap_Cache_Data', $result );
- $this->assertSame( $sitemap, $result->get_sitemap() );
- $this->assertSame( 'ok', $result->get_status() );
- }
- else {
- // PHP 7.3 and lower.
- $this->assertNull( $result );
- }
- }
-
- /**
- * Clearing all cache.
- *
- * @covers WPSEO_Sitemaps_Cache::clear
- * @covers WPSEO_Sitemaps_Cache::clear_queued
- */
- public function test_clear() {
-
- $type = 'page';
- $page = 1;
- $test_content = 'test_content';
-
- $cache_key = WPSEO_Sitemaps_Cache_Validator::get_storage_key( $type, $page );
- set_transient( $cache_key, $test_content );
-
- // Act.
- WPSEO_Sitemaps_Cache::clear();
- WPSEO_Sitemaps_Cache::clear_queued();
-
- $cache_key = WPSEO_Sitemaps_Cache_Validator::get_storage_key( $type, $page );
- $content = get_transient( $cache_key );
-
- // Assert.
- $this->assertEquals( $test_content, $content );
- }
-
- /**
- * Clearing specific cache.
- *
- * @covers WPSEO_Sitemaps_Cache::clear
- * @covers WPSEO_Sitemaps_Cache::clear_queued
- */
- public function test_clear_type() {
-
- $type = 'page';
- $page = 1;
- $test_content = 'test_content';
-
- $cache_key = WPSEO_Sitemaps_Cache_Validator::get_storage_key( $type, $page );
- set_transient( $cache_key, $test_content );
-
- // Act.
- WPSEO_Sitemaps_Cache::clear( [ $type ] );
- WPSEO_Sitemaps_Cache::clear_queued();
-
- // Get the key again.
- $cache_key = WPSEO_Sitemaps_Cache_Validator::get_storage_key( $type, $page );
- $result = get_transient( $cache_key );
-
- // Assert.
- $this->assertEquals( $test_content, $result );
- }
-
- /**
- * Clearing specific cache should also clear index.
- *
- * @covers WPSEO_Sitemaps_Cache::clear
- * @covers WPSEO_Sitemaps_Cache::clear_queued
- */
- public function test_clear_index_also_cleared() {
-
- $test_index_content = 'test_content';
-
- $index_cache_key = WPSEO_Sitemaps_Cache_Validator::get_storage_key();
- set_transient( $index_cache_key, $test_index_content );
-
- /*
- * The cache invalidator is based on time so if there isn't enough time
- * difference between the two generations we will end up with the same
- * cache invalidator, failing this test.
- */
- usleep( 10000 );
-
- // Act.
- WPSEO_Sitemaps_Cache::clear( [ 'page' ] );
- WPSEO_Sitemaps_Cache::clear_queued();
-
- // Get the key again.
- $index_cache_key = WPSEO_Sitemaps_Cache_Validator::get_storage_key();
- $result = get_transient( $index_cache_key );
-
- // Assert.
- $this->assertEquals( $test_index_content, $result );
- }
-
- /**
- * Clearing specific cache should not touch other type.
- *
- * @covers WPSEO_Sitemaps_Cache::clear
- * @covers WPSEO_Sitemaps_Cache::clear_queued
- */
- public function test_clear_type_isolation() {
-
- $type_a = 'page';
- $type_a_content = 'content_a';
-
- $type_b = 'post';
- $type_b_content = 'content_b';
-
- $type_a_key = WPSEO_Sitemaps_Cache_Validator::get_storage_key( $type_a );
- set_transient( $type_a_key, $type_a_content );
-
- $type_b_key = WPSEO_Sitemaps_Cache_Validator::get_storage_key( $type_b );
- set_transient( $type_b_key, $type_b_content );
-
- // Act.
- WPSEO_Sitemaps_Cache::clear( [ $type_a ] );
- WPSEO_Sitemaps_Cache::clear_queued();
-
- // Get the key again.
- $type_b_key = WPSEO_Sitemaps_Cache_Validator::get_storage_key( $type_b );
- $result = get_transient( $type_b_key );
-
- // Assert.
- $this->assertEquals( $type_b_content, $result );
- }
-
- /**
- * Make sure the hook is registered on registration.
- *
- * @covers WPSEO_Sitemaps_Cache::__construct
- */
- public function test_register_clear_on_option_update() {
-
- new WPSEO_Sitemaps_Cache();
- // Hook will be added on default priority.
- $has_action = has_action(
- 'update_option',
- [ 'WPSEO_Sitemaps_Cache', 'clear_on_option_update' ]
- );
- $this->assertEquals( 10, $has_action );
- }
-
- /**
- * Option update should clear cache for registered type.
- *
- * @covers WPSEO_Sitemaps_Cache::clear_queued
- */
- public function test_clear_transient_cache() {
-
- $type = 'page';
- $page = 1;
- $test_content = 'test_content';
- $option = 'my_option';
-
- new WPSEO_Sitemaps_Cache();
-
- WPSEO_Sitemaps_Cache::register_clear_on_option_update( $option, $type );
-
- $cache_key = WPSEO_Sitemaps_Cache_Validator::get_storage_key( $type, $page );
- set_transient( $cache_key, $test_content );
-
- // Act.
- // Updating the option should clear cache for specified type.
- do_action( 'update_option', $option );
- WPSEO_Sitemaps_Cache::clear_queued();
-
- // Get the key again.
- $cache_key = WPSEO_Sitemaps_Cache_Validator::get_storage_key( $type, $page );
- $result = get_transient( $cache_key );
-
- // Assert.
- $this->assertEquals( $test_content, $result );
- }
-
- /**
- * Tests the attempt to clear the author sitemap for an unknown user, which should return false.
- *
- * @covers WPSEO_Sitemaps_Cache::invalidate_author
- */
- public function test_clearing_author_sitemap_by_unknown_userid() {
- $this->assertFalse( WPSEO_Sitemaps_Cache::invalidate_author( -1 ) );
- }
-
- /**
- * Tests the attempt to clear the author sitemap for a user with the proper roles, which should return true.
- *
- * @covers WPSEO_Sitemaps_Cache::invalidate_author
- */
- public function test_clearing_author_sitemap_by_userid() {
- $user_id = $this->factory->user->create(
- [ 'role' => 'administrator' ]
- );
-
- $this->assertTrue( WPSEO_Sitemaps_Cache::invalidate_author( $user_id ) );
- }
-
- /**
- * Tests the attempt to clear the author sitemap for a user with the subscriber role, which should return false.
- *
- * @covers WPSEO_Sitemaps_Cache::invalidate_author
- */
- public function test_clearing_author_sitemap_by_userid_with_subscriber_role() {
- $user_id = $this->factory->user->create(
- [ 'role' => 'subscriber' ]
- );
-
- $this->assertFalse( WPSEO_Sitemaps_Cache::invalidate_author( $user_id ) );
- }
-}
diff --git a/tests/integration/sitemaps/test-class-wpseo-sitemaps.php b/tests/integration/sitemaps/test-class-wpseo-sitemaps.php
index 6996af9e1c3..b8411a9f94f 100644
--- a/tests/integration/sitemaps/test-class-wpseo-sitemaps.php
+++ b/tests/integration/sitemaps/test-class-wpseo-sitemaps.php
@@ -113,7 +113,7 @@ public function test_index_links_filter() {
static function( $links ) {
$links[] = [
'loc' => 'test-sitemap.xml',
- 'lastmod' => date( '1' ),
+ 'lastmod' => gmdate( '1' ),
];
return $links;
}
@@ -123,42 +123,4 @@ static function( $links ) {
$this->expectOutputContains( 'test-sitemap.xml' );
}
-
- /**
- * Test for last modified date.
- *
- * @covers WPSEO_Sitemaps::get_last_modified_gmt
- */
- public function test_last_modified_post_type() {
-
- $older_date = '2015-01-01 12:00:00';
- $newest_date = '2016-01-01 12:00:00';
-
- $post_type_args = [
- 'public' => true,
- 'has_archive' => true,
- ];
- register_post_type( 'yoast', $post_type_args );
-
- $post_args = [
- 'post_status' => 'publish',
- 'post_type' => 'yoast',
- 'post_date' => $newest_date,
- ];
- $this->factory->post->create( $post_args );
-
- $post_args['post_date'] = $older_date;
- $this->factory->post->create( $post_args );
-
- $this->assertEquals( $newest_date, WPSEO_Sitemaps::get_last_modified_gmt( [ 'yoast' ] ) );
- }
-
- /**
- * Test for last modified date with invalid post types.
- *
- * @covers WPSEO_Sitemaps::get_last_modified_gmt
- */
- public function test_last_modified_with_invalid_post_type() {
- $this->assertFalse( WPSEO_Sitemaps::get_last_modified_gmt( [ 'invalid_post_type' ] ) );
- }
}
diff --git a/tests/unit/builders/indexable-author-builder-test.php b/tests/unit/builders/indexable-author-builder-test.php
index 2febb5a6dc7..d8a26c26e94 100644
--- a/tests/unit/builders/indexable-author-builder-test.php
+++ b/tests/unit/builders/indexable-author-builder-test.php
@@ -127,6 +127,7 @@ protected function set_up() {
FROM {$this->wpdb->posts} AS p
WHERE p.post_status IN (%s)
AND p.post_author = %d
+ AND p.post_password = \"\"
AND p.post_type IN (%s, %s)
",
[ 'publish', 1, 'post', 'my-cpt' ]
diff --git a/tests/unit/builders/indexable-post-type-archive-builder-test.php b/tests/unit/builders/indexable-post-type-archive-builder-test.php
index 6cee5340d22..bbf412a9541 100644
--- a/tests/unit/builders/indexable-post-type-archive-builder-test.php
+++ b/tests/unit/builders/indexable-post-type-archive-builder-test.php
@@ -66,6 +66,7 @@ public function test_build() {
FROM {$wpdb->posts} AS p
WHERE p.post_status IN (%s)
AND p.post_type = %s
+ AND p.post_password = \"\"
",
[ 'publish', 'my-post-type' ]
)->andReturn( 'PREPARED_QUERY' );
diff --git a/tests/unit/builders/indexable-term-builder-test.php b/tests/unit/builders/indexable-term-builder-test.php
index 0bd0b71d2e3..77c37329228 100644
--- a/tests/unit/builders/indexable-term-builder-test.php
+++ b/tests/unit/builders/indexable-term-builder-test.php
@@ -273,6 +273,7 @@ public function test_build() {
AND term_tax.taxonomy = %s
AND term_tax.term_id = %d
WHERE p.post_status IN (%s)
+ AND p.post_password = \"\"
",
[ 'category', 1, 'publish' ]
)->andReturn( 'PREPARED_QUERY' );