Starting from scratch: Preparation work and environment setup
Before starting to write your first plugin, it is essential to carry out thorough preparatory work. This includes understanding the basic structure of WordPress plugins, setting up a secure local development environment, and grasping the core philosophy of plugin development.
A typical WordPress plugin is a file that is located in the `wp-content/plugins` directory. /wp-content/plugins/ The folder is located within the directory and contains an independent sub-folder. At its core lies a PHP main file with a specific file header. This main file not only contains the metadata of the plugin but also serves as the entry point for all the plugin’s functionality. There are two ways to initiate the plugin: one is by using a combination of procedural functions, and the other is by employing an object-oriented class structure. The latter approach is more favored in modern plugin development due to its better encapsulation and code organization capabilities.
A local development environment is crucial for efficient development. It is recommended to use tools such as XAMPP, Local by Flywheel, or Docker to set up a runtime environment that integrates Apache/Nginx, MySQL, and PHP. This allows you to freely test and debug your code without affecting the live website.
Recommended Reading Starting from scratch: A complete guide to WordPress plugin development and sharing of best practices。
Building a plugin skeleton: Structure and core files
This section will provide a detailed explanation of how to create a basic plugin skeleton from scratch, one that complies with WordPress standards. A good plugin skeleton serves as the foundation for a stable, secure, and maintainable plugin.
Create the necessary main plugin files.
The life of a plugin begins with a main file. The name of this file is usually the same as the name of your plugin folder. For example, if your plugin folder is named… my-custom-pluginSo, the main file can be named… my-custom-plugin.phpAt the very top of this file, you must use a comment block that follows a specific format to declare the plugin information. This is crucial for WordPress to recognize the plugin and display it in the administration panel.
The following is an example of the most basic plugin file header:
<?php
/**
* Plugin Name: 我的自定义功能模块
* Plugin URI: https://example.com/my-custom-plugin
* Description: 这是一个用于演示如何从零开始开发WordPress插件的示例模块。
* Version: 1.0.0
* Author: 你的名字
* Author URI: https://example.com
* License: GPL v2 or later
* Text Domain: my-custom-plugin
* Domain Path: /languages
*/ The “Plugin Name” field is mandatory in this comment; all other fields are optional but are recommended to be filled in. The “Text Domain” and “Domain Path” are used for plugin internationalization (i18n), to prepare the plugin for translation into multiple languages.
Files and directories for organizing plugins
As the functionality of plugins increases, it is not advisable to accumulate all the code in the main file. A well-organized plugin structure typically consists of multiple subdirectories and files. A common structure is as follows:
Recommended Reading Complete Guide to WordPress Plugin Development: A Practical Tutorial from Zero to Live Deployment。
my-custom-plugin/
├── my-custom-plugin.php # 主插件文件(入口)
├── includes/ # 核心功能类文件目录
│ ├── class-core.php # 核心逻辑类
│ └── class-admin.php # 后台管理逻辑类
├── admin/ # 后台相关文件
│ ├── css/ # 后台样式表
│ ├── js/ # 后台JavaScript
│ └── partials/ # 后台模板片段
├── public/ # 前端相关文件
│ ├── css/
│ ├── js/
│ └── partials/
├── assets/ # 公共资源(如图标、图片)
├── languages/ # 国际化语言包(.po, .mo文件)
└── uninstall.php # 插件卸载清理脚本 In my-custom-plugin.php In the main file, we usually do not write the business logic directly. Instead, it serves as a “launcher” that contains the necessary files and initializes the core classes.
// 防止直接文件访问
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// 定义插件路径常量,方便在其他文件中引用
define( 'MY_PLUGIN_PATH', plugin_dir_path( __FILE__ ) );
define( 'MY_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
// 包含核心类文件
require_once MY_PLUGIN_PATH . 'includes/class-core.php';
require_once MY_PLUGIN_PATH . 'includes/class-admin.php';
// 初始化插件
function my_custom_plugin_init() {
$plugin_core = new My_Plugin_Core();
$plugin_admin = new My_Plugin_Admin();
// 执行初始化操作...
}
add_action( 'plugins_loaded', 'my_custom_plugin_init' ); Link to WordPress Core: Action and Filter Hooks
The strength of WordPress’s plugin system lies in the numerous event points it offers, which allow developers to modify or enhance the behavior of WordPress without having to alter the core code. These mechanisms are known as “Hooks,” and they are primarily divided into two categories: Actions and Filters. They serve as the bridge through which plugins can interact with the core of WordPress.
Understanding and utilizing action hooks
Action hooks allow you to execute custom code when a specific event occurs. For example, when an article is published, when a user logs in, or when a certain page in the administration backend is loaded. add_action() Functions can be “mounted” to these hooks.
Assuming you want to automatically add a copyright statement at the bottom of an article, you can use the following approach: the_content This “action hook” (which is actually a filter, but its usage is similar to that of an action; classic examples often use this terminology for clarity) is a more appropriate example of an action: sending a welcome email to a user after their successful registration.
// 这是一个动作钩子的使用示例
function my_plugin_send_welcome_email( $user_id ) {
$user = get_userdata( $user_id );
$to = $user->user_email;
$subject = '欢迎加入我们的网站!';
$message = '亲爱的' . $user->display_name . ',感谢您注册!';
wp_mail( $to, $subject, $message );
}
// 将函数挂载到`user_register`这个动作钩子上
add_action( 'user_register', 'my_plugin_send_welcome_email' ); Master the usage of filters.
Filter hooks allow you to modify data. When WordPress executes a certain process, it passes specific data through a series of filters. Your plugin can intercept this data, modify it, and then send it back. add_filter() It can be implemented using a function.
The most common example is modifying the output of article content. For instance, adding a featured image at the beginning of each article as a guide image automatically:
Recommended Reading A Beginner's Guide to WordPress Plugin Development: Building Your First Custom Functional Module from Scratch。
function my_plugin_prepend_featured_image_to_content( $content ) {
// 只在主循环的单篇文章页面生效
if ( is_single() && in_the_loop() && is_main_query() ) {
$featured_image = get_the_post_thumbnail( null, 'medium', array( 'class' => 'alignleft' ) );
if ( $featured_image ) {
$content = $featured_image . $content;
}
}
return $content;
}
// 将函数挂载到`the_content`这个过滤器钩子上
add_filter( 'the_content', 'my_plugin_prepend_featured_image_to_content' ); Implement plugin functionality: integrate the management backend with the front-end.
A complete plugin usually needs to interact with users, which means you will need to create a management interface to handle settings, and you may also need to add new features or content to the front-end pages.
Create a background settings page.
Providing a clear backend settings page for a plugin is key to a good user experience. WordPress offers a variety of APIs for adding top-level menus or sub-menus. This process generally involves two steps: registering the menu items and defining the callback functions corresponding to those menu items, which are used to generate the content for the respective pages.
Here is an example of adding a sub-menu item under the “Settings” main menu:
class My_Plugin_Admin {
public function __construct() {
add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
add_action( 'admin_init', array( $this, 'register_settings' ) );
}
public function add_admin_menu() {
add_options_page(
'我的插件设置', // 页面标题
'我的插件', // 菜单标题
'manage_options', // 所需权限
'my-plugin-settings', // 菜单slug
array( $this, 'render_settings_page' ) // 回调函数
);
}
public function register_settings() {
// 注册一个设置选项
register_setting( 'my_plugin_settings_group', 'my_plugin_option_name' );
// 添加一个设置区块
add_settings_section( 'my_plugin_main_section', '主要设置', null, 'my-plugin-settings' );
// 向区块中添加一个字段
add_settings_field(
'my_plugin_text_field',
'示例文本字段',
array( $this, 'render_text_field' ),
'my-plugin-settings',
'my_plugin_main_section'
);
}
public function render_settings_page() {
?>
<div class="wrap">
<h1>My plugin settings</h1>
<form action="/en/options.php/" method="post" data-trp-original-action="options.php">
<?php
settings_fields( 'my_plugin_settings_group' );
do_settings_sections( 'my-plugin-settings' );
submit_button();
?>
<input type="hidden" name="trp-form-language" value="en"/></form>
</div>
<?php
}
public function render_text_field() {
$option = get_option( 'my_plugin_option_name' );
echo '<input type="text" name="my_plugin_option_name" value="' . esc_attr( $option ) . '" />';
}
} Output the functionality to the website frontend.
There are various ways to implement front-end functionality, such as using Shortcodes, Widgets, or modifying existing outputs through APIs. Shortcodes are a simple and effective method that allows users to invoke plugin features by entering a simple tag in the article or page editor.
To create short codes, you need to use… add_shortcode() Function:
// 定义一个简单的短代码,用于显示一个问候语
function my_plugin_greeting_shortcode( $atts ) {
// 解析短代码属性
$atts = shortcode_atts( array(
'name' => '访客',
), $atts, 'greeting' );
// 返回输出内容
return '<p class="my-plugin-greeting">Hello, '. esc_html($atts['name'])'. ‘!</p>'输出:
'n';
};
add_shortcode( 'greeting', 'my_plugin_greeting_shortcode' ); Users simply need to enter the content in the article editor. [greeting name="张三"]The front-end will then display the paragraph “Hello, Zhang San!”
summarize
WordPress plugin development involves integrating custom ideas into the vast WordPress ecosystem, with the core technology lying in the understanding and utilization of action and filter hooks. Successful development begins with a clear, modular file structure, which lays a solid foundation for future maintenance and expansion. By creating administrative settings pages, plugins can be made user-friendly and easy to configure; additional features can be seamlessly integrated into the website’s front-end using shortcodes, widgets, or custom APIs. Security, internationalization compatibility, and code quality are key considerations throughout the development process. Adhering to WordPress coding standards and best practices not only ensures the stable operation of the plugin but also helps it integrate better into the ecosystem, providing benefits to a wider range of users.
FAQ Frequently Asked Questions
How deep of PHP knowledge is required to develop WordPress plugins?
Developing a simple plugin requires only a basic understanding of PHP, including variables, functions, conditional statements, and loops. However, to create a commercial-grade plugin with complex functionality, security, and efficiency, a more in-depth understanding of various concepts is necessary, such as object-oriented programming (OOP) and the WordPress core API (including classes for database operations). wpdbHTTP request APIs, best practices for security (such as data validation, encoding, protection against SQL injection and CSRF attacks), as well as basic knowledge of JavaScript and CSS are required for front-end interactions.
It is recommended to start by modifying existing small plugins or building simple functions, and then gradually gain a deeper understanding of the technology.
How can I ensure that the plugins I develop are secure?
Ensuring the security of plugins is a multi-step process. First and foremost, it is essential to rigorously validate and clean all user-generated or external inputs, such as form data, URL parameters, and cookies. Make use of functions provided by WordPress for this purpose. sanitize_text_field(), esc_html(), esc_url() and wp_kses() etc.
Secondly, when performing database operations, it is essential to use… $wpdb The methods provided by the class (such as) prepare() Use statements to prevent SQL injection. Whenever any data is sent to the front end, it must be properly escaped. For operations that involve permissions, always use capability check functions. current_user_can()In addition, non-CE (nonce) values are used to verify the intent of requests, in order to prevent Cross-Site Request Forgery (CSRF).
After the plugin has been developed, how should it be distributed and updated?
There are mainly two ways to distribute plugins: through the official WordPress plugin directory or by providing a direct download link on one’s own website. If you want the plugin to be installed with one click in the user’s admin panel and to receive automatic updates, it must be submitted to the official directory. This requires that the plugin code comply with the GPLv2 (or a later version) license and pass strict security and code quality reviews.
After you submit your changes, WordPress will retrieve your plugin code from its official Subversion (SVN) repository. You need to update the “Version” number in the main file header comments within the SVN repository and then submit a new tag that includes the version number to trigger an update notification.
For commercial plugins, an update service is usually set up on their own servers. This involves writing an update checker that utilizes hooks (such as…) pre_set_site_transient_update_pluginsIntegrate with WordPress’s update system to compare version information on remote servers and provide update packages.
How can class names and function names in plugins be avoided from conflicting with those in other plugins?
Avoiding naming conflicts is a fundamental requirement in professional plugin development. The best practice is to use prefixes. For all classes, functions, constants, and even global variables (which should be used as rarely as possible) in your plugin, use a unique and descriptive prefix. This prefix can typically be an abbreviation or the full name of your company or the name of your plugin.
For example, if your plugin is called “Awesome Gallery,” the prefix for your functions could be… ag_ Or awesome_gallery_The class name can be… AG_Core Or Awesome_Gallery_Admin。
A better approach is to encapsulate the code within a class, so that most of the functions will exist as class methods. $this->method()This approach prevents the contamination of the global namespace. For functions that must be defined in the global space, it is essential to use long and unique prefixes.
What's next, what's next?
Extended reading and practical knowledge
The following are related to the topic of this article and are suitable for further in-depth reading. Prioritize starting with the article that is closest to your current problem, and gradually expanding to surrounding topics usually works better.
- WordPress Plugin Development Guide: Creating Your First Custom Plugin from Scratch
- Becoming a WordPress Plugin Developer: A Complete Guide from Scratch
- WordPress Plugin Development Complete Guide: From Beginner to Expert – Creating Professional Extensions
- WordPress Plugin Development: From Beginner to Expert – Building Your First Custom Plugin
- From Zero to One: A Comprehensive Guide to Developing Your First WordPress Plugin Step by Step