Much like the plugin API, it’s almost impossible to build a plugin without using the options API. This can be a problem if you’re trying to learn object-oriented programming with WordPress. You need a way to build a class around it.

Lucky for us, this is a lot less complicated to do than with the plugin API. The options API is really nothing more than an API around a data store. Our job as designers is simple. We need to design a class that mirrors this API.

That said, we don’t have to limit ourselves to just copying the options API as is. We can also push things further by adding some extra functionality around it. This is what makes designing a class around the options API so interesting.

Review options API

But first, let’s do a quick review of the options API. As we mentioned earlier, the options API is an API designed to interact with a data store. This data store is the wp_options table.

The options API has functions for adding, deleting, getting and updating these options. That said, in practice, there’s no reason to use the adding function. (Updating an option that doesn’t exist will create the option for us.) So we’re not going to use the adding function with our class.

Building our basic Options class

We’re going to start by building an Options class that mirrors the options API. This means that, at first, our class will only be a wrapper around the API. But don’t worry! After that, we’ll look at adding some extra features to it.

/** * Manages options using the WordPress options API. */ class MyPlugin_Options { /** * Gets the option for the given name. Returns the default value if the value does not exist. * * @param string $name * @param mixed $default * * @return mixed */ public function get($name, $default = null) { return get_option($name, $default); } /** * Removes the option with the given name. * * @param string $name */ public function remove($name) { delete_option($name); } /** * Sets an option. Overwrites the existing option if the name is already in use. * * @param string $name * @param mixed $value */ public function set($name, $value) { update_option($name, $value); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 /** * Manages options using the WordPress options API. */ class MyPlugin_Options { /** * Gets the option for the given name. Returns the default value if the value does not exist. * * @param string $name * @param mixed $default * * @return mixed */ public function get ( $name , $default = null ) { return get_option ( $name , $default ) ; } /** * Removes the option with the given name. * * @param string $name */ public function remove ( $name ) { delete_option ( $name ) ; } /** * Sets an option. Overwrites the existing option if the name is already in use. * * @param string $name * @param mixed $value */ public function set ( $name , $value ) { update_option ( $name , $value ) ; } }

Here’s our basic MyPlugin_Options class. It has three methods: get , remove and set . As we just mentioned, each method is a simple wrapper for an options API function.

You might wonder why we picked these names for our class method. The get method makes sense with the get_option function. But using update doesn’t make sense when it’s both adding and updating an option. set makes more sense in that context since we’re setting an option in our data store.

As for the choice of remove as a method name, this is more of a personal choice. Removing an option from a data store makes more sense than deleting an option. But feel free to use whichever names you prefer! (There’s no right answer!)

Adding new functionality

Right now, our MyPlugin_Options class is pretty underwhelming. It’s only a wrapper around options API functions. But this was just the beginning!

Now, we get to the fun part! Let’s look at adding some new functionality to our MyPlugin_Options class. There are quite a few things that we can do now that we have our foundation in place.

Checking if an option exists

The first thing that we can do is create a method that checks if an option exists or not. This is a good helper method to have since checking if an option exists is a pretty common thing to do! We’ll call this method the has method.

/** * Manages options using the WordPress options API. */ class MyPlugin_Options { // ... /** * Checks if the option with the given name exists or not. * * @param string $name * * @return bool */ public function has($name) { return null !== $this->get($name); } // ... } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 /** * Manages options using the WordPress options API. */ class MyPlugin_Options { // ... /** * Checks if the option with the given name exists or not. * * @param string $name * * @return bool */ public function has ( $name ) { return null !== $this -> get ( $name ) ; } // ... }

As you can see above, there’s nothing too complicated about our has method. We leverage our existing get method to fetch an option with the name argument. Since we didn’t pass a default value to the get method, it’ll return null if it doesn’t find our option.

We use this fact to check if the option exists or not. We do this by using the !== comparison operator. It’ll only return true if get returns null and nothing else. (If we used != , PHP would return true for empty strings, empty arrays, 0 and false .)

Better handling of arrays

One issue that’s a bit annoying with the options API is how it handles arrays in certain situations. What do we mean by that? Let’s imagine for a moment that you save an empty array as an option like this:

update_option('myplugin_option', array()); 1 update_option ( 'myplugin_option' , array ( ) ) ;

We just used the update_option to save an empty array as the myplugin_option option. Now, you might think that the options API would save an empty array in the database in that situation. But it didn’t, it saved an empty string. This becomes a problem when you use the get_option function.

$maybe_array = get_option('myplugin_option', array()); foreach ($maybe_array as $item) { // ... } 1 2 3 4 5 $maybe_array = get_option ( 'myplugin_option' , array ( ) ) ; foreach ( $maybe_array as $item ) { // ... }

The above code will generate a PHP error even if we saved an empty array beforehand. That’s because the get_option function will return an empty string. And that’s even if we said that the default value was an array.

$maybe_array = (array) get_option('myplugin_option', array()); foreach ($maybe_array as $item) { // ... } 1 2 3 4 5 $maybe_array = ( array ) get_option ( 'myplugin_option' , array ( ) ) ; foreach ( $maybe_array as $item ) { // ... }

Now, we can fix that by using type casting like we did above. But it’s easy to forget about it and have bugs in your code because of it. That said, it’s something that we can fix with our MyPlugin_Options class!

/** * Manages options using the WordPress options API. */ class MyPlugin_Options { // ... /** * Gets the option for the given name. Returns the default value if the value does not exist. * * @param string $name * @param mixed $default * * @return mixed */ public function get($name, $default = null) { $option = get_option($name, $default); if (is_array($default) && !is_array($option)) { $option = (array) $option; } return $option; } // ... } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 /** * Manages options using the WordPress options API. */ class MyPlugin_Options { // ... /** * Gets the option for the given name. Returns the default value if the value does not exist. * * @param string $name * @param mixed $default * * @return mixed */ public function get ( $name , $default = null ) { $option = get_option ( $name , $default ) ; if ( is_array ( $default ) && ! is_array ( $option ) ) { $option = ( array ) $option ; } return $option ; } // ... }

As you can see, we modified the get method inside our MyPlugin_Options class. We added a guard clause that does two checks to see if it should cast the option retrieved by the get_option function as an array. First, it checks if the default value that we got is an array using is_array . If it is, it checks if the option retrieved by the get_option function is also an array. Only if it isn’t does it cast the option as an array.

Prefixing our options

A good habit to have when building a plugin is to prefix our plugin options. This can be annoying to do all the time when you’re building your plugin. But with our MyPlugin_Options class, it’s easy to implement!

/** * Manages options using the WordPress options API. */ class MyPlugin_Options { /** * The prefix used by all option names. * * @var string */ private $prefix; /** * Constructor. * * @param string $prefix */ public function __construct($prefix = '') { $this->prefix = $prefix; } // ... } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 /** * Manages options using the WordPress options API. */ class MyPlugin_Options { /** * The prefix used by all option names. * * @var string */ private $prefix ; /** * Constructor. * * @param string $prefix */ public function __construct ( $prefix = '' ) { $this -> prefix = $prefix ; } // ... }

First, we want to add a constructor to our MyPlugin_Options class. This constructor has a single parameter called prefix . That’s the prefix that we want to use with all our options.

We store the value of the prefix in the prefix internal variable. By default, prefix is an empty string. This means that our updated MyPlugin_Options class will behave as it did earlier by default.

/** * Manages options using the WordPress options API. */ class MyPlugin_Options { // ... /** * Gets the option for the given name. Returns the default value if the value does not exist. * * @param string $name * @param mixed $default * * @return mixed */ public function get($name, $default = null) { $option = get_option($this->prefix . $name, $default); if (is_array($default) && !is_array($option)) { $option = (array) $option; } return $option; } /** * Checks if the option with the given name exists or not. * * @param string $name * * @return bool */ public function has($name) { return null !== $this->get($name); } /** * Removes the option with the given name. * * @param string $name */ public function remove($name) { delete_option($this->prefix . $name); } /** * Sets an option. Overwrites the existing option if the name is already in use. * * @param string $name * @param mixed $value */ public function set($name, $value) { update_option($this->prefix . $name, $value); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 /** * Manages options using the WordPress options API. */ class MyPlugin_Options { // ... /** * Gets the option for the given name. Returns the default value if the value does not exist. * * @param string $name * @param mixed $default * * @return mixed */ public function get ( $name , $default = null ) { $option = get_option ( $this -> prefix . $name , $default ) ; if ( is_array ( $default ) && ! is_array ( $option ) ) { $option = ( array ) $option ; } return $option ; } /** * Checks if the option with the given name exists or not. * * @param string $name * * @return bool */ public function has ( $name ) { return null !== $this -> get ( $name ) ; } /** * Removes the option with the given name. * * @param string $name */ public function remove ( $name ) { delete_option ( $this -> prefix . $name ) ; } /** * Sets an option. Overwrites the existing option if the name is already in use. * * @param string $name * @param mixed $value */ public function set ( $name , $value ) { update_option ( $this -> prefix . $name , $value ) ; } }

Above is the rest of our updated MyPlugin_Options class. All that we did is concatenate our prefix internal variable to every name variable. Everything else stayed the same.

Prefixing using inheritance

Now, you don’t have to rework the MyPlugin_Options class to add the prefix functionality. We can also do it by using inheritance. This would let us keep our original MyPlugin_Options as is.

/** * Manages options using the WordPress options API using a prefix. */ class MyPlugin_PrefixedOptions extends MyPlugin_Options { /** * The prefix used by all option names. * * @var string */ private $prefix; /** * Constructor. * * @param string $prefix */ public function __construct($prefix) { $this->prefix = $prefix; } /** * Gets the option for the given name. Returns the default value if the value does not exist. * * @param string $name * @param mixed $default * * @return mixed */ public function get($name, $default = null) { return parent::get($this->prefix.$name, $default); } /** * Checks if the option with the given name exists or not. * * @param string $name * * @return bool */ public function has($name) { return parent::has($name); } /** * Removes the option with the given name. * * @param string $name */ public function remove($name) { parent::remove($this->prefix.$name); } /** * Sets an option. Overwrites the existing option if the name is already in use. * * @param string $name * @param mixed $value */ public function set($name, $value) { parent::set($this->prefix.$name, $value); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 /** * Manages options using the WordPress options API using a prefix. */ class MyPlugin_PrefixedOptions extends MyPlugin_Options { /** * The prefix used by all option names. * * @var string */ private $prefix ; /** * Constructor. * * @param string $prefix */ public function __construct ( $prefix ) { $this -> prefix = $prefix ; } /** * Gets the option for the given name. Returns the default value if the value does not exist. * * @param string $name * @param mixed $default * * @return mixed */ public function get ( $name , $default = null ) { return parent :: get ( $this -> prefix . $name , $default ) ; } /** * Checks if the option with the given name exists or not. * * @param string $name * * @return bool */ public function has ( $name ) { return parent :: has ( $name ) ; } /** * Removes the option with the given name. * * @param string $name */ public function remove ( $name ) { parent :: remove ( $this -> prefix . $name ) ; } /** * Sets an option. Overwrites the existing option if the name is already in use. * * @param string $name * @param mixed $value */ public function set ( $name , $value ) { parent :: set ( $this -> prefix . $name , $value ) ; } }

Here’s our MyPlugin_PrefixedOptions class which extends our original MyPlugin_Options class. The constructor is pretty much the same as the previous one that we did for the MyPlugin_Options class. The only difference is that we didn’t make the prefix parameter optional. We want people who use our MyPlugin_PrefixedOptions class to supply a prefix to the class.

Getting the name of the prefixed option

Using a prefixed options class like the ones we just saw isn’t without issues. The most common one you might run into is you might need to know the full name of an option. For example, it’s necessary to know the name of an option if you want to register it with the settings API.

/** * Manages options using the WordPress options API. */ class MyPlugin_Options { /** * The prefix used by all option names. * * @var string */ private $prefix; /** * Constructor. * * @param string $prefix */ public function __construct($prefix = '') { $this->prefix = $prefix; } /** * Get the option name used to store the option in the WordPress database. * * @param string $name * * @return string */ public function get_option_name($name) { return $this->prefix . $name; } // ... } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 /** * Manages options using the WordPress options API. */ class MyPlugin_Options { /** * The prefix used by all option names. * * @var string */ private $prefix ; /** * Constructor. * * @param string $prefix */ public function __construct ( $prefix = '' ) { $this -> prefix = $prefix ; } /** * Get the option name used to store the option in the WordPress database. * * @param string $name * * @return string */ public function get_option_name ( $name ) { return $this -> prefix . $name ; } // ... }

Above, we reworked the MyPlugin_Options class that we saw earlier. We added a new method called get_option_name . It returns the option name concatenated with the prefix inside our class. We also made the method public so that you can use it outside the class.

/** * Manages options using the WordPress options API. */ class MyPlugin_Options { // ... /** * Gets the option for the given name. Returns the default value if the value does not exist. * * @param string $name * @param mixed $default * * @return mixed */ public function get($name, $default = null) { $option = get_option($this->get_option_name($name), $default); if (is_array($default) && !is_array($option)) { $option = (array) $option; } return $option; } /** * Checks if the option with the given name exists or not. * * @param string $name * * @return bool */ public function has($name) { return null !== $this->get($name); } /** * Removes the option with the given name. * * @param string $name */ public function remove($name) { delete_option($this->get_option_name($name)); } /** * Sets an option. Overwrites the existing option if the name is already in use. * * @param string $name * @param mixed $value */ public function set($name, $value) { update_option($this->get_option_name($name), $value); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 /** * Manages options using the WordPress options API. */ class MyPlugin_Options { // ... /** * Gets the option for the given name. Returns the default value if the value does not exist. * * @param string $name * @param mixed $default * * @return mixed */ public function get ( $name , $default = null ) { $option = get_option ( $this -> get_option_name ( $name ) , $default ) ; if ( is_array ( $default ) && ! is_array ( $option ) ) { $option = ( array ) $option ; } return $option ; } /** * Checks if the option with the given name exists or not. * * @param string $name * * @return bool */ public function has ( $name ) { return null !== $this -> get ( $name ) ; } /** * Removes the option with the given name. * * @param string $name */ public function remove ( $name ) { delete_option ( $this -> get_option_name ( $name ) ) ; } /** * Sets an option. Overwrites the existing option if the name is already in use. * * @param string $name * @param mixed $value */ public function set ( $name , $value ) { update_option ( $this -> get_option_name ( $name ) , $value ) ; } }

Next, we reworked the rest of the class to use our new get_option_name method. And now that you have the get_option_name method, you can register options using the settings API. Here’s a small code sample to show you how:

register_setting('myplugin_admin', $options->get_option_name('option_name')); 1 register_setting ( 'myplugin_admin' , $options -> get_option_name ( 'option_name' ) ) ;

Creating option classes for site options

You can also use similar classes to check for site options. Site options are options that are only used when you use the WordPress multisite feature. WordPress stores these options in a different table as well.

Creating a class to use site options isn’t complicated. Instead of using the options API functions for regular options, we just need to use the ones for site options. This results in a class that looks like this:

/** * Manages site options using the WordPress options API. */ class MyPlugin_SiteOptions { /** * Gets the site option for the given name. Returns the default value if the value does not exist. * * @param string $name * @param mixed $default * * @return mixed */ public function get($name, $default = null) { $option = get_site_option($name, $default); if (is_array($default) && !is_array($option)) { $option = (array) $option; } return $option; } /** * Checks if the site option with the given name exists or not. * * @param string $name * * @return bool */ public function has($name) { return null !== $this->get($name); } /** * Removes the site option with the given name. * * @param string $name */ public function remove($name) { delete_site_option($name); } /** * Sets a site option. Overwrites the existing site option if the name is already in use. * * @param string $name * @param mixed $value */ public function set($name, $value) { update_site_option($name, $value); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 /** * Manages site options using the WordPress options API. */ class MyPlugin_SiteOptions { /** * Gets the site option for the given name. Returns the default value if the value does not exist. * * @param string $name * @param mixed $default * * @return mixed */ public function get ( $name , $default = null ) { $option = get_site_option ( $name , $default ) ; if ( is_array ( $default ) && ! is_array ( $option ) ) { $option = ( array ) $option ; } return $option ; } /** * Checks if the site option with the given name exists or not. * * @param string $name * * @return bool */ public function has ( $name ) { return null !== $this -> get ( $name ) ; } /** * Removes the site option with the given name. * * @param string $name */ public function remove ( $name ) { delete_site_option ( $name ) ; } /** * Sets a site option. Overwrites the existing site option if the name is already in use. * * @param string $name * @param mixed $value */ public function set ( $name , $value ) { update_site_option ( $name , $value ) ; } }

As you can see, the MyPlugin_SiteOptions class really is almost the same as the MyPlugin_Options class. We changed the options API functions that the class used, but that’s it. Nothing else changed.

Using an abstract class

Since both the MyPlugin_Options and MyPlugin_SiteOptions classes are almost the same, why don’t we look into reusing code between the two? This is something that’s easy to do using an abstract class. So let’s create one for our options classes.

abstract class MyPlugin_AbstractOptions { /** * Checks if the option with the given name exists or not. * * @param string $name * * @return bool */ public function has($name) { return null !== $this->get($name); } /** * Gets the option for the given name. Returns the default value if the value does not exist. * * @param string $name * @param mixed $default * * @return mixed */ abstract public function get($name, $default = null); /** * Removes the option with the given name. * * @param string $name */ abstract public function remove($name); /** * Sets an option. Overwrites the existing option if the name is already in use. * * @param string $name * @param mixed $value */ abstract public function set($name, $value); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 abstract class MyPlugin_AbstractOptions { /** * Checks if the option with the given name exists or not. * * @param string $name * * @return bool */ public function has ( $name ) { return null !== $this -> get ( $name ) ; } /** * Gets the option for the given name. Returns the default value if the value does not exist. * * @param string $name * @param mixed $default * * @return mixed */ abstract public function get ( $name , $default = null ) ; /** * Removes the option with the given name. * * @param string $name */ abstract public function remove ( $name ) ; /** * Sets an option. Overwrites the existing option if the name is already in use. * * @param string $name * @param mixed $value */ abstract public function set ( $name , $value ) ; }

Here’s our MyPlugin_AbstractOptions class. It looks a lot like the classes that we’ve built so far. The has method is the only defined method in the class.

The other three methods are abstract methods. If you’re not familiar with abstract methods, they’re placeholder methods that a child class must implement. (If you don’t, PHP will throw an error.) But it’s because of those placeholders that we can have the has method inside our MyPlugin_AbstractOptions class.

All that we have to do now is update both our MyPlugin_Options and MyPlugin_SiteOptions classes. We need them to extend our new MyPlugin_AbstractOptions class. And we also need to remove the has method in both of them. You can find the result below:

/** * Manages options using the WordPress options API. */ class MyPlugin_Options extends MyPlugin_AbstractOptions { /** * Gets the option for the given name. Returns the default value if the value does not exist. * * @param string $name * @param mixed $default * * @return mixed */ public function get($name, $default = null) { $option = get_option($name, $default); if (is_array($default) && !is_array($option)) { $option = (array) $option; } return $option; } /** * Removes the option with the given name. * * @param string $name */ public function remove($name) { delete_option($name); } /** * Sets an option. Overwrites the existing option if the name is already in use. * * @param string $name * @param mixed $value */ public function set($name, $value) { update_option($name, $value); } } /** * Manages site options using the WordPress options API. */ class MyPlugin_SiteOptions extends MyPlugin_AbstractOptions { /** * Gets the site option for the given name. Returns the default value if the value does not exist. * * @param string $name * @param mixed $default * * @return mixed */ public function get($name, $default = null) { $option = get_site_option($name, $default); if (is_array($default) && !is_array($option)) { $option = (array) $option; } return $option; } /** * Removes the site option with the given name. * * @param string $name */ public function remove($name) { delete_site_option($name); } /** * Sets a site option. Overwrites the existing site option if the name is already in use. * * @param string $name * @param mixed $value */ public function set($name, $value) { update_site_option($name, $value); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 /** * Manages options using the WordPress options API. */ class MyPlugin_Options extends MyPlugin_AbstractOptions { /** * Gets the option for the given name. Returns the default value if the value does not exist. * * @param string $name * @param mixed $default * * @return mixed */ public function get ( $name , $default = null ) { $option = get_option ( $name , $default ) ; if ( is_array ( $default ) && ! is_array ( $option ) ) { $option = ( array ) $option ; } return $option ; } /** * Removes the option with the given name. * * @param string $name */ public function remove ( $name ) { delete_option ( $name ) ; } /** * Sets an option. Overwrites the existing option if the name is already in use. * * @param string $name * @param mixed $value */ public function set ( $name , $value ) { update_option ( $name , $value ) ; } } /** * Manages site options using the WordPress options API. */ class MyPlugin_SiteOptions extends MyPlugin_AbstractOptions { /** * Gets the site option for the given name. Returns the default value if the value does not exist. * * @param string $name * @param mixed $default * * @return mixed */ public function get ( $name , $default = null ) { $option = get_site_option ( $name , $default ) ; if ( is_array ( $default ) && ! is_array ( $option ) ) { $option = ( array ) $option ; } return $option ; } /** * Removes the site option with the given name. * * @param string $name */ public function remove ( $name ) { delete_site_option ( $name ) ; } /** * Sets a site option. Overwrites the existing site option if the name is already in use. * * @param string $name * @param mixed $value */ public function set ( $name , $value ) { update_site_option ( $name , $value ) ; } }

Wrapping things up

This is a good place to finish this article! At this point, you have everything you need to build one or more classes to manage WordPress options. While these classes are pretty small, they have a lot of reuse potential.

This makes them very useful inside an object-oriented plugin. (And the ultimate goal of this site is to help you do that!) So go ahead and give them a try!