get_option array value defaults and best practices

First question: Is this a good practice?

Based on the type of value you are saving, array as settings value is definitely good practice.


How can I do this for a value within an array ex. get_option('header')['header_logo']['image']?

If you believe option value for header might be empty or not saved, you can use this –

get_option( 
  'header', 
  array( 
    'header_logo' => array(
      'image' => '.../default-image.png',
      'width' => '200',
      'height' => '60'
    )
  )
)['header_logo']['image']

However, if the option header contains any value (even empty array), then the above option will not override the default. In that case, you could write a wrapper function for get_option function.

function wpse_get_header_settings( $group = '', $key = '', $default = null ) {

  $settings = get_option( 'header' );

  $defaults = array( 
    'header_logo' => array(
      'image' => '.../default-image.png',
      'width' => '200',
      'height' => '60'
    )
  );

  $settings = wp_parse_args( $settings, $defaults );

  if ( ! empty( $group ) ) {
    if ( ! array_key_exists( $group, $settings ) ) {
      return $default;
    }

    $settings = $settings[ $group ];

    if ( ! empty( $key ) ) {
      return array_key_exists( $key, $settings ) ? $settings[ $key ] : $default;
    }
  }

  return $settings;
}

And then, you can safely get any desired value –

// would return the logo image
echo wpse_get_header_settings( 'header_logo', 'image' );

// would return the header text color
wpse_get_header_settings( 'header_color' );

Which is the best practice when it comes to setting default option values?

It’s better to save default option values using after_theme_setup for theme and register_activation_hook for plugin. But, if you use a wrapper function to call settings, you shouldn’t save it.