I'm building a plugin and I have hard time handling options on it. I already did try several ways but I think i'm stuck on a structural problem here. I would like to have your input on all of this because I really don't know how to make this smarter.

My main functions about the options are

  • get_options() - get the plugin's options. If it is not defined in the options table, get the default option. Override those options with get_presets() : presets have the priority over user defined options. There is a hook to filter the options.
  • get_default_options() - the plugin's default options
  • get_presets() - get the plugin's presets, which are empty, but external plugins can add presets using a filter hook. If a preset is defined, it will override the matching options and grey out the field in the backend, so the user cannot define a custom value for it.

The questions I have :

  • I usually make a check to see if a variable ($options or $options_presets) is already defined. If it is, it is not populated again and the hook is not fired. I made this to make the code faster, but I'm not sure the idea is good ?
  • If I try to add presets with the filter hook, the code is usually going crazy (infinite loops and so).

Here's a sample of my code (reduced). I would be glad to have your comments / ideas on it to make it better. Thanks !

class MyPlugin{

    var $options = null;
    var $options_presets = null;

    function get_options($keys = null){

        //populate only once
        if ( $this->options === null ) {

            $default = self::get_default_options();

            //1 - override default options with custom options (user defined)
            if ($custom = get_post_meta($this->post_id, 'myPluginOptions', true)){
                $this->options = array_replace_recursive($default, $custom);

            //2 - override custom options with presets (presets priority is higher)
            if ( $presets = $this->get_presets() ){
                $this->options = array_replace_recursive($this->options, $presets);

            //allow plugins to filter the options when they are populated
            $this->options = apply_filters('my_plugin_get_options',$this->options,$this);


        return self::et_array_value($keys,$this->options);


    function get_default_options($keys = null){
        $defaults = array(...); //default options are set here
        return self::get_array_value($keys,$defaults);

     * Get Presets : 
     * Allow external plugins to define presets; which will override custom options.
     * In the plugin options form, a field will be greyed out if a preset is defined.

    function get_presets($keys = null){

        //populate only once
        if ( $this->options_presets === null ) {

            $options = get_post_meta($this->post_id, 'myPluginOptions', true);

            //allow plugins to filter the presets when they are populated
            $this->options_presets = apply_filters('my_plugin_get_presets',$this->options_presets,$options,$this);


        return self::get_array_value($keys,$this->options_presets);


     * Get a value in a multidimensional array

    function get_array_value($keys = null, $array){

        if (!$keys) return $array;
        if (is_string($keys) && isset($array[$keys])) return $array[$keys];

        if (!isset($array[$keys[0]])) return false;

        if(count($keys) > 1) {
            return xspfpl_get_array_value(array_slice($keys, 1), $array[$keys[0]]);
            return $array[$keys[0]];



function add_presets_example($presets,$meta_options,$plugin){

    //get options will call get_presets and so the hook my_plugin_get_presets loop and crash the plugin.
    //I could eventually unhook - rehook it with : 
    // remove_filter('my_plugin_get_presets','add_presets_example',10,3);
    // add_filter('my_plugin_get_presets','add_presets_example',10,3);
    // , but this is extra work and not very clean.

    if ($website_url = $plugin->get_options('website_url')) {

    return $presets;



1 Answer 1


You hadn't included how had you arrived at this concept, but the whole "presets" branch of logic is pretty strange.

Post-filtering vs pre-filtering

WordPress filters are de-facto centered on filtering results. Your my_plugin_get_options filter follows that model — the results are produced and offered for modification.

Sometimes that is undesirable, what if we do all that processing just to throw result away? The fallacy is to pre–filter and affect results before they are produced. Which looks somewhat like what you are trying to do with my_plugin_get_presets. Usually this ends in unholy fragile mess. Even core uses this in few places with same underwhelming results.


You don't need to retrieve and process defaults on every access.

When plugin activates if settings are empty save defaults as settings. The only thing left to handle what if set of defaults changes, on top of my head you can probably check and add missing defaults on save.

Cut everything away

So you get rid of defaults and of presets — you end up with very little code (which is a good thing). You just retrieve your saved options and filter them. Every plugin who needs to do whatever with options can use that filter perfectly fine.

That leaves out making fields read only. In my opinion that could be just different filter altogether. There might be a need to filter options without making them read only and other way around. They don't have to be coupled.

  • Hi, thanks for that nice reply. Your explanation of pre/post-filtering is great. Here's how I arrived to that concept of presets overriding saved values : Backend, I have a wizard that allow you to scrap data from websites, using CSS selectors (which are the options saved). Those selectors will potentially change each time a website is updated... ...So, I use filters to override those options with presets defined for some popular websites. The presets are used if the website URL matches a regex... See the point ? Any ideas ? Thanks !
    – gordie
    Commented Jun 11, 2015 at 14:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.