In my previous blog post we created a "Read Time" plugin. The idea is simple: Show how long it takes to read the article. There is one major issue with it though: The reading time is calculated using 200 words per minute, and this value is hardcoded.
Let's say you want to change this, because you know your users read faster or slower. Maybe you even have a per-user setting for this. It'll be impossible to do that with the current state of the plugin. Hardcoding variables like these is bad practice. So let's fix it by adding a setting.
This is the second post in our "Mastering WordPress Plugins" series. In this part we will make the words per minute dynamic by adding a setting with the WordPress Setting API , and retrieving it with the WordPress Options API.
The Goal
We're going to:
- Add a "Settings" link for our plugin in the WordPress admin menu.
- Create a simple form where the administrators can enter their preferred "Words Per Minute" value.
- Save that value securely using the Settings API.
- Update our calculation logic to use this new value.
Prerequisites
You'll need the code from part 1. If you don't have it already, here's the link to the GitHub Gist.
Step 1: Registering the Menu Item
Let's start by creating the setting. The administrators will need to be able to access this, so we'll add it as a sub-menu item under the "Settings" menu item on the WordPress Dashboard Sidebar.
Open your simple-read-time.php file and copy-paste this code at the end of the file:
/**
* Registers the settings page.
*/
function srt_register_settings_page() {
add_options_page(
'Simple Read Time Settings', // Page Title
'Read Time', // Menu Title
'manage_options', // Capability required (Admins only)
'simple-read-time', // Menu Slug
'srt_render_settings_page' // Callback function to render HTML
);
}
add_action( 'admin_menu', 'srt_register_settings_page' );
By adding this code we're letting WordPress know that there is a new sub-menu item under "Settings". For this purpose we're hooking into the admin_menu action and calling add_options_page() inside of it. The srt_render_settings_page callback function will render the form and display the content.
Step 2: Registering the Setting
Let's first tell WordPress about this new setting so that it actually gets stored inside the database before we create the form for it. We will now hook into admin_init and call register_setting() function to achieve this.
Add this code to your file:
/**
* Registers the setting to be saved in the database.
*/
function srt_register_settings() {
register_setting(
'srt_options_group', // Option Group Name
'srt_reading_speed', // Option Name (Database Key)
array(
'type' => 'integer',
'default' => 200,
'sanitize_callback' => 'absint', // Security: Force it to be a positive integer
)
);
}
add_action( 'admin_init', 'srt_register_settings' );
You probably noticed the sanitize_callback and that its value is absint. This is a WordPress function which returns the absolute value of an integer (i.e. -50 -> 200).
This is a security measure. We're making sure that, even if a user types in an incompatible value such as -50 or even "hello", WordPress converts it into a positive integer (or zero) before saving it.
We do this because you can never trust user input.
Step 3: Rendering the Settings Page
Now that we have everything else set up, let's create the actual HTML form. WordPress provides some helper functions which do make life a bit easier.
Once again, copy-paste this code to the bottom of your file:
/**
* Renders the HTML for the settings page.
*/
function srt_render_settings_page() {
?>
<div class="wrap">
<h1>Simple Read Time Settings</h1>
<form method="post" action="options.php">
<?php
// Output security fields for the registered setting "srt_options_group"
settings_fields( 'srt_options_group' );
// Output setting sections and their fields
do_settings_sections( 'srt_options_group' );
?>
<table class="form-table">
<tr>
<th scope="row">Words Per Minute</th>
<td>
<input type="number" name="srt_reading_speed" value="<?php echo esc_attr( get_option( 'srt_reading_speed', 200 ) ); ?>" />
<p class="description">Average reading speed (Default: 200)</p>
</td>
</tr>
</table>
<?php submit_button(); ?>
</form>
</div>
<?php
}
There's quite a bit going on here but don't worry, we'll break it down:
-
settings_fields: This function will output hidden inputs like the Nonce (security token). -
get_option: This is part of the WordPress Options API. It's used to fetch an option's value from the database. The second parameter (200) is the default value which is used if the value doesn't exist yet.
What is a Nonce you ask? It stands for "Number used ONCE" and it's used to prevent malicious attacks like Cross-Site Request Forgery (CSRF). It basically makes sure that the request came from your website, and not somewhere else.
Step 4: Connecting the Logic
So far so good! We can now save the value into the database. It's time to update our logic from Part 1 and utilize the stored database value for the words per minute.
Find your srt_add_reading_time function and modify the $reading_speed line:
function srt_add_reading_time( $content ) {
// ... existing check code ...
$word_count = str_word_count( strip_tags( $content ) );
// OLD: $reading_speed = 200;
// NEW: Get the dynamic value from the DB
$reading_speed = get_option( 'srt_reading_speed', 200 );
// Safety check: Prevent division by zero if someone saves "0"
if ( $reading_speed < 1 ) {
$reading_speed = 200;
}
$reading_time = ceil( $word_count / $reading_speed );
// ... existing HTML wrapper code ...
}
Refresh your page and you should be seeing something like this:
Conclusion
You're done! Your hardcoded value is now gone and replaced with a proper solution.
You have learned how to:
- Register a menu page (
add_options_page). - Register a database setting (
register_setting). - Sanitize input (
absint). - Retrieve dynamic values (
get_option).
So what's next for your plugin? Well, sky's the limit, but the next part of this series will focus on WordPress Security through Nonces which I mentioned earlier.
In this guide we are automatically protected by using the settings_fields() function. But what if you are building a custom form? You'll need to handle this part yourself.
I have also created a GitHub Gist of the updated plugin.

Top comments (0)