Building your first WordPress plugin (part 3)

Default avatar.
November 21, 2012
Building your first WordPress plugin (part 3).

ThumbThere is no need to ask why anybody would want to write a plugin for WordPress. It’s one of the primary features that makes WordPress so flexible and a good fit for a wide range of projects. In the first part of our series we created the base for a WordPress plugin recognisible by the core. Then, in the second part we learnt how to alter the default functionality of the core. Today we’re going to look at plugin options. This is one of the most common tasks that plugins need to perform.

Commonly you will need to create a set of parameters (options) and give the user the ability to assign appropriate values to them. Values are stored in the database and can be retrieved on request. The plugin will normally perform different actions based on these values, produce different output for example.

What tools does WordPress give us to make this scenario possible? It allows us to register options with the system and retrieve them by assigned ID — The Options API is responsible for that. WordPress also provides a Settings API to create an admin GUI for options dialogues. Apart from that it allows us to add custom items into the admin menu so that the plugin can have its own settings page. Finally, WordPress takes care of plugin security and provides a set of capabilities and cleaning methods to handle user input safely.

Let’s take a detailed look at each part.

Options API

The Options API is a standardized way of storing custom data in the database. All the data is saved in the wp_​options table under a given custom name and can be accessed by it from somewhere in the code. The most important functions of the API are: 

<?php get_option('opting_name'); ?>
<?php update_option('option_name', $new_value); ?>

The get_​option function simply extracts from the database any information stored under a given name and returns it. The function update_​option takes an option name and its value and updates the corresponding entry in the database. If there is no such entry it will be created automatically. Both functions can operate with arrays as well as single values. That means you can store array data under a single name in the database and the API will handle serialization and mineralization actions for you. That’s a recommended practice for plugins: store all plugin options as an array under a single name.

Plugin options’ page

You can create a settings page or group of pages for your plugin in the admin menu. If you are creating a group of pages you should add a top level page first:

<?php add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $output_function, $icon_url, $position ); ?>

The parameter values are self-explanatory but you can refer to the source for details. Now you have to add internal pages one by one in the following manner:

<?php add_submenu_page( $parent_slug, $page_title, $menu_title, $capability, $menu_slug, $function ); ?>

As a $parent_​slug parameter you have to use the ID of the top level page — in the case of a custom top level page it’s the value you provided as $menu_​slug upon registration. If you don’t need several pages you can create a single settings page under one of existing top-level sections — commonly under Settings” (options-general.php should be used as the $parent_​slug). Alternatively there is are shortcut functions for adding subpages under certain admin menu items, in the case of Settings” it’s add_​options_​page().

Settings API

The Settings API allows you to create an interface to manage the plugin’s settings; mark a page as a settings page (to process input automatically) and output sections on that page and fields inside each section for accepting user input. To achieve that your first goal is to register settings with the system and create section-fields structure for them:

<?php register_setting($option_group, $option_name, $sanitise_callback); ?>
<?php add_settings_section( $id, $title, $callback, $page ); ?>
<?php add_settings_field( $id, $title, $callback, $page, $section, $args ); ?>

Refer to the Codex for a detailed description of the parameters, but the logic is quite simple: first of all, we register our options name (if there are a lot of options, they could be organised into groups); then we register section(s) with an internal ID and set of fields for each section; the API gives us the ability to specify custom callbacks for input validation and for displaying each field and section.

After registering our options and corresponding fields we have to display them on the settings page — the following functions have to be called inside the <form> tag:

<?php settings_fields($option_group); ?>
<?php do_settings_sections($page); ?>

The settings_​fields function takes care of obligatory hidden fields for the native WordPress options mechanism to work. The do_​settings_​sections actually outputs previously registered sections and fields.

Security considerations

The fundamental security rule when dealing with options is very simple: clean the input, escape the output and take care of capabilities. In other words, if you accept input from a user, you have to check that its format is correct and does not include malicious content (that’s validation), after that you can pass the data for further processing. When displaying data extracted from the database, it should be escaped to output special characters (especially HTML) properly. For both tasks WordPress provides native functions that can be used in different contexts (read more on the subject here).

Another point of concern is the users’ permissions. WordPress has built-in mechanism controlling users’ roles and capabilities that blocks access to certain admin areas for users with insufficient permissions. Only admins are allowed everywhere. When creating options pages you have to assign correct capabilities to them (normally it’s manage_​options’) and do not allow users with low privileges to access the page (for more information about WordPress roles and capabilities please refer to the Codex).

Put it all into work

Let’s see the whole scenario in action.

We’ll continue to develop our Hello World’ example (started in the previous parts of the series) that displays guest author’s information under a post with the help of custom taxonomy.

The author’s block markup was previously hard coded into the plugin. Now we are going to give a user the ability to specify a template for that markup using placeholders for author-specific data (name, url and description). Our plugin already has two includable PHP files: core.php (containing the main code) and admin.php (containing admin-related code).

What changes do we need to make?

1. Create a plugin options page (in admin.php)

/* register menu item */
function msp_helloworld_admin_menu_setup(){
'Helloworld Settings',
add_action('admin_menu', 'msp_helloworld_admin_menu_setup'); //menu setup

/* display page content */
function msp_helloworld_admin_page_screen() {
global $submenu;
// access page settings
$page_data = array();
foreach($submenu['options-general.php'] as $i => $menu_item) {
if($submenu['options-general.php'][$i][2] == 'msp_helloworld')
$page_data = $submenu['options-general.php'][$i];

// output
<div class="wrap">
<?php screen_icon();?>
<h2><?php echo $page_data[3];?></h2>
<form id="msp_helloworld_options" action="options.php" method="post">
submit_button('Save options', 'primary', 'msp_helloworld_options_submit');
/* settings link in plugin management screen */
function msp_helloworld_settings_link($actions, $file) {
if(false !== strpos($file, 'msp-helloworld'))
$actions['settings'] = '<a href="options-general.php?page=msp_helloworld">Settings</a>';
return $actions;
add_filter('plugin_action_links', 'msp_helloworld_settings_link', 2, 2);

In this snippet msp_​helloworld_​admin_​menu_​setup creates a subpage under the Settings’ menu (it should be executed on the admin_​menu’ action hook to work correctly). Then we output the settings form with msp_​helloworld_​admin_​page_​screen. It uses the Settings API functions for fields and pre-built WordPress functions for other elements of the interface (like the submit button). Note the action attribute of the <form> tag: it should point to options.php’ to process options correctly. Finally, msp_​helloworld_​settings_​link filter creates a shortcut link to the options page on the plugin management screen.

2. Register plugin options with the system and create fields and rules for them

/* register settings */
function msp_helloworld_settings_init(){


'Author\'s box',


add_action('admin_init', 'msp_helloworld_settings_init');

/* validate input */
function msp_helloworld_options_validate($input){
global $allowedposttags, $allowedrichhtml;
$input['authorbox_template'] = wp_kses_post($input['authorbox_template']);
return $input;

/* description text */
function msp_helloworld_authorbox_desc(){
echo "<p>Enter the template markup for author box using placeholders: [gauthor_name], [gauthor_url], [gauthor_desc] for name, URL and description of author correspondingly.</p>";

/* filed output */
function msp_helloworld_authorbox_field() {
$options = get_option('msp_helloworld_options');
$authorbox = (isset($options['authorbox_template'])) ? $options['authorbox_template'] : '';
$authorbox = esc_textarea($authorbox); //sanitise output
<textarea id="authorbox_template" name="msp_helloworld_options[authorbox_template]" cols="50" rows="5" class="large-text code">
<?php echo $authorbox;?>

I should point out that all plugin options should be stored as an array. Despite the fact that we only have one option (authorbox_​template), we include it in an array and the corresponding field into the section for demonstration purposes. The registration function msp_​helloworld_​settings_​init should be executed on the admin_​init’ hook. The function msp_​helloworld_​options_​validate takes care of user input by cleaning it with the native wp_​kses_​post filter that relies on the KSES library. The function msp_​helloworld_​authorbox_​desc creates a description for the form’s section and msp_​helloworld_​authorbox_​field outputs a textarea to handle inputted markup. Note that we assign the CSS classes large-text code” to it so that built-in admin styling is applied.

All this produces the following screen in the WordPress admin panel.

3. Modify the function that outputs author’s box (in core.php)

We do this so that it gets the template from the database and replaces placeholder data ([gauthor_​name], [gauthor_​url], [gauthor_​desc]) with the corresponding values.

/* Create author's box markup */
function msp_helloworld_author_block(){
global $post;

$author_terms = wp_get_object_terms($post->ID, 'gauthor');

$name = stripslashes($author_terms[0]->name);
$url = esc_url(get_term_link($author_terms[0]));
$desc = wp_filter_post_kses($author_terms[0]->description);

//get template from option
$options = get_option('msp_helloworld_options');
$out = (isset($options['authorbox_template'])) ? $options['authorbox_template'] : '';

$out = str_replace(
array('[gauthor_url]', '[gauthor_name]', '[gauthor_desc]'),
array($url, $name, $desc),

return $out;

Finally, our plugin (after applying some styles) produces a nice guest author’s box under post content.


Storing and accessing option data is a very common task, that a lot of plugins need to perform. Through the options mechanism you can provide your users with the ability to tune the plugin to their needs (which they will certainly appreciate). Even developing for yourself you may need a way to store details of a particular installation. Relying on native WordPress APIs and functions when solving such tasks is a good way to create maintainable, secure and future-proof code.

What sort of plugins would you like to see available for WordPress? Have you built your own using this series? Let us know in the comments below.

Featured image uses module image via Shutterstock

Anna Ladoshkina

Anna Ladoshkina is a freelance web designer and developer who likes to build pretty things with WordPress and write about it. Connect with Anna on Twitter (@foralien) or on her website, www​.foralien​.com.

Read Next

Are Simple Websites Better For Business?

As web design technologies raise the bar on what it is possible to achieve on a realistic budget, there’s a rising deba…

Apple Opts for AR over VR at WWDC

An Apple VR headset has been one of the most widely-rumored devices of the last few years, and it was finally settled a…

Exciting New Tools for Designers, June 2023

We’re halfway through 2023 already, and the number of incredible apps, tools, and resources for designers is mounting.

3 Essential Design Trends, June 2023

This month we are focusing on three trends within a bigger website design trend – different navigation menu styles and …

15 Best New Fonts, May 2023

The choices you make when selecting a typeface have more impact on your design than almost any other decision, so it’s …

10+ Best Tools & Resources for Web Designers and Agencies (2023 updated)

Having the ability to envision a tastefully designed website (i.e., the role creativity plays) is important. But being …

20 Best New Websites, May 2023

This month, there are tons of great new agency websites to get excited about. 3D animated prisms are a popular theme, a…

How to Find the Right White Label Website Builder for Your Agency

Web design agencies face a lot of obstacles in closing the deal with new clients. One of the most common ones is the ar…

Exciting New Tools For Designers, May 2023

There are hundreds of new tools for designers and developers released each month. We sift through them all to bring you…

3 Essential Design Trends, May 2023

All three of the website design trends here mimic something bigger going on in the tech space, from a desire to have mo…

10 Best AI Tools for Web Designers (2023)

It’s time to stop worrying if AI is going to take your job and instead start using AI to expand the services you can of…

10 Best Marketing Agency Websites (Examples, Inspo, and Templates!)

Marketers are skilled in developing strategies, producing visual assets, writing text with high impact, and optimizing …