Tìm hiểu sâu về phát triển plugin WordPress: Bắt đầu từ đầu để xây dựng plugin tùy chỉnh đầu tiên của bạn

4 phút đọc
2026-03-13
2026-06-03
1,882
Tôi kiếm được hoa hồng khi bạn mua sắm thông qua các liên kết dưới đây, mà không phát sinh thêm chi phí nào cho bạn.

Những kiến thức cơ bản về plugin WordPress và môi trường phát triển

Trước khi bắt đầu viết mã, việc hiểu rõ các khái niệm cơ bản về plugin WordPress và thiết lập một môi trường phát triển phù hợp là điều vô cùng quan trọng. Một plugin WordPress về cơ bản là một thư mục chứa mã PHP, được sử dụng để mở rộng các chức năng cốt lõi của WordPress thông qua API phong phú mà nền tảng này cung cấp. Các plugin có thể đơn giản chỉ là những đoạn mã ngắn, hoặc phức tạp đến mức tạo thành một hệ thống quản lý hoàn chỉnh. Nguyên tắc cốt lõi của plugin WordPress là “không sửa đổi mã nguồn gốc” (do WordPress cung cấp), điều này đảm bảo rằng các tính năng tùy chỉnh của bạn sẽ không bị mất đi khi WordPress được nâng cấp.

Để phát triển hiệu quả, bạn cần một môi trường phát triển trên máy tính cá nhân (môi trường local). Bạn có thể sử dụng các công cụ như XAMPP, MAMP, Local by Flywheel hoặc Docker để nhanh chóng thiết lập một máy chủ chứa PHP và MySQL. Sau đó, hãy cài đặt phiên bản mới nhất của WordPress. Trong quá trình phát triển, điều được khuyến nghị là…wp-config.phpKích hoạt trong tệpWP_DEBUGĐiều này nhằm mục đích phát hiện lỗi một cách kịp thời.

“Trái tim” của một plugin chính là một tệp PHP chính. Các dòng chú thích ở đầu tệp này chính là “hồ sơ nhận dạng” của plugin đó; WordPress sử dụng những thông tin này để nhận diện và quản lý plugin trong nền. Các dòng chú thích tiêu chuẩn ở đầu tệp chính của một plugin được trình bày như sau:

Đọc thêm WordPress Theme là gì

<?php
/**
 * Plugin Name: 我的第一个自定义插件
 * Plugin URI:  https://example.com/my-first-plugin
 * Description: 这是一个用于学习插件开发的简单插件,它将在文章末尾添加自定义内容。
 * Version:     1.0.0
 * Author:      开发者名称
 * License:     GPL v2 or later
 * Text Domain: my-first-plugin
 */

Tạo ra plugin chức năng đầu tiên của bạn.

Hãy bắt đầu với một tính năng thực tế: tự động thêm một đoạn văn bản tùy chỉnh vào cuối mỗi bài viết trên trang web. Ví dụ này sẽ được sử dụng xuyên suốt quá trình phát triển plugin.

UltaHost – Nhà cung cấp dịch vụ máy chủ WordPress chuyên nghiệp
Bảo đảm hoàn tiền trong 30 ngày, băng thông và cơ sở dữ liệu không giới hạn, bảo vệ DDoS miễn phí, mua 3 năm ưu đãi 50%

Tệp chính của plugin và quá trình khởi tạo (Plugin main file and initialization)

Trước hết, trong WordPress…wp-content/pluginsví dụmy-first-pluginTrong thư mục đó, hãy tạo tập tin PHP chính; bạn có thể đặt tên nó là…my-first-plugin.phpVui lòng sao chép đoạn mã chú thích ở trên vào đây.

Tiếp theo, chúng ta cần một cách an toàn để thực thi đoạn mã khởi tạo plugin. Thực hành tốt nhất là đóng gói tất cả các chức năng vào một lớp, hoặc sử dụng các hàm trong không gian tên (namespace). Ở đây, chúng ta sẽ sử dụng một lớp đơn giản để tổ chức mã nguồn. Sau phần chú thích ở đầu tệp chính, hãy thêm định nghĩa lớp như sau:

if ( ! defined( 'ABSPATH' ) ) {
	exit; // 防止直接访问文件
}

class My_First_Plugin {
    public function __construct() {
        // 构造函数,在这里挂载钩子
    }
}

// 初始化插件
new My_First_Plugin();

if ( ! defined( ‘ABSPATH’ ) )Dòng mã này là một tiêu chuẩn bảo mật trong quá trình phát triển plugin cho WordPress, nhằm ngăn ngừa người dùng truy cập trực tiếp vào các tệp tin plugin của bạn thông qua địa chỉ URL.

Sử dụng các “hook” (các công cụ tích hợp sẵn trong hệ thống) để thêm nội dung chân trang cho bài viết.

Cấu trúc plugin của WordPress được xây dựng dựa trên hệ thống “Hook” (Khoác), bao gồm hai loại chính: Action (Hành động) và Filter (Bộ lọc). Action cho phép bạn thực thi mã lệnh vào những thời điểm cụ thể trong quá trình hoạt động của website, trong khi Filter cho phép bạn thay đổi dữ liệu.

Đọc thêm Hướng dẫn Tối ưu Hiệu suất Website WordPress Toàn diện: Từ Cấu hình Cơ bản đến Phân tích Plugin Cache

Mục tiêu của chúng ta là thêm văn bản sau nội dung bài viết; đây là một quá trình “lọc” nội dung. Do đó, chúng ta sẽ sử dụng…the_contentBạn muốn tôi chỉnh sửa hàm khởi tạo (constructor) và các phương thức (methods) trong lớp của bộ lọc này như thế nào? Hãy cung cấp chi tiết cụ thể về những thay đổi bạn muốn thực hiện.

class My_First_Plugin {
    public function __construct() {
        // 将自定义方法挂载到‘the_content’过滤器上
        add_filter( 'the_content', array( $this, 'add_footer_to_content' ) );
    }

/**
     * 在文章内容后添加自定义页脚
     *
     * @param string $content 原始文章内容。
     * @return string 修改后的文章内容。
     */
    public function add_footer_to_content( $content ) {
        // 确保只在主循环的单篇文章页面显示
        if ( is_single() &amp;&amp; in_the_loop() &amp;&amp; is_main_query() ) {
            $custom_footer = '<div class="my-plugin-footer"><p>Cảm ơn bạn đã đọc bài viết này! Nội dung bài viết được tạo ra bởi 【My First Plugin】.</p></div>';
            $content .= $custom_footer;
        }
        return $content;
    }
}

add_filter()The function will use the class method.add_footer_to_contentĐăng ký vàothe_contentBộ lọc. Khi WordPress chuẩn bị xuất nội dung bài viết, nó sẽ gọi phương thức của chúng ta và truyền vào nội dung gốc. Chúng ta sử dụng điều kiện kiểm tra để đảm bảo chỉ thêm chân trang ở trang bài viết riêng lẻ, tránh thêm trùng lặp ở trang chủ, trang lưu trữ. Cuối cùng, thêm HTML tùy chỉnh vào sau nội dung và trả về.

Thêm tùy chọn quản trị cho plugin

Một plugin chuyên nghiệp thường cho phép người dùng thực hiện các thiết lập từ phía backend (phía máy chủ). Chúng ta sẽ thêm một tùy chọn thiết lập đơn giản cho đoạn văn bản ở phần chân trang (footer) vừa rồi, để người dùng có thể tự định nghĩa nội dung văn bản được hiển thị.

Hosting.com - lưu trữ chia sẻ
Hiệu năng cao, được trang bị CPU AMD EPYC, lưu trữ SSD NVMe và LiteSpeed, hỗ trợ chuyên gia nội bộ 24 giờ/ngày, các biện pháp bảo mật tiên tiến bao gồm SSL, chống brute force, phần mềm độc hại và bảo vệ DDoS, tiết kiệm tới 73%.

Tạo trang menu cài đặt

Chúng tôi cần thêm một trang con trong menu “Cài đặt” (Settings) của giao diện quản trị WordPress. Điều này đòi hỏi sử dụng công cụ hoặc thao tác cụ thể nào đó để thực hiện.add_options_page()Hàm (function) thường được gắn (mount) vào…admin_menuTrên những “khúc móc hành động” (action hooks) đó.

Trước hết, hãy thêm một phương thức mới trong lớp plugin để đăng ký menu và trang (page):

class My_First_Plugin {
    // ... 之前的构造函数和方法 ...

public function __construct() {
        add_filter( 'the_content', array( $this, 'add_footer_to_content' ) );
        // 挂载后台管理菜单
        add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
        // 挂载初始化设置选项
        add_action( 'admin_init', array( $this, 'settings_init' ) );
    }

public function add_admin_menu() {
        add_options_page(
            '我的第一个插件设置', // 页面标题
            '自定义页脚设置',     // 菜单标题
            'manage_options',     // 所需权限
            'my-first-plugin',    // 菜单slug
            array( $this, 'options_page_html' ) // 回调函数,用于输出页面HTML
        );
    }
}

Trường thiết lập đăng ký và trang hiển thị (Rendering Page)

Tiếp theo, chúng ta cần sử dụng WordPress Settings API để đăng ký, lưu trữ và xác thực các tùy chọn một cách an toàn. Điều này bao gồm…register_setting(), add_settings_section()add_settings_field()các hàm, v.v.

Đọc thêm WooCommerce là gì? Tìm hiểu chi tiết về các tính năng cốt lõi và trường hợp sử dụng của nó

public function settings_init() {
    // 注册一个设置选项组
    register_setting( 'my_first_plugin_settings', 'my_first_plugin_options' );

// 在页面中添加一个设置区域
    add_settings_section(
        'my_first_plugin_section',
        '页脚内容配置',
        array( $this, 'section_callback' ),
        'my-first-plugin'
    );

// 向该区域添加一个字段
    add_settings_field(
        'footer_text',
        '页脚显示文本',
        array( $this, 'footer_text_field_render' ),
        'my-first-plugin',
        'my_first_plugin_section'
    );
}

public function section_callback() {
    echo '<p>Hãy thực hiện cấu hình nội dung văn bản sẽ được hiển thị ở cuối bài viết tại đây.</p>';
}

public function footer_text_field_render() {
    $options = get_option( 'my_first_plugin_options' );
    $value = $options['footer_text'] ?? '感谢阅读本文!由【我的第一个插件】生成。'; // 默认值
    ?&gt;
    <input type='text' name='my_first_plugin_options[footer_text]' value='<?php echo esc_attr( $value ); ?>' style='width: 400px;'>
    <p class="description">Hỗ trợ các thẻ HTML đơn giản, chẳng hạn như… <strong>, <em>, <a>。</p>
    <?php
}

public function options_page_html() {
    // 检查用户权限
    if ( ! current_user_can( 'manage_options' ) ) {
        return;
    }
    ?>
    <div class="wrap">
        <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
        <form action='/vi/options.php/' method='post' data-trp-original-action="options.php">
            <?php
            settings_fields( 'my_first_plugin_settings' );
            do_settings_sections( 'my-first-plugin' );
            submit_button();
            ?>
        <input type="hidden" name="trp-form-language" value="vi"/></form>
    </div>
    &lt;?php
}

Cuối cùng, hãy sửa đổi hàm hiển thị ở phía trước (front-end).add_footer_to_contentHãy thực hiện điều đó để đọc văn bản từ các tùy chọn trong cơ sở dữ liệu:

public function add_footer_to_content( $content ) {
    if ( is_single() &amp;&amp; in_the_loop() &amp;&amp; is_main_query() ) {
        $options = get_option( 'my_first_plugin_options' );
        $footer_text = $options['footer_text'] ?? '感谢阅读本文!由【我的第一个插件】生成。';
        $custom_footer = '<div class="my-plugin-footer"><p>'`. wp_kses_post($Footer_text)`.'</p></div>';
        $content .= $custom_footer;
    }
    return $content;
}

wp_kses_post()Hàm này đảm bảo rằng trong văn bản mà người dùng nhập vào chỉ được phép sử dụng những thẻ HTML an toàn; đây là một biện pháp bảo mật rất quan trọng.

Máy chủ chia sẻ của InterServer
Lưu trữ chia sẻ với mức phí $2,50 USD mỗi tháng, giảm giá $0,1 USD trong tháng đầu tiên, mã giảm giá tryinterserver, với 461 ứng dụng đám mây và cài đặt chỉ bằng một cú nhấp chuột.

Quốc tế hóa plugin và thực hành tốt nhất

Để các plugin có thể được sử dụng bởi người dùng trên toàn thế giới, việc hỗ trợ đa ngôn ngữ (internationalization – i18n) là bước không thể thiếu. WordPress sử dụng framework GNU gettext để thực hiện công việc dịch thuật.

Text field and translation function

Trước hết, hãy đảm bảo rằng bạn đã định nghĩa các thông tin cần thiết trong phần chú thích ở đầu plugin của mình.Text Domainví dụmy-first-pluginSau đó, tại tất cả các chuỗi cần được dịch trong plugin, hãy sử dụng một hàm cụ thể để bao bọc chúng.

Hãy sửa đổi đoạn mã trước đây của chúng ta để bổ sung tính năng hỗ trợ dịch văn bản cho kết quả đầu ra.

// 在构造函数中加载翻译文件
public function __construct() {
    // ... 其他钩子 ...
    add_action( 'plugins_loaded', array( $this, 'load_textdomain' ) );
}

public function load_textdomain() {
    load_plugin_textdomain(
        'my-first-plugin',
        false,
        dirname( plugin_basename( __FILE__ ) ) . '/languages/'
    );
}

// 修改设置页面的字符串
public function section_callback() {
    echo '<p>' . esc_html__( '在这里配置显示在文章末尾的文本内容。', 'my-first-plugin' ) . '</p>';
}

public function footer_text_field_render() {
    $options = get_option( 'my_first_plugin_options' );
    $value = $options['footer_text'] ?? __( '感谢阅读本文!由【我的第一个插件】生成。', 'my-first-plugin' );
    ?&gt;
    <input type='text' name='my_first_plugin_options[footer_text]' value='<?php echo esc_attr( $value ); ?>' style='width: 400px;'>
    <p class="description">&lt;?php esc_html_e( &#039;支持简单的HTML标签,如 <strong>, <em>, <a>。', 'my-first-plugin' ); ?&gt;</p>
    &lt;?php
}

// 注意:用户在前台输入的“页脚文本”本身通常不需要翻译,因为它是由管理员设置的具体内容。

__()Dùng để dịch và trả về chuỗi ký tự.esc_html__()Dùng để dịch và mã hóa (escape) nội dung HTML được hiển thị trên trang web._e()Dùng để dịch và hiển thị ngay lập tức các chuỗi văn bản. Tham số thứ hai của hàm là ô văn bản; nó phải trùng khớp với thông tin được định nghĩa bởi plugin.

An toàn, hiệu suất và tổ chức mã nguồn

Ngoài việc hỗ trợ nhiều ngôn ngữ (đa ngôn ngữ), còn cần tuân theo các thực tiễn tốt nhất sau:
1. 安全:对所有用户输入进行验证、清理和转义。使用sanitize_text_field(), esc_html(), wp_kses_post()Các hàm như vậy. Hãy sử dụng chúng.wp_nonce_field()Ngăn chặn các cuộc tấn công CSRF (Cross-Site Request Forgery).
2. 性能:合理使用钩子,避免在每次页面加载时执行不必要的数据库查询。可以考虑对输出结果进行 transient 缓存。
3. 代码组织:对于复杂的插件,应将文件按功能模块拆分。主文件负责引导,类和方法放在includes/Mục lục: Các tài nguyên phía trước (CSS, JS) được đặt ở đây.assets/Mục lục.
4. 卸载清理:如果你的插件创建了数据库表或选项,应该提供卸载功能来清理数据。可以通过一个独立的uninstall.phptrong thư mục gốc của trang web.

Tóm lại

Thông qua hướng dẫn này, chúng ta đã hoàn thành toàn bộ quy trình phát triển một plugin tùy chỉnh cho WordPress với các tính năng cơ bản: từ việc thiết lập môi trường phát triển, tạo cấu trúc cơ bản của plugin, sử dụng hệ thống hook để thêm các chức năng mới, đến việc xây dựng trang cấu hình nền tảng, và cuối cùng là áp dụng các biện pháp hỗ trợ đa ngôn ngữ (internationalization) và bảo mật (security). Bạn đã học được cách thực hiện tất cả những bước này một cách hiệu quả.add_filteradd_actionTương tác với lõi WordPress, cách sử dụng Settings API để tạo trang tùy chọn đáng tin cậy, và cách thông quaload_plugin_textdomainHãy đảm bảo rằng plugin của bạn hỗ trợ nhiều ngôn ngữ. Hãy nhớ rằng, khi phát triển plugin, an toàn, khả năng bảo trì và trải nghiệm người dùng là những yếu tố quan trọng nhất cần được xem xét. Dựa trên những yếu tố này, bạn có thể tiếp tục khám phá các tính năng nâng cao hơn như các loại bài viết tùy chỉnh, dữ liệu meta (metadata), mã ngắn (shortcodes), và các điểm cuối API REST (REST API endpoints), để xây dựng những tiện ích mở rộng (extensions) cho WordPress mạnh mẽ hơn.

FAQ 常见问题

Tại sao plugin của tôi không hiển thị trong menu nền?

Điều này thường xảy ra do vấn đề về quyền truy cập hoặc lỗi trong mã nguồn. Trước hết, hãy đảm bảo rằng bạn đã kiểm tra và sửa các lỗi liên quan đến quyền truy cập, cũng như các lỗi trong đoạn mã của mình.add_options_pageadd_menu_pageCác tham số quyền được chỉ định trong hàm (như…)‘manage_options’Nó phải phù hợp với vai trò người dùng mà bạn đang đăng nhập hiện tại. Thứ hai, hãy kiểm tra…admin_menuHãy kiểm tra xem các “hook” (các đoạn mã được gắn vào hệ thống WordPress để thực hiện các tác vụ nhất định) có được khởi tạo đúng cách hay không, và đảm bảo rằng các hàm được gọi trong các hook đó không chứa lỗi ngữ pháp nào có thể gây ra sự gián đoạn trong quá trình thực thi PHP. Cách đơn giảnWP_DEBUGHãy kiểm tra xem có thông báo lỗi nào được hiển thị không.

Làm thế nào để thêm các tệp CSS và JavaScript tùy chỉnh cho một plugin?

Cách làm đúng là sử dụngwp_enqueue_style()wp_enqueue_script()Đối với các tài nguyên phía trước (front-end resources), chúng nên được gắn (mount) vào đúng vị trí thích hợp.wp_enqueue_scriptsTrên các “khóa hành động” (action hooks); đối với các tài nguyên trong giao diện quản trị (backend resources), chúng sẽ được gắn (mounted) vào đó.admin_enqueue_scriptshook.

Trong lớp plugin của bạn, bạn có thể thêm các phương thức sau:

public function enqueue_frontend_assets() {
    wp_enqueue_style( ‘my-plugin-style’, plugin_dir_url( __FILE__ ) . ‘assets/css/style.css’, array(), ‘1.0.0’ );
}

Sau đó, trong hàm khởi tạo (constructor), thực hiện thông qua…add_action( ‘wp_enqueue_scripts’, array( $this, ‘enqueue_frontend_assets’ ) );Đăng ký. Điều này sẽ đảm bảo rằng việc quản lý các thành phần phụ thuộc (dependencies) được thực hiện một cách chính xác và tránh xung đột với các plugin hoặc theme khác.

Khi người dùng gỡ cài đặt tiện ích mở rộng (plugin), làm thế nào để dọn dẹp các tùy chọn dữ liệu mà tôi đã tạo ra?

WordPress cung cấp hai phương pháp chính để thực hiện việc này. Phương pháp đầu tiên là đăng ký một “hook” (một hàm được gọi tự động trong quá trình thực hiện một số thao tác nhất định của hệ thống), nhưng phương pháp này không được sử dụng phổ biến trong các plugin được viết theo nguyên lý hướng đối tượng. Phương pháp được khuyến nghịuninstall.phpĐây là một tệp tin, và nó nằm ở cùng cấp độ với tệp tin plugin chính của bạn.

Khi người dùng xóa một plugin thông qua giao diện quản trị của WordPress, WordPress sẽ tự động kiểm tra và thực hiện các thao tác liên quan đến file đó.uninstall.phpTrong trường hợp này, bạn cần kiểm tra trước.WP_UNINSTALL_PLUGINLiệu các hằng số (constants) có được định nghĩa trước hay không? Sau đó, tất cả các tùy chọn được tạo ra bởi plugin, các bảng cơ sở dữ liệu tùy chỉnh, và các dữ liệu khác có được xóa một cách an toàn không? Ví dụ:

if ( ! defined( ‘WP_UNINSTALL_PLUGIN’ ) ) {
    exit;
}
delete_option( ‘my_first_plugin_options’ );
// 如果有自定义表:$wpdb->query( “DROP TABLE IF EXISTS {$wpdb->prefix}my_table” );

Làm thế nào để plugin của tôi tương thích với nhiều phiên bản WordPress hơn?

Chìa khóa để duy trì tính tương thích nằm ở việc sử dụng thận trọng các hàm và tính năng mới trong phiên bản mới, đồng thời cung cấp các giải pháp thay thế cho các phiên bản cũ. Trước khi gọi một hàm có thể chỉ tồn tại trong phiên bản mới, hãy…function_exists()Hãy tiến hành kiểm tra. Ví dụ, nếu bạn muốn sử dụng những tính năng được giới thiệu trong phiên bản 5.0…wp_dateHàm có thể:

if ( function_exists( ‘wp_date’ ) ) {
    $date = wp_date( get_option( ‘date_format’ ), $timestamp );
} else {
    $date = date_i18n( get_option( ‘date_format’ ), $timestamp );
}

Đồng thời, hãy ghi rõ phiên bản WordPress tối thiểu đã được kiểm thử trong phần chú thích ở đầu plugin và trong tệp readme. Việc thường xuyên kiểm thử trên các phiên bản WordPress cũ là cách tốt nhất để đảm bảo tính tương thích.