Blog, WordPress, WordPress Snippet Codes

How To Create A Custom WordPress Plugin Step by Step With Example

Create a custom WordPress plugin is a subject for WordPress developers. We will start from the basics of plugin development step by step with example.

These handy tools let you add all kinds of features to your site in seconds. Sometimes you can’t find a plugin that meets your needs or you may simply want to try your hand at developing your own solution.

Plugins add extra functionality to your WordPress site over and above what comes with WordPress core. Everything from a booking calendar or animated slider, to a full-featured learning management system or online marketplace — you can add them all to your site with plugins.

How To Create Custom WordPress Plugin

Writing your own WordPress plugin is not that difficult if you are a web developer with basic PHP skills.

A plugin is a simple program, a set of functions, that adds a specific set of features and services that can execute in different sections of your WordPress site.

What is WordPress Plugin?

A WordPress plugin is a standalone set of code that enhances and extends the functionality of WordPress. By using any combination of PHP, HTML, CSS, JavaScript/jQuery, or any other web programming language, a plugin can add new features to any part of your website, including the Admin Control Panel.

You can modify the default behavior of WordPress, or remove unwanted behavior completely. Plugins allow you to easily customize and personalize WordPress to fit your needs.

The Basics of a WordPress Plugin

Plugins are external features that you can add to your WordPress website to increase the functionality of your site. The programming language of WP plugins is PHP and WP uses the MySQL database. If you want to create a plugin with integration with the database, you should know MySQL.

1. How to Install a WordPress Plugin

There are thousands of free and paid plugins available for WordPress. In this step by step guide, we will show you how to install a WordPress plugin.

1.1. How to Install a WordPress Plugin using WordPress Plugin Search

The easiest way of installing a WordPress plugin is to use the plugin search. The only downside of this option is that a plugin must be in the WordPress plugin directory which is limited to only free plugins.

Go to the Plugins > Add New page inside your WordPress admin area.

Find the plugin by typing the plugin name or the functionality you are looking for in the search box.

Find your plugin in the plugins list and click the “Install Now” button. After this, you’ll notice the ‘Install Now’ button will change into the “Activate” button. Activate the plugin by clicking the “Activate” button.

1.2. How to Install a WordPress Plugin using the WordPress Admin Plugin Upload

Paid WordPress plugins are not listed in the WordPress plugin directory. These plugins cannot be installed using the above method.

To install these plugins, you need to download the plugin from the source (which will be a zip file). Next, you need to go to the WordPress admin area and visit Plugins > Add New page.

After adding click on the “Upload Plugin” button on top of the page.

This will reveal the plugin upload form. Here you need to click on the “Choose File” button and select the plugin file you downloaded.

After you have selected the file, you need to click on the ‘Install Now’ button. After installation, you need to click on the “Activate Plugin” link to start using the plugin.

1.3. How to Install a WordPress Plugin Manually using FTP

In some rare cases, your WordPress hosting provider may have file restrictions that could limit your ability to install a plugin from the admin area.

It is your best choice to install the plugin manually using FTP.

First you will need to download the plugin’s source file (it will be a zip file).

Open the FTP client on your computer and connect to your website using the login credentials provided by your web host. After connection, you need to go to the /wp-content/plugins/ folder on your website.

Next, upload the zip file you downloaded to the folder on your web server and extract it.

After uploading the file, you need to visit the WordPress admin area and click on the Plugins link in the admin menu. You’ll see your plugin successfully installed on the plugins page.

You need to click on the Activate link below the plugin to start using it.

Types of Custom WordPress Plugin

Plugins can carry out lots of tasks. What they all have in common is that they add extra functionality to your site. Types of WordPress plugin include:

  • site maintenance plugins: for things like security, performance, or backups
  • marketing and sales plugins: for things like SEO, social media, or eCommerce
  • content plugins: custom post types, widgets, Shortcodes and etc.
  • API plugins: that work with the WordPress REST API or pull in external content from services like Google Maps
  • community plugins: that add social networking features

The Basics of a WordPress Plugin Development

You can create your custom WordPress plugin in three steps.

  • Create a folder and name it to your desire name for your plugin
  • Create a PHP file inside the folder and name it whatever you want
  • Insert below code into the PHP file
/**
 * Plugin Name: YOUR PLUGIN NAME
 */

How to Create a Custom WordPress Plugin?

1. Prepare Basics

Before start coding, it is helpful to understand best practices for plugins so your code can be high quality right from the start. 

To develop a custom WordPress plugin go to /wp-content/plugins/ of your site.

Create folder and name it hs-example.

Create Custom WordPress Plugin

Go to the hs-example and create example.php file.

Create Custom WordPress Plugin

Open example.php file.

Add below code into the PHP file.

<?php
/**
 * Plugin Name: Example
 */

WordPress will detect your plugin by the above code. If you go to Plugins in your admin panel, your plugin will be there.

Create Custom WordPress Plugin

Activate it. you can see there is just a name without other information about the plugin. To add more information edit the code like below.

<?php
/**
 * Plugin Name:       Example
 * Plugin URI:        https://example.com/plugins/example/
 * Description:       Example is a simple plugin.
 * Version:           1.0.0
 * Requires at least: 5.2
 * Requires PHP:      7.2
 * Author:            Honar Systems
 * Author URI:        https://honarsystems.com/
 * License:           GPL v2 or later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:       hs-example
 * Domain Path:       /hs-example
 */

If go to plugins in admin panel you will see the changes.

Create Custom WordPress Plugin

Read the items description and add or remove them based on your desire.

1.1. Items we can define in Plugin Main File

  • Plugin Name: (required) The name of your plugin.
  • Plugin URI: The home page of the plugin, which should be a unique URL, preferably on your own website. This must be unique to your plugin. You cannot use a WordPress.org URL here.
  • Description: A short description of the plugin, as displayed in the Plugins section in the WordPress Admin. This description should be fewer than 140 characters.
  • Version: The current version number of the plugin.
  • Requires at least: The lowest WordPress version that the plugin will work on.
  • Requires PHP: The minimum required PHP version.
  • Author: The name of the plugin author.
  • Author URI: The author’s website or profile on another website.
  • License: The short name (slug) of the plugin’s license (e.g. GPLv2).
  • License URI: A link to the full text of the license.
  • Text Domain: The text domain of the plugin.
  • Domain Path: The domain path lets WordPress know where to find the translations.
  • Network: Whether the plugin can only be activated network-wide. Can only be set to true, and should be left out when not needed.

Below example allows file-level PHPDoc DocBlock.

/**
 * Plugin Name
 *
 * @package           PluginPackage
 * @author            Your Name
 * @copyright         2019 Your Name or Company Name
 * @license           GPL-2.0-or-later
 *
 * @wordpress-plugin
 * Plugin Name:       Plugin Name
 * Plugin URI:        https://example.com/plugin-name
 * Description:       Description of the plugin.
 * Version:           1.0.0
 * Requires at least: 5.2
 * Requires PHP:      7.2
 * Author:            Your Name
 * Author URI:        https://example.com
 * Text Domain:       plugin-slug
 * License:           GPL v2 or later
 * License URI:       http://www.gnu.org/licenses/gpl-2.0.txt
 */

2. Development of Custom WordPress Plugin

Now your plugin is ready for programming. You can add your PHP codes after the introductions comment.

We are going to change “Read More” text of post in our plugin.

/**
 * Plugin Name:       Example
 * Plugin URI:        https://example.com/plugins/example/
 * Description:       Example is a simple plugin.
 * Version:           1.0.0
 * Requires at least: 5.2
 * Requires PHP:      7.2
 * Author:            Honar Systems
 * Author URI:        https://honarsystems.com/
 * License:           GPL v2 or later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:       hs-example
 * Domain Path:       /languages
 */

function hs_modify_read_more_link() {
    return '<a class="more-link" href="' . get_permalink() . '">Click to Read!</a>';
}

add_filter( 'the_content_more_link', 'hs_modify_read_more_link' );

Above code will change

Create Custom WordPress Plugin

To

Create Custom WordPress Plugin

An Introduction to Hooks, Actions, and Filters

WordPress Plugins interact with core code using hooks. There are two different types of hooks.

  • Actions: These are used to add or change WordPress functionality.
  • Filters: These are used to alter the functionality of actions.

If you’re going to start coding your own plugins, You should familiarize yourself with how actions and filters work and which ones are available for you to use.

1. Actions and Action Hooks

In visiting any page of a WordPress website, a series of PHP functions (named actions) are calling at various points, and they are attaching to action hooks. Using the action hooks provided by WordPress, you can add your own functions to the list of actions that run when any action hook is calling, and you can also remove pre-existing functions from any action hook.

1.1. Adding Functions To An Action Hook Using add_action()

To add a function to any action hook, your plugin must call the WordPress function named add_action(), with at least two parameters.

function YOUR_FUNCTION_NAME(){
    //Function code goes here
}
add_action( 'ACTION_HOOK', 'YOUR_FUNCTION_NAME' , PRIORITY, NUMBER_OF_ARGS);
  • The first required parameter is the name of the action hook that you want to attach to
  • The second required parameter is the name of the function that you want to run
  • The third parameter (optional) is the priority of the function you want to run.
  • The fourth parameter (optional) is the number of arguments, which means how many parameters your custom function is able to take. The default is 1.

For example:

function hs_add_Cookie() {
    setcookie("hs_last_visit_time", date("r"), time()+60*60*24*30, "/");
}
add_action( 'init', 'hs_add_Cookie' );

Hook to the “init” action, which is called after WordPress is finished loading the core code.

add_action("wp_footer", "hs_add_custom_text_to_footer");
function hs_add_custom_text_to_footer()
{
    echo "<p style='color: black;'>After the footer is loaded, my text is added!</p>";
}

This plugin hooks the wp_footer() action hook, which is called right before the closing </body> tag of every page, and adds a new function named hs_add_custom_text_to_footer().

1.2. Removing Functions From an Action Hook Using remove_action()

To remove an action from an action hook, you must write a new function that calls remove_action(), then call the function you have written using add_action().

add_action( 'init', 'remove_hs_add_custom_text_to_footer' );
function remove_hs_add_custom_text_to_footer()
{
    remove_action('wp_footer', 'hs_add_custom_text_to_footer');
}

In this example we removed hs_add_custom_text_to_footer() function we just add in the Adding Functions To An Action Hook above.

  • The first required parameter is the name of the action hook the function is hooked to
  • The second required parameter is the name of the function that you want to remove
  • The third parameter (optional) is the priority of the original function.

2. Filters and Filter Hooks

A filter function allows you to modify the resulting data that is returned by existing functions and must be hooked to one of the filter hooks. The available filter hooks are different from the action hooks.

They behave similarly to action hooks in that they are called at various points in the script and are contextual. A full list of filter hooks and the context in which they are called can be found on the WordPress Plugin API/Filter Reference page.

1.1. Adding Filters Using add_filter()

To add a filter function to any filter hook, your plugin must call the WordPress function named add_filter(), with at least two parameters.

add_filter('FILTER_HOOK', 'YOUR_FUNCTION_NAME' , PRIORITY, NUMBER_OF_ARGS);
function YOUR_FUNCTION_NAME(ARGUMENTS)
{
    //Your code goes here
}
  • The first required parameter is the name of the filter hook that you want to hook
  • The second required parameter is the name of the filter function that you want to run
  • The third parameter (optional) is the priority of the function you want to run.
  • The fourth parameter (optional) is the number of arguments, which means how many parameters your custom filter function is able to take. The default is 1.

WordPress has a function that retrieves the content of a post named the_content(). This function is a filter hook. The function to actually display the content calls the_content() to retrieve it, that’s where the filter is applied and the content is altered before being displayed.

This plugin defines a filter function that takes the content as its only input parameter and fixes text spacing every time the the_content() is called.

add_filter("the_content", "hs_fix_text_spacing");
function hs_fix_text_spacing($content)
{
    $the_new_content = str_replace("  ", " ", $content);
    return $the_new_content;
}

As the return value of the the_content() function is the actual content text, it is automatically entered as the function’s parameter $content when called using  add_filter(). The function you define must return the new value.

1.2. Removing Filters Using remove_filter()

Removing a filter is much simpler than removing an action because you can call the remove_filter() function without defining a new function.

remove_filter("the_content", "hs_fix_text_spacing");

In this example, we removed the hs_fix_text_spacing() function we just add in the previous section.

Provide a Shortcut to Your Settings Page with Plugin Action Links

Create Custom WordPress Plugin

See the “Deactivate” and “Settings” links underneath the name of the plugin? Those are plugin action links, and WordPress provides a filter named plugin_action_links for you to add more.

To adding this link, go to your main file of your plugin which is example.php.

Add following codes in the file

add_filter('plugin_action_links', 'hs_plugin_action_links', 10, 2);
function hs_plugin_action_links($links, $file) {
    static $this_plugin;

    if (!$this_plugin) {
        $this_plugin = plugin_basename(__FILE__);
    }

    if ($file == $this_plugin) {
        $settings_link = '<a href="' . get_bloginfo('wpurl') . '/wp-admin/admin.php?page=myplugin-settings">Settings</a>';
        array_unshift($links, $settings_link);
    }

    return $links;
}

Here we provided a plugin action link to the Settings admin page. The benefit of the plugin action link is that users see it immediately after they activate the plugin, thus adding to the overall experience.

Plugin Updater From WordPress.org

Every plugin should have an update announcement to tell the users, it is time to update your plugin. If you upload your plugin to wordpress.org, your plugin users should notice that there is a new update. In the following codes, we are going to create a details popup page and updater for our plugin.

After review of your plugin by wordpress.org team, your plugin will have a information file in JSON format in link below.

https://api.wordpress.org/plugins/info/1.0/{your_plugin_slug}.json

In this file, you can file information of your plugin in there like name, slug, version, download link and etc.

Add following codes in your updater file or if your plugin don’t have a updater file, then add them in your main file (example.php).

add_filter('plugins_api', 'hs_plugin_info', 20, 3);
function hs_plugin_info($res, $action, $args)
{
    // do nothing if this is not about getting plugin information
    if ('plugin_information' !== $action) {
        return false;
    }
    $plugin_slug = 'hs-example'; // we are going to use it in many places in this function
    // do nothing if it is not our plugin
    if ($plugin_slug !== $args->slug) {
        return false;
    }
    // info.json is the file with the actual plugin information on your server
    $remote = wp_remote_get('https://api.wordpress.org/plugins/info/1.0/hs-example.json', array(
            'timeout' => 10,
            'headers' => array(
                'Accept' => 'application/json'
            ))
    );
    if (!is_wp_error($remote) && isset($remote['response']['code']) && $remote['response']['code'] == 200 && !empty($remote['body'])) {
        $remote = json_decode($remote['body']);
        $res = new stdClass();
        $res->name = $remote->name;
        $res->slug = $plugin_slug;
        $res->version = $remote->version;
        $res->tested = $remote->tested;
        $res->requires = $remote->requires;
        $res->author = '<a href="https://honarsystems.com">Honar Systems</a>';
        $res->author_profile = 'https://profiles.wordpress.org/honarsystems';
        $res->download_link = $remote->download_link;
        $res->trunk = $remote->download_link;
        $res->requires_php = $remote->requires_php;
        $res->last_updated = $remote->last_updated;
        $res->sections = array(
            'description' => $remote->sections->description,
            'installation' => $remote->sections->installation,
            'changelog' => $remote->sections->changelog
            // you can add your custom sections (tabs) here
        );
        // in case you want the screenshots tab, use the following HTML format for its content:
        if (!empty($remote->sections->screenshots)) {
            $res->sections['screenshots'] = $remote->sections->screenshots;
        }
        $res->banners = array(
            'low' => 'https://honarsystems.com/plugins/example-772x250.jpg',
            'high' => 'https://honarsystems.com/plugins/example-1544x500.jpg'
        );
        return $res;
    }
    return false;
}

add_filter('site_transient_update_plugins', 'hs_push_update');
function hs_push_update($transient)
{
    if (empty($transient->checked)) {
        return $transient;
    }
    // info.json is the file with the actual plugin information on your server
    //https://api.wordpress.org/plugins/info/1.0/wordfence.json
    $remote = wp_remote_get('https://api.wordpress.org/plugins/info/1.0/hs-example.json', array(
            'timeout' => 10,
            'headers' => array(
                'Accept' => 'application/json'
            ))
    );
    if ($remote) {
        $remote = json_decode($remote['body']);
        // your installed plugin version should be on the line below! You can obtain it dynamically of course
        if ($remote && version_compare('1.0', $remote->version, '<') && version_compare($remote->requires, get_bloginfo('version'), '<')) {
            $res = new stdClass();
            $res->slug = 'hs-example';
            $res->plugin = 'hs-example/example.php'; // it could be just YOUR_PLUGIN_SLUG.php if your plugin doesn't have its own directory
            $res->new_version = $remote->version;
            $res->tested = $remote->tested;
            $res->package = $remote->download_link;
            $transient->response[$res->plugin] = $res;
        }
    }
    return $transient;
}

Go to your plugin section in your admin page and you will see the below view

Create Custom WordPress Plugin

If you click on View details you will see the information of your plugin.

Create Custom WordPress Plugin

1. hs_plugin_info

This function passes with plugins_api to define plugin information when you click the View details link.

As you see there are sections (Description, Installation, Change Log and Screenshots) and Version and Other Information.

In the function we got information of the plugin by below code.

$remote = wp_remote_get('https://api.wordpress.org/plugins/info/1.0/hs-example.json', array(
            'timeout' => 10,
            'headers' => array(
                'Accept' => 'application/json'
            ))
    );

After getting the JSON file, we parse it and fill the items from the JSON file.

2. hs_push_update

In this function, we are going to tell WordPress to update our plugin based on our information.

Fill in the necessary items especially the download link to download the new version of the plugin and install it.

After passing this function by site_transient_update_plugins you can see the link that appeared below of the plugin name that telling you, it is time to update your plugin.

Self-Hosted Plugin Updates

Sometimes you need to update your plugin from your website instead of the WordPress official website. To do this you need to change some code to upgrade.

Create a .json File with Update Information and Plugin ZIP Archive on Your Server

Json file should contain plugin name, version, download URL, description and etc.

In this example, our current plugin version is 1.0.0 and we are going to update it to 1.0.1. Then we will have a JSON file with the following format.

{
  "name": "Example",
  "slug": "hs-example",
  "version": "1.0.1",
  "author": "<a href=\"https:\/\/www.honarsystems.com\/\">Honar Systems<\/a>",
  "author_profile": "https:\/\/profiles.wordpress.org\/honarsystems",
  "requires": "3.9",
  "tested": "5.5.3",
  "requires_php": "5.3",
  "last_updated": "2020-10-21 3:33pm GMT",
  "sections": {
    "description": "Description of plugin",
    "installation": "Installation instructions",
    "faq": "FAQ",
    "changelog": "Change log information",
    "screenshots":"<ol><li><a href=\"https:\/\/honarsystems.com\/plugins\/example\/img1.jpg\"><img src=\"https:\/\/honarsystems.com\/plugins\/example\/img1.jpg\" alt=\"Your Alt\"><\/a><p>Your Caption<\/p><\/li><\/ol>"
  },
  "download_link": "https:\/\/honarsystems.com\/plugins\/example.zip",
  "screenshots" : {
    "src":"https://honarsystems.com/example-img1.jpg",
    "caption":"IMG 1"
  }
}

As you see there is information about the plugin (hs-example) in this JSON file. Upload this file on your server.

In previous section, this file created by wordpress.org, but in here our plugin is not hosted on wordpress.org.

This example.json file can be the same as wordpress.org file format our your own format. If you change the format to your own format just remember that you have to fill the items based on your format.

In the following codes, we are going to define the information section and update management.

Create Plugin Updater for Custom WordPress Plugin

Add the following codes in the updater file of your plugin. If there is no updater file, you can add them to the main file.

add_filter('plugins_api', 'hs_plugin_info', 20, 3);
function hs_plugin_info($res, $action, $args)
{
    // do nothing if this is not about getting plugin information
    if ('plugin_information' !== $action) {
        return false;
    }
    $plugin_slug = 'hs-example'; // we are going to use it in many places in this function
    // do nothing if it is not our plugin
    if ($plugin_slug !== $args->slug) {
        return false;
    }
    // info.json is the file with the actual plugin information on your server
    $remote = wp_remote_get('https://honarsystems.com/plugins/example.json', array(
            'timeout' => 10,
            'headers' => array(
                'Accept' => 'application/json'
            ))
    );
    if (!is_wp_error($remote) && isset($remote['response']['code']) && $remote['response']['code'] == 200 && !empty($remote['body'])) {
        $remote = json_decode($remote['body']);
        $res = new stdClass();
        $res->name = $remote->name;
        $res->slug = $plugin_slug;
        $res->version = $remote->version;
        $res->tested = $remote->tested;
        $res->requires = $remote->requires;
        $res->author = '<a href="https://honarsystems.com">Honar Systems</a>';
        $res->author_profile = 'https://profiles.wordpress.org/honarsystems';
        $res->download_link = $remote->download_link;
        $res->trunk = $remote->download_link;
        $res->requires_php = $remote->requires_php;
        $res->last_updated = $remote->last_updated;
        $res->sections = array(
            'description' => $remote->sections->description,
            'installation' => $remote->sections->installation,
            'changelog' => $remote->sections->changelog
            // you can add your custom sections (tabs) here
        );
        // in case you want the screenshots tab, use the following HTML format for its content:
        if (!empty($remote->sections->screenshots)) {
            $res->sections['screenshots'] = $remote->sections->screenshots;
        }
        $res->banners = array(
            'low' => 'https://honarsystems.com/plugins/example-772x250.jpg',
            'high' => 'https://honarsystems.com/plugins/example-1544x500.jpg'
        );
        return $res;
    }
    return false;
}

add_filter('site_transient_update_plugins', 'hs_push_update');
function hs_push_update($transient)
{
    if (empty($transient->checked)) {
        return $transient;
    }
    // info.json is the file with the actual plugin information on your server
    //https://api.wordpress.org/plugins/info/1.0/wordfence.json
    $remote = wp_remote_get('https://honarsystems.com/plugins/example.json', array(
            'timeout' => 10,
            'headers' => array(
                'Accept' => 'application/json'
            ))
    );
    if ($remote) {
        $remote = json_decode($remote['body']);
        // your installed plugin version should be on the line below! You can obtain it dynamically of course
        if ($remote && version_compare('1.0', $remote->version, '<') && version_compare($remote->requires, get_bloginfo('version'), '<')) {
            $res = new stdClass();
            $res->slug = 'hs-example';
            $res->plugin = 'hs-example/example.php'; // it could be just YOUR_PLUGIN_SLUG.php if your plugin doesn't have its own directory
            $res->new_version = $remote->version;
            $res->tested = $remote->tested;
            $res->package = $remote->download_link;
            $transient->response[$res->plugin] = $res;
        }
    }
    return $transient;
}

There is tiny differnces between updating from wordpress.org or from your own server. This is JSON file link. In the wordpress.org we used

https://api.wordpress.org/plugins/info/1.0/hs-example.json

And in the updating from our server we downloaded from our own server which was link below.

https://honarsystems.com/plugins/example.json