How to write a WordPress Widget?

WordPress widget is good re-usable component for developing WordPress sites. You must have dragged and dropped widgets in wordpress admin panel. If you are a wordpress savvy, you may have already created your own widgets.

In this tutorial, we will make a WordPress widget that can be used on the homepage of our site. This widget is very simple just for demonstration. It has a title that can be specified in wordpress admin control panel, and a text “Hello, World!” that is hard-coded in the widget’s implementation. We decide to put the widget code in a plugin file homepagewidget.php so that it can be used by different themes(so called plugin widget).

homepagewidget1

After the widget plugin is activated, wp-load.php will load all activated plugin files so code in the widget php file(homepagewidget.php) is executed. In fact, there is only one line of code add_action( ‘widgets_init’, ‘homepage_load_widget’ ) is executed now(other codes in homepagewidget.php are the implementation of our widget class homepage_widget and a function homepage_load_widget, and the main pieces of code are shown in the figures). Later wp-settings.php will do_action( 'init' ); wp_widgets_init() was hooked in “init” action and called now to do_action( 'widgets_init' ); Now homepage_load_widget defined in our plugin file is called. This function only calls register_widget( 'homepage_widget' ) to register our widget. Registering a widget means newing a widget class (homepage_widget) and storing it in WP_Widget_Factory’s array with the class name(homepage_widget) as the key. Later an action(_register_widgets) provided by WP_Widget_Factory is done to call every widget’s _register() to do the real registration.

Widget registration fills the information(i.e. the settings of all instances of the widget) got from widget_$baseid field in wp_options table into three arrays, and if no instance of this widget class has been added to a sidebar, will create a default one and save it to the three arrays. The first array is $wp_registered_widgets with widget id (got from the wp_option table) as the key.  The array member is also an array including the information of the widget instance and other information(the most important information is the callback used to display the widget on the webpage front-end). The second array is $wp_registered_widget_updates with the widget’s id_base as the key. The array member is also an array including the information of the widget instance and other information. The third array is $wp_registered_widget_controls with the widget’s id as the key. The array member is also an array including the information of the widget instance and other information(the most important information is a callback used to display the setting form of the widget on admin panel). Note that since $wp_registered_widget_updates uses id_base as the key, there is only one entry for the widget class.

All widgets are displayed in dashboard in wp_list_widgets(). The function traverses the members of $wp_registered_widgets and displays the template of every widget(but only once for all instances of a widget) on the available widgets column, from which you can drag a widget to a sidebar on the right column.

By far we know there are three callbacks for a widget class: display_callback is used to display widget instances on front-end, update_callback is used to deal with changed settings, form_callback is used to set parameters of a widget instance on back-end. display_callback eventually calls widget() of the widget class, update_callback eventually calls update() of the widget class, and form_callback eventually calls form() of the widget class. So as a widget developer, you will focus on the implementation of the three overloaded functions: widget(), update(), and form() of your widget class.

We can see registering a widget is not a trivial task. But registering a sidebar is much easier, just to fill the array $wp_registered_sidebars with the sidebar’s information array using the sidebar’s id as the key.

The widget’s settings is retrieved from wp-options table. The option name is widget_$id_base.

You may think for every occurrence  of a widget there is a widget object created according to the widget’s class. However this is not true. There is only one php object for one widget class. In admin panel, you can add a widget multiple times into a sidebar, or add a widget into multiple sidebars. This is implemented by Javascript codes. php only outputs an html template in the available widgets column. This template includes a form like:

<div class="widget-inside"><form action="" method="post">
<div class="widget-content"><input class="widget-id" name="widget-id" type="hidden" value="my_homepage_widget_id-__i__" />
<input class="id_base" name="id_base" type="hidden" value="my_homepage_widget_id" />
<input class="widget-width" name="widget-width" type="hidden" value="250" />
<input class="widget-height" name="widget-height" type="hidden" value="200" />
<input class="widget_number" name="widget_number" type="hidden" value="-1" />
<input class="multi_number" name="multi_number" type="hidden" value="3" />
<input class="add_new" name="add_new" type="hidden" value="multi" />
........</div>
</form>

When you add the widget to a sidebar, this template is cloned and appended to the sidebar with “__i__” in the template  replaced by a number that is increased 1 every time you add a new occurrence of the widget(the beginning number is got from the value of multi_number input).

We know the layout wp-admin/widgets.php has  two columns. The left column displays the available widgets for you to put in a sidebar, and the right column displays all the sidebars(on every sidebar you can find its widgets). The two columns are put into divs of class widget-liquid-left and widget-liquid-right, respectively. the two columns are contained by div of class wrap.  The containing relationship of divs of the available widgets are: <div class="widget-liquid-left"> contains <div id="widgets-left">, which contains <div id="available-widgets" class="widgets-holder-wrap ui-droppable">, which contains <div class=”widget-holder”>, which contains <div id="widget-list">, which contains individual widgets such as <div id="widget-6_my_homepage_widget_id-__i__" class="widget ui-draggable">. Each widget div contains <div class="widget-top">, <div class="widget-inside">,  and <div class="widget-description"><div class="widget-top"> contains <div class="widget-title"> which displays the title you input in the second parameter of WP_Widget’s constructor. <div class="widget-description"> displays the description you input in the third parameter of WP_Widget’s constructor. There is a div <div class="widgets-chooser"> that is parallel to <div class="wrap"> which has a list of available sidebars. If you click the title of an available widget, it pops up the widgets-chooser for you to choose a sidebar to add the widget to(the widgets-chooser div is moved by javascript code to be after the selected <div class="widget-description"> and its style is changed to “display:block” to actually show the chooser).

availablewidget

 

After you select a sidebar and click “Add Widget” button, the widget will be shown on the corresponding sidebar on the right column with some parameters to be filled by you.

widgetform

 

Notice how the codes in the overloaded function form() of class homepage_widget are mapped to the UI elements in the admin panel.

After filling the settings and clicking “Save” button, the settings are transferred back to the server via AJAX call(yes, it is AJAX,  not form action although the Save button is a submit input in a form. All click events are bound to a function $(document.body).bind('click.widgets-toggle', function(e) {}) in wp-admin/js/widget.js. In this function,  proper action is done according to the class of the event target or a parent of the event target. In this case, by finding the event target.i.e., the Save button, has a class widget-control-save, it calls wpWidgets.save( target.closest('div.widget'), 0, 1, 0 ), which issues the AJAX call with the settings, the widget id, and the sidebar id, etc. as the post data. The full parameters are: "widget-my_homepage_widget_id%5B8%5D%5Btitle%5D=my+New+title&widget-id=my_homepage_widget_id-8&id_base=my_homepage_widget_id&widget-width=250&widget-height=200&widget_number=8&multi_number=&add_new=&action=save-widget&savewidgets=5fbc6518c7&sidebar=home-widget-1". After the server(wp-admin/admin-ajax.php) receives the AJAX call, it will save the settings into the wp_option table.

A widget is always associated with one or more sidebars. To display a widget, you should use dynamic_sidebar(sidebarid) to display the sidebar specified by sidebarid on which the widget resides. The function retrieves all sidebars and their containing widget ids  from the field sidebars_widgets in the  table wp_options. With the widget id, it finds the widget’s display callback in $wp_registered_widgets and call it to display the widget. The callback got two parameters, one is the sidebar information got from  $wp_registered_sidebars, the other is the number of the widget instance got from $wp_registered_widgets. The settings for all instances of the widget are read again from the database table wp_options and looked up for the settings of this widget instance with the number. Now the display callback has both the sidebar information and the settings of this widget instance, then it calls the widget’s widget() function with the two pieces of information as the two parameters.  widget() actually displays the widget instance.

From the above description, we can know that WordPress Widget class is different than ordinary class which objects are created according to the class and have their own data in memory. In fact, the objects(widget instances) have their data(settings) stored in the database and the widget class is just a collection  of functions.

Leave a Reply