Wordpress插件开发知识

功能扩展实现方式

  • 自定义独立插件
  • 主题functions.php中定义

插件加载

插件加载顺序先于主题

1
2
3
4
5
wp-blog-header.php
    wp-load.php
        wp-config.php(加载wp设置)
        wp-settings.php(加载已激活的插件)
    template-loader.php(加载模板)

插件定义

插件目录及入口文件

插件目录和入口文件名称相同

  • 标准插件信息(在入口文件中添加以下内容)
1
2
3
4
5
6
7
8
/* 
Plugin Name: 插件名称
Plugin URI: http://www.1024plus.com/my-plugin
Description: 插件的描述
Version: 1.0
Author: Hollis
Author URI: http://www.1024plus.com
*/

插件的启用函数

  • register_activation_hook
1
2
3
4
5
function myplugin_activate () {
	// Activation code here...    
}

register_activation_hook(__FILE__, 'myplugin_activate');

插件的停用函数

  • register_deactivation_hook
1
2
3
4
5
function myplugin_deactivate () {
    // Deativation code here...
}

register_deactivation_hook(__FILE__, 'myplugin_deactivate');

插件的卸载脚本

  • 插件根目录添加uninstall.php
1
2
3
4
5
6
7
8
<?php

// Exit if accessed directly.
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
    exit;
}

// do something

认识钩子

动作钩子

  • do_action
 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
/**
 * Calls the callback functions that have been added to an action hook.
 *
 * This function invokes all functions attached to action hook `$hook_name`.
 * It is possible to create new action hooks by simply calling this function,
 * specifying the name of the new hook using the `$hook_name` parameter.
 *
 * You can pass extra arguments to the hooks, much like you can with `apply_filters()`.
 *
 * Example usage:
 *
 *     // The action callback function.
 *     function example_callback( $arg1, $arg2 ) {
 *         // (maybe) do something with the args.
 *     }
 *     add_action( 'example_action', 'example_callback', 10, 2 );
 *
 *     /*
 *      * Trigger the actions by calling the 'example_callback()' function
 *      * that's hooked onto `example_action` above.
 *      *
 *      * - 'example_action' is the action hook.
 *      * - $arg1 and $arg2 are the additional arguments passed to the callback.
 *     do_action( 'example_action', $arg1, $arg2 );
 *
 * @since 1.2.0
 * @since 5.3.0 Formalized the existing and already documented `...$arg` parameter
 *              by adding it to the function signature.
 *
 * @global WP_Hook[] $wp_filter         Stores all of the filters and actions.
 * @global int[]     $wp_actions        Stores the number of times each action was triggered.
 * @global string[]  $wp_current_filter Stores the list of current filters with the current one last.
 *
 * @param string $hook_name The name of the action to be executed.
 * @param mixed  ...$arg    Optional. Additional arguments which are passed on to the
 *                          functions hooked to the action. Default empty.
 */
function do_action( $hook_name, ...$arg )
  • add_action
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/**
 * @param string   $hook_name       The name of the action to add the callback to.
 * @param callable $callback        The callback to be run when the action is called.
 * @param int      $priority        Optional. Used to specify the order in which the functions
 *                                  associated with a particular action are executed.
 *                                  Lower numbers correspond with earlier execution,
 *                                  and functions with the same priority are executed
 *                                  in the order in which they were added to the action. Default 10.
 * @param int      $accepted_args   Optional. The number of arguments the function accepts. Default 1.
 * @return true Always returns true.
 */
function add_action( $hook_name, $callback, $priority = 10, $accepted_args = 1 ) {
	return add_filter( $hook_name, $callback, $priority, $accepted_args );
}

常用动作钩子实用案例

加载过程

  • init
1
2
3
4
//为页面添加摘要功能
add_action('init', function () {
    add_post_type_support('page', ['excerpt']);
});
  • wp_head
1
2
3
4
5
6
7
add_action('wp_head', function () {
    if (is_home()) {
        ?>
        <meta name="description" content="<?php bloginfo('description') ?>" />
        <?
    }
});
  • wp_enqueue_scripts
1
2
3
4
add_action('wp_enqueue_scripts', function () {
    wp_enqueue_script('main-js', plugins_url('js/main.js', __FILE__));
    wp_enquque_style('main-css', plugins_url('css/main.css', __FILE__));
});

文章

  • save_post
  • wp_trash_post
  • delete_post

评论

  • wp_insert_comment
1
2
3
4
5
6
7
8
9
function comment_inserted($comment_id, $comment_object) {
    $comments_count = wp_count_comments($comment_object->comment_post_ID);
    $comment_arr = [];
    $comment_arr['comment_ID'] = $comment_id;
    $comment_arr['comment_content'] = '第{$comments_count->total_comments}个评论:' . $comment_object->comment_content;
    wp_update_comment($comment_arr);
}

add_action('wp_insert_comment', 'comment_inserted', 10, 2);

用户

  • user_register
1
2
3
4
5
6
7
8
function myplugin_registration_save($user_id) {
    wp_update_user([
        'ID' => $user_id,
        'description' => '注册时间:' .date('Y-m-d H:i:s');
    ]);
}

add_action('user_register', 'myplugin_registration_save', 10, 1);

其他

  • remove_action 移除指定钩子上的某一个方法。还可以指定移除某一个优先级的方法
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
 * Removes a callback function from an action hook.
 *
 * This can be used to remove default functions attached to a specific action
 * hook and possibly replace them with a substitute.
 *
 * To remove a hook, the `$callback` and `$priority` arguments must match
 * when the hook was added. This goes for both filters and actions. No warning
 * will be given on removal failure.
 *
 * @since 1.2.0
 *
 * @param string                $hook_name The action hook to which the function to be removed is hooked.
 * @param callable|string|array $callback  The name of the function which should be removed.
 *                                         This function can be called unconditionally to speculatively remove
 *                                         a callback that may or may not exist.
 * @param int                   $priority  Optional. The exact priority used when adding the original
 *                                         action callback. Default 10.
 * @return bool Whether the function is removed.
 */
function remove_action( $hook_name, $callback, $priority = 10 ) {
	return remove_filter( $hook_name, $callback, $priority );
}
  • remove_all_actions 移除指定钩子上的所有方法。还可以指定移除某一个优先级的方法,其他优先级不会移除
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
/**
 * Removes all of the callback functions from an action hook.
 *
 * @since 2.7.0
 *
 * @param string    $hook_name The action to remove callbacks from.
 * @param int|false $priority  Optional. The priority number to remove them from.
 *                             Default false.
 * @return true Always returns true.
 */
function remove_all_actions( $hook_name, $priority = false ) {
	return remove_all_filters( $hook_name, $priority );
}

过滤器钩子

  • apply_filters
 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
/**
 * Calls the callback functions that have been added to a filter hook.
 *
 * This function invokes all functions attached to filter hook `$hook_name`.
 * It is possible to create new filter hooks by simply calling this function,
 * specifying the name of the new hook using the `$hook_name` parameter.
 *
 * The function also allows for multiple additional arguments to be passed to hooks.
 *
 * Example usage:
 *
 *     // The filter callback function.
 *     function example_callback( $string, $arg1, $arg2 ) {
 *         // (maybe) modify $string.
 *         return $string;
 *     }
 *     add_filter( 'example_filter', 'example_callback', 10, 3 );
 *
 *     /*
 *      * Apply the filters by calling the 'example_callback()' function
 *      * that's hooked onto `example_filter` above.
 *      *
 *      * - 'example_filter' is the filter hook.
 *      * - 'filter me' is the value being filtered.
 *      * - $arg1 and $arg2 are the additional arguments passed to the callback.
 *     $value = apply_filters( 'example_filter', 'filter me', $arg1, $arg2 );
 *
 * @since 0.71
 * @since 6.0.0 Formalized the existing and already documented `...$args` parameter
 *              by adding it to the function signature.
 *
 * @global WP_Hook[] $wp_filter         Stores all of the filters and actions.
 * @global int[]     $wp_filters        Stores the number of times each filter was triggered.
 * @global string[]  $wp_current_filter Stores the list of current filters with the current one last.
 *
 * @param string $hook_name The name of the filter hook.
 * @param mixed  $value     The value to filter.
 * @param mixed  ...$args   Optional. Additional parameters to pass to the callback functions.
 * @return mixed The filtered value after all hooked functions are applied to it.
 */
function apply_filters( $hook_name, $value, ...$args )
  • add_filter
 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
/**
 * Adds a callback function to a filter hook.
 *
 * WordPress offers filter hooks to allow plugins to modify
 * various types of internal data at runtime.
 *
 * A plugin can modify data by binding a callback to a filter hook. When the filter
 * is later applied, each bound callback is run in order of priority, and given
 * the opportunity to modify a value by returning a new value.
 *
 * The following example shows how a callback function is bound to a filter hook.
 *
 * Note that `$example` is passed to the callback, (maybe) modified, then returned:
 *
 *     function example_callback( $example ) {
 *         // Maybe modify $example in some way.
 *         return $example;
 *     }
 *     add_filter( 'example_filter', 'example_callback' );
 *
 * Bound callbacks can accept from none to the total number of arguments passed as parameters
 * in the corresponding apply_filters() call.
 *
 * In other words, if an apply_filters() call passes four total arguments, callbacks bound to
 * it can accept none (the same as 1) of the arguments or up to four. The important part is that
 * the `$accepted_args` value must reflect the number of arguments the bound callback *actually*
 * opted to accept. If no arguments were accepted by the callback that is considered to be the
 * same as accepting 1 argument. For example:
 *
 *     // Filter call.
 *     $value = apply_filters( 'hook', $value, $arg2, $arg3 );
 *
 *     // Accepting zero/one arguments.
 *     function example_callback() {
 *         ...
 *         return 'some value';
 *     }
 *     add_filter( 'hook', 'example_callback' ); // Where $priority is default 10, $accepted_args is default 1.
 *
 *     // Accepting two arguments (three possible).
 *     function example_callback( $value, $arg2 ) {
 *         ...
 *         return $maybe_modified_value;
 *     }
 *     add_filter( 'hook', 'example_callback', 10, 2 ); // Where $priority is 10, $accepted_args is 2.
 *
 * *Note:* The function will return true whether or not the callback is valid.
 * It is up to you to take care. This is done for optimization purposes, so
 * everything is as quick as possible.
 *
 * @since 0.71
 *
 * @global WP_Hook[] $wp_filter A multidimensional array of all hooks and the callbacks hooked to them.
 *
 * @param string   $hook_name     The name of the filter to add the callback to.
 * @param callable $callback      The callback to be run when the filter is applied.
 * @param int      $priority      Optional. Used to specify the order in which the functions
 *                                associated with a particular filter are executed.
 *                                Lower numbers correspond with earlier execution,
 *                                and functions with the same priority are executed
 *                                in the order in which they were added to the filter. Default 10.
 * @param int      $accepted_args Optional. The number of arguments the function accepts. Default 1.
 * @return true Always returns true.
 */
function add_filter( $hook_name, $callback, $priority = 10, $accepted_args = 1 )

常用过滤器钩子实用案例

文章

  • the_content

  • content_save_pre

1
2
3
add_fliter('content_save_pre', function($content) {
    return str_replace("Hollis", "<a href="https://www.1024plus.com">Hollis</a>", $content);
});
  • the_title

附件

  • wp_handle_upload_prefilter
1
2
3
4
5
//重新生成附件文件名
add_filter('wp_handle_upload_prefilter', function ($file) {
    $time = date('Y-m-d');
    $file['name'] = $time . '' . mt_rand(1, 100) . '.' . pathinfo($file['name'], PATHINFO_EXTENDSION);
});

评论

  • comment_text
1
2
3
add_filter('comment_text', function ($content) {
    return str_replace("Hollis", "<a href="https://www.1024plus.com">Hollis</a>", $content);
});

后台整合

创建菜单和子菜单

 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
function add_my_menu() {
    //顶级菜单
    add_menu_page(
        'my plugin', //page title
    	'my plugin', //menu title
        'manage_options',
        'my_plugin_menu', //menu_slug 需要唯一
    	function () {
            ?>
            <h2>插件顶级菜单</h2>  
            <?
        },
        plugins_url('/images/icon.png', __FILE__)
    );
    
    //子菜单
    add_submenu_page(
        'my_plugin_menu', //parent_slug
        'setting',
        'setting',
        'manage_options',
        'my_plugin_setting', //menu_slug 需要唯一
        function () {
            ?>
            <h2>子菜单</h2>  
            <?
        }
    );
}

add_action('admin_menu', 'add_my_menu');

创建小工具

  • 定义小工具类
 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
class My_Widget extends WP_Widget {

    // Widget setup
    public function __construct() {
        parent::__construct(
            'custom_widget', // Base ID
            __('Custom Widget', 'text_domain'), // Widget name
            array( 'description' => __( 'A simple custom widget for displaying recent posts.', 'text_domain' ), ) // Widget description
        );
    }
    
    // 前台显示小工具
    public function widget($args, $instance)
    {
        // Widget content output
        extract($args);
        echo $before_widget;
        echo $before_title . apply_filters( 'widget_title', $instance['title'] ) . $after_title;
        // Custom widget content
        echo '<ul>';
        $recent_posts = wp_get_recent_posts( array(
            'numberposts' => 5, // Number of recent posts to display
            'post_status' => 'publish',
        ) );
        foreach ( $recent_posts as $post ) {
            echo '<li><a href="' . get_permalink( $post['ID'] ) . '">' . $post['post_title'] . '</a></li>';
        }
        echo '</ul>';
        echo $after_widget;
    }

    // 保存设置
    public function update($new_instance, $old_instance)
    {
        $instance = array();
        $instance['title'] = ( ! empty( $new_instance['title'] ) ) ? sanitize_text_field( $new_instance['title'] ) : '';
        return $instance;
    }

    // 小工具管理界面
    public function form($instance)
    {
        // Widget form fields
        $title = ! empty( $instance['title'] ) ?
            $instance['title'] :
            __( 'Recent Posts', 'text_domain' );
        ?>
        <p>
            <label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:' ); ?></label>
            <input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>">
        </p>
        <?php
    }

}
  • 注册小工具
1
2
3
4
5
// Register the custom widget
function register_custom_widget() {
    register_widget( 'Custom_Widget' );
}
add_action( 'widgets_init', 'register_custom_widget' );
  • 使用小工具(需要注册边栏,将小工具和边栏关联起来,通过边栏显示小工具)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
//1.注册小工具
register_sidebar([
    'name' => '新闻推荐',
    'id' => 'news-recommend-tool',
    'before_widget' => '<div class="sidebar-news-list recommend">',
    'after_widget'   => "</div>",
]);

//2.后台小工具管理拖动小工具到指定的边栏
//3.前台使用边栏
dynamic_sidebar('news-recommend-tool');

元数据框

  • add_meta_box
 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
// 添加自定义元数据框
function custom_meta_box() {
    add_meta_box(
        'custom_meta_box', // 元数据框的ID
        'Custom Meta Box', // 元数据框的标题
        'display_custom_meta_box', // 显示元数据框的回调函数
        'post', // 元数据框应用的文章类型
        'normal', // 元数据框的位置:normal(默认)、side(侧边栏)、advanced(高级)
        'high' // 元数据框的优先级:high、core、default、low
    );
}
add_action( 'add_meta_boxes', 'custom_meta_box' );

// 显示元数据框的内容
function display_custom_meta_box( $post ) {
    wp_nonce_field( 'my_action', 'my_nonce' );
    // 获取已保存的元数据值
    $custom_meta_value = get_post_meta( $post->ID, '_custom_meta_key', true );
    ?>
    <p>
        <label for="custom_meta_field">Custom Meta Field:</label>
        <input type="text" id="custom_meta_field" name="custom_meta_field" value="<?php echo esc_attr( $custom_meta_value ); ?>" />
    </p>
    <?php
}

// 保存元数据框的值
function save_custom_meta_box( $post_id ) {
    // 检查是否保存了新的元数据值
    if ( isset( $_POST['custom_meta_field'] ) ) {
        // 更新元数据值
        update_post_meta( $post_id, '_custom_meta_key', sanitize_text_field( $_POST['custom_meta_field'] ) );
    }
}
add_action( 'save_post', 'save_custom_meta_box' );

自定义插件页面

  • 内置wrap样式
  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
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
<div class="wrap">
    <h2>插件顶级菜单</h2>
    <div id="message" class="updated">设置保存成功</div>
    <div id="message" class="error">保存出现错误</div>
    <p>
        <input type="submit" name="Save" value="保存设置" />
        <input type="submit" name="Save" value="保存设置" class="button" />
        <input type="submit" name="Save" value="保存设置" class="button button-primary" />
        <input type="submit" name="Save" value="保存设置" class="button button-secondary" />
        <input type="submit" name="Save" value="保存设置" class="button button-large" />
        <input type="submit" name="Save" value="保存设置" class="button button-small" />
        <input type="submit" name="Save" value="保存设置" class="button button-hero" />
    </p>
    <p>
        <a href="#">搜索</a>
        <a href="#" class="button">搜索</a>
        <a href="#" class="button button-primary">搜索</a>
        <a href="#" class="button button-secondary">搜索</a>
        <a href="#" class="button button-large">搜索</a>
        <a href="#" class="button button-small">搜索</a>
        <a href="#" class="button button-hero">搜索</a>
    </p>

    <form method="POST" action="">
        <table class="form-table">
            <tr valign="top">
                <th><label for="xingming">姓名:</label></th>
                <td><input id="xingming" name="xingming" /></td>
            </tr>
            <tr valign="top">
                <th><label for="shenfen">身份:</label></th>
                <td>
                    <select name="shenfen">
                        <option value="在校">在校</option>
                        <option value="毕业">毕业</option>
                    </select>
                </td>
            </tr>
            <tr valign="top">
                <th><label for="tongyi">同意注册</label></th>
                <td><input type="checkbox" name="tongyi" /></td>
            </tr>
            <tr valign="top">
                <th><label for="xingbie">性别</label></th>
                <td>
                    <input type="radio" name="xingbie" value="男" /> 
                    <input type="radio" name="xingbie" value="女" /> 
                </td>
            </tr>
            <tr valign="top">
                <th><label for="beizhu">备注</label></th>
                <td><textarea name="beizhu"></textarea></td>
            </tr>
            <tr valign="top">
                <td>
                    <input type="submit" name="save" value="保存" class="button-primary" />
                    <input type="submit" name="reset" value="重置" class="button-secondary" />
                </td>
            </tr>
        </table>
    </form>

    <table class="widefat striped">
        <thead>
        <tr>
            <th>序号</th>
            <th>姓名</th>
        </tr>
        </thead>
        <tbody>
        <tr>
            <td>1</td>
            <td>黄聪</td>
        </tr>
        <tr>
            <td>2</td>
            <td>黄聪</td>
        </tr>
        <tr>
            <td>3</td>
            <td>黄聪</td>
        </tr>
        </tbody>
        <tfoot>
        <tr>
            <th>序号</th>
            <th>姓名</th>
        </tr>
        </tfoot>
    </table>

    <div class="tablenav">
        <div class="tablenav-pages">
            <span class="displaying-num">第1页,共458页</span>
            <span class="page-numbers current">1</span>
            <a href="#" class="page-numbers">2</a>
            <a href="#" class="page-numbers">3</a>
            <a href="#" class="page-numbers">4</a>
            <a href="#" class="next page-numbers">»</a>
        </div>
    </div>
</div>

保存插件设置

option表

1
2
3
4
5
//更新设置
update_option( 'my_plugin_color', $_POST['my_plugin_color'] );

//获取设置
$color = get_option( "my_plugin_color" );

自己的数据表

1
2
3
4
5
6
7
global $wpdb;
//更新设置
$wpdb->update( "{$wpdb->prefix}test", array( 'color' => $_POST['color'], 'size' => $_POST['size'] ), array( 'id' => 1 ) );

//获取设置
$sql = "SELECT * FROM `{$wpdb->prefix}test`";
$row = $wpdb->get_row( $sql, ARRAY_A );

使用API保存插件设置

  • 定义相关字段
 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
add_action( 'admin_init', 'register_hc_test_setting' );
//使用register_setting()注册要存储的字段
function register_hc_test_setting() {

    //注册一个选项,用于装载所有插件设置项option_group和option_name可以相同
    $option_group = "my_option_group";
    $option_name = "my_option_name";
    $page = 'my_admin_menu';//需要对应add_menu_page()中的menu_slug
    register_setting( $option_group, $option_name );

    //添加选项设置区域
    $setting_section = "my_setting_section";
    add_settings_section(
        $setting_section,
        $setting_section,
        function () {}, //用来设置section的描述文案的,可以为空
        $page
    );

    //设置字体颜色
    add_settings_field(
        'font_color',
        '字体颜色',
        function () {
            $my_option_name = get_option( "my_option_name" );
            ?>
            <input name='my_option_name[font_color]' type='text' value='<? echo $my_option_name["font_color"]; ?>' />
                <?
        },
        $option_group,
        $setting_section
    );
}
  • 调用相关字段
 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
add_action( 'admin_menu', 'my_admin_menu' );

function my_admin_menu() {
	
	//创建顶级菜单
	add_menu_page( 
		'My Admin Menu', 
		'My Admin Menu', 
		'manage_options', 
		'my_admin_menu' ,
		function () {
			?>
            <div class="wrap">
                <h2>My Admin Menu Settting</h2>
                <form action="options.php" method="post">
                <?
                    $option_group = "my_option_group";
            		$option_name = "my_option_name";
                    //输出一些必要的字段,包括验证信息等
                    settings_fields( $option_group );
                    //输出选项设置区域$option_name是保存到数据表中的值
                    do_settings_sections( $option_name );
                    //输出按钮
                    submit_button();
                ?>
                </form>
            </div>
            <?
		},
		plugins_url( '/images/icon.png', __FILE__ )
	);
}

为自己的插件创建类

 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
<?php

namespace hollisho\wp\plugin;

class Loading {

    function __construct()
    {
        $this->initWpHooks();
    }

    /**
	 * @desc 执行 wp 钩子函数
	 */
	public function initWpHooks() {
		// 启用
		register_activation_hook( __FILE__, array( $this, 'plugin_activation' ) );

		// 停用
		register_deactivation_hook( __FILE__, array( $this, 'plugin_deactivation' ) );

		// 卸载
		register_uninstall_hook( __FILE__, array( __CLASS__, 'plugin_uninstall' ) );

		// 初始化
		add_action( 'plugins_loaded', array( $this, 'plugin_init' ) );


	}

/**
	 * @desc 启用
	 */
	public function plugin_activation() {
		
	}

	/**
	 * @desc 停用
	 */
	public function plugin_deactivation() {
		
	}

	/**
	 * @desc 卸载
	 */
	public function plugin_uninstall() {
		
	}

	/**
	 * @desc 插件初始化
	 * @return false|void
	 */
	public function plugin_init() {
		
	}



}

new Loading();

添加设置项到现有的管理界面中

  • 添加设置项到 设置->常规
1
2
//注册一个选项,用于装载所有插件设置项
register_setting( 'general', 'hc_test_option' );

调用ajax

  • test.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
jQuery(document).ready( function($) {
	$("input[name='hc_test_option[color]']").blur( function() {
		$.ajax({
			type: "POST",
			data: "color=" + $(this).val() + "&action=hcsem_color_check",//action参数对应
			url: ajax_object.ajax_url,  //通过wp_localize_script定义ajax_object
			beforeSend: function() {
				$('#error_color').html('校验中...'); 
			},
			success: function( $data ) {
				if( $data == 'ok'){
				    $('#error_color').html('输入正确');  
				} else {
				    $('#error_color').html('颜色不能为空!'); 
				}
			}
		});
	});
});
  • 加载钩子
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
function my_custom_script() {
    // 定义要传递到 JavaScript 的数据
    $data_to_pass = array(
        'ajax_url' => admin_url( 'admin-ajax.php' ), // 用于 AJAX 请求的 URL
        'site_name' => get_bloginfo( 'name' ), // 网站名称
    );

    // 注意:wp_localize_script的$handle参数要和wp_enqueue_script$handle参数对应
    //使用ajax校验信息,第一个参数$handle为'test-js'
	wp_enqueue_script( 'test-js', plugins_url('js/test.js', __FILE__), array('jquery') );
    // 将数据传递到 JavaScript 中,第一个参数$handle为'test-js'
    wp_localize_script( 'test-js', 'ajax_object', $data_to_pass );
}
add_action( 'wp_enqueue_scripts', 'my_custom_script' );
  • 接收数据

详情参考wp-admin/admin-ajax.php

 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
//wp-admin/admin-ajax.php代码片段

$action = $_REQUEST['action'];

if ( is_user_logged_in() ) {
	// If no action is registered, return a Bad Request response.
	if ( ! has_action( "wp_ajax_{$action}" ) ) {
		wp_die( '0', 400 );
	}

	/**
	 * Fires authenticated Ajax actions for logged-in users.
	 *
	 * The dynamic portion of the hook name, `$action`, refers
	 * to the name of the Ajax action callback being fired.
	 *
	 * @since 2.1.0
	 */
	do_action( "wp_ajax_{$action}" );
} else {
	// If no action is registered, return a Bad Request response.
	if ( ! has_action( "wp_ajax_nopriv_{$action}" ) ) {
		wp_die( '0', 400 );
	}

	/**
	 * Fires non-authenticated Ajax actions for logged-out users.
	 *
	 * The dynamic portion of the hook name, `$action`, refers
	 * to the name of the Ajax action callback being fired.
	 *
	 * @since 2.8.0
	 */
	do_action( "wp_ajax_nopriv_{$action}" );
}
  • 使用举例
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
//已登录用户
add_action( 'wp_ajax_{$action}', function () {
    if( trim( $_POST['color'] ) != "" ){ echo "ok"; }
		wp_die();
}
           
//未登录用户
add_action( 'wp_ajax_nopriv_{$action}', function () {
    if( trim( $_POST['color'] ) != "" ){ echo "ok"; }
		wp_die();
}

本地化/多语言

创建PO文件

  • 采用poedit工具

加载多语言文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
add_action( 'init', array( $this, 'my_load_textdomain' ) ); 
	
function my_load_textdomain() {

    //加载 languages 目录下的翻译文件 zh_CN
    $currentLocale = get_locale();

    if( !empty( $currentLocale ) ) {

        $moFile = dirname(__FILE__) . "/languages/{$currentLocale}.mo";

        if( @file_exists( $moFile ) && is_readable( $moFile ) ) load_textdomain( 'my-test', $moFile );
    }
}


//使用方式
echo __( 'color', 'my-test' ),
_e( 'color', 'my-test' ),

插件权限控制

  • current_user_can
1
2
3
if( current_user_can('edit_posts') ) {
    // do something
}
  • 创建菜单的时候判断权限
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
add_menu_page( 
    'My Plugin', 
    'My Plugin', 
    'read', //这里指定权限
    'my_plugin_slug' ,
    function () {
        
    },
    plugins_url( $this->icon_url, __FILE__ )
);
  • get_current_screen
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
add_action( 'admin_enqueue_scripts', array( $this, 'load_script' ) );

function load_script() {
    //使用ajax校验信息
    $screen = get_current_screen();    
    //只有在指定页面才加载指定的js
    if ( is_object( $screen ) && $screen->id == 'my_plugin_slug' ) {
        wp_enqueue_script( 'test-js', plugins_url('js/test.js', __FILE__), array('jquery') );
        // 将数据传递到 JavaScript 中
        wp_localize_script( 'my-script', 'ajax_object', array( 'ajax_url' => admin_url( 'admin-ajax.php' ) ) );
    }
}

短标签

  • shortcode_atts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
/**
 * Combines user attributes with known attributes and fill in defaults when needed.
 *
 * The pairs should be considered to be all of the attributes which are
 * supported by the caller and given as a list. The returned attributes will
 * only contain the attributes in the $pairs list.
 *
 * If the $atts list has unsupported attributes, then they will be ignored and
 * removed from the final returned list.
 *
 * @since 2.5.0
 *
 * @param array  $pairs     Entire list of supported attributes and their defaults.
 * @param array  $atts      User defined attributes in shortcode tag.
 * @param string $shortcode Optional. The name of the shortcode, provided for context to enable filtering
 * @return array Combined and filtered attribute list.
 */
function shortcode_atts( $pairs, $atts, $shortcode = '' )
  • add_shortcode
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
/**
 * Adds a new shortcode.
 *
 * Care should be taken through prefixing or other means to ensure that the
 * shortcode tag being added is unique and will not conflict with other,
 * already-added shortcode tags. In the event of a duplicated tag, the tag
 * loaded last will take precedence.
 *
 * @since 2.5.0
 *
 * @global array $shortcode_tags
 *
 * @param string   $tag      Shortcode tag to be searched in post content.
 * @param callable $callback The callback function to run when the shortcode is found.
 *                           Every shortcode callback is passed three parameters by default,
 *                           including an array of attributes (`$atts`), the shortcode content
 *                           or null if not set (`$content`), and finally the shortcode tag
 *                           itself (`$shortcode_tag`), in that order.
 */
function add_shortcode( $tag, $callback )
  • do_shortcode
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
/**
 * Searches content for shortcodes and filter shortcodes through their hooks.
 *
 * If there are no shortcode tags defined, then the content will be returned
 * without any filtering. This might cause issues when plugins are disabled but
 * the shortcode will still show up in the post or content.
 *
 * @since 2.5.0
 *
 * @global array $shortcode_tags List of shortcode tags and their callback hooks.
 *
 * @param string $content     Content to search for shortcodes.
 * @param bool   $ignore_html When true, shortcodes inside HTML elements will be skipped.
 *                            Default false.
 * @return string Content with shortcodes filtered out.
 */
function do_shortcode( $content, $ignore_html = false )
  • 使用举例
 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
// 定义短代码处理函数
function my_custom_shortcode_function( $atts ) {
    // 解析短代码属性
    $atts = shortcode_atts(
        array(
            'name' => 'World', // 默认值为 World
            'age' => '',       // 默认值为空
        ),
        $atts,
        'my_shortcode' // 短代码名称
    );

    // 使用短代码属性执行操作
    $output = 'Hello ' . $atts['name'];
    if ( ! empty( $atts['age'] ) ) {
        $output .= ', you are ' . $atts['age'] . ' years old.';
    }

    return $output;
}

// 注册短代码
add_shortcode( 'my_shortcode', 'my_custom_shortcode_function' );


//使用短代码,传入age参数值,name参数没有传值,采用默认值World
do_shortcode('[my_shortcode age="30"]')

扩展自定义内容类型和分类方式

自定义内容类型

 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
function custom_post_type() {
    $labels = array(
        'name'               => __( 'Books', 'textdomain' ),
        'singular_name'      => __( 'Book', 'textdomain' ),
        'menu_name'          => __( 'Books', 'textdomain' ),
        'add_new'            => __( 'Add New', 'textdomain' ),
        'add_new_item'       => __( 'Add New Book', 'textdomain' ),
        'edit_item'          => __( 'Edit Book', 'textdomain' ),
        'new_item'           => __( 'New Book', 'textdomain' ),
        'view_item'          => __( 'View Book', 'textdomain' ),
        'search_items'       => __( 'Search Books', 'textdomain' ),
        'not_found'          => __( 'No books found', 'textdomain' ),
        'not_found_in_trash' => __( 'No books found in Trash', 'textdomain' ),
        'parent_item_colon'  => __( 'Parent Book:', 'textdomain' ),
        'all_items'          => __( 'All Books', 'textdomain' ),
        'archives'           => __( 'Book Archives', 'textdomain' ),
        'attributes'         => __( 'Book Attributes', 'textdomain' ),
        'insert_into_item'   => __( 'Insert into book', 'textdomain' ),
        'uploaded_to_this_item' => __( 'Uploaded to this book', 'textdomain' ),
        'featured_image'        => _x( 'Featured Image', 'book', 'textdomain' ),
        'set_featured_image'    => _x( 'Set featured image', 'book', 'textdomain' ),
        'remove_featured_image' => _x( 'Remove featured image', 'book', 'textdomain' ),
        'use_featured_image'    => _x( 'Use as featured image', 'book', 'textdomain' ),
        'filter_items_list'     => __( 'Filter books list', 'textdomain' ),
        'items_list_navigation' => __( 'Books list navigation', 'textdomain' ),
        'items_list'            => __( 'Books list', 'textdomain' ),
    );

    $args = array(
        'labels'             => $labels,
        'public'             => true,
        'publicly_queryable' => true,
        'show_ui'            => true,
        'show_in_menu'       => true,
        'query_var'          => true,
        'rewrite'            => array( 'slug' => 'book' ),
        'capability_type'    => 'post',
        'has_archive'        => true,
        'hierarchical'       => false,
        'menu_position'      => 5,
        'supports'           => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments' ),
    );

    register_post_type( 'book', $args );
}
add_action( 'init', 'custom_post_type' );

自定义分类方式

 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
function custom_taxonomy() {
    $labels = array(
        'name'                       => __( 'Genres', 'textdomain' ),
        'singular_name'              => __( 'Genre', 'textdomain' ),
        'search_items'               => __( 'Search Genres', 'textdomain' ),
        'popular_items'              => __( 'Popular Genres', 'textdomain' ),
        'all_items'                  => __( 'All Genres', 'textdomain' ),
        'parent_item'                => null,
        'parent_item_colon'          => null,
        'edit_item'                  => __( 'Edit Genre', 'textdomain' ),
        'update_item'                => __( 'Update Genre', 'textdomain' ),
        'add_new_item'               => __( 'Add New Genre', 'textdomain' ),
        'new_item_name'              => __( 'New Genre Name', 'textdomain' ),
        'separate_items_with_commas' => __( 'Separate genres with commas', 'textdomain' ),
        'add_or_remove_items'        => __( 'Add or remove genres', 'textdomain' ),
        'choose_from_most_used'      => __( 'Choose from the most used genres', 'textdomain' ),
        'not_found'                  => __( 'No genres found.', 'textdomain' ),
        'menu_name'                  => __( 'Genres', 'textdomain' ),
    );

    $args = array(
        'hierarchical'          => true,
        'labels'                => $labels,
        'show_ui'               => true,
        'show_admin_column'     => true,
        'update_count_callback' => '_update_post_term_count',
        'query_var'             => true,
        'rewrite'               => array( 'slug' => 'genre' ),
    );

    register_taxonomy( 'genre', array( 'book' ), $args );
}
add_action( 'init', 'custom_taxonomy' );
使用 Hugo 构建
主题 StackJimmy 设计