cleanup_stored_paths_batch(); if ( ! $can_continue ) { return; } if ( ! $cleanup->should_schedule_followup ) { return; } // 'batch' arg is necessary, to tell WP that this is a unique event // and allow it to be run within 10 minutes of the last. // See https://developer.wordpress.org/reference/functions/wp_schedule_single_event/#description if ( ! wp_next_scheduled( 'jetpack_boost_minify_cron_cleanup_concat_paths_followup', array( 'batch' ) ) ) { wp_schedule_single_event( time(), 'jetpack_boost_minify_cron_cleanup_concat_paths_followup', array( 'batch' ) ); } } /** * Cleans up expired stored paths. * * @return bool True if there are more entries to process, false if not. */ public function cleanup_stored_paths_batch() { $stored_paths = $this->get_stored_paths(); if ( ! $stored_paths ) { // Cleanup after the cleanup. delete_option( $this->last_processed_option_key ); return false; } // Used to tell the cleanup to skip the entries that were checked in the previous run. // Avoids processing the same entries over and over again. $last_processed_option_id = false; foreach ( $stored_paths as $option ) { $value = maybe_unserialize( $option['option_value'] ); if ( ! is_array( $value ) ) { continue; } if ( $value['expire'] <= time() ) { $this->delete_static_file_by_hash( str_replace( 'jb_transient_concat_paths_', '', $option['option_name'] ) ); delete_option( $option['option_name'] ); } $last_processed_option_id = $option['option_id']; } update_option( $this->last_processed_option_key, $last_processed_option_id, false ); return true; } /** * Deletes the static files by hash. * * @param string $hash The hash of the file. * @return void */ private function delete_static_file_by_hash( $hash ) { // Since we don't have a way to know if this file is JS or CSS, // we delete both. $js_file_path = jetpack_boost_get_minify_file_path( $hash . '.min.js' ); if ( file_exists( $js_file_path ) ) { wp_delete_file( $js_file_path ); } $css_file_path = jetpack_boost_get_minify_file_path( $hash . '.min.css' ); if ( file_exists( $css_file_path ) ) { wp_delete_file( $css_file_path ); } } /** * Gets the stored paths to process, and skips the ones that were checked in the previous run. * Also sets a flag that determines if there should be a followup cleanup or not. * * @return array The stored paths. */ private function get_stored_paths() { $last_processed_option_id = get_option( $this->last_processed_option_key ); global $wpdb; $query = "SELECT * FROM {$wpdb->options} WHERE option_name LIKE 'jb_transient_concat_paths_%'"; if ( $last_processed_option_id ) { $query .= $wpdb->prepare( ' AND option_id > %d', $last_processed_option_id ); } // Add 1 to use it as a flag to know if there are more entries to process. $max_options_to_process_offset = $this->max_options_to_process + 1; $query .= $wpdb->prepare( ' ORDER BY option_id ASC LIMIT %d', $max_options_to_process_offset ); $stored_paths = $wpdb->get_results( $query, ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared // If the number of stored paths is equal to the offset of max options to process, // it means that there are more entries to process, so a followup cleanup is needed. if ( count( $stored_paths ) === $max_options_to_process_offset ) { $this->should_schedule_followup = true; // Since 1 was added to the limit, it needs to be removed from the list. array_pop( $stored_paths ); } return $stored_paths; } }