Wordpress - Plugin upgrading: Widget settings

I've did a quick test on just changing the option and it seems to work.

What I did is:

  1. Wrote a widget that has just 2 fields: "Title" and "Name". Add several instances of this widget to my sidebars. Been sure that they are shown correctly in frontend.
  2. Edited the class to use 3 fields: "Title" and "First Name" (to replace "Name") and added "Last Name".
  3. Edited the function that register the widget on 'widgets_init' to call a function that update the widget options:

    add_action( 'widgets_init', 'my_example_widget_register' );
    
    function my_example_widget_register() {
    
      $widget_name = 'my_example_widget';  // <-- You will probably replace this
    
      $options = get_option("widget_{$widget_name}");
    
      // if the widget is not updated, run a function that updates it
      if ($options && ! get_option("is_{$widget_name}_updated")) {
          // use class below to update options
          $updater = new MyExampleWidgetUpdater($widget_name, $options);
          $updater->update();
      }
    
      register_widget('My_Example_Widget'); // <-- You will probably replace this
    }
    
  4. Wrote a simple class to update widget options:

    class MyExampleWidgetUpdater
    {
    
      private $name;
      private $options;
    
      public function __construct($name, $options) {
         $this->name = $name;
         $this->options = $options;
      }
    
      public function update() {
        // loop all the options
        array_walk($this->options, function(&$option, $key) {
            if (is_array($option) && is_numeric($key)) {
              $option = $this->getOption($option);
            }
        });
        // update all options in DB
        update_option("widget_{$this->name}", $this->options);
        // set the widget as updated
        update_option("is_{$this->name}_updated", 1);
      }
    
      private function getOption($options) {
        if (!isset($options['name'])) {
           return $options;
        }
        $options['first_name'] = $options['name'];
        $options['last_name'] = '';
        unset($options['name']);
        return $options;
      }
    }
    
  5. I edited the widget class to save the option "is_{$widget_name}_updated" inside the update() method, in this way the updater class will never be called for new users that never installed old widget

    class My_Example_Widget {
    
        ...
    
        public function update($new_instance, $old_instance) {
            ...
    
            $widget_name = 'my_example_widget';
            update_option("is_{$widget_name}_updated", 1);
        }
    }
    
  6. I visited my site and the widgets saved with old options are displayed with no issue using new options. (Of course "last name" is always empty).

A good idea may be replace the "is_{$widget_name}_updated" option, with an option that store the actual version of the widget, in this way it will be handy next time you need an update.