/wp-content/plugins/wordpress-seo/inc/sitemaps/class-post-type-sitemap-provider.php
https://bitbucket.org/carloskikea/helpet · PHP · 641 lines · 301 code · 128 blank · 212 comment · 48 complexity · 6a4519005405420c64050d82c5bf6477 MD5 · raw file
- <?php
- /**
- * WPSEO plugin file.
- *
- * @package WPSEO\XML_Sitemaps
- */
- /**
- * Sitemap provider for author archives.
- */
- class WPSEO_Post_Type_Sitemap_Provider implements WPSEO_Sitemap_Provider {
- /** @var string $home_url Holds the home_url() value. */
- protected static $home_url;
- /** @var WPSEO_Sitemap_Image_Parser $image_parser Holds image parser instance. */
- protected static $image_parser;
- /** @var object $classifier Holds instance of classifier for a link. */
- protected static $classifier;
- /** @var int $page_on_front_id Static front page ID. */
- protected static $page_on_front_id;
- /** @var int $page_for_posts_id Posts page ID. */
- protected static $page_for_posts_id;
- /**
- * Set up object properties for data reuse.
- */
- public function __construct() {
- add_filter( 'save_post', array( $this, 'save_post' ) );
- }
- /**
- * Get all the options
- *
- * @deprecated 7.0
- */
- protected function get_options() {
- _deprecated_function( __METHOD__, 'WPSEO 7.0', 'WPSEO_Options::get' );
- }
- /**
- * Get front page ID
- *
- * @return int
- */
- protected function get_page_on_front_id() {
- if ( ! isset( self::$page_on_front_id ) ) {
- self::$page_on_front_id = (int) get_option( 'page_on_front' );
- }
- return self::$page_on_front_id;
- }
- /**
- * Get page for posts ID
- *
- * @return int
- */
- protected function get_page_for_posts_id() {
- if ( ! isset( self::$page_for_posts_id ) ) {
- self::$page_for_posts_id = (int) get_option( 'page_for_posts' );
- }
- return self::$page_for_posts_id;
- }
- /**
- * 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;
- }
- /**
- * Get the Classifier for a link
- *
- * @return WPSEO_Link_Type_Classifier
- */
- protected function get_classifier() {
- if ( ! isset( self::$classifier ) ) {
- self::$classifier = new WPSEO_Link_Type_Classifier( $this->get_home_url() );
- }
- return self::$classifier;
- }
- /**
- * Get Home URL
- *
- * This has been moved from the constructor because wp_rewrite is not available on plugins_loaded in multisite.
- * It will now be requested on need and not on initialization.
- *
- * @return string
- */
- protected function get_home_url() {
- if ( ! isset( self::$home_url ) ) {
- self::$home_url = WPSEO_Utils::home_url();
- }
- return self::$home_url;
- }
- /**
- * Check if provider supports given item type.
- *
- * @param string $type Type string to check for.
- *
- * @return boolean
- */
- public function handles_type( $type ) {
- return post_type_exists( $type );
- }
- /**
- * @param int $max_entries Entries per sitemap.
- *
- * @return array
- */
- public function get_index_links( $max_entries ) {
- global $wpdb;
- // Consider using WPSEO_Post_Type::get_accessible_post_types() to filter out any `no-index` post-types.
- $post_types = WPSEO_Post_Type::get_accessible_post_types();
- $post_types = array_filter( $post_types, array( $this, 'is_valid_post_type' ) );
- $last_modified_times = WPSEO_Sitemaps::get_last_modified_gmt( $post_types, true );
- $index = array();
- foreach ( $post_types as $post_type ) {
- $total_count = $this->get_post_type_count( $post_type );
- if ( $total_count === 0 ) {
- continue;
- }
- $max_pages = 1;
- if ( $total_count > $max_entries ) {
- $max_pages = (int) ceil( $total_count / $max_entries );
- }
- $all_dates = array();
- if ( $max_pages > 1 ) {
- $sql = "
- SELECT post_modified_gmt
- FROM ( SELECT @rownum:=0 ) init
- JOIN {$wpdb->posts} USE INDEX( type_status_date )
- WHERE post_status IN ( 'publish', 'inherit' )
- 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 ) {
- if ( ! empty( $last_modified_times[ $post_type ] ) ) {
- $date = $last_modified_times[ $post_type ];
- }
- }
- else {
- $date = $all_dates[ $page_counter ];
- }
- $index[] = array(
- 'loc' => WPSEO_Sitemaps_Router::get_base_url( $post_type . '-sitemap' . $current_page . '.xml' ),
- 'lastmod' => $date,
- );
- }
- }
- return $index;
- }
- /**
- * Get set of sitemap link data.
- *
- * @param string $type Sitemap type.
- * @param int $max_entries Entries per sitemap.
- * @param int $current_page Current page of the sitemap.
- *
- * @return array
- */
- public function get_sitemap_links( $type, $max_entries, $current_page ) {
- $links = array();
- $post_type = $type;
- if ( ! $this->is_valid_post_type( $post_type ) ) {
- return $links;
- }
- $steps = min( 100, $max_entries );
- $offset = ( $current_page > 1 ) ? ( ( $current_page - 1 ) * $max_entries ) : 0;
- $total = ( $offset + $max_entries );
- $typecount = $this->get_post_type_count( $post_type );
- if ( $total > $typecount ) {
- $total = $typecount;
- }
- if ( $current_page === 1 ) {
- $links = array_merge( $links, $this->get_first_links( $post_type ) );
- }
- if ( $typecount === 0 ) {
- return $links;
- }
- $stacked_urls = array();
- $posts_to_exclude = $this->get_excluded_posts();
- 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 );
- if ( ! isset( $url['loc'] ) ) {
- continue;
- }
- /**
- * 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 ) ) {
- continue;
- }
- if ( $post->ID === $this->get_page_for_posts_id() || $post->ID === $this->get_page_on_front_id() ) {
- array_unshift( $links, $url );
- continue;
- }
- $links[] = $url;
- }
- 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 );
- }
- }
- /**
- * Check if post type should be present in sitemaps.
- *
- * @param string $post_type Post type string to check for.
- *
- * @return bool
- */
- public function is_valid_post_type( $post_type ) {
- if ( ! WPSEO_Post_Type::is_post_type_indexable( $post_type ) ) {
- return false;
- }
- /**
- * 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, $post_type ) ) {
- return false;
- }
- return true;
- }
- /**
- * Retrieves a list with the excluded post ids.
- *
- * @return array Array with post ids to exclude.
- */
- protected function get_excluded_posts() {
- /**
- * Filter: 'wpseo_exclude_from_sitemap_by_post_ids' - Allow extending and modifying the posts to exclude.
- *
- * @api array $posts_to_exclude The posts to exclude.
- */
- $excluded_posts_ids = apply_filters( 'wpseo_exclude_from_sitemap_by_post_ids', array() );
- if ( ! is_array( $excluded_posts_ids ) || $excluded_posts_ids === array() ) {
- return array();
- }
- return array_map( 'intval', $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 = array();
- $needs_archive = true;
- if ( ! $this->get_page_on_front_id() && ( $post_type === 'post' || $post_type === 'page' ) ) {
- $links[] = array(
- 'loc' => $this->get_home_url(),
- // Deprecated, kept for backwards data compat. R.
- 'chf' => 'daily',
- 'pri' => 1,
- );
- $needs_archive = false;
- }
- elseif ( $this->get_page_on_front_id() && $post_type === 'post' && $this->get_page_for_posts_id() ) {
- $page_for_posts_url = get_permalink( $this->get_page_for_posts_id() );
- $links[] = array(
- 'loc' => $page_for_posts_url,
- // Deprecated, kept for backwards data compat. R.
- 'chf' => 'daily',
- 'pri' => 1,
- );
- $needs_archive = false;
- }
- if ( ! $needs_archive ) {
- return $links;
- }
- $archive_url = $this->get_post_type_archive_link( $post_type );
- /**
- * 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', $archive_url, $post_type );
- if ( $archive_url ) {
- /**
- * Filter the priority of the URL Yoast SEO uses in the XML sitemap.
- *
- * @param float $priority The priority for this URL, ranging from 0 to 1
- * @param string $post_type The post type this archive is for.
- */
- $links[] = array(
- '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 ) {
- if ( WPSEO_Options::get( 'noindex-ptarchive-' . $post_type, false ) ) {
- return false;
- }
- // Post archive should be excluded if it isn't front page or posts page.
- if ( $post_type === 'post' && get_option( 'show_on_front' ) !== 'posts' && ! $this->get_page_for_posts_id() ) {
- return false;
- }
- $archive_url = get_post_type_archive_link( $post_type );
- return $archive_url;
- }
- /**
- * 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 = array();
- if ( ! isset( $filters[ $post_type ] ) ) {
- // Make sure you're wpdb->preparing everything you throw into this!!
- $filters[ $post_type ] = array(
- /**
- * 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: http://wordpress.org/support/topic/plugin-wordpress-seo-by-yoast-performance-suggestion.
- // Also see 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_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 = array();
- foreach ( $posts as $post ) {
- $post->post_type = $post_type;
- $post->post_status = 'publish';
- $post->filter = 'sample';
- $post->ID = (int) $post->ID;
- $post->post_parent = (int) $post->post_parent;
- $post->post_author = (int) $post->post_author;
- $post_ids[] = $post->ID;
- }
- update_meta_cache( 'post', $post_ids );
- return $posts;
- }
- /**
- * @param string $post_type Post type slug.
- *
- * @return string
- */
- protected function get_sql_where_clause( $post_type ) {
- global $wpdb;
- $join = '';
- $status = "{$wpdb->posts}.post_status = 'publish'";
- // Based on WP_Query->get_posts(). R.
- if ( 'attachment' === $post_type ) {
- $join = " LEFT JOIN {$wpdb->posts} AS p2 ON ({$wpdb->posts}.post_parent = p2.ID) ";
- $status = "p2.post_status = 'publish' AND p2.post_password = ''";
- }
- $where_clause = "
- {$join}
- WHERE {$status}
- 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 = array();
- /**
- * 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 );
- /**
- * Do not include external URLs.
- *
- * @see https://wordpress.org/plugins/page-links-to/ can rewrite permalinks to external URLs.
- */
- if ( $this->get_classifier()->classify( $url['loc'] ) === WPSEO_Link::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.
- $url['images'] = $this->get_image_parser()->get_images( $post );
- return $url;
- }
- }