Developing WordPress Plugins 101 (Part 2)

Prerequisites: If you have not read Part 1, you should do that now.

Class is in Session

Yesterday we got started with the first piece of the plugin puzzle, the header. This is what tells WordPress all about your plugin and also includes your license information. Today I will show you a little bit more about developing your own plugin.

Now before I start programming the plugin there is another fork in the road ahead. If you are familiar with PHP development then you know that you can write functions, and you can also write functions within classes. Within the same PHP application you cannot use the same function name more than once. This means that when you are writing functions you need unique but appropriate names that will not collide with any other themes or plugins that could possibly be installed at the same time. Typically if you are writing functions you could start them all with your plugin name, for example solve360_cool_function() instead of cool_function(). One way you can ensure compatibility but at the same time keep the ability to choose any function name you would like is to write your plugin using classes. This way it is like taking all your functions and putting them in a folder separate from all your other functions, similar to how you cannot use the same file name on your desktop unless you put the files in separate folders.

The Programming Begins

The first line of the plugin that is not a comment is a safe guard. This is checking to see if the class Solve360 has been declared already. If when this plugin is loaded there is already a class named Solve360 then your website will not function correctly. By simply having the script return rather than using die() or wp_die() the rest of the website will continue to function, and the Solve360 class does not get declared again causing errors.

After the if statement that checks if Solve360 already exists, I would declare the class and write the empty __construct() function. This function will be where I add all my add_action()’s as the plugin is built. Now I have a class!

1
2
3
4
5
6
if (!class_exists('Solve360')) return;

class Solve360 {
    function __construct() {
    }
}

Ultimately this plugin will have a lot more features but for my initial purposes I only need an options page and a widget. Today I will just be adding an options page to save our settings. WordPress has a full settings API that allows you to easily tie into existing options pages or create your own. Both have their merits and appropriate uses, in this case as we only need to save five settings I am going to just add the settings to the general settings page.

The way I create my options page is I put all my options and required info into an array and then foreach my way through it registering the sections, settings, setting defaults, etc. A lazy programmer is an efficient programmer, so rather than type out the whole array just copy and paste this function into your class.

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
function settings_init() {
    $settings = array(
        array(
            'name'      => 'solve360_api_info',
            'title'     => __('Your Solve360 API Credentials','solve360'),
            'page'      => 'general',
            'callback'  => array(&$this,'settings_section_api_info'),
                'settings'  => array(
                array(
                    'name'      => 'solve360_api_info_user',
                        'title'     => __('User Email','solve360'),
                    'callback'  => array(&$this,'settings_field_user'),
                ),
                array(
                    'name'      => 'solve360_api_info_token',
                    'title'     => __('API Token','solve360'),
                    'callback'  => array(&$this,'settings_field_token'),
                ),
                array(
                    'name'      => 'solve360_api_info_notifications',
                    'title'     => __('Notifications Email','solve360'),
                    'callback'  => array(&$this,'settings_field_notifications'),
                ),
                array(
                    'name'      => 'solve360_api_info_thankyou',
                    'title'     => __('Thank You URL','solve360'),
                    'callback'  => array(&$this,'settings_field_thankyou'),
                ),
                array(
                    'name'      => 'solve360_api_info_error',
                    'title'     => __('Error URL','solve360'),
                    'callback'  => array(&$this,'settings_field_error'),
                ),
            ),
        ),
    );
}

This creates an array where the first level is settings sections, within that you have another array for each of the settings you want to have within that section. Now I have not taken the time to find an easier way to do this, but each section and setting has to have a callback to another function that echo‘s out the input fields. Now that you have the array you will also need the foreach statement. Just put the code below the array but before the last curly bracket.

1
2
3
4
5
6
7
foreach($settings as $name=>$section) {
    add_settings_section($section['name'],$section['title'],$section['callback'],$section['page']);
    foreach($section['settings'] as $setting=>$option) {
        add_settings_field($option['name'],$option['title'],$option['callback'],$section['page'],$section['name']);
        register_setting($section['page'],$option['name']);
    }
}

If you are familiar with foreach in PHP then you may be able to pick out how this part of the function works. Within the first level of the array you may have 3 sections, foreach will take each section and run it through whatever code you put inside the statement. So in this case for each section, the plugin will add the settings section to WordPress. Since the ‘page’ key in the array is set to ‘general’ the settings will be added onto the general settings page. Then the foreach will go into another foreach where each setting then is added to the settings section just added, as well as registering the settings with WordPress. Today I don’t have any defaults that I want to set, but you could easily add the below line right under the register_setting and set a default setting.

1
if(isset($option['default'])) add_option($option['name'], $option['default']);

This code will run every time the plugin script is run, however there are a few safegaurds here. The add_option function is not run unless $option[‘default’] is set (ie. exists), and add_option will never overwrite an existing option. If you check the codex, if the option exists then it will return false.

Quick Code Check

So my function will look like this:

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
function settings_init() {
    $settings = array(
        'Solve360 API Info' => array(
            'name'      => 'solve360_api_info',
            'title'     => __('Solve360 Settings','solve360'),
            'page'      => 'general',
            'callback'  => array(&$this,'settings_section_api_info'),
            'settings'  => array(
                array(
                    'name'      => 'solve360_api_info_user',
                    'title'     => __('User Email','solve360'),
                    'callback'  => array(&$this,'settings_field_user'),
                ),
                array(
                    'name'      => 'solve360_api_info_token',
                    'title'     => __('API Token','solve360'),
                    'callback'  => array(&$this,'settings_field_token'),
                ),
                array(
                    'name'      => 'solve360_api_info_notifications',
                    'title'     => __('Notifications Email','solve360'),
                    'callback'  => array(&$this,'settings_field_notifications'),
                ),
                array(
                    'name'      => 'solve360_api_info_thankyou',
                    'title'     => __('Thank You URL','solve360'),
                    'callback'  => array(&$this,'settings_field_thankyou'),
                ),
                array(
                    'name'      => 'solve360_api_info_error',
                    'title'     => __('Error URL','solve360'),
                    'callback'  => array(&$this,'settings_field_error'),
                ),
            ),
        ),
    );
    
    foreach($settings as $name=>$section) {
        add_settings_section($section['name'],$section['title'],$section['callback'],$section['page']);
        foreach($section['settings'] as $setting=>$option) {
            add_settings_field($option['name'],$option['title'],$option['callback'],$section['page'],$section['name']);
            register_setting($section['page'],$option['name']);
        }
    }
}

You may have noticed the callbacks on each item in the array. Each callback is pointing back to a function that as of right now, does not exist. There are some pretty useful pieces you may pick up from the next few functions, mostly making your life easier and allowing others to translate your plugin. If you ever write a plugin and do not use classes, then you do not add this as an array. You only put a string of the function name. Since this plugin is within a class you need to include it as an array that has the class name (or $this if within the same class) and then the function name.

1
2
3
4
5
6
function settings_section_api_info() { echo wpautop(__('You can find your API token by logging into Solve360 and checking the "My Account" window.','solve360')); }
function settings_field_user() { echo '<input type="text" name="solve360_api_info_user" id="solve360_api_info_user" value="'.get_option('solve360_api_info_user').'" />'; }
function settings_field_token() { echo '<input type="password" name="solve360_api_info_token" id="solve360_api_info_token" value="'.get_option('solve360_api_info_token').'" />'; }
function settings_field_notifications() { echo '<input type="text" name="solve360_api_info_notifications" id="solve360_api_info_notifications" value="'.get_option('solve360_api_info_notifications').'" />'; }
function settings_field_thankyou() { echo '<input type="text" name="solve360_api_info_thankyou" id="solve360_api_info_thankyou" value="'.get_option('solve360_api_info_thankyou').'" />'; }
function settings_field_error() { echo '<input type="text" name="solve360_api_info_error" id="solve360_api_info_error" value="'.get_option('solve360_api_info_error').'" />'; }

The first thing I notice when looking at this is the strange word wpautop. This just stands for WordPress Auto P, as in a HTML paragraph. This function will wrap whatever content you put inside it with the paragraph tag.

There are a few places where you will see what looks like a function but it is just two underscores, __(‘Some stuff here’). This is called gettext and is used for internationalization or I18n for short. Within your solve360 plugin folder create a folder named “languages” – this is where translations will be stored. I do not touch I18n much myself, but I like to try an keep with the standards enough that others could come and translate my plugins. To tell WordPress that you have folder for translations just add one short function. This function tells WordPress that the translation files for the textdomain “solve360″ are in the languages folder of the plugin (that folder you just created).

1
2
3
function i18n() {
    load_plugin_textdomain('solve360', false, basename( dirname( __FILE__ ) ) . '/languages' );
}

Another function is the get_option() function which allows you to access options stored by WordPress. This gives you an easy way to store and access options without having to perform a bunch of SQL queries yourself. Just how function names and class names need to be unique you do need unique option names. Your PHP will not throw errors with a duplicate option name but you will be overwriting your own options (or other plugin options!) if you are not unique. I try to keep the first word of the option name the plugin name as I do with functions and classes, this is prefix of sorts that (most likely) no one else will use.

Let there be light an options page!

At this point I have programmed all my options, programmed my callbacks, even kept with some I18n standards for some translations of my options section. So save your code and go check your General Settings page!

Some Plugin Action Adding Action

Jokes on you! You we are not done yet! The major programming has been done but none of the code is being prompted to run. Just add two small snippets and then you can try the options page again. Above your class but below the if statement add these two lines. Once we get past the if statement WordPress will know to add the function Solve360 to the “plugins_loaded” action by using add_action().

1
2
add_action ('plugins_loaded', 'Solve360');
function Solve360() { $Solve360 = new Solve360; }

Then in the __construct() function add these two lines. This will add two actions, on the “init” (global) side of WordPress the i18n function is loaded so translations are loaded on both the visitor and admin sides of WordPress. Then on “admin_init” (wp-admin) the settings_init function is loaded. This function then will add the callbacks to the options, so a chain of events is started that adds everything you need.

1
2
add_action('init', array(&$this, 'i18n'));
add_action('admin_init',array(&$this,'settings_init'));

Now check inside “General Settings” and you should see your options at the bottom of the screen.

Tomorrow: Finishing things up with a widget.

Tags

Comments (1)

Participate