File: /var/www/hcv/wp-content/plugins/wordpress-seo-premium/src/integrations/cleanup-integration.php
<?php
namespace Yoast\WP\SEO\Premium\Integrations;
use wpdb;
use Yoast\WP\Lib\Model;
use Yoast\WP\SEO\Analytics\Domain\To_Be_Cleaned_Indexable_Bucket;
use Yoast\WP\SEO\Analytics\Domain\To_Be_Cleaned_Indexable_Count;
use Yoast\WP\SEO\Integrations\Integration_Interface;
use Yoast\WP\SEO\Repositories\Indexable_Cleanup_Repository;
/**
* Adds cleanup hooks.
*/
class Cleanup_Integration implements Integration_Interface {
/**
* The indexable cleanup repository.
*
* @var Indexable_Cleanup_Repository
*/
private $indexable_cleanup_repository;
/**
* The constructor.
*
* @param Indexable_Cleanup_Repository $indexable_cleanup_repository The indexable cleanup repository.
*/
public function __construct( Indexable_Cleanup_Repository $indexable_cleanup_repository ) {
$this->indexable_cleanup_repository = $indexable_cleanup_repository;
}
/**
* Returns the conditionals based in which this loadable should be active.
*
* @return array The array of conditionals.
*/
public static function get_conditionals() {
return [];
}
/**
* Initializes the integration.
*
* This is the place to register hooks and filters.
*
* @return void
*/
public function register_hooks() {
\add_filter( 'wpseo_cleanup_tasks', [ $this, 'add_cleanup_tasks' ] );
\add_action( 'wpseo_add_cleanup_counts_to_indexable_bucket', [ $this, 'add_cleanup_counts' ] );
}
/**
* Adds cleanup tasks for the cleanup integration.
*
* @param array $tasks Array of tasks to be added.
*
* @return array An associative array of tasks to be added to the cleanup integration.
*/
public function add_cleanup_tasks( $tasks ) {
return \array_merge(
$tasks,
[
'clean_orphaned_indexables_prominent_words' => function ( $limit ) {
return $this->cleanup_orphaned_from_table( 'Prominent_Words', 'indexable_id', $limit );
},
'clean_old_prominent_word_entries' => function ( $limit ) {
return $this->cleanup_old_prominent_words( $limit );
},
'clean_old_prominent_word_version_numbers' => function ( $limit ) {
return $this->cleanup_old_prominent_word_version_numbers( $limit );
},
]
);
}
/**
* Adds cleanup counts to the data bucket object.
*
* @param To_Be_Cleaned_Indexable_Bucket $to_be_cleaned_indexable_bucket The bucket with current indexable count data.
*
* @return void
*/
public function add_cleanup_counts( To_Be_Cleaned_Indexable_Bucket $to_be_cleaned_indexable_bucket ): void {
$to_be_cleaned_indexable_bucket->add_to_be_cleaned_indexable_count( new To_Be_Cleaned_Indexable_Count( 'orphaned_indexables_prominent_words', $this->indexable_cleanup_repository->count_orphaned_from_table( 'Prominent_Words', 'indexable_id' ) ) );
$to_be_cleaned_indexable_bucket->add_to_be_cleaned_indexable_count( new To_Be_Cleaned_Indexable_Count( 'orphaned_prominent_word_entries', $this->count_old_prominent_words() ) );
$to_be_cleaned_indexable_bucket->add_to_be_cleaned_indexable_count( new To_Be_Cleaned_Indexable_Count( 'orphaned_prominent_word_version_numbers', $this->count_old_prominent_word_version_numbers() ) );
}
/**
* Cleans orphaned rows from a yoast table.
*
* @param string $table The table to cleanup.
* @param string $column The table column the cleanup will rely on.
* @param int $limit The limit we'll apply to the queries.
*
* @return int The number of deleted rows.
*/
public function cleanup_orphaned_from_table( $table, $column, $limit ) {
global $wpdb;
$table = Model::get_table_name( $table );
$indexable_table = Model::get_table_name( 'Indexable' );
// Warning: If this query is changed, make sure to update the query in cleanup_orphaned_from_table in Free as well.
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reason: There is no unescaped user input.
$query = $wpdb->prepare(
"
SELECT table_to_clean.{$column}
FROM {$table} table_to_clean
LEFT JOIN {$indexable_table} AS indexable_table
ON table_to_clean.{$column} = indexable_table.id
WHERE indexable_table.id IS NULL
AND table_to_clean.{$column} IS NOT NULL
LIMIT %d",
$limit
);
// phpcs:enable
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reason: Already prepared.
$orphans = $wpdb->get_col( $query );
if ( empty( $orphans ) ) {
return 0;
}
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reason: Already prepared.
return \intval( $wpdb->query( "DELETE FROM $table WHERE {$column} IN( " . \implode( ',', $orphans ) . ' ) ' ) );
}
/**
* Cleans up old style prominent words from the database.
*
* @param int $limit The maximum amount of old prominent words to clean up in one go. Defaults to 1000.
*
* @return int The number of deleted rows.
*/
public function cleanup_old_prominent_words( $limit = 1000 ) {
global $wpdb;
$taxonomy_ids = $this->retrieve_prominent_word_taxonomies( $wpdb, $limit );
if ( \count( $taxonomy_ids ) === 0 ) {
return 0;
}
$nr_of_deleted_rows = $this->delete_prominent_word_taxonomies_and_terms( $wpdb, $taxonomy_ids );
if ( $nr_of_deleted_rows === false ) {
// Failed query.
return 0;
}
return $nr_of_deleted_rows;
}
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
/**
* Count up old style prominent words from the database.
*
* @return int The number of old prominent word rows.
*/
public function count_old_prominent_words() {
global $wpdb;
$query = $wpdb->prepare(
"SELECT count(term_taxonomy_id) FROM {$wpdb->term_taxonomy} WHERE taxonomy = %s",
[ 'yst_prominent_words' ]
);
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reason: Already prepared.
return $wpdb->get_col( $query )[0];
}
/**
* Retrieve a list of prominent word taxonomy IDs.
*
* @param wpdb $wpdb The WordPress database object.
* @param int $limit The maximum amount of prominent word taxonomies to retrieve.
*
* @return string[] A list of prominent word taxonomy IDs (of size 'limit').
*/
protected function retrieve_prominent_word_taxonomies( $wpdb, $limit ) {
return $wpdb->get_col(
$wpdb->prepare(
"SELECT term_taxonomy_id FROM {$wpdb->term_taxonomy} WHERE taxonomy = %s LIMIT %d",
[ 'yst_prominent_words', $limit ]
)
);
}
/**
* Deletes the given list of taxonomies and their terms.
*
* @param wpdb $wpdb The WordPress database object.
* @param string[] $taxonomy_ids The IDs of the taxonomies to remove and their corresponding terms.
*
* @return bool|int `false` if the query failed, the amount of rows deleted otherwise.
*/
protected function delete_prominent_word_taxonomies_and_terms( $wpdb, $taxonomy_ids ) {
return $wpdb->query(
$wpdb->prepare(
"DELETE t, tr, tt FROM {$wpdb->term_taxonomy} tt
LEFT JOIN {$wpdb->terms} t ON tt.term_id = t.term_id
LEFT JOIN {$wpdb->term_relationships} tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
WHERE tt.term_taxonomy_id IN ( " . \implode( ', ', \array_fill( 0, \count( $taxonomy_ids ), '%s' ) ) . ' )',
$taxonomy_ids
)
);
}
/**
* Cleans up the old prominent word versions from the postmeta table in the database.
*
* @param int $limit The maximum number of prominent word version numbers to clean in one go.
*
* @return bool|int The number of cleaned up prominent word version numbers, or `false` if the query failed.
*/
protected function cleanup_old_prominent_word_version_numbers( $limit ) {
global $wpdb;
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reason: There is no unescaped user input.
$query = $wpdb->prepare(
"DELETE FROM {$wpdb->postmeta} WHERE meta_key = %s LIMIT %d",
[ '_yst_prominent_words_version', $limit ]
);
// phpcs:enable
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reason: Already prepared.
return $wpdb->query( $query );
}
/**
* Counts up the old prominent word versions from the postmeta table in the database.
*
* @return bool|int The number of prominent word version numbers.
*/
protected function count_old_prominent_word_version_numbers() {
global $wpdb;
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reason: There is no unescaped user input.
$query = $wpdb->prepare(
"SELECT count(*) FROM {$wpdb->postmeta} WHERE meta_key = %s",
[ '_yst_prominent_words_version' ]
);
// phpcs:enable
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reason: Already prepared.
return $wpdb->get_col( $query )[0];
}
// phpcs:enable
}