Rows removed per log. */ public static function run(): array { global $wpdb; $core = \AiRankSuite\Kernel::service( Options::class )->get( 'core', \AiRankSuite\Modules\Settings\SettingsModule::defaults() ); $out = array(); $usage_days = (int) ( $core['retention_ai_usage_days'] ?? 0 ); if ( $usage_days > 0 ) { $table = Database::ai_usage(); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared $out['ai_usage'] = (int) $wpdb->query( $wpdb->prepare( "DELETE FROM {$table} WHERE created_at < %s", gmdate( 'Y-m-d H:i:s', time() - $usage_days * DAY_IN_SECONDS ) ) ); } $nf_days = (int) ( $core['retention_404_days'] ?? 0 ); if ( $nf_days > 0 ) { $table = Database::not_found(); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared $out['not_found'] = (int) $wpdb->query( $wpdb->prepare( "DELETE FROM {$table} WHERE last_hit < %s", gmdate( 'Y-m-d H:i:s', time() - $nf_days * DAY_IN_SECONDS ) ) ); } $crawl_days = (int) ( $core['retention_crawler_days'] ?? 0 ); if ( $crawl_days > 0 ) { $out['crawlers'] = self::prune_crawler_log( $crawl_days ); } update_option( 'airanksuite_retention_last', array( 'time' => time(), 'removed' => $out ), false ); return $out; } /** Trim the per-bot day counters kept by the crawler log. */ private static function prune_crawler_log( int $days ): int { $log = get_option( 'airanksuite_crawler_hits' ); if ( ! is_array( $log ) ) { return 0; } $cut = gmdate( 'Y-m-d', time() - $days * DAY_IN_SECONDS ); $removed = 0; foreach ( $log as $bot => $entry ) { if ( empty( $entry['days'] ) || ! is_array( $entry['days'] ) ) { continue; } foreach ( array_keys( $entry['days'] ) as $day ) { if ( (string) $day < $cut ) { unset( $log[ $bot ]['days'][ $day ] ); ++$removed; } } } if ( $removed > 0 ) { update_option( 'airanksuite_crawler_hits', $log, false ); } return $removed; } }