A Guide to Building Your Own Custom WordPress Plugin

In this tutorial, we will dive into the development of a Custom WordPress Plugin using the PHP language. The primary goal is to provide students and beginners, particularly those new to WordPress and PHP, with a reference to learning useful techniques to enhance their knowledge and programming capabilities. I'll be offering sample snippets and a sample WordPress plugin source code that demonstrates our main goal in this tutorial.

What is WordPress?

WordPress is a widely utilized open-source content management system (CMS) that enables users to create and manage websites and blogs. It is written in PHP and employs a MySQL or MariaDB database. WordPress has evolved from its origins as a blogging platform to a versatile CMS utilized by individuals, businesses, and organizations for various types of websites, including blogs, portfolios, e-commerce sites, and corporate websites.

What is WordPress Plugin?

A WordPress plugin is a software component that extends and enhances the functionality of a WordPress website. Plugins are pivotal in making WordPress a flexible and customizable content management system (CMS). They enable users to incorporate specific features or capabilities into their websites without modifying the core WordPress code.

How to Create a Custom WordPress Plugin?

Step 1

To create a Custom WordPress Plugin, make a new directory in your WordPress website within the `source_code_path>wp-contents>plugins` folder. Then, create a new PHP file.

Step 2

Open your newly created PHP file with your preferred code editor, such as MS VS Code, Sublime, or Notepad++.

Step 3

Next, we need to provide the Plugin's information header at the top line of the PHP file. The information header is enclosed in comment tags. Below is an example information header for a sample WordPress Plugin:

  1. <?php
  2. /**
  3.  * Plugin Name: My Custom Plugin Name
  4.  * Plugin URI: URI of your plugin where your docs, updates, or etc located
  5.  * Description: A brief description of yout plugin
  6.  * Version: your plugin version
  7.  * Author: the plugins author name
  8.  * Author URI: the plugins author URI
  9.  * License: license name e.g. GPL2
  10.  */
  11. ?>

After completing these steps, you will be able to locate your Custom WordPress Plugin on the Plugins Page of your WordPress admin side.

Custom WordPress Plugin

For a more better understanding of creating a Custom WordPress Plugin, I have developed a simple plugin that includes CRUD (Create, Read, Update, and Delete) features on the admin side. The provided plugin script also involves the creation of a new database table.

Below is the PHP script for the simple plugin I created:

  1. <?php
  2. /**
  3.   * Plugin Name: My First Custom Plugin
  4.   * Plugin URI: https://sample-domain.com
  5.   * Description: Lorem ipsum dolor sit amet consectetur adipisicing elit.
  6.   * Version: 1.0
  7.   * Author: oretnom23
  8.   * Author URI: https://www.sourcecodester.com/user/257130/activity
  9.   * License: GPLv2 or later
  10.   */
  11.  
  12. if ( ! defined( 'ABSPATH' ) ) {
  13. die;
  14. }
  15.  
  16. class MyFirstPlugin{
  17. public $notices = [];
  18. function __construct()
  19. {
  20. add_action( 'admin_menu', [ $this, 'pages' ] );
  21. add_action( 'admin_enqueue_scripts', [$this, 'bootstrap_scripts'], 10, 1 );
  22.  
  23. // Process Form
  24. add_action( 'admin_init', [ $this, 'save_form' ] );
  25. add_action( 'admin_init', [ $this, 'delete_mfcp' ] );
  26.  
  27. $this->notices = get_option("mfcp_notices", array());
  28. // print_r(gettype($this->notices));exit;
  29. add_action( 'admin_notices', [$this, 'display_notices'] );
  30. }
  31.  
  32. function pages(){
  33. //add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $function, $icon_url, $position );
  34. add_menu_page(__('My First Custom Plugin'), __('Custom Plugin'), 'administrator', 'my-first-custom-plugin-page', [$this, 'my_plugin_list_page'], '', 25);
  35. add_submenu_page('my-first-custom-plugin-page', __('Manage Plugin\'s DB'), __('Add New'), 'administrator', 'my-first-custom-plugin-page-form', [$this, 'my_plugin_form_page']);
  36. }
  37.  
  38. function display_notices(){
  39. // print_r($this->notices);exit;
  40. if(count($this->notices) > 0){
  41. foreach($this->notices as $k =>$notice){
  42. ?>
  43. <div class="notice notice-<?= $notice['type'] ?> <?= ($notice['dismissible']) ? "is-dismissible" : "" ?>">
  44. <?= htmlspecialchars_decode($notice['message']) ?>
  45. </div>
  46. <?php
  47. unset($this->notices[$k]);
  48. }
  49. }
  50. update_option("mfcp_notices", []);
  51.  
  52. }
  53. function activate(){
  54. // Install Plugin's Database
  55. $this->install_db();
  56. }
  57. function deactivate(){
  58.  
  59. }
  60. function uninstall(){
  61. }
  62.  
  63. /**
  64.   * Enqueue Styles and Scripts
  65.   */
  66. function bootstrap_scripts($hook){
  67. // Enqueue Bootstrap CDN CSS and JS only in plugin's page
  68. if($hook == 'toplevel_page_my-first-custom-plugin-page' || $hook == 'custom-plugin_page_my-first-custom-plugin-page-form'){
  69. wp_enqueue_style("fontawesome-icon-cdn","https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.2/css/all.min.css",[],"6.1.2", "all");
  70. wp_enqueue_style("bootstrap-css-cdn","https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css",[],"5.3.2", "all");
  71.  
  72. wp_enqueue_script("fontawesome-icon-js-cdn","https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.2/js/all.min.js",[],"6.1.2", "all");
  73. wp_enqueue_script("bootstrap-js-cdn","https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js",[],"5.3.2", "all");
  74. }
  75. // echo $hook;exit;
  76. }
  77.  
  78. /**
  79.   * Create Plugin's Database
  80.   */
  81. function install_db(){
  82. global $wpdb;
  83. $table = $wpdb->prefix."mfp_tbl";
  84. $qry = $wpdb->prepare("SHOW TABLES LIKE %s", $wpdb->esc_like($table));
  85. if($wpdb->get_var($qry) == $table){
  86. /**
  87.   * Table Already Exists
  88.   */
  89.  
  90. }else{
  91. /**
  92.   * Table Doesn't Exists on the Database
  93.   * - Install Table
  94.   */
  95. $charset_collate = $wpdb->get_charset_collate();
  96.  
  97. $sql = "CREATE TABLE IF NOT EXISTS $table (
  98. `id` int(30) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  99. `meta_field` text DEFAULT NULL,
  100. `meta_value` text DEFAULT NULL,
  101. `created_at` datetime NOT NULL DEFAULT current_timestamp(),
  102. `updated_at` datetime DEFAULT NULL ON UPDATE current_timestamp()
  103. ) $charset_collate;";
  104. require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
  105. dbDelta($sql);
  106. }
  107. }
  108.  
  109. /**
  110.   * Create Plugin's Form Page
  111.   */
  112. function my_plugin_form_page(){
  113. global $wpdb;
  114. if(isset($_GET['mfcp_id'])){
  115. $data = $wpdb->get_row(
  116. $wpdb->prepare("SELECT * FROM `{$wpdb->prefix}mfp_tbl` where `id` = %d", [$_GET['mfcp_id']]),
  117. ARRAY_A
  118. );
  119. extract($data, EXTR_PREFIX_ALL, "mfcp");
  120. }
  121. ?>
  122. <style>
  123. .card{
  124. max-width: unset !important;
  125. }
  126. </style>
  127. <div class="container-fluid">
  128. <div class="card shadow col-lg-5 col-md-8 col-sm-12 col-12 mx-auto p-0">
  129. <div class="card-header rounded-0">
  130. <div class="card-title">Plugin's Sample Form <?= (!empty($mfcp_id ?? "")? " - Updating #{$mfcp_id}" : "") ?></div>
  131. </div>
  132. <div class="card-body">
  133. <div class="container-fluid">
  134. <form action="" id="sample-form" method="POST">
  135. <?php wp_nonce_field( "mfcp-sample-form" ); ?>
  136. <input type="hidden" name="action" value="save-mfcp-sample-form">
  137. <input type="hidden" name="id" value="<?= $mfcp_id ?? "" ?>">
  138. <div class="mb-3">
  139. <label for="meta_field" class="form-label">Meta Field <small class="text-danger">*</small></label>
  140. <input type="text" class="form-control rounded-0" id="meta_field" name="meta_field" value="<?= $_POST['meta_field'] ?? $mfcp_meta_field ?? "" ?>" required="required" autofocus>
  141. </div>
  142. <div class="mb-3">
  143. <label for="meta_value" class="form-label">Meta Value <small class="text-danger">*</small></label>
  144. <textarea name="meta_value" id="meta_value" rows="3" class="form-control rounded-0" requried=""><?= $_POST['meta_value'] ?? $mfcp_meta_value ?? "" ?></textarea>
  145. </div>
  146. <div class="mb-3">
  147. <button class="btn btn-primary rounded-0" type="submit">Save</button>
  148. </div>
  149. </form>
  150. </div>
  151. </div>
  152. </div>
  153. </div>
  154. <?
  155. }
  156.  
  157. /**
  158.   * Create Plugin's List Page
  159.   */
  160.  
  161. function my_plugin_list_page(){
  162. global $wpdb;
  163. $data = $wpdb->get_results(
  164. $wpdb->prepare("
  165. SELECT * FROM `{$wpdb->prefix}mfp_tbl` order by id ASC
  166. "),
  167. ARRAY_A
  168. );
  169. ?>
  170. <style>
  171. .card{
  172. max-width: unset !important;
  173. }
  174. </style>
  175. <div class="container-fluid">
  176. <div class="card shadow col-12 p-0">
  177. <div class="card-header rounded-0">
  178. <div class="card-title">Plugin's Database Data</div>
  179. </div>
  180. <div class="card-body">
  181. <div class="container-fluid">
  182. <div class="table-responsive">
  183. <table class="table table-striped table-hover table-bordered">
  184. <colgroup>
  185. <col width="20%">
  186. <col width="30%">
  187. <col width="30%">
  188. <col width="20%">
  189. </colgroup>
  190. <thead>
  191. <tr>
  192. <th class="text-center px-2 py-1 text-light bg-primary">Date Added</th>
  193. <th class="text-center px-2 py-1 text-light bg-primary">Meta Field</th>
  194. <th class="text-center px-2 py-1 text-light bg-primary">Meta Value</th>
  195. <th class="text-center px-2 py-1 text-light bg-primary">Action</th>
  196. </tr>
  197. </thead>
  198. <tbody>
  199. <?php if(!empty($data)): ?>
  200. <?php foreach($data as $row): ?>
  201. <tr>
  202. <td class="px-2 py-1"><?= date("M d, Y g:i A", strtotime($row['created_at'])) ?></td>
  203. <td class="px-2 py-1"><?= $row['meta_field'] ?></td>
  204. <td class="px-2 py-1"><?= $row['meta_value'] ?></td>
  205. <td class="px-2 py-1">
  206. <div class="input-group d-flex justify-content-center">
  207. <a href="<?= admin_url("admin.php?page=my-first-custom-plugin-page-form&mfcp_id={$row['id']}") ?>" class="btn btn-sm btn-outline-primary rounded-0" title="Edit"><i class="fa fa-edit"></i></a>
  208. <a href="<?= admin_url("admin.php?page=my-first-custom-plugin-page-form&action=delete_mfcp&mfcp_id={$row['id']}") ?>" class="btn btn-sm btn-outline-danger rounded-0 delete-mfcp" title="Delete"><i class="fa fa-trash"></i></a>
  209. </div>
  210. </td>
  211. </tr>
  212. <?php endforeach; ?>
  213. <?php else: ?>
  214. <tr>
  215. <th class="text-center px-2 py-1" colspan="4">No Data Found!</th>
  216. </tr>
  217. <?php endif; ?>
  218. </tbody>
  219. </table>
  220. </div>
  221. </div>
  222. </div>
  223. </div>
  224. </div>
  225. <script>
  226. var delete_mfcp = document.querySelectorAll('.delete-mfcp')
  227. delete_mfcp.forEach(el=>{
  228. el.addEventListener('click', function(e){
  229. if(confirm(`Are you sure to delete this data?`) === false)
  230. e.preventDefault();
  231. })
  232. })
  233. </script>
  234. <?
  235. }
  236.  
  237.  
  238. /**
  239.   * Save Form Data
  240.   */
  241. function save_form(){
  242. global $wpdb;
  243. extract($_POST, EXTR_PREFIX_ALL, "mfcp");
  244. $resp=[];
  245. if(
  246. isset($_POST['action']) &&
  247. $_POST['action'] == 'save-mfcp-sample-form'
  248. ){
  249. if(!wp_verify_nonce( $mfcp__wpnonce, 'mfcp-sample-form')){
  250. $resp = ["type" => "danger", "message" => "Security Check Failed!"];
  251. }else{
  252. $mfcp_meta_field = sanitize_text_field( $mfcp_meta_field );
  253. $mfcp_meta_value = sanitize_textarea_field( $mfcp_meta_value );
  254. if(empty($mfcp_id)){
  255. $save = $wpdb->insert(
  256. "{$wpdb->prefix}mfp_tbl",
  257. [
  258. "meta_field" => $mfcp_meta_field,
  259. "meta_value" => $mfcp_meta_value
  260. ],
  261. [
  262. '%s',
  263. '%s'
  264. ]
  265. );
  266. }else{
  267. $save = $wpdb->update("{$wpdb->prefix}mfp_tbl",
  268. [
  269. "meta_field" => $mfcp_meta_field,
  270. "meta_value" => $mfcp_meta_value
  271. ],
  272. [
  273. "id" => $mfcp_id,
  274. ],
  275. [
  276. '%s',
  277. '%s'
  278. ],
  279. [
  280. '%d',
  281. ]
  282. );
  283. }
  284. if($wpdb->last_error){
  285. $resp = ["type" => "danger", "message" => $wpdb->last_error];
  286. }else{
  287. $resp = ["type" => "success", "message" => "Data has been saved successfully!"];
  288. }
  289. }
  290.  
  291. $this->notices[] = $resp;
  292. update_option("mfcp_notices", $this->notices);
  293. echo "<script>location.replace(`".admin_url("admin.php?page=my-first-custom-plugin-page")."`)</script>";
  294. }
  295.  
  296. }
  297.  
  298. /**
  299.   * Delete Data
  300.   */
  301. function delete_mfcp(){
  302. global $wpdb;
  303. if(
  304. isset($_GET['page']) &&
  305. $_GET['page'] == 'my-first-custom-plugin-page-form' &&
  306. isset($_GET['action']) &&
  307. $_GET['action'] == 'delete_mfcp'&&
  308. isset($_GET['mfcp_id'])
  309. ){
  310. $delete = $wpdb->delete("{$wpdb->prefix}mfp_tbl", [ "id" => $_GET['mfcp_id']], ['%d']);
  311. if($wpdb->last_error){
  312. $resp = ["type" => "danger", "message" => $wpdb->last_error];
  313. }else{
  314. $resp = ["type" => "success", "message" => "Data has been deleted successfully!"];
  315. }
  316. $this->notices[] = $resp;
  317. update_option("mfcp_notices", $this->notices);
  318. echo "<script>location.replace(document.referrer)</script>";
  319. }
  320. }
  321.  
  322. }
  323.  
  324. if(class_exists("MyFirstPlugin")){
  325. $MyFirstPlugin = new MyFirstPlugin();
  326. register_activation_hook(__FILE__ , array ( $MyFirstPlugin , 'activate' ) );
  327. }
  328.  

Snapshots

Explore the snapshots below, capturing the results of the Custom WordPress Plugin Script provided above:

Plugin's Custom Form Page

Custom WordPress Plugin

Plugin's Custom List Page

Custom WordPress Plugin

And there you have it! I trust this Custom WordPress Plugin Guide proves helpful for your needs and becomes a valuable resource for your current and future WordPress projects.

Consider exploring the following topics as well:

Explore more into this website for an array of Tutorials, Free Source Codes, and Articles spanning various programming languages.

Happy Coding =)

Add new comment