What is a Plugin?
A WordPress plugin is a standalone PHP file (or set of files) that enhances or changes WordPress functionality without altering the WordPress core. It lets you "hook into" WordPress to add, modify, or extend features for your entire site or for a single use case.
Basic Plugin Structure
Always create your plugin inside its own directory under wp-content/plugins/your-plugin-name/. The main PHP file should be named after your plugin, and it's the only file that requires the plugin header comment.
/your-plugin-name
your-plugin-name.php (Main plugin file with header)
uninstall.php (Optional for clean-up on uninstall)
/admin (Admin-only code)
/public (Frontend code)
/includes (Reusable code across front-end and back-end)
/languages (Localization files)
/js, /css, /images (Assets)
The Plugin Header Comment
The header comment in your main PHP file is essential. It signals to WordPress that the file is a plugin and provides key metadata that appears in the admin dashboard. While only Plugin Name is required, filling out the others increases clarity, compatibility, and user trust.
<?php
/*
* Plugin Name: My Basics Plugin
* Plugin URI: https://example.com/my-plugin/
* Description: A plugin to handle the basics (max 140 chars)
* Version: 1.0.0
* Requires at least:5.2
* Requires PHP: 7.2
* Author: Your Name
* Author URI: https://example.com/
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: my-plugin
* Domain Path: /languages
* Network: true
* Update URI: https://example.com/my-plugin/
* Requires Plugins: my-dependent-plugin
*/
Core Concepts: Hooks & Lifecycle
Hooks are the backbone of plugin flexibility in WordPress. They let your code interact with core WordPress events, themes, and other plugins.
- Actions
- Do something at a certain point (e.g., after saving a post).
Example:add_action('init', 'my_init_function'); - Filters
- Change or filter data before it’s rendered or saved.
Example:add_filter('the_title', 'my_filter_function'); - Custom Hooks
- Allow others to extend your plugin via
do_action()andapply_filters().
Essential Plugin Lifecycle Hooks
You should always handle plugin installation, deactivation, and uninstallation cleanly to provide a good user experience.
register_activation_hook(__FILE__, 'my_activate_function');- Runs setup code when the plugin is activated, like creating database tables or setting default options.
register_deactivation_hook(__FILE__, 'my_deactivate_function');- Runs when the plugin is deactivated. Good for cleaning temporary data or caches.
register_uninstall_hook(__FILE__, 'my_uninstall_function');- Runs when a user deletes the plugin. This is where you should permanently remove all plugin-created data and settings.
Best Practices & Architecture
Following established best practices ensures your plugin is secure, maintainable, and compatible with the broader WordPress ecosystem.
- Avoid Naming Collisions
- Prefix all your functions, classes, constants, and options with a unique, plugin-specific prefix (e.g.,
wpbp_). - Use Classes or Namespaces
- An object-oriented structure simplifies maintenance, improves readability, and avoids conflicts.
- File Organization
- Keep code modular. Separate admin, public, and shared logic into different directories and files.
- Check for Existing Implementations
-
PHP provides a number of functions to verify existence of variables, functions, classes and constants. All of these will return true if the entity exists.
- Variables:
isset()(includes arrays, objects, etc.) - Functions:
function_exists() - Classes:
class_exists() - Constants:
defined()
- Variables:
- Security: Prevent Direct File Access
- Add
if ( ! defined( 'ABSPATH' ) ) exit;to the top of every PHP file to prevent it from being accessed directly via its URL. - Conditional Loading
- Load admin-specific code only when needed by using the
is_admin()check. - Use WordPress APIs
- Leverage built-in APIs (Options API, Transients API, HTTP API) to interact safely and efficiently with data and WordPress core.
- Translation Ready
- Use the
Text DomainandDomain Pathheaders in conjunction with localization functions to make your plugin translatable.
Determining Plugin and Content Directories
Never hardcode paths or URLs like /wp-content/plugins/my-plugin/. This will break if a user renames their wp-content directory or installs WordPress in a subdirectory. Instead, use WordPress helper functions with PHP's __FILE__ magic constant. The __FILE__ constant always returns the full server path to the file it's used in, providing a reliable "anchor" for WordPress to locate your plugin's files and folders.
plugin_dir_path( __FILE__ )
Purpose: To get the absolute server file system path to your plugin's directory. This is used for including or requiring other PHP files, like require_once( plugin_dir_path( __FILE__ ) . 'includes/functions.php' );. This path is for internal use and not accessible in a browser.
Sample Output: /var/www/html/wp-content/plugins/your-plugin-name/ (Note the trailing slash)
plugins_url( 'images/logo.png', __FILE__ )
Purpose: To get the full, web-accessible URL to a specific file inside your plugin directory. This is essential for linking to assets like CSS, JavaScript, or image files from the front-end.
Sample Output: https://yourdomain.com/wp-content/plugins/your-plugin-name/images/logo.png
plugin_dir_url( __FILE__ )
Purpose: To get the full, web-accessible URL to your plugin's main directory. This is useful for more complex situations, like passing the base URL to a JavaScript file.
Sample Output: https://yourdomain.com/wp-content/plugins/your-plugin-name/ (Note the trailing slash)
Example: A Complete, Simple Plugin
This basic plugin incorporates all the essential elements described above. It adds a customizable greeting message before the content of every single post and enqueues a stylesheet.
Licensing & Sharing
Before sharing, always provide a license compatible with WordPress (GPLv2 or later is recommended) so others know how they can legally use, modify, and distribute your code.
<?php
/*
* Plugin Name: Custom Greetings
* Plugin URI: https://yourdomain.com/custom-greetings
* Description: Displays a customizable greeting message on posts.
* Version: 1.0
* Requires at least:6.0
* Requires PHP: 7.2
* Author: Jane Doe
* Author URI: https://yourdomain.com/
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: custom-greetings
* Domain Path: /languages
*/
// Security: Prevent direct file access
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// Activation: Set default option
function cg_activate() {
add_option('cg_greeting', 'Hello, welcome to my website!');
}
register_activation_hook( __FILE__, 'cg_activate' );
// Deactivation: Clean up temporary data (optional)
function cg_deactivate() {
// Nothing to do here in this example.
}
register_deactivation_hook( __FILE__, 'cg_deactivate' );
// Uninstall: Delete the option from the database
function cg_uninstall() {
delete_option('cg_greeting');
}
register_uninstall_hook( __FILE__, 'cg_uninstall' );
// Filter 'the_content' to add our greeting
function cg_display_greeting($content) {
if ( is_singular('post') ) {
$greeting = get_option('cg_greeting', 'Hello!');
$greeting_html = "<p class='cg-greeting'>" . esc_html($greeting) . "</p>";
return $greeting_html . $content;
}
return $content;
}
add_filter('the_content', 'cg_display_greeting');
// Enqueue a simple stylesheet for the front-end
function cg_enqueue_styles() {
wp_enqueue_style( 'cg-style', plugins_url('css/cg-style.css', __FILE__) );
}
add_action( 'wp_enqueue_scripts', 'cg_enqueue_styles' );