public static function render($info)
 {
     $providers_previews = \Korobochkin\CurrencyConverter\Models\DataProviders::getInstance()->get_providers_preview();
     if ($providers_previews[$info['id']]['status'] === 'ok') {
         echo '<p>' . sprintf(__('Status: <code>%1$s</code>. The number of currencies: %2$s. Available currencies:', Plugin::NAME), __('OK', Plugin::NAME), count($providers_previews[$info['id']]['currencies'])) . '</p>';
         echo '<p><code>' . implode('</code>, <code>', $providers_previews[$info['id']]['currencies']) . '</code>.</p>';
     } else {
         echo '<p>' . sprintf(__('Status: <code>%1$s</code>. Error message(s):', Plugin::NAME), __('FAILED', Plugin::NAME)) . '</p>';
         echo '<ol><li>' . implode('</li><li>', $providers_previews[$info['id']]['errors']) . '</li></ol>';
     }
 }
 static function update($show_warning_in_admin = false)
 {
     // Получаем полностью все настройки + дефолтные на 100%
     $settings = get_option(\Korobochkin\CurrencyConverter\Models\Settings\General::$option_name, array());
     $settings = wp_parse_args($settings, \Korobochkin\CurrencyConverter\Models\Settings\General::get_defaults());
     $providers_obj = \Korobochkin\CurrencyConverter\Models\DataProviders::getInstance();
     $providers = $providers_obj->get_providers();
     if (array_key_exists($settings['data_provider_name'], $providers)) {
         $api = new \Korobochkin\CurrencyConverter\API\API($providers[$settings['data_provider_name']]);
         self::$newRates = $api->get_rates();
         if (!is_wp_error(self::$newRates)) {
             update_option(\Korobochkin\CurrencyConverter\Plugin::NAME . '_rates', self::$newRates);
             $settings['rates_available'] = true;
             $settings['cached_rates_by_data_provider_name'] = $settings['data_provider_name'];
             $result = update_option(\Korobochkin\CurrencyConverter\Models\Settings\General::$option_name, $settings);
             return true;
         }
     }
     return false;
 }
    public static function render()
    {
        $options = get_option(\Korobochkin\CurrencyConverter\Models\Settings\General::$option_name, array());
        $options = wp_parse_args($options, \Korobochkin\CurrencyConverter\Models\Settings\General::get_defaults());
        $providersObj = \Korobochkin\CurrencyConverter\Models\DataProviders::getInstance();
        $providers = $providersObj->get_providers();
        ?>
		<select id="<?php 
        echo Plugin::NAME;
        ?>
__[data_provider_name]" name="<?php 
        echo Plugin::NAME;
        ?>
[data_provider_name]">
			<?php 
        foreach ($providers as $provider) {
            printf('<option value="%1$s" %2$s %3$s>%4$s</option>', esc_attr($provider['abbreviated_name']), selected($provider['abbreviated_name'], $options['data_provider_name'], false), disabled($provider['active'], false, false), esc_html($provider['name']));
        }
        ?>
		</select>
		<?php 
    }
 /**
  * Санитайзинг срабатывает каждый раз, когда вызывается update_option(),
  * а не только когда сохранются настройки, поэтому надо чистить входные значения,
  * иначе при update_option() мы затираем настройки (при сохранении приходит лишь 1 значение,
  * а при update_option() могут прийти все значения.
  *
  * Способом, которым мы тут фильтруем, невозможно выкинуть старые настройки — они всегда остаются в опции,
  * если когда-либо в нее попали.
  *
  * @param mixed $values New options.
  *
  * @return array New options merged with old (current) options.
  */
 public static function sanitize($values)
 {
     /**
      * Идея фикс.
      * 1) Получаем опции из БД (какие есть).
      * 2) Мерджим их с дефолтными (тем самым дополняя к бдшным те, что появились в новой версии плагина и т п).
      * 3) Мерджим отфильтрованные опции с теми, что получили в пункте 2).
      *
      * Проблема: если мы не выполняем сохранение настроек, то при обновлении структуры опций есть шанс опять иметь в БД не все дефолтные настройки.
      */
     // Получаем настройки из бд и добавляем к ним дефолтные
     $current_options = get_option(\Korobochkin\CurrencyConverter\Models\Settings\General::$option_name, array());
     $current_options = wp_parse_args($current_options, \Korobochkin\CurrencyConverter\Models\Settings\General::get_defaults());
     if (isset($values['data_provider_name'])) {
         $providers = \Korobochkin\CurrencyConverter\Models\DataProviders::getInstance()->get_providers();
         if (!array_key_exists($values['data_provider_name'], $providers)) {
             $values['data_provider_name'] = $current_options['data_provider_name'];
             //$filtered_values['data_provider_name'] = sanitize_text_field( $values['data_provider_name'] );
         }
     }
     // Соединяем дефолотные + текущие с теми, что были введены сейчас
     $values = wp_parse_args($values, $current_options);
     return $values;
 }
<?php

namespace Korobochkin\CurrencyConverter;

// If uninstall is not called from WordPress, exit
if (!defined('WP_UNINSTALL_PLUGIN')) {
    exit;
}
/**
 * Autoloader for all classes.
 *
 * @since 0.0.0
 */
require_once 'vendor/autoload.php';
delete_option(\Korobochkin\CurrencyConverter\Models\Settings\General::$option_name);
// TODO: Maybe delete this option by name from variable, not directly by name.
delete_option(\Korobochkin\CurrencyConverter\Plugin::NAME . '_rates');
delete_transient(\Korobochkin\CurrencyConverter\Models\DataProviders::getInstance()->get_transient_name());
wp_clear_scheduled_hook(\Korobochkin\CurrencyConverter\Plugin::NAME . \Korobochkin\CurrencyConverter\Cron\UpdateCurrency::$action_name);