Khi một plugin chỉ có vài dòng hook, bạn nhét tất cả vào một file .php cũng chẳng sao. Nhưng khi nó phình ra hàng chục lớp, có trang cài đặt, có REST endpoint, có cron, có khối Gutenberg riêng, thì cái file đơn lẻ ấy biến thành mớ bòng bong. Đây là lúc một khung sườn được định sẵn cấu trúc trở nên đáng giá.
Boilerplate giải quyết vấn đề gì
Boilerplate (mã khung lặp lại) là phần mã ai cũng phải viết giống nhau ở mọi dự án: file header plugin, hàm kích hoạt/gỡ kích hoạt, cơ chế nạp lớp, chỗ móc các hook. Thay vì gõ lại từ đầu mỗi lần, bạn chép một khung đã chuẩn hoá rồi đổi tên.
Giá trị thật không nằm ở chuyện tiết kiệm vài phút gõ phím. Nó nằm ở tính nhất quán. Khi nhiều người cùng làm, hoặc khi bạn quay lại plugin sau sáu tháng, một cấu trúc quen thuộc giúp định vị file trong vài giây mà không phải đọc lại toàn bộ. Theo tài liệu của dự án WordPress Plugin Boilerplate (WPPB), quy ước tách phần quản trị và phần công khai chính là để các lập trình viên định hướng nhanh trên một codebase mới.
Cấu trúc thư mục chuẩn 2026
Một khung sườn hiện đại thường chia làm ba vùng trách nhiệm. Cách bố trí phổ biến trông như sau:
ten-plugin/
├── ten-plugin.php ← file header + điểm khởi động
├── uninstall.php ← dọn dữ liệu khi gỡ hẳn
├── composer.json ← khai báo autoload PSR-4
├── includes/ ← phần LÕI dùng chung
│ ├── class-plugin.php (lớp điều phối trung tâm)
│ ├── class-loader.php (gom & đăng ký hook)
│ ├── class-activator.php
│ └── class-i18n.php
├── admin/ ← chỉ chạy trong wp-admin
│ ├── class-admin.php
│ ├── css/ js/
│ └── partials/
├── public/ ← chỉ chạy ở giao diện ngoài
│ ├── class-public.php
│ ├── css/ js/
│ └── partials/
└── languages/ ← file .pot, .po, .moBa thư mục includes, admin, public phản ánh đúng vòng đời thực tế của plugin: phần lõi luôn nạp, phần admin chỉ nạp khi is_admin() đúng, phần public chỉ nạp ở front-end. Tách như vậy giúp bạn không vô tình tải mã quản trị nặng nề lên trang khách xem.
File khởi động giữ vai trò “công tắc”
File chính chỉ nên làm một việc: khai báo header, định nghĩa hằng số đường dẫn, rồi gọi lớp điều phối. Toàn bộ logic nằm trong các lớp, không nằm trong file này.
<?php
/**
* Plugin Name: Ten Plugin
* Version: 1.0.0
* Requires PHP: 8.1
*/
defined( 'ABSPATH' ) || exit;
define( 'TEN_PLUGIN_VERSION', '1.0.0' );
define( 'TEN_PLUGIN_PATH', plugin_dir_path( __FILE__ ) );
require TEN_PLUGIN_PATH . 'vendor/autoload.php';
register_activation_hook( __FILE__, [ 'Web22TenPluginActivator', 'activate' ] );
add_action( 'plugins_loaded', function () {
( new Web22TenPluginPlugin() )->run();
} );Dòng defined( 'ABSPATH' ) || exit; chặn truy cập trực tiếp file. Việc khởi tạo đặt trong hook plugins_loaded để chắc chắn WordPress đã nạp xong môi trường trước khi plugin chạy.

OOP và namespace: vì sao plugin lớn cần
OOP (lập trình hướng đối tượng) đóng gói mỗi nhóm chức năng vào một lớp riêng, có trạng thái và phương thức riêng, thay vì rải hàng trăm hàm rời rạc ra phạm vi toàn cục. Với plugin nhỏ, khác biệt không lớn. Với plugin lớn, nó là khác biệt giữa bảo trì được và không.
Namespace (không gian tên) giải quyết một rủi ro rất thật của WordPress: mọi plugin chạy chung một không gian. Nếu plugin của bạn có hàm get_settings() và một plugin khác cũng vậy, site sẽ chết vì lỗi trùng tên. Đặt mã trong namespace Web22TenPlugin; tạo một “họ” riêng, gần như loại bỏ va chạm. Đây là lý do Web22 luôn dùng namespace cho plugin web22-core tự code chạy trên web22.dev, dù chỉ phục vụ một site.
Autoload PSR-4: bỏ hẳn require_once
PSR-4 là một chuẩn cộng đồng PHP quy định cách ánh xạ tên lớp sang đường dẫn file. Khi bật autoload theo chuẩn này, bạn không còn phải viết hàng loạt dòng require_once nữa — gọi tới lớp nào, PHP tự tìm và nạp đúng file đó.
Cách gọn nhất là khai báo qua Composer trong composer.json:
{
"autoload": {
"psr-4": {
"Web22TenPlugin": "includes/"
}
}
}Chạy composer dump-autoload một lần để Composer sinh ra trình nạp tự động. Sau đó chỉ cần require 'vendor/autoload.php' trong file chính là xong. Quy ước ánh xạ: namespace Web22TenPluginLoader trỏ tới file includes/Loader.php.
Nếu không muốn phụ thuộc Composer (ví dụ plugin gửi lên kho WordPress.org cần mã thuần), bạn có thể tự viết một hàm autoload ngắn đăng ký qua spl_autoload_register() — chuyển dấu gạch chéo ngược trong tên lớp thành đường dẫn thư mục. Cách thủ công này không cần thư mục vendor nhưng phải tự lo việc đặt tên file khớp với lớp.
Lớp Loader: nơi gom toàn bộ hook
Một mẫu hay gặp trong khung sườn là tách riêng việc đăng ký hook ra một lớp Loader. Thay vì rải add_action khắp nơi, mỗi lớp khai báo hook của mình rồi đẩy vào Loader, Loader gọi add_action/add_filter một lượt. Cách này cho bạn một “bản đồ” duy nhất nhìn thấy mọi điểm móc của plugin — cực kỳ tiện khi gỡ lỗi. Để hiểu sâu cơ chế đứng sau, xem bài về cách action và filter hook vận hành trong WordPress.
Chọn boilerplate nào năm 2026
- WPPB (WordPress Plugin Boilerplate) — khung lâu đời, sinh mã sẵn qua trình generator, tách admin/public rõ ràng. Phù hợp người mới làm quen quy ước WordPress, nhưng dùng autoload kiểu cũ.
- Các boilerplate PSR-4 hiện đại — như code-soup hay UVLabs, viết cho PHP 8.1+, dùng namespace, PSR-4, có sẵn công cụ đóng gói tài sản (Webpack) và kiểm tra chất lượng mã. Phù hợp dự án mới, đội nhiều người.
Lời khuyên thực tế: đừng chọn boilerplate “to nhất”. Chọn cái bạn đọc hiểu được toàn bộ. Một khung sườn có mười lớp bạn không biết để làm gì sẽ thành gánh nặng chứ không phải trợ thủ.

Khi nào KHÔNG cần boilerplate
Nếu plugin của bạn chỉ là vài hook đơn giản — đăng ký một shortcode, sửa một filter — thì dựng cả bộ khung OOP là thừa thãi. Boilerplate chỉ trả công khi plugin đủ lớn để chi phí cấu trúc nhỏ hơn chi phí hỗn loạn. Nếu bạn mới bắt đầu và chỉ cần một file chạy được, hãy xem trước các bước viết một plugin WordPress đầu tiên rồi mới tính tới việc tách lớp.
Câu hỏi thường gặp
Boilerplate có làm plugin nặng hơn không?
Gần như không. Autoload chỉ nạp lớp khi thực sự được gọi, nên thêm nhiều file không đồng nghĩa với chậm hơn. Cái nặng là tải nhầm mã admin ra front-end — và cấu trúc tách thư mục giúp tránh đúng lỗi đó.
Gửi plugin lên WordPress.org có dùng được thư mục vendor của Composer không?
Được, miễn bạn nén kèm vendor vào gói cuối. Nhiều người chọn tự viết autoload thuần để gói gọn nhẹ, không phụ thuộc Composer khi phát hành.
Boilerplate khác gì với việc tự dựng cấu trúc tay?
Không khác về kết quả nếu bạn đủ kinh nghiệm. Boilerplate chỉ là cấu trúc tay đã được người khác chuẩn hoá sẵn, để bạn khỏi quyết định lại từ đầu mỗi dự án.
Web22 tự phát triển theme và plugin web22-core trên chính WordPress, nên hiểu rõ chỗ một plugin dễ phình ra mất kiểm soát. Nếu bạn cần một plugin viết riêng theo nghiệp vụ được dựng trên khung sườn gọn gàng, hoặc muốn tìm hiểu rộng hơn về cách Web22 làm website WordPress, đây là điểm bắt đầu hợp lý.
