prefix}better_payment"; // No table yet (activation has not run): nothing to fix, and nothing written // before the fix can appear later. Claim so the check is not repeated. $table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ) === $table; $max_id = $table_exists ? (int) $wpdb->get_var( "SELECT MAX(id) FROM {$table}" ) : 0; if ( ! self::claim( $max_id ) ) { return 0; } if ( $max_id <= 0 ) { return 0; } $converted = 0; $after_id = 0; do { $rows = $wpdb->get_results( $wpdb->prepare( "SELECT id, payment_date FROM {$table} WHERE referer = %s AND id > %d AND id <= %d ORDER BY id ASC LIMIT %d", 'woocommerce', $after_id, $max_id, self::BATCH ) ); foreach ( (array) $rows as $row ) { $after_id = (int) $row->id; $utc = self::to_utc( $row->payment_date ); if ( null === $utc || $utc === $row->payment_date ) { continue; } $updated = $wpdb->update( $table, array( 'payment_date' => $utc ), array( 'id' => (int) $row->id ), array( '%s' ), array( '%d' ) ); if ( $updated ) { ++$converted; } } } while ( is_array( $rows ) && count( $rows ) === self::BATCH ); return $converted; } /** * A site-time `Y-m-d H:i:s` as UTC, or null when it is not a usable date. * * @param mixed $site_time Stored value. * @return string|null */ public static function to_utc( $site_time ) { $site_time = is_string( $site_time ) ? trim( $site_time ) : ''; if ( ! preg_match( '/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $site_time ) || 0 === strpos( $site_time, '0000-' ) ) { return null; } $utc = get_gmt_from_date( $site_time, 'Y-m-d H:i:s' ); return '' !== $utc ? $utc : null; } /** * Create the flag, atomically. * * `add_option()` is not a claim: it writes with `ON DUPLICATE KEY UPDATE`, * so a second request with a different value succeeds too. * * @param int $max_id Highest row id covered. * @return bool True for the one request that created it. */ private static function claim( $max_id ) { global $wpdb; $inserted = $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO {$wpdb->options} (option_name, option_value, autoload) VALUES (%s, %s, %s)", self::OPTION, (string) $max_id, 'yes' ) ); // Keep the options cache in line with the row just written (or lost). wp_cache_delete( 'notoptions', 'options' ); wp_cache_delete( 'alloptions', 'options' ); wp_cache_delete( self::OPTION, 'options' ); return 1 === (int) $inserted; } }