Файловый менеджер - Редактировать - /home/avadvi5/public_html/wp-content/plugins/advanced-database-cleaner-pro/includes/models/class-adbc-transients.php
Ðазад
<?php // Exit if accessed directly if ( ! defined( 'ABSPATH' ) ) exit; /** * ADBC transients class. * * This class provides the transients functions * */ class ADBC_Transients { private const BIG_TRANSIENTS_THRESHOLD_WARNING = 150 * 1024; // 150 KB. (If you change this value, change it as well in js filter message and slice) private const TRUNCATE_LENGTH = 20; // Length to truncate the transient value for display /** * Get the transients list for the endpoint. * * @param array $filters Output of sanitize_filters(). * * @return WP_REST_Response The list of transients. */ public static function get_transients_list( $filters ) { // Prepare variables $transients_list = []; $total_transients = 0; $scan_counter = new ADBC_Scan_Counter(); $startRecord = ( $filters['current_page'] - 1 ) * $filters['items_per_page']; $endRecord = $startRecord + $filters['items_per_page']; $currentRecord = 0; $limit = ADBC_Settings::instance()->get_setting( 'database_rows_batch' ); $offset = 0; do { // Loop through all transients in batches of $limit to avoid memory issues $transients = self::get_transients_list_batch( $filters, $limit, $offset ); $fetched_count = count( $transients ); if ( ADBC_VERSION_TYPE === 'PREMIUM' ) ADBC_Scan_Results::instance()->load_scan_results_to_items_rows( $transients, 'transients' ); // Load scan results to the transients rows else ADBC_Common_Model::load_scan_results_to_items_for_free_version( $transients ); // Load scan results to the transients rows for free version ADBC_Hardcoded_Items::instance()->load_hardcoded_scan_results_to_items_rows( $transients, 'transients' ); // Load hardcoded items to the transients rows foreach ( $transients as $index => $transient ) { $scan_counter->refresh_categorization_count( $transient->belongs_to ); if ( ! ADBC_Common_Model::is_item_satisfies_belongs_to( $filters, $transient->belongs_to ) ) continue; $total_transients++; // Count transients that satisfy all filters and belongs_to // Only process the current batch if it's within the desired page range if ( $currentRecord >= $startRecord && $currentRecord < $endRecord ) { $transients_list[] = [ // This id is used to identify the option in the frontend and take actions on it 'composite_id' => [ 'items_type' => 'transients', 'site_id' => (int) $transient->site_id, 'id' => (int) $transient->id, 'found_in' => $transient->found_in, 'name' => $transient->name, ], 'id' => $transient->id, 'name' => $transient->name, // Used in the known addons modal & "show value modal". To be generic and work for all items types. 'transient_name' => $transient->name, 'value' => $transient->value, 'expired' => $transient->expired, 'timeout' => $transient->timeout, 'found_in' => $transient->found_in, // 'options' | 'sitemeta' 'size' => $transient->size, 'autoload' => $transient->autoload, 'site_id' => $transient->site_id, 'belongs_to' => $transient->belongs_to, 'known_plugins' => $transient->known_plugins, 'known_themes' => $transient->known_themes, ]; } $currentRecord++; } $offset += $limit; } while ( $fetched_count == $limit ); // Continue if the last batch was full // Loop over the $transients_list and $scan_counter add the plugins/themes names from the dictionary if they are empty // This is because load_scan_results_to_rows() only loads the names of the plugins/themes that are currently installed if ( ADBC_VERSION_TYPE === 'PREMIUM' ) ADBC_Dictionary::add_missing_addons_names_from_dictionary( $transients_list, $scan_counter, 'transients' ); // Calculate total number of pages to verify that the current page sent by the user is within the range $total_real_pages = max( 1, ceil( $total_transients / $filters['items_per_page'] ) ); return ADBC_Rest::success( "", [ 'items' => $transients_list, 'total_items' => $total_transients, 'real_current_page' => min( $filters['current_page'], $total_real_pages ), 'categorization_count' => $scan_counter->get_categorization_count(), 'plugins_count' => $scan_counter->get_plugins_count(), 'themes_count' => $scan_counter->get_themes_count(), ] ); } /** * Get the transients list that satisfy the UI filters. * * @param array $filters Output of sanitize_filters(). * @param int $limit Limit for the number of rows to return. * @param int $offset Offset for the number of rows to return. * * @return array List of transients that satisfy the filters. */ private static function get_transients_list_batch( $filters, $limit, $offset ) { global $wpdb; $site_arg = $filters['site_id']; $sort_col = $filters['sort_by']; $sort_dir = $filters['sort_order']; /* ────────────────────────────────────────────────────────────── * Build a safe ORDER BY clause * ─────────────────────────────────────────────────────────────*/ $allowed_columns = [ 'transient_name' => '`name`', 'size' => '`size`', 'autoload' => '`autoload`', 'site_id' => '`site_id`', 'expired' => '`expired`', 'found_in' => '`found_in`', ]; $sort_col = $filters['sort_by'] ?? ''; $sort_dir = strtoupper( $filters['sort_order'] ?? 'ASC' ); $sort_dir = $sort_dir === 'DESC' ? 'DESC' : 'ASC'; // Add 'order by' clause if the column is allowed. $maybe_order_by = isset( $allowed_columns[ $sort_col ] ) ? "ORDER BY {$allowed_columns[ $sort_col ]} {$sort_dir}" : ''; $branch_batch_size = $offset + $limit; // ---- Build branches --------------------------------------------- $branches = []; $needs_collation_fix = ! ADBC_Database::is_collaction_unified( 'options', true, $site_arg ); foreach ( ADBC_Sites::instance()->get_sites_list( $site_arg ) as $site ) { $branches = array_merge( $branches, self::build_branches_for_site( $site['id'], $filters, // pass whole arg set for WHERE glue $maybe_order_by, // pass sorting column and order or null $branch_batch_size, // pass limit for this branch $needs_collation_fix ) ); } $union_sql = implode( "\nUNION ALL\n", $branches ); $sql = " SELECT * FROM ( {$union_sql} ) AS rows_merged {$maybe_order_by} LIMIT %d OFFSET %d "; $sql = $wpdb->prepare( $sql, $limit, $offset ); return $wpdb->get_results( $sql, OBJECT ); } /** * Returns the prepared SQL condition to filter transients based on search term. * This is used to search for transients by name or value. * * @param array $filters The arguments for the size filter, including 'size' and 'size_unit'. * @param array $template The sql template from which we get the table columns to search in (sitemeta, options) * * @return string The prepared SQL condition for the search filter, or an empty string if no no search term is provided. */ private static function search_sql( $filters, $template ) { global $wpdb; $search_for = $filters['search_for'] ?? ''; $search_in = $filters['search_in'] ?? 'all'; $name_column = $template['name_col']; $value_column = $template['value_col']; if ( $search_for === '' ) { return ''; } $like = '%' . $wpdb->esc_like( $search_for ) . '%'; switch ( $search_in ) { case 'name': $search_sql = $wpdb->prepare( " AND {$name_column} LIKE %s", $like ); break; case 'value': $search_sql = $wpdb->prepare( " AND {$value_column} LIKE %s", $like ); break; default: $search_sql = $wpdb->prepare( " AND ( {$name_column} LIKE %s OR {$value_column} LIKE %s )", $like, $like ); } return $search_sql; } /** * Returns the prepared SQL condition to filter transients based on their size. * This is used to filter transients by their total size across all columns. * * @param array $filters The arguments for the size filter, including 'size' and 'size_unit'. * @param array $template The sql template from which we get the table columns (sitemeta, options) to calculate the size for. * * @return string The prepared SQL condition for the size filter, or an empty string if no size is specified. */ private static function size_sql( $filters, $template ) { global $wpdb; $size = $filters['size'] ?? 0; $size_unit = $filters['size_unit'] ?? 'B'; if ( $size === 0 ) { return ''; } $bytes = ADBC_Common_Utils::convert_size_to_bytes( $size, $size_unit ); // ——— build the expression with safe NULL-to-0 handling ——— if ( strpos( $template['name_col'], 'meta' ) !== false ) { // sitemeta $expr = ' COALESCE( LENGTH(a.meta_id ), 0 ) + COALESCE( LENGTH(a.meta_key ), 0 ) + COALESCE( LENGTH(a.meta_value), 0 ) '; } else { // options $expr = ' COALESCE( LENGTH(a.option_id ), 0 ) + COALESCE( LENGTH(a.option_name ), 0 ) + COALESCE( LENGTH(a.option_value), 0 ) + COALESCE( LENGTH(a.autoload ), 0 ) '; } // add the prepared comparison return $wpdb->prepare( " AND ( {$expr} ) >= %d", $bytes ); } /** * Returns the SQL templates array to fetch transients depending on the current site. * * @return array The array of SQL templates to fetch transients for the current site, each item contains the 'sql', 'name_col' and 'val_col' */ private static function get_sql_templates() { global $wpdb; $length = self::TRUNCATE_LENGTH; $site_id = get_current_blog_id(); $templates = [ // Regular transients from options table [ 'sql' => " SELECT a.option_name AS name, a.option_id AS id, SUBSTRING(a.option_value,1,$length) AS value, b.option_value AS timeout, {$site_id} AS site_id, 'options' AS found_in, a.autoload AS autoload, LENGTH(a.option_id) + LENGTH(a.option_name) + LENGTH(a.option_value) + LENGTH(a.autoload) AS size, CASE WHEN b.option_value IS NOT NULL AND b.option_value < UNIX_TIMESTAMP() THEN 'yes' ELSE 'no' END AS expired FROM {$wpdb->options} a LEFT JOIN {$wpdb->options} b ON b.option_name = CONCAT( '_transient_timeout_', SUBSTRING(a.option_name, CHAR_LENGTH('_transient_') + 1) ) WHERE a.option_name LIKE '\_transient\_%' AND a.option_name NOT LIKE '\_transient\_timeout\_%' ", 'name_col' => 'a.option_name', 'value_col' => 'a.option_value', ], // Site transients from options table [ 'sql' => " SELECT a.option_name AS name, a.option_id AS id, SUBSTRING(a.option_value,1,$length) AS value, b.option_value AS timeout, {$site_id} AS site_id, 'options' AS found_in, a.autoload AS autoload, LENGTH(a.option_id) + LENGTH(a.option_name) + LENGTH(a.option_value) + LENGTH(a.autoload) AS size, CASE WHEN b.option_value IS NOT NULL AND b.option_value < UNIX_TIMESTAMP() THEN 'yes' ELSE 'no' END AS expired FROM {$wpdb->options} a LEFT JOIN {$wpdb->options} b ON b.option_name = CONCAT( '_site_transient_timeout_', SUBSTRING(a.option_name, CHAR_LENGTH('_site_transient_') + 1) ) WHERE a.option_name LIKE '\_site\_transient\_%' AND a.option_name NOT LIKE '\_site\_transient\_timeout\_%' ", 'name_col' => 'a.option_name', 'value_col' => 'a.option_value', ], ]; // Add sitemeta template for multisite main site if ( is_multisite() && is_main_site( $site_id ) ) { $templates[] = [ 'sql' => " SELECT a.meta_key AS name, a.meta_id AS id, SUBSTRING(a.meta_value,1,$length) AS value, b.timeout_value AS timeout, {$site_id} AS site_id, 'sitemeta' AS found_in, 'off' AS autoload, LENGTH(a.meta_id) + LENGTH(a.meta_key) + LENGTH(a.meta_value) AS size, CASE WHEN b.timeout_value IS NOT NULL AND b.timeout_value < UNIX_TIMESTAMP() THEN 'yes' ELSE 'no' END AS expired FROM {$wpdb->sitemeta} a LEFT JOIN ( SELECT meta_key, MIN(CAST(meta_value AS UNSIGNED)) AS timeout_value FROM {$wpdb->sitemeta} WHERE meta_key LIKE '\_site\_transient\_timeout\_%' GROUP BY meta_key ) b ON b.meta_key = CONCAT( '_site_transient_timeout_', SUBSTRING(a.meta_key, CHAR_LENGTH('_site_transient_') + 1) ) WHERE a.meta_key LIKE '\_site\_transient\_%' AND a.meta_key NOT LIKE '\_site\_transient\_timeout\_%' ", 'name_col' => 'a.meta_key', 'value_col' => 'a.meta_value', ]; } return $templates; } /** * Builds the prepared SQL branches for a specific site. * This is used to fetch data from multiple sites in a multisite environment. * * @param int $site_id The site ID to build the branch for. * @param array $filters The arguments for the query. * @param string $maybe_order_by The ORDER BY clause if sorting is applied. * @param int $sub_limit The limit to set for this branch. * * @return array An array containing the SQL branch for the specified site. */ private static function build_branches_for_site( $site_id, $filters, $maybe_order_by, $sub_limit, $needs_collation_fix = false ) { global $wpdb; ADBC_Sites::instance()->switch_to_blog_id( $site_id ); $branches = []; $collation = ! empty( $wpdb->collate ) ? $wpdb->collate : 'utf8mb4_unicode_ci'; foreach ( self::get_sql_templates() as $template ) { $search = self::search_sql( $filters, $template ); $size = self::size_sql( $filters, $template ); $autoload = self::autoload_sql( $filters, $template ); // Build the inner query first $inner_sql = "{$template['sql']}{$search}{$size}{$autoload}"; if ( $needs_collation_fix ) { $inner_sql = " SELECT CONVERT(name USING utf8mb4) COLLATE {$collation} AS name, id, CONVERT(value USING utf8mb4) COLLATE {$collation} AS value, CONVERT(timeout USING utf8mb4) COLLATE {$collation} AS timeout, site_id, CONVERT(found_in USING utf8mb4) COLLATE {$collation} AS found_in, CONVERT(autoload USING utf8mb4) COLLATE {$collation} AS autoload, size, CONVERT(expired USING utf8mb4) COLLATE {$collation} AS expired FROM ( {$inner_sql} ) AS t "; } // Check if we need to filter by expired status if ( $filters['expired'] !== 'all' ) { // Wrap in subquery to filter on calculated expired column $expired_filter = $filters['expired'] === 'yes' ? "expired = 'yes'" : "expired = 'no'"; $sql = " SELECT * FROM ( {$inner_sql} ) AS inner_query WHERE {$expired_filter} {$maybe_order_by} LIMIT %d "; } else { $sql = " {$inner_sql} {$maybe_order_by} LIMIT %d "; } $branches[] = '(' . $wpdb->prepare( $sql, $sub_limit ) . ')'; } ADBC_Sites::instance()->restore_blog(); return $branches; } /** * Returns the prepared SQL condition to filter transients based on their autoload status. * This is used to filter transients by their autoload status. * * @param array $filters The arguments for the autoload filter, including 'autoload'. * @param array $template The sql template from which we get the table columns to get the autoload column from. (sitemeta, options) * * @return string The prepared SQL condition for the autoload filter, or an empty string if no autoload is specified. */ private static function autoload_sql( $filters, $template ) { $autoload_filter = $filters['autoload'] ?? 'all'; if ( $autoload_filter === 'all' ) { return ''; } // Determine the correct column name based on the template $autoload_column = strpos( $template['name_col'], 'meta' ) !== false ? "'off'" // sitemeta doesn't have autoload, so we use a literal : 'a.autoload'; // options table uses a.autoload switch ( $autoload_filter ) { case 'yes': return " AND {$autoload_column} IN ('yes','on','auto-on','auto')"; case 'no': return " AND {$autoload_column} NOT IN ('yes','on','auto-on','auto')"; default: return ''; } } /** * Get transient names that still exist anywhere across the network from a provided list. * This checks both options tables for all sites and the sitemeta table on the main site. * * @param array $transients_names List of transient option/meta keys to check for existence. * * @return array Existing names found across all sites. */ public static function get_transients_names_that_exists_from_list( $transients_names ) { global $wpdb; if ( empty( $transients_names ) || ! is_array( $transients_names ) ) return []; $branches = []; $sites = ADBC_Sites::instance()->get_sites_list(); $in_placeholders = implode( ',', array_fill( 0, count( $transients_names ), '%s' ) ); foreach ( $sites as $site ) { $prefix = $site['prefix']; $options_table = $prefix . 'options'; // Options branch (regular and site transients that may live in options) $sql_options = $wpdb->prepare( "SELECT DISTINCT option_name AS name FROM `{$options_table}` WHERE option_name IN ( {$in_placeholders} ) AND option_name NOT LIKE %s AND option_name NOT LIKE %s", ...array_merge( $transients_names, [ '\_transient\_timeout\_%', '\_site\_transient\_timeout\_%' ] ) ); $branches[] = '(' . $sql_options . ')'; // Sitemeta branch only for multisite main site $site_id = $site['id']; if ( is_multisite() && $site_id !== null && is_main_site( $site_id ) ) { $sitemeta_table = $prefix . 'sitemeta'; $sql_meta = $wpdb->prepare( "SELECT DISTINCT meta_key AS name FROM `{$sitemeta_table}` WHERE meta_key IN ( {$in_placeholders} ) AND meta_key NOT LIKE %s", ...array_merge( $transients_names, [ '\_site\_transient\_timeout\_%' ] ) ); $branches[] = '(' . $sql_meta . ')'; } } if ( empty( $branches ) ) return []; $union_sql = implode( "\nUNION ALL\n", $branches ); $query = "SELECT DISTINCT name FROM ( {$union_sql} ) AS existing_names"; $existing_names = $wpdb->get_col( $query ); return array_values( array_unique( array_filter( (array) $existing_names ) ) ); } /** * Get the count of big transients in all sites. * * @return int Total count of big transients. */ public static function count_big_transients() { global $wpdb; $threshold = self::BIG_TRANSIENTS_THRESHOLD_WARNING; // LIKE patterns for filtering transients $transient_like = $wpdb->esc_like( '_transient_' ) . '%'; $site_transient_like = $wpdb->esc_like( '_site_transient_' ) . '%'; $transient_timeout_like = $wpdb->esc_like( '_transient_timeout_' ) . '%'; $site_transient_timeout_like = $wpdb->esc_like( '_site_transient_timeout_' ) . '%'; $total_count = 0; // Loop through all sites in multisite, or just current site in single site $sites = ADBC_Sites::instance()->get_sites_list(); foreach ( $sites as $site ) { $prefix = $site['prefix']; // Options table count $options_table = $prefix . 'options'; $sql_options = $wpdb->prepare( "SELECT COUNT(*) FROM `{$options_table}` WHERE ( option_name LIKE %s OR option_name LIKE %s ) AND option_name NOT LIKE %s AND option_name NOT LIKE %s AND LENGTH(option_value) > %d", $transient_like, $site_transient_like, $transient_timeout_like, $site_transient_timeout_like, $threshold ); $total_count += (int) $wpdb->get_var( $sql_options ); // Sitemeta table count (only for multisite main site) $site_id = $site['id']; if ( is_multisite() && $site_id !== null && is_main_site( $site_id ) ) { $sitemeta_table = $prefix . 'sitemeta'; $sql_sitemeta = $wpdb->prepare( "SELECT COUNT(*) FROM `{$sitemeta_table}` WHERE meta_key LIKE %s AND meta_key NOT LIKE %s AND LENGTH(meta_value) > %d", $site_transient_like, $site_transient_timeout_like, $threshold ); $total_count += (int) $wpdb->get_var( $sql_sitemeta ); } } return $total_count; } /** * Count the total number of transients that are not scanned. * * @return int Total not scanned transients. */ public static function count_total_not_scanned_transients() { $sites_prefixes = array_keys( ADBC_Sites::instance()->get_all_prefixes() ); $total_not_scanned = 0; $limit = ADBC_Settings::instance()->get_setting( 'database_rows_batch' ); foreach ( $sites_prefixes as $site_prefix ) { $offset = 0; do { // Loop through all transients in batches of $limit to avoid memory issues $transients = self::get_transients_names( $site_prefix, $limit, $offset, false ); $fetched_count = count( $transients ); $not_scanned_count = 0; if ( ADBC_VERSION_TYPE === 'PREMIUM' ) $not_scanned_count = ADBC_Scan_Utils::count_not_scanned_items_in_list( 'transients', $transients ); else $not_scanned_count = ADBC_Common_Model::count_not_scanned_items_in_list_for_free( 'transients', $transients ); $total_not_scanned += $not_scanned_count; $offset += $limit; } while ( $fetched_count == $limit ); // Continue if the last batch was full } return $total_not_scanned; } /** * Get transients names for a specific site keyed by name * * @param string $site_prefix Site prefix. * @param int $limit Limit. * @param int $offset Offset. * @param boolean $keyed wether or not to key the array by names * * @return array Associative transients names. */ public static function get_transients_names( $site_prefix, $limit, $offset, $keyed = true ) { global $wpdb; // LIKE patterns $transient_like = $wpdb->esc_like( '_transient_' ) . '%'; $site_transient_like = $wpdb->esc_like( '_site_transient_' ) . '%'; $transient_timeout_like = $wpdb->esc_like( '_transient_timeout_' ) . '%'; $site_transient_timeout_like = $wpdb->esc_like( '_site_transient_timeout_' ) . '%'; // Tables $options_table = $site_prefix . 'options'; // Do we also have to union with sitemeta (multisite main site)? $prefix_site_id = ADBC_Sites::instance()->get_site_id_from_prefix( $site_prefix ); $do_union = is_multisite() && $prefix_site_id !== null && is_main_site( $prefix_site_id ); if ( $do_union ) { $sitemeta = $site_prefix . 'sitemeta'; $sql = " SELECT name FROM ( SELECT option_name AS name FROM `{$options_table}` WHERE ( option_name LIKE %s OR option_name LIKE %s ) AND option_name NOT LIKE %s AND option_name NOT LIKE %s UNION ALL SELECT meta_key AS name FROM `{$sitemeta}` WHERE meta_key LIKE %s AND meta_key NOT LIKE %s ) AS all_transients LIMIT %d OFFSET %d "; $query = $wpdb->prepare( $sql, $transient_like, $site_transient_like, $transient_timeout_like, $site_transient_timeout_like, $site_transient_like, $site_transient_timeout_like, $limit, $offset ); } else { // Single-site (or non-main multisite blog) – only options table $sql = " SELECT option_name AS name FROM `{$options_table}` WHERE ( option_name LIKE %s OR option_name LIKE %s ) AND option_name NOT LIKE %s AND option_name NOT LIKE %s LIMIT %d OFFSET %d "; $query = $wpdb->prepare( $sql, $transient_like, $site_transient_like, $transient_timeout_like, $site_transient_timeout_like, $limit, $offset ); } $names = $wpdb->get_col( $query ); if ( $keyed ) return array_fill_keys( $names, true ); else return $names; } /** * Get transients names from their id|table in a specific site prefix. * * @param string $site_prefix Site prefix of the transients. * @param array $transients_map [ <int ID> => 'options'|'sitemeta', … ] * * @return array Associative transients names. */ public static function get_transients_names_from_ids( $site_prefix, $transients_map ) { global $wpdb; if ( empty( $transients_map ) ) { return []; } // LIKE patterns $transient_like = $wpdb->esc_like( '_transient_' ) . '%'; $site_transient_like = $wpdb->esc_like( '_site_transient_' ) . '%'; $transient_timeout_like = $wpdb->esc_like( '_transient_timeout_' ) . '%'; $site_transient_timeout_like = $wpdb->esc_like( '_site_transient_timeout_' ) . '%'; // Partition incoming IDs by table $option_ids = $sitemeta_ids = []; foreach ( $transients_map as $transient_map ) { // get the key and value of the array $id = key( $transient_map ); $table = $transient_map[ $id ]; $table === 'options' ? $option_ids[] = (int) $id : $sitemeta_ids[] = (int) $id; } $transients_names = []; // Fetch from options if ( ! empty( $option_ids ) ) { $options_table = $site_prefix . 'options'; $placeholders = implode( ',', array_fill( 0, count( $option_ids ), '%d' ) ); $sql = " SELECT option_name AS name FROM `{$options_table}` WHERE option_id IN ( {$placeholders} ) AND ( option_name LIKE %s OR option_name LIKE %s ) AND option_name NOT LIKE %s AND option_name NOT LIKE %s "; // we add the second LIKE to avoid false positives $args = array_merge( $option_ids, [ $transient_like, $site_transient_like, $transient_timeout_like, $site_transient_timeout_like ] ); $query = $wpdb->prepare( $sql, $args ); $transients_names = $wpdb->get_col( $query ); } // Determine if we need to fetch from sitemeta $prefix_site_id = ADBC_Sites::instance()->get_site_id_from_prefix( $site_prefix ); $get_from_sitemeta = ! empty( $sitemeta_ids ) && is_multisite() && $prefix_site_id !== null && is_main_site( $prefix_site_id ); // Fetch from wp_sitemeta (only site-wide transients) if ( $get_from_sitemeta ) { $sitemeta_table = $site_prefix . 'sitemeta'; $placeholders = implode( ',', array_fill( 0, count( $sitemeta_ids ), '%d' ) ); $sql = " SELECT meta_key AS name FROM `{$sitemeta_table}` WHERE meta_id IN ( {$placeholders} ) AND meta_key LIKE %s AND meta_key NOT LIKE %s "; // we add the second LIKE to avoid false positives $args = array_merge( $sitemeta_ids, [ $site_transient_like, $site_transient_timeout_like ] ); $query = $wpdb->prepare( $sql, $args ); $transients_names = array_merge( $transients_names, $wpdb->get_col( $query ) ); } return array_fill_keys( $transients_names, true ); } /** * Delete transients. * * @param array $grouped_selected Selected transients to delete grouped by site ID. * @return array An array of transients names that were not processed. */ public static function delete_transients( $grouped_selected ) { $cleanup_method = ADBC_Settings::instance()->get_setting( 'sql_or_native_cleanup_method' ); if ( $cleanup_method === 'native' ) { return self::delete_transients_native( $grouped_selected ); } return self::delete_transients_sql( $grouped_selected ); } /** * Deletes transients using the current WordPress native delete logic (unchanged). * * @param array $grouped_selected * @return array Not processed transients names. */ protected static function delete_transients_native( $grouped_selected ) { $not_processed = []; foreach ( $grouped_selected as $site_id => $group ) { ADBC_Sites::instance()->switch_to_blog_id( $site_id ); foreach ( $group as $selected ) { $full_name = $selected['name']; // full option / meta key $found_in = $selected['found_in']; // 'options' | 'sitemeta' // site_transient if ( strpos( $full_name, '_site_transient_' ) === 0 ) { $base_name = substr( $full_name, 16 ); // strip prefix // Edge case where in multisite and site_transient is in options table if ( is_multisite() && $found_in === 'options' ) { if ( delete_option( $full_name ) ) { delete_option( "_site_transient_timeout_$base_name" ); } else { $not_processed[] = $selected['name']; } } else { if ( ! delete_site_transient( $base_name ) ) { // fallback to direct delete in sitemeta table // This can happen if the transient have an invalid site ID in sitemeta if ( $found_in === 'sitemeta' ) { global $wpdb; $timeout_key = "_site_transient_timeout_{$base_name}"; $sql = "DELETE FROM {$wpdb->sitemeta} WHERE meta_key IN ( %s, %s )"; $deleted_rows = $wpdb->query( $wpdb->prepare( $sql, $full_name, $timeout_key ) ); if ( $deleted_rows !== false && $deleted_rows === 0 ) { $not_processed[] = $selected['name']; // delete using direct query failed } } else { // delete using native wordpress function failed $not_processed[] = $selected['name']; } } } continue; } // transient if ( strpos( $full_name, '_transient_' ) === 0 ) { $base_name = substr( $full_name, 11 ); if ( ! delete_transient( $base_name ) ) { $not_processed[] = $selected['name']; } } } ADBC_Sites::instance()->restore_blog(); } return $not_processed; } /** * Deletes transients using direct SQL (bulk) for each site. * * @param array $grouped_selected * @return array Not processed transients names. */ protected static function delete_transients_sql( $grouped_selected ) { global $wpdb; $not_processed = []; foreach ( $grouped_selected as $site_id => $group ) { $option_keys = []; $sitemeta_keys = []; foreach ( $group as $selected ) { $full_name = $selected['name']; // full option / meta key $found_in = $selected['found_in']; // 'options' | 'sitemeta' // site_transient if ( strpos( $full_name, '_site_transient_' ) === 0 ) { $base_name = substr( $full_name, 16 ); $timeout_key = "_site_transient_timeout_{$base_name}"; if ( is_multisite() && $found_in === 'sitemeta' ) { $sitemeta_keys[] = $full_name; $sitemeta_keys[] = $timeout_key; } else { $option_keys[] = $full_name; $option_keys[] = $timeout_key; } continue; } // transient if ( strpos( $full_name, '_transient_' ) === 0 ) { $base_name = substr( $full_name, 11 ); $timeout_key = "_transient_timeout_{$base_name}"; $option_keys[] = $full_name; $option_keys[] = $timeout_key; } } ADBC_Sites::instance()->switch_to_blog_id( $site_id ); // Bulk delete from options. if ( ! empty( $option_keys ) ) { $placeholders = implode( ',', array_fill( 0, count( $option_keys ), '%s' ) ); $sql = "DELETE FROM {$wpdb->options} WHERE option_name IN ($placeholders)"; $sql = $wpdb->prepare( $sql, ...$option_keys ); $wpdb->query( $sql ); $check_sql = "SELECT option_name FROM {$wpdb->options} WHERE option_name IN ($placeholders)"; $check_sql = $wpdb->prepare( $check_sql, ...$option_keys ); $remaining = $wpdb->get_col( $check_sql ); if ( ! empty( $remaining ) ) foreach ( $remaining as $name ) $not_processed[] = $name; } // Bulk delete from sitemeta. if ( ! empty( $sitemeta_keys ) ) { $placeholders = implode( ',', array_fill( 0, count( $sitemeta_keys ), '%s' ) ); $sql = "DELETE FROM {$wpdb->sitemeta} WHERE meta_key IN ($placeholders)"; $sql = $wpdb->prepare( $sql, ...$sitemeta_keys ); $wpdb->query( $sql ); $check_sql = "SELECT meta_key FROM {$wpdb->sitemeta} WHERE meta_key IN ($placeholders)"; $check_sql = $wpdb->prepare( $check_sql, ...$sitemeta_keys ); $remaining = $wpdb->get_col( $check_sql ); if ( ! empty( $remaining ) ) foreach ( $remaining as $name ) $not_processed[] = $name; } ADBC_Sites::instance()->restore_blog(); } return $not_processed; } /** * Set autoload to "no" for grouped transients. Transients are grouped by site ID as key. * * @param array $grouped_selected Grouped selected transients to set autoload to "no". * * @return array An array of transients names that were not processed. */ public static function set_autoload_to_no( $grouped_selected ) { $autoload_value = function_exists( 'wp_autoload_values_to_autoload' ) ? 'off' : 'no'; $not_processed = self::set_autoload( $grouped_selected, $autoload_value ); return $not_processed; } /** * Set autoload to "yes" for grouped transients. Transients are grouped by site ID as key. * * @param array $grouped_selected Grouped selected transients to set autoload to "yes". * * @return array An array of transients names that were not processed. */ public static function set_autoload_to_yes( $grouped_selected ) { $autoload_value = function_exists( 'wp_autoload_values_to_autoload' ) ? 'on' : 'yes'; $not_processed = self::set_autoload( $grouped_selected, $autoload_value ); return $not_processed; } /** * Change autoload value for transients. Transients are grouped by site ID as key. * * @param array $grouped_selected Grouped selected transients to set autoload for. * @param string $autoload_value The value to set for autoload, typically "yes"/"no" (or "on"/"off" for WP 6.6.0 and above). * * @return array Normally should return an array of transients names that were not processed, but in this case it returns an empty array. */ private static function set_autoload( $grouped_selected, $autoload_value ) { global $wpdb; foreach ( $grouped_selected as $site_id => $group ) { if ( empty( $group ) ) continue; // get only ids where found_in !== "sitemeta" because sitemeta transients doesn't have autoload $ids = array_column( array_filter( $group, function ($item) { return $item['found_in'] !== 'sitemeta'; } ), 'id' ); if ( empty( $ids ) ) continue; $ids_placeholder = implode( ',', array_fill( 0, count( $ids ), '%d' ) ); ADBC_Sites::instance()->switch_to_blog_id( $site_id ); $sql = $wpdb->prepare( "UPDATE {$wpdb->options} SET autoload = %s WHERE option_id IN ( $ids_placeholder )", ...array_merge( [ $autoload_value ], $ids ) ); $wpdb->query( $sql ); // Clear the object cache so WordPress picks up the new autoload flags wp_cache_delete( 'alloptions', 'options' ); ADBC_Sites::instance()->restore_blog(); } return []; } /** * Get the total number of transients across all sites. * * @return int Total number of transients. */ public static function get_total_transients_count() { global $wpdb; $total_transients = 0; $sites = ADBC_Sites::instance()->get_sites_list(); // LIKE patterns for filtering transients $transient_like = $wpdb->esc_like( '_transient_' ) . '%'; $site_transient_like = $wpdb->esc_like( '_site_transient_' ) . '%'; $transient_timeout_like = $wpdb->esc_like( '_transient_timeout_' ) . '%'; $site_transient_timeout_like = $wpdb->esc_like( '_site_transient_timeout_' ) . '%'; foreach ( $sites as $site ) { $prefix = $site['prefix']; $options_table = $prefix . 'options'; // Count transients stored in options (regular and site transients that may live in options) $sql_options = $wpdb->prepare( "SELECT COUNT(*) FROM `{$options_table}` WHERE ( option_name LIKE %s OR option_name LIKE %s ) AND option_name NOT LIKE %s AND option_name NOT LIKE %s", $transient_like, $site_transient_like, $transient_timeout_like, $site_transient_timeout_like ); $total_transients += (int) $wpdb->get_var( $sql_options ); // Count site-wide transients stored in sitemeta (only for multisite main site) $site_id = $site['id']; if ( is_multisite() && $site_id !== null && is_main_site( $site_id ) ) { $sitemeta_table = $prefix . 'sitemeta'; $sql_sitemeta = $wpdb->prepare( "SELECT COUNT(*) FROM `{$sitemeta_table}` WHERE meta_key LIKE %s AND meta_key NOT LIKE %s", $site_transient_like, $site_transient_timeout_like ); $total_transients += (int) $wpdb->get_var( $sql_sitemeta ); } } return $total_transients; } }
| ver. 1.1 | |
.
| PHP 8.3.30 | Ð“ÐµÐ½ÐµÑ€Ð°Ñ†Ð¸Ñ Ñтраницы: 0 |
proxy
|
phpinfo
|
ÐаÑтройка