插件开发
本章节深入讲解 WordPress 插件开发,从基础结构到高级技巧,帮助你创建专业级 WordPress 插件。
📚 课程大纲
mermaid
graph LR
A[插件开发] --> B[基础结构]
A --> C[钩子系统]
A --> D[数据库操作]
A --> E[REST API]
A --> F[安全防护]
B --> G[插件头部]
B --> H[主文件]
C --> I[动作钩子]
C --> J[过滤钩子]1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
🏗️ 插件基本结构
wp-content/plugins/
└── my-plugin/
├── my-plugin.php # 主插件文件(必需)
├── uninstall.php # 卸载时执行
├── readme.txt # WordPress.org 说明
├── index.php # 安全文件
├── admin/ # 管理界面
│ ├── index.php
│ ├── css/
│ ├── js/
│ └── partials/
├── public/ # 公共界面
│ ├── index.php
│ ├── css/
│ ├── js/
│ └── partials/
├── includes/ # 核心类
│ ├── class-my-plugin.php
│ └── index.php
├── languages/ # 国际化
│ └── my-plugin.pot
└── assets/ # 资源文件
└── screenshot-1.png1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
📝 插件主文件
php
<?php
/**
* Plugin Name: 我的插件
* Plugin URI: https://example.com/my-plugin
* Description: 这是插件的简短描述
* Version: 1.0.0
* Requires at least: 6.0
* Requires PHP: 7.4
* Author: 开发者名称
* 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
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
// 定义插件常量
define('MY_PLUGIN_VERSION', '1.0.0');
define('MY_PLUGIN_PATH', plugin_dir_path(__FILE__));
define('MY_PLUGIN_URL', plugin_dir_url(__FILE__));
// 加载翻译文件
add_action('init', 'my_plugin_load_textdomain');
function my_plugin_load_textdomain() {
load_plugin_textdomain(
'my-plugin',
false,
dirname(plugin_basename(__FILE__)) . '/languages'
);
}
// 包含核心类
require_once MY_PLUGIN_PATH . 'includes/class-my-plugin.php';
// 初始化插件
function my_plugin_init() {
$plugin = new My_Plugin();
$plugin->run();
}
my_plugin_init();
// 注册激活钩子
register_activation_hook(__FILE__, 'my_plugin_activate');
function my_plugin_activate() {
// 激活时执行的代码
// 创建数据库表、设置选项等
flush_rewrite_rules();
}
// 注册停用钩子
register_deactivation_hook(__FILE__, 'my_plugin_deactivate');
function my_plugin_deactivate() {
// 停用时执行的代码
flush_rewrite_rules();
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
🎯 主插件类
php
<?php
/**
* 主插件类
*/
class My_Plugin {
/**
* 插件实例
*/
private static $instance = null;
/**
* 获取实例
*/
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* 构造函数
*/
private function __construct() {
$this->load_dependencies();
$this->define_admin_hooks();
$this->define_public_hooks();
}
/**
* 加载依赖文件
*/
private function load_dependencies() {
require_once MY_PLUGIN_PATH . 'admin/class-admin.php';
require_once MY_PLUGIN_PATH . 'public/class-public.php';
}
/**
* 定义管理界面钩子
*/
private function define_admin_hooks() {
$admin = new My_Plugin_Admin();
add_action('admin_menu', array($admin, 'add_admin_menu'));
add_action('admin_enqueue_scripts', array($admin, 'enqueue_styles'));
add_action('admin_enqueue_scripts', array($admin, 'enqueue_scripts'));
}
/**
* 定义公共界面钩子
*/
private function define_public_hooks() {
$public = new My_Plugin_Public();
add_action('wp_enqueue_scripts', array($public, 'enqueue_styles'));
add_action('wp_enqueue_scripts', array($public, 'enqueue_scripts'));
add_shortcode('my_shortcode', array($public, 'render_shortcode'));
}
/**
* 运行插件
*/
public function run() {
// 插件运行时执行的代码
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
🪝 钩子系统
动作钩子 (Actions)
php
<?php
// 添加自定义动作
add_action('init', 'my_custom_init');
function my_custom_init() {
// 初始化代码
}
// 带优先级的动作
add_action('the_content', 'my_custom_content', 20);
function my_custom_content($content) {
// 修改内容
return $content;
}
// 带参数的动作
add_action('save_post', 'my_save_post_callback', 10, 3);
function my_save_post_callback($post_ID, $post, $update) {
// 保存文章时的回调
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
过滤钩子 (Filters)
php
<?php
// 添加自定义过滤
add_filter('the_content', 'my_custom_content_filter');
function my_custom_content_filter($content) {
// 在内容前添加内容
$before = '<div class="custom-box">';
$after = '</div>';
return $before . $content . $after;
}
// 修改标题
add_filter('the_title', 'my_custom_title_filter', 10, 2);
function my_custom_title_filter($title, $post_id) {
if (is_singular('post')) {
$title = '📌 ' . $title;
}
return $title;
}
// 修改摘要长度
add_filter('excerpt_length', function($length) {
return 50;
});1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
📝 短代码
php
<?php
/**
* 注册短代码
*/
// 基本短代码
add_shortcode('my_button', 'my_button_shortcode');
function my_button_shortcode($atts, $content = null) {
$atts = shortcode_atts(array(
'url' => '#',
'class' => 'btn',
'target' => '_self',
), $atts, 'my_button');
return sprintf(
'<a href="%s" class="%s" target="%s">%s</a>',
esc_url($atts['url']),
esc_attr($atts['class']),
esc_attr($atts['target']),
do_shortcode($content)
);
}
// 内容型短代码
add_shortcode('my_box', 'my_box_shortcode');
function my_box_shortcode($atts, $content = null) {
$atts = shortcode_atts(array(
'title' => '',
'type' => 'info',
), $atts, 'my_box');
$output = '<div class="my-box ' . esc_attr($atts['type']) . '">';
if (!empty($atts['title'])) {
$output .= '<h3>' . esc_html($atts['title']) . '</h3>';
}
$output .= '<div class="my-box-content">';
$output .= do_shortcode($content);
$output .= '</div></div>';
return $output;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
🗄️ 数据库操作
使用 $wpdb
php
<?php
global $wpdb;
// 插入数据
$wpdb->insert(
$wpdb->prefix . 'my_table',
array(
'name' => 'John',
'email' => 'john@example.com',
'date' => current_time('mysql'),
),
array('%s', '%s', '%s')
);
// 获取插入的 ID
$insert_id = $wpdb->insert_id;
// 查询数据
$results = $wpdb->get_results(
"SELECT * FROM {$wpdb->prefix}my_table WHERE status = 'active'",
ARRAY_A
);
// 获取单个值
$count = $wpdb->get_var(
"SELECT COUNT(*) FROM {$wpdb->prefix}my_table"
);
// 更新数据
$wpdb->update(
$wpdb->prefix . 'my_table',
array('name' => 'Jane'),
array('id' => 1),
array('%s'),
array('%d')
);
// 删除数据
$wpdb->delete(
$wpdb->prefix . 'my_table',
array('id' => 1),
array('%d')
);
// 准备语句
$name = $wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}my_table WHERE name = %s AND status = %s",
$name,
$status
);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
创建数据库表
php
<?php
function my_plugin_create_table() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$table_name = $wpdb->prefix . 'my_table';
$sql = "CREATE TABLE IF NOT EXISTS $table_name (
id bigint(20) NOT NULL AUTO_INCREMENT,
name varchar(100) NOT NULL,
email varchar(100) NOT NULL,
created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY name (name)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
🔒 安全防护
php
<?php
// 数据验证
function my_plugin_sanitize_data($input) {
$sanitized = array();
// 文本
$sanitized['text'] = sanitize_text_field($input['text']);
// 邮箱
$sanitized['email'] = sanitize_email($input['email']);
// URL
$sanitized['url'] = esc_url_raw($input['url']);
// HTML 内容
$sanitized['html'] = wp_kses_post($input['html']);
// 整数
$sanitized['number'] = absint($input['number']);
// 数组
if (is_array($input['items'])) {
$sanitized['items'] = array_map('sanitize_text_field', $input['items']);
}
return $sanitized;
}
// 非ce权限检查
function my_plugin_admin_init() {
if (!current_user_can('manage_options')) {
wp_die(__('您没有足够的权限访问此页面。', 'my-plugin'));
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
🧹 卸载处理
php
<?php
/**
* uninstall.php - 插件卸载时执行
*/
// 如果不是从 WP 调用的,则退出
if (!defined('WP_UNINSTALL_PLUGIN')) {
exit;
}
// 删除选项
delete_option('my_plugin_options');
delete_option('my_plugin_version');
// 删除自定义表
global $wpdb;
$wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}my_table");
// 删除文章元数据
delete_post_meta_by_key('_my_plugin_meta');
delete_metadata('user', 0, 'my_plugin_user_meta', '', true);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
🎯 最佳实践
最佳实践
- 使用类封装 - 代码组织更清晰
- 遵循命名约定 - 前缀避免冲突
- 数据验证 - 所有输入必须验证
- 权限检查 - 关键操作需权限验证
- 国际化 - 支持多语言
- 安全清理 - 卸载时清理数据
- 使用 $wpdb 准备语句 - 防止 SQL 注入
