Your IP : 216.73.216.162


Current Path : /home/x/b/o/xbodynamge/namtation/wp-content/
Upload File :
Current File : /home/x/b/o/xbodynamge/namtation/wp-content/indexing.tar

indexable-prepare-indexation-action.php000066600000000452151145133310014261 0ustar00<?php

namespace Yoast\WP\SEO\Actions\Indexing;

/**
 * Action for preparing the indexing routine.
 *
 * @deprecated 15.3 - Use \Yoast\WP\SEO\Actions\Indexing\Indexing_Prepare_Action instead.
 * @codeCoverageIgnore
 */
class Indexable_Prepare_Indexation_Action extends Indexing_Prepare_Action {

}
abstract-indexing-action.php000066600000005472151146247620012162 0ustar00<?php

namespace Yoast\WP\SEO\Actions\Indexing;

/**
 * Base class of indexing actions.
 */
abstract class Abstract_Indexing_Action implements Indexation_Action_Interface, Limited_Indexing_Action_Interface {

	/**
	 * The transient name.
	 *
	 * This is a trick to force derived classes to define a transient themselves.
	 *
	 * @var string
	 */
	const UNINDEXED_COUNT_TRANSIENT = null;

	/**
	 * The transient cache key for limited counts.
	 *
	 * @var string
	 */
	const UNINDEXED_LIMITED_COUNT_TRANSIENT = self::UNINDEXED_COUNT_TRANSIENT . '_limited';

	/**
	 * Builds a query for selecting the ID's of unindexed posts.
	 *
	 * @param bool $limit The maximum number of post IDs to return.
	 *
	 * @return string The prepared query string.
	 */
	abstract protected function get_select_query( $limit );

	/**
	 * Builds a query for counting the number of unindexed posts.
	 *
	 * @return string The prepared query string.
	 */
	abstract protected function get_count_query();

	/**
	 * Returns a limited number of unindexed posts.
	 *
	 * @param int $limit Limit the maximum number of unindexed posts that are counted.
	 *
	 * @return int The limited number of unindexed posts. 0 if the query fails.
	 */
	public function get_limited_unindexed_count( $limit ) {
		$transient = \get_transient( static::UNINDEXED_LIMITED_COUNT_TRANSIENT );
		if ( $transient !== false ) {
			return (int) $transient;
		}

		\set_transient( static::UNINDEXED_LIMITED_COUNT_TRANSIENT, 0, ( \MINUTE_IN_SECONDS * 15 ) );

		$query = $this->get_select_query( $limit );

		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Function get_count_query returns a prepared query.
		$unindexed_object_ids = $this->wpdb->get_col( $query );
		$count                = (int) \count( $unindexed_object_ids );

		\set_transient( static::UNINDEXED_LIMITED_COUNT_TRANSIENT, $count, ( \MINUTE_IN_SECONDS * 15 ) );

		return $count;
	}

	/**
	 * Returns the total number of unindexed posts.
	 *
	 * @return int|false The total number of unindexed posts. False if the query fails.
	 */
	public function get_total_unindexed() {
		$transient = \get_transient( static::UNINDEXED_COUNT_TRANSIENT );
		if ( $transient !== false ) {
			return (int) $transient;
		}

		// Store transient before doing the query so multiple requests won't make multiple queries.
		// Only store this for 15 minutes to ensure that if the query doesn't complete a wrong count is not kept too long.
		\set_transient( static::UNINDEXED_COUNT_TRANSIENT, 0, ( \MINUTE_IN_SECONDS * 15 ) );

		$query = $this->get_count_query();

		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Function get_count_query returns a prepared query.
		$count = $this->wpdb->get_var( $query );

		if ( \is_null( $count ) ) {
			return false;
		}

		\set_transient( static::UNINDEXED_COUNT_TRANSIENT, $count, \DAY_IN_SECONDS );

		return (int) $count;
	}
}
abstract-link-indexing-action.php000066600000005344151146247620013113 0ustar00<?php

namespace Yoast\WP\SEO\Actions\Indexing;

use wpdb;
use Yoast\WP\SEO\Builders\Indexable_Link_Builder;
use Yoast\WP\SEO\Models\SEO_Links;
use Yoast\WP\SEO\Repositories\Indexable_Repository;

/**
 * Reindexing action for link indexables.
 */
abstract class Abstract_Link_Indexing_Action extends Abstract_Indexing_Action {

	/**
	 * The link builder.
	 *
	 * @var Indexable_Link_Builder
	 */
	protected $link_builder;

	/**
	 * The indexable repository.
	 *
	 * @var Indexable_Repository
	 */
	protected $repository;

	/**
	 * The WordPress database instance.
	 *
	 * @var wpdb
	 */
	protected $wpdb;

	/**
	 * Indexable_Post_Indexing_Action constructor
	 *
	 * @param Indexable_Link_Builder $link_builder The indexable link builder.
	 * @param Indexable_Repository   $repository   The indexable repository.
	 * @param wpdb                   $wpdb         The WordPress database instance.
	 */
	public function __construct(
		Indexable_Link_Builder $link_builder,
		Indexable_Repository $repository,
		wpdb $wpdb
	) {
		$this->link_builder = $link_builder;
		$this->repository   = $repository;
		$this->wpdb         = $wpdb;
	}

	/**
	 * Builds links for indexables which haven't had their links indexed yet.
	 *
	 * @return SEO_Links[] The created SEO links.
	 */
	public function index() {
		$objects = $this->get_objects();

		$indexables = [];
		foreach ( $objects as $object ) {
			$indexable = $this->repository->find_by_id_and_type( $object->id, $object->type );
			$this->link_builder->build( $indexable, $object->content );
			$indexable->save();

			$indexables[] = $indexable;
		}

		if ( \count( $indexables ) > 0 ) {
			\delete_transient( static::UNINDEXED_COUNT_TRANSIENT );
			\delete_transient( static::UNINDEXED_LIMITED_COUNT_TRANSIENT );
		}

		return $indexables;
	}

	/**
	 * In the case of term-links and post-links we want to use the total unindexed count, because using
	 * the limited unindexed count actually leads to worse performance.
	 *
	 * @param int|bool $limit Unused.
	 *
	 * @return int The total number of unindexed links.
	 */
	public function get_limited_unindexed_count( $limit = false ) {
		return $this->get_total_unindexed();
	}

	/**
	 * Returns the number of texts that will be indexed in a single link indexing pass.
	 *
	 * @return int The limit.
	 */
	public function get_limit() {
		/**
		 * Filter 'wpseo_link_indexing_limit' - Allow filtering the number of texts indexed during each link indexing pass.
		 *
		 * @api int The maximum number of texts indexed.
		 */
		return \apply_filters( 'wpseo_link_indexing_limit', 5 );
	}

	/**
	 * Returns objects to be indexed.
	 *
	 * @return array Objects to be indexed, should be an array of objects with object_id, object_type and content.
	 */
	abstract protected function get_objects();
}
indexable-indexing-complete-action.php000066600000001316151146247620014111 0ustar00<?php

namespace Yoast\WP\SEO\Actions\Indexing;

use Yoast\WP\SEO\Helpers\Indexable_Helper;

/**
 * Indexing action to call when the indexable indexing process is completed.
 */
class Indexable_Indexing_Complete_Action {

	/**
	 * The options helper.
	 *
	 * @var Indexable_Helper
	 */
	protected $indexable_helper;

	/**
	 * Indexable_Indexing_Complete_Action constructor.
	 *
	 * @param Indexable_Helper $indexable_helper The indexable helper.
	 */
	public function __construct( Indexable_Helper $indexable_helper ) {
		$this->indexable_helper = $indexable_helper;
	}

	/**
	 * Wraps up the indexing process.
	 *
	 * @return void
	 */
	public function complete() {
		$this->indexable_helper->finish_indexing();
	}
}
indexable-post-indexation-action.php000066600000013264151146247620013630 0ustar00<?php

namespace Yoast\WP\SEO\Actions\Indexing;

use wpdb;
use Yoast\WP\Lib\Model;
use Yoast\WP\SEO\Helpers\Post_Helper;
use Yoast\WP\SEO\Helpers\Post_Type_Helper;
use Yoast\WP\SEO\Models\Indexable;
use Yoast\WP\SEO\Repositories\Indexable_Repository;
use Yoast\WP\SEO\Values\Indexables\Indexable_Builder_Versions;

/**
 * Reindexing action for post indexables.
 */
class Indexable_Post_Indexation_Action extends Abstract_Indexing_Action {

	/**
	 * The transient cache key.
	 *
	 * @var string
	 */
	const UNINDEXED_COUNT_TRANSIENT = 'wpseo_total_unindexed_posts';

	/**
	 * The transient cache key for limited counts.
	 *
	 * @var string
	 */
	const UNINDEXED_LIMITED_COUNT_TRANSIENT = self::UNINDEXED_COUNT_TRANSIENT . '_limited';

	/**
	 * The post type helper.
	 *
	 * @var Post_Type_Helper
	 */
	protected $post_type_helper;

	/**
	 * The post helper.
	 *
	 * @var Post_Helper
	 */
	protected $post_helper;

	/**
	 * The indexable repository.
	 *
	 * @var Indexable_Repository
	 */
	protected $repository;

	/**
	 * The WordPress database instance.
	 *
	 * @var wpdb
	 */
	protected $wpdb;

	/**
	 * The latest version of Post Indexables.
	 *
	 * @var int
	 */
	protected $version;

	/**
	 * Indexable_Post_Indexing_Action constructor
	 *
	 * @param Post_Type_Helper           $post_type_helper The post type helper.
	 * @param Indexable_Repository       $repository       The indexable repository.
	 * @param wpdb                       $wpdb             The WordPress database instance.
	 * @param Indexable_Builder_Versions $builder_versions The latest versions for each Indexable type.
	 * @param Post_Helper                $post_helper      The post helper.
	 */
	public function __construct(
		Post_Type_Helper $post_type_helper,
		Indexable_Repository $repository,
		wpdb $wpdb,
		Indexable_Builder_Versions $builder_versions,
		Post_Helper $post_helper
	) {
		$this->post_type_helper = $post_type_helper;
		$this->repository       = $repository;
		$this->wpdb             = $wpdb;
		$this->version          = $builder_versions->get_latest_version_for_type( 'post' );
		$this->post_helper      = $post_helper;
	}

	/**
	 * Creates indexables for unindexed posts.
	 *
	 * @return Indexable[] The created indexables.
	 */
	public function index() {
		$query = $this->get_select_query( $this->get_limit() );

		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Function get_select_query returns a prepared query.
		$post_ids = $this->wpdb->get_col( $query );

		$indexables = [];
		foreach ( $post_ids as $post_id ) {
			$indexables[] = $this->repository->find_by_id_and_type( (int) $post_id, 'post' );
		}

		if ( \count( $indexables ) > 0 ) {
			\delete_transient( static::UNINDEXED_COUNT_TRANSIENT );
			\delete_transient( static::UNINDEXED_LIMITED_COUNT_TRANSIENT );
		}

		return $indexables;
	}

	/**
	 * Returns the number of posts that will be indexed in a single indexing pass.
	 *
	 * @return int The limit.
	 */
	public function get_limit() {
		/**
		 * Filter 'wpseo_post_indexation_limit' - Allow filtering the amount of posts indexed during each indexing pass.
		 *
		 * @api int The maximum number of posts indexed.
		 */
		$limit = \apply_filters( 'wpseo_post_indexation_limit', 25 );

		if ( ! \is_int( $limit ) || $limit < 1 ) {
			$limit = 25;
		}

		return $limit;
	}

	/**
	 * Builds a query for counting the number of unindexed posts.
	 *
	 * @return string The prepared query string.
	 */
	protected function get_count_query() {
		$indexable_table = Model::get_table_name( 'Indexable' );

		$post_types             = $this->post_type_helper->get_indexable_post_types();
		$excluded_post_statuses = $this->post_helper->get_excluded_post_statuses();
		$replacements           = \array_merge(
			$post_types,
			$excluded_post_statuses
		);

		$replacements[] = $this->version;

		// Warning: If this query is changed, makes sure to update the query in get_select_query as well.
		// @phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
		return $this->wpdb->prepare(
			"
			SELECT COUNT(P.ID)
			FROM {$this->wpdb->posts} AS P
			WHERE P.post_type IN (" . \implode( ', ', \array_fill( 0, \count( $post_types ), '%s' ) ) . ')
			AND P.post_status NOT IN (' . \implode( ', ', \array_fill( 0, \count( $excluded_post_statuses ), '%s' ) ) . ")
			AND P.ID not in (
				SELECT I.object_id from $indexable_table as I
				WHERE I.object_type = 'post'
				AND I.version = %d )",
			$replacements
		);
	}

	/**
	 * Builds a query for selecting the ID's of unindexed posts.
	 *
	 * @param bool $limit The maximum number of post IDs to return.
	 *
	 * @return string The prepared query string.
	 */
	protected function get_select_query( $limit = false ) {
		$indexable_table = Model::get_table_name( 'Indexable' );

		$post_types             = $this->post_type_helper->get_indexable_post_types();
		$excluded_post_statuses = $this->post_helper->get_excluded_post_statuses();
		$replacements           = \array_merge(
			$post_types,
			$excluded_post_statuses
		);
		$replacements[]         = $this->version;

		$limit_query = '';
		if ( $limit ) {
			$limit_query    = 'LIMIT %d';
			$replacements[] = $limit;
		}

		// Warning: If this query is changed, makes sure to update the query in get_count_query as well.
		// @phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
		return $this->wpdb->prepare(
			"
			SELECT P.ID
			FROM {$this->wpdb->posts} AS P
			WHERE P.post_type IN (" . \implode( ', ', \array_fill( 0, \count( $post_types ), '%s' ) ) . ')
			AND P.post_status NOT IN (' . \implode( ', ', \array_fill( 0, \count( $excluded_post_statuses ), '%s' ) ) . ")
			AND P.ID not in (
				SELECT I.object_id from $indexable_table as I
				WHERE I.object_type = 'post'
				AND I.version = %d )
			$limit_query",
			$replacements
		);
	}
}
indexation-action-interface.php000066600000001432151146247620012644 0ustar00<?php

namespace Yoast\WP\SEO\Actions\Indexing;

/**
 * Interface definition of reindexing action for indexables.
 */
interface Indexation_Action_Interface {

	/**
	 * Returns the total number of unindexed objects.
	 *
	 * @return int The total number of unindexed objects.
	 */
	public function get_total_unindexed();

	/**
	 * Indexes a number of objects.
	 *
	 * NOTE: ALWAYS use limits, this method is intended to be called multiple times over several requests.
	 *
	 * For indexing that requires JavaScript simply return the objects that should be indexed.
	 *
	 * @return array The reindexed objects.
	 */
	public function index();

	/**
	 * Returns the number of objects that will be indexed in a single indexing pass.
	 *
	 * @return int The limit.
	 */
	public function get_limit();
}
indexing-prepare-action.php000066600000001264151146247620012010 0ustar00<?php

namespace Yoast\WP\SEO\Actions\Indexing;

use Yoast\WP\SEO\Helpers\Indexing_Helper;

/**
 * Class Indexing_Prepare_Action.
 *
 * Action for preparing the indexing routine.
 */
class Indexing_Prepare_Action {

	/**
	 * The indexing helper.
	 *
	 * @var Indexing_Helper
	 */
	protected $indexing_helper;

	/**
	 * Action for preparing the indexing routine.
	 *
	 * @param Indexing_Helper $indexing_helper The indexing helper.
	 */
	public function __construct(
		Indexing_Helper $indexing_helper
	) {
		$this->indexing_helper = $indexing_helper;
	}

	/**
	 * Prepares the indexing routine.
	 *
	 * @return void
	 */
	public function prepare() {
		$this->indexing_helper->prepare();
	}
}
indexing-complete-action.php000066600000001227151146247620012161 0ustar00<?php

namespace Yoast\WP\SEO\Actions\Indexing;

use Yoast\WP\SEO\Helpers\Indexing_Helper;

/**
 * Indexing action to call when the indexing is completed.
 */
class Indexing_Complete_Action {

	/**
	 * The indexing helper.
	 *
	 * @var Indexing_Helper
	 */
	protected $indexing_helper;

	/**
	 * Indexing_Complete_Action constructor.
	 *
	 * @param Indexing_Helper $indexing_helper The indexing helper.
	 */
	public function __construct( Indexing_Helper $indexing_helper ) {
		$this->indexing_helper = $indexing_helper;
	}

	/**
	 * Wraps up the indexing process.
	 *
	 * @return void
	 */
	public function complete() {
		$this->indexing_helper->complete();
	}
}
term-link-indexing-action.php000066600000006424151146247620012257 0ustar00<?php

namespace Yoast\WP\SEO\Actions\Indexing;

use Yoast\WP\Lib\Model;
use Yoast\WP\SEO\Helpers\Taxonomy_Helper;

/**
 * Reindexing action for term link indexables.
 */
class Term_Link_Indexing_Action extends Abstract_Link_Indexing_Action {

	/**
	 * The transient name.
	 *
	 * @var string
	 */
	const UNINDEXED_COUNT_TRANSIENT = 'wpseo_unindexed_term_link_count';

	/**
	 * The transient cache key for limited counts.
	 *
	 * @var string
	 */
	const UNINDEXED_LIMITED_COUNT_TRANSIENT = self::UNINDEXED_COUNT_TRANSIENT . '_limited';

	/**
	 * The post type helper.
	 *
	 * @var Taxonomy_Helper
	 */
	protected $taxonomy_helper;

	/**
	 * Sets the required helper.
	 *
	 * @required
	 *
	 * @param Taxonomy_Helper $taxonomy_helper The taxonomy helper.
	 *
	 * @return void
	 */
	public function set_helper( Taxonomy_Helper $taxonomy_helper ) {
		$this->taxonomy_helper = $taxonomy_helper;
	}

	/**
	 * Returns objects to be indexed.
	 *
	 * @return array Objects to be indexed.
	 */
	protected function get_objects() {
		$query = $this->get_select_query( $this->get_limit() );

		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Function get_select_query returns a prepared query.
		$terms = $this->wpdb->get_results( $query );

		return \array_map(
			static function ( $term ) {
				return (object) [
					'id'      => (int) $term->term_id,
					'type'    => 'term',
					'content' => $term->description,
				];
			},
			$terms
		);
	}

	/**
	 * Builds a query for counting the number of unindexed term links.
	 *
	 * @return string The prepared query string.
	 */
	protected function get_count_query() {
		$public_taxonomies = $this->taxonomy_helper->get_indexable_taxonomies();
		$placeholders      = \implode( ', ', \array_fill( 0, \count( $public_taxonomies ), '%s' ) );
		$indexable_table   = Model::get_table_name( 'Indexable' );

		// Warning: If this query is changed, makes sure to update the query in get_select_query as well.
		return $this->wpdb->prepare(
			"
			SELECT COUNT(T.term_id)
			FROM {$this->wpdb->term_taxonomy} AS T
			LEFT JOIN $indexable_table AS I
				ON T.term_id = I.object_id
				AND I.object_type = 'term'
				AND I.link_count IS NOT NULL
			WHERE I.object_id IS NULL
				AND T.taxonomy IN ($placeholders)",
			$public_taxonomies
		);
	}

	/**
	 * Builds a query for selecting the ID's of unindexed term links.
	 *
	 * @param int|false $limit The maximum number of term link IDs to return.
	 *
	 * @return string The prepared query string.
	 */
	protected function get_select_query( $limit = false ) {
		$public_taxonomies = $this->taxonomy_helper->get_indexable_taxonomies();

		$indexable_table = Model::get_table_name( 'Indexable' );
		$replacements    = $public_taxonomies;

		$limit_query = '';
		if ( $limit ) {
			$limit_query    = 'LIMIT %d';
			$replacements[] = $limit;
		}

		// Warning: If this query is changed, makes sure to update the query in get_count_query as well.
		return $this->wpdb->prepare(
			"
			SELECT T.term_id, T.description
			FROM {$this->wpdb->term_taxonomy} AS T
			LEFT JOIN $indexable_table AS I
				ON T.term_id = I.object_id
				AND I.object_type = 'term'
				AND I.link_count IS NOT NULL
			WHERE I.object_id IS NULL
				AND T.taxonomy IN (" . \implode( ', ', \array_fill( 0, \count( $public_taxonomies ), '%s' ) ) . ")
			$limit_query",
			$replacements
		);
	}
}
indexable-general-indexation-action.php000066600000007164151146247620014262 0ustar00<?php

namespace Yoast\WP\SEO\Actions\Indexing;

use Yoast\WP\SEO\Models\Indexable;
use Yoast\WP\SEO\Repositories\Indexable_Repository;

/**
 * General reindexing action for indexables.
 */
class Indexable_General_Indexation_Action implements Indexation_Action_Interface, Limited_Indexing_Action_Interface {

	/**
	 * The transient cache key.
	 */
	const UNINDEXED_COUNT_TRANSIENT = 'wpseo_total_unindexed_general_items';

	/**
	 * Represents the indexables repository.
	 *
	 * @var Indexable_Repository
	 */
	protected $indexable_repository;

	/**
	 * Indexable_General_Indexation_Action constructor.
	 *
	 * @param Indexable_Repository $indexable_repository The indexables repository.
	 */
	public function __construct( Indexable_Repository $indexable_repository ) {
		$this->indexable_repository = $indexable_repository;
	}

	/**
	 * Returns the total number of unindexed objects.
	 *
	 * @return int The total number of unindexed objects.
	 */
	public function get_total_unindexed() {
		$transient = \get_transient( static::UNINDEXED_COUNT_TRANSIENT );
		if ( $transient !== false ) {
			return (int) $transient;
		}

		$indexables_to_create = $this->query();

		$result = \count( $indexables_to_create );

		\set_transient( static::UNINDEXED_COUNT_TRANSIENT, $result, \DAY_IN_SECONDS );

		return $result;
	}

	/**
	 * Returns a limited number of unindexed posts.
	 *
	 * @param int $limit Limit the maximum number of unindexed posts that are counted.
	 *
	 * @return int|false The limited number of unindexed posts. False if the query fails.
	 */
	public function get_limited_unindexed_count( $limit ) {
		return $this->get_total_unindexed();
	}

	/**
	 * Creates indexables for unindexed system pages, the date archive, and the homepage.
	 *
	 * @return Indexable[] The created indexables.
	 */
	public function index() {
		$indexables           = [];
		$indexables_to_create = $this->query();

		if ( isset( $indexables_to_create['404'] ) ) {
			$indexables[] = $this->indexable_repository->find_for_system_page( '404' );
		}

		if ( isset( $indexables_to_create['search'] ) ) {
			$indexables[] = $this->indexable_repository->find_for_system_page( 'search-result' );
		}

		if ( isset( $indexables_to_create['date_archive'] ) ) {
			$indexables[] = $this->indexable_repository->find_for_date_archive();
		}
		if ( isset( $indexables_to_create['home_page'] ) ) {
			$indexables[] = $this->indexable_repository->find_for_home_page();
		}

		\set_transient( static::UNINDEXED_COUNT_TRANSIENT, 0, \DAY_IN_SECONDS );

		return $indexables;
	}

	/**
	 * Returns the number of objects that will be indexed in a single indexing pass.
	 *
	 * @return int The limit.
	 */
	public function get_limit() {
		// This matches the maximum number of indexables created by this action.
		return 4;
	}

	/**
	 * Check which indexables already exist and return the values of the ones to create.
	 *
	 * @return array The indexable types to create.
	 */
	private function query() {
		$indexables_to_create = [];
		if ( ! $this->indexable_repository->find_for_system_page( '404', false ) ) {
			$indexables_to_create['404'] = true;
		}

		if ( ! $this->indexable_repository->find_for_system_page( 'search-result', false ) ) {
			$indexables_to_create['search'] = true;
		}

		if ( ! $this->indexable_repository->find_for_date_archive( false ) ) {
			$indexables_to_create['date_archive'] = true;
		}

		$need_home_page_indexable = ( (int) \get_option( 'page_on_front' ) === 0 && \get_option( 'show_on_front' ) === 'posts' );

		if ( $need_home_page_indexable && ! $this->indexable_repository->find_for_home_page( false ) ) {
			$indexables_to_create['home_page'] = true;
		}

		return $indexables_to_create;
	}
}
limited-indexing-action-interface.php000066600000000763151146247620013742 0ustar00<?php

namespace Yoast\WP\SEO\Actions\Indexing;

/**
 * Interface definition of a reindexing action for indexables that have a limited unindexed count.
 */
interface Limited_Indexing_Action_Interface {

	/**
	 * Returns a limited number of unindexed posts.
	 *
	 * @param int $limit Limit the maximum number of unindexed posts that are counted.
	 *
	 * @return int|false The limited number of unindexed posts. False if the query fails.
	 */
	public function get_limited_unindexed_count( $limit );
}
indexable-term-indexation-action.php000066600000011723151146247620013610 0ustar00<?php

namespace Yoast\WP\SEO\Actions\Indexing;

use wpdb;
use Yoast\WP\Lib\Model;
use Yoast\WP\SEO\Helpers\Taxonomy_Helper;
use Yoast\WP\SEO\Models\Indexable;
use Yoast\WP\SEO\Repositories\Indexable_Repository;
use Yoast\WP\SEO\Values\Indexables\Indexable_Builder_Versions;

/**
 * Reindexing action for term indexables.
 */
class Indexable_Term_Indexation_Action extends Abstract_Indexing_Action {

	/**
	 * The transient cache key.
	 */
	const UNINDEXED_COUNT_TRANSIENT = 'wpseo_total_unindexed_terms';

	/**
	 * The transient cache key for limited counts.
	 *
	 * @var string
	 */
	const UNINDEXED_LIMITED_COUNT_TRANSIENT = self::UNINDEXED_COUNT_TRANSIENT . '_limited';

	/**
	 * The post type helper.
	 *
	 * @var Taxonomy_Helper
	 */
	protected $taxonomy;

	/**
	 * The indexable repository.
	 *
	 * @var Indexable_Repository
	 */
	protected $repository;

	/**
	 * The WordPress database instance.
	 *
	 * @var wpdb
	 */
	protected $wpdb;

	/**
	 * The latest version of the Indexable term builder
	 *
	 * @var int
	 */
	protected $version;

	/**
	 * Indexable_Term_Indexation_Action constructor
	 *
	 * @param Taxonomy_Helper            $taxonomy         The taxonomy helper.
	 * @param Indexable_Repository       $repository       The indexable repository.
	 * @param wpdb                       $wpdb             The WordPress database instance.
	 * @param Indexable_Builder_Versions $builder_versions The latest versions of all indexable builders.
	 */
	public function __construct(
		Taxonomy_Helper $taxonomy,
		Indexable_Repository $repository,
		wpdb $wpdb,
		Indexable_Builder_Versions $builder_versions
	) {
		$this->taxonomy   = $taxonomy;
		$this->repository = $repository;
		$this->wpdb       = $wpdb;
		$this->version    = $builder_versions->get_latest_version_for_type( 'term' );
	}

	/**
	 * Creates indexables for unindexed terms.
	 *
	 * @return Indexable[] The created indexables.
	 */
	public function index() {
		$query = $this->get_select_query( $this->get_limit() );

		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Function get_select_query returns a prepared query.
		$term_ids = $this->wpdb->get_col( $query );

		$indexables = [];
		foreach ( $term_ids as $term_id ) {
			$indexables[] = $this->repository->find_by_id_and_type( (int) $term_id, 'term' );
		}

		if ( \count( $indexables ) > 0 ) {
			\delete_transient( static::UNINDEXED_COUNT_TRANSIENT );
			\delete_transient( static::UNINDEXED_LIMITED_COUNT_TRANSIENT );
		}

		return $indexables;
	}

	/**
	 * Returns the number of terms that will be indexed in a single indexing pass.
	 *
	 * @return int The limit.
	 */
	public function get_limit() {
		/**
		 * Filter 'wpseo_term_indexation_limit' - Allow filtering the number of terms indexed during each indexing pass.
		 *
		 * @api int The maximum number of terms indexed.
		 */
		$limit = \apply_filters( 'wpseo_term_indexation_limit', 25 );

		if ( ! \is_int( $limit ) || $limit < 1 ) {
			$limit = 25;
		}

		return $limit;
	}

	/**
	 * Builds a query for counting the number of unindexed terms.
	 *
	 * @return string The prepared query string.
	 */
	protected function get_count_query() {
		$indexable_table   = Model::get_table_name( 'Indexable' );
		$taxonomy_table    = $this->wpdb->term_taxonomy;
		$public_taxonomies = $this->taxonomy->get_indexable_taxonomies();

		$taxonomies_placeholders = \implode( ', ', \array_fill( 0, \count( $public_taxonomies ), '%s' ) );

		$replacements = [ $this->version ];
		\array_push( $replacements, ...$public_taxonomies );

		// Warning: If this query is changed, makes sure to update the query in get_count_query as well.
		return $this->wpdb->prepare(
			"
			SELECT COUNT(term_id)
			FROM {$taxonomy_table} AS T
			LEFT JOIN $indexable_table AS I
				ON T.term_id = I.object_id
				AND I.object_type = 'term'
				AND I.version = %d
			WHERE I.object_id IS NULL
				AND taxonomy IN ($taxonomies_placeholders)",
			$replacements
		);
	}

	/**
	 * Builds a query for selecting the ID's of unindexed terms.
	 *
	 * @param bool $limit The maximum number of term IDs to return.
	 *
	 * @return string The prepared query string.
	 */
	protected function get_select_query( $limit = false ) {
		$indexable_table   = Model::get_table_name( 'Indexable' );
		$taxonomy_table    = $this->wpdb->term_taxonomy;
		$public_taxonomies = $this->taxonomy->get_indexable_taxonomies();
		$placeholders      = \implode( ', ', \array_fill( 0, \count( $public_taxonomies ), '%s' ) );

		$replacements = [ $this->version ];
		\array_push( $replacements, ...$public_taxonomies );

		$limit_query = '';
		if ( $limit ) {
			$limit_query    = 'LIMIT %d';
			$replacements[] = $limit;
		}

		// Warning: If this query is changed, makes sure to update the query in get_count_query as well.
		return $this->wpdb->prepare(
			"
			SELECT term_id
			FROM {$taxonomy_table} AS T
			LEFT JOIN $indexable_table AS I
				ON T.term_id = I.object_id
				AND I.object_type = 'term'
				AND I.version = %d
			WHERE I.object_id IS NULL
				AND taxonomy IN ($placeholders)
			$limit_query",
			$replacements
		);
	}
}
indexable-post-type-archive-indexation-action.php000066600000013462151146247620016226 0ustar00<?php

namespace Yoast\WP\SEO\Actions\Indexing;

use Yoast\WP\SEO\Builders\Indexable_Builder;
use Yoast\WP\SEO\Helpers\Post_Type_Helper;
use Yoast\WP\SEO\Models\Indexable;
use Yoast\WP\SEO\Repositories\Indexable_Repository;
use Yoast\WP\SEO\Values\Indexables\Indexable_Builder_Versions;

/**
 * Reindexing action for post type archive indexables.
 *
 * @phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded
 */
class Indexable_Post_Type_Archive_Indexation_Action implements Indexation_Action_Interface, Limited_Indexing_Action_Interface {

	/**
	 * The transient cache key.
	 */
	const UNINDEXED_COUNT_TRANSIENT = 'wpseo_total_unindexed_post_type_archives';

	/**
	 * The post type helper.
	 *
	 * @var Post_Type_Helper
	 */
	protected $post_type;

	/**
	 * The indexable repository.
	 *
	 * @var Indexable_Repository
	 */
	protected $repository;

	/**
	 * The indexable builder.
	 *
	 * @var Indexable_Builder
	 */
	protected $builder;

	/**
	 * The current version of the post type archive indexable builder.
	 *
	 * @var int
	 */
	protected $version;

	/**
	 * Indexation_Post_Type_Archive_Action constructor.
	 *
	 * @param Indexable_Repository       $repository The indexable repository.
	 * @param Indexable_Builder          $builder    The indexable builder.
	 * @param Post_Type_Helper           $post_type  The post type helper.
	 * @param Indexable_Builder_Versions $versions   The current versions of all indexable builders.
	 */
	public function __construct(
		Indexable_Repository $repository,
		Indexable_Builder $builder,
		Post_Type_Helper $post_type,
		Indexable_Builder_Versions $versions
	) {
		$this->repository = $repository;
		$this->builder    = $builder;
		$this->post_type  = $post_type;
		$this->version    = $versions->get_latest_version_for_type( 'post-type-archive' );
	}

	/**
	 * Returns the total number of unindexed post type archives.
	 *
	 * @param int $limit Limit the number of counted objects.
	 *
	 * @return int The total number of unindexed post type archives.
	 */
	public function get_total_unindexed( $limit = false ) {
		$transient = \get_transient( static::UNINDEXED_COUNT_TRANSIENT );
		if ( $transient !== false ) {
			return (int) $transient;
		}

		\set_transient( static::UNINDEXED_COUNT_TRANSIENT, 0, \DAY_IN_SECONDS );

		$result = \count( $this->get_unindexed_post_type_archives( $limit ) );

		\set_transient( static::UNINDEXED_COUNT_TRANSIENT, $result, \DAY_IN_SECONDS );

		return $result;
	}

	/**
	 * Creates indexables for post type archives.
	 *
	 * @return Indexable[] The created indexables.
	 */
	public function index() {
		$unindexed_post_type_archives = $this->get_unindexed_post_type_archives( $this->get_limit() );

		$indexables = [];
		foreach ( $unindexed_post_type_archives as $post_type_archive ) {
			$indexables[] = $this->builder->build_for_post_type_archive( $post_type_archive );
		}

		if ( \count( $indexables ) > 0 ) {
			\delete_transient( static::UNINDEXED_COUNT_TRANSIENT );
		}

		return $indexables;
	}

	/**
	 * Returns the number of post type archives that will be indexed in a single indexing pass.
	 *
	 * @return int The limit.
	 */
	public function get_limit() {
		/**
		 * Filter 'wpseo_post_type_archive_indexation_limit' - Allow filtering the number of posts indexed during each indexing pass.
		 *
		 * @api int The maximum number of posts indexed.
		 */
		$limit = \apply_filters( 'wpseo_post_type_archive_indexation_limit', 25 );

		if ( ! \is_int( $limit ) || $limit < 1 ) {
			$limit = 25;
		}

		return $limit;
	}

	/**
	 * Retrieves the list of post types for which no indexable for its archive page has been made yet.
	 *
	 * @param int|false $limit Limit the number of retrieved indexables to this number.
	 *
	 * @return array The list of post types for which no indexable for its archive page has been made yet.
	 */
	protected function get_unindexed_post_type_archives( $limit = false ) {
		$post_types_with_archive_pages = $this->get_post_types_with_archive_pages();
		$indexed_post_types            = $this->get_indexed_post_type_archives();

		$unindexed_post_types = \array_diff( $post_types_with_archive_pages, $indexed_post_types );

		if ( $limit ) {
			return \array_slice( $unindexed_post_types, 0, $limit );
		}

		return $unindexed_post_types;
	}

	/**
	 * Returns the names of all the post types that have archive pages.
	 *
	 * @return array The list of names of all post types that have archive pages.
	 */
	protected function get_post_types_with_archive_pages() {
		// We only want to index archive pages of public post types that have them.
		$public_post_types       = $this->post_type->get_public_post_types( 'object' );
		$post_types_with_archive = \array_filter( $public_post_types, [ $this->post_type, 'has_archive' ] );

		// We only need the post type names, not the objects.
		$post_types = [];
		foreach ( $post_types_with_archive as $post_type_with_archive ) {
			$post_types[] = $post_type_with_archive->name;
		}

		return $post_types;
	}

	/**
	 * Retrieves the list of post type names for which an archive indexable exists.
	 *
	 * @return array The list of names of post types with unindexed archive pages.
	 */
	protected function get_indexed_post_type_archives() {
		$results = $this->repository->query()
			->select( 'object_sub_type' )
			->where( 'object_type', 'post-type-archive' )
			->where_equal( 'version', $this->version )
			->find_array();

		if ( $results === false ) {
			return [];
		}

		$callback = static function( $result ) {
			return $result['object_sub_type'];
		};

		return \array_map( $callback, $results );
	}

	/**
	 * Returns a limited number of unindexed posts.
	 *
	 * @param int $limit Limit the maximum number of unindexed posts that are counted.
	 *
	 * @return int|false The limited number of unindexed posts. False if the query fails.
	 */
	public function get_limited_unindexed_count( $limit ) {
		return $this->get_total_unindexed( $limit );
	}
}
post-link-indexing-action.php000066600000007514151146247620012276 0ustar00<?php

namespace Yoast\WP\SEO\Actions\Indexing;

use Yoast\WP\Lib\Model;
use Yoast\WP\SEO\Helpers\Post_Type_Helper;

/**
 * Reindexing action for post link indexables.
 */
class Post_Link_Indexing_Action extends Abstract_Link_Indexing_Action {

	/**
	 * The transient name.
	 *
	 * @var string
	 */
	const UNINDEXED_COUNT_TRANSIENT = 'wpseo_unindexed_post_link_count';

	/**
	 * The transient cache key for limited counts.
	 *
	 * @var string
	 */
	const UNINDEXED_LIMITED_COUNT_TRANSIENT = self::UNINDEXED_COUNT_TRANSIENT . '_limited';

	/**
	 * The post type helper.
	 *
	 * @var Post_Type_Helper
	 */
	protected $post_type_helper;

	/**
	 * Sets the required helper.
	 *
	 * @required
	 *
	 * @param Post_Type_Helper $post_type_helper The post type helper.
	 *
	 * @return void
	 */
	public function set_helper( Post_Type_Helper $post_type_helper ) {
		$this->post_type_helper = $post_type_helper;
	}

	/**
	 * Returns objects to be indexed.
	 *
	 * @return array Objects to be indexed.
	 */
	protected function get_objects() {
		$query = $this->get_select_query( $this->get_limit() );

		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Function get_select_query returns a prepared query.
		$posts = $this->wpdb->get_results( $query );

		return \array_map(
			static function ( $post ) {
				return (object) [
					'id'      => (int) $post->ID,
					'type'    => 'post',
					'content' => $post->post_content,
				];
			},
			$posts
		);
	}

	/**
	 * Builds a query for counting the number of unindexed post links.
	 *
	 * @return string The prepared query string.
	 */
	protected function get_count_query() {
		$public_post_types = $this->post_type_helper->get_indexable_post_types();
		$indexable_table   = Model::get_table_name( 'Indexable' );
		$links_table       = Model::get_table_name( 'SEO_Links' );

		// Warning: If this query is changed, makes sure to update the query in get_select_query as well.
		return $this->wpdb->prepare(
			"SELECT COUNT(P.ID)
			FROM {$this->wpdb->posts} AS P
			LEFT JOIN $indexable_table AS I
				ON P.ID = I.object_id
				AND I.link_count IS NOT NULL
				AND I.object_type = 'post'
			LEFT JOIN $links_table AS L
				ON L.post_id = P.ID
				AND L.target_indexable_id IS NULL
				AND L.type = 'internal'
				AND L.target_post_id IS NOT NULL
				AND L.target_post_id != 0
			WHERE ( I.object_id IS NULL OR L.post_id IS NOT NULL )
				AND P.post_status = 'publish'
				AND P.post_type IN (" . \implode( ', ', \array_fill( 0, \count( $public_post_types ), '%s' ) ) . ')',
			$public_post_types
		);
	}

	/**
	 * Builds a query for selecting the ID's of unindexed post links.
	 *
	 * @param int|false $limit The maximum number of post link IDs to return.
	 *
	 * @return string The prepared query string.
	 */
	protected function get_select_query( $limit = false ) {
		$public_post_types = $this->post_type_helper->get_indexable_post_types();
		$indexable_table   = Model::get_table_name( 'Indexable' );
		$links_table       = Model::get_table_name( 'SEO_Links' );
		$replacements      = $public_post_types;

		$limit_query = '';
		if ( $limit ) {
			$limit_query    = 'LIMIT %d';
			$replacements[] = $limit;
		}

		// Warning: If this query is changed, makes sure to update the query in get_count_query as well.
		return $this->wpdb->prepare(
			"
			SELECT P.ID, P.post_content
			FROM {$this->wpdb->posts} AS P
			LEFT JOIN $indexable_table AS I
				ON P.ID = I.object_id
				AND I.link_count IS NOT NULL
				AND I.object_type = 'post'
			LEFT JOIN $links_table AS L
				ON L.post_id = P.ID
				AND L.target_indexable_id IS NULL
				AND L.type = 'internal'
				AND L.target_post_id IS NOT NULL
				AND L.target_post_id != 0
			WHERE ( I.object_id IS NULL OR L.post_id IS NOT NULL )
				AND P.post_status = 'publish'
				AND P.post_type IN (" . \implode( ', ', \array_fill( 0, \count( $public_post_types ), '%s' ) ) . ")
			$limit_query",
			$replacements
		);
	}
}