pinned_backup_ids = $pinned_backup_ids ?? fn(): array => RecoveryManager::pinned_backup_ids( $this->wpdb ); } /** * Removes old backups exceeding the retention limit, oldest first, per pool. * Backups pinned as a recovery restore point are kept regardless of age. * * @param int|null $limit Maximum rows to keep in the pool; null reads the manual-backup setting, 0 keeps everything. * @param string $pool self::POOL_MANUAL or self::POOL_ROLLBACK. * @return int Number of backups deleted. */ public function clean( ?int $limit = null, string $pool = self::POOL_MANUAL ): int { $limit = $limit ?? self::retention(); if ( $limit <= 0 ) { return 0; } $wpdb = $this->wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- custom table, fresh read required $rows = $wpdb->get_results( $wpdb->prepare( 'SELECT id, label FROM %i ORDER BY created_at DESC LIMIT %d OFFSET %d', $wpdb->prefix . 'shiftora_backups', PHP_INT_MAX, 0 ) ); $pinned = array_map( 'intval', (array) ( $this->pinned_backup_ids )() ); $kept = 0; $deleted = 0; foreach ( $rows ?? [] as $row ) { $id = (int) $row->id; $is_safety = str_starts_with( (string) ( $row->label ?? '' ), 'rollback-' ); if ( self::POOL_ROLLBACK === $pool ? ! $is_safety : $is_safety ) { continue; } $kept++; if ( $kept > $limit && ! in_array( $id, $pinned, true ) ) { $this->manager->delete( $id ); $deleted++; } } return $deleted; } /** Removes all backups for a migration, e.g. after permanent deletion. */ public function clean_for_migration( int $migration_id ): void { $backups = $this->manager->list_for_migration( $migration_id ); foreach ( $backups as $backup ) { $this->manager->delete( $backup->id ); } } }