Gỡ cài đặt, Kích hoạt, Tắt plugin: Các tính năng điển hình và cách thực hiện
Contents
Câu hỏi:
Tôi đang tạo một plugin WordPress. Tôi nên đưa những tính năng cơ bản gì vào uninstall feature (tính năng gỡ cài đặt)?
Ví dụ: Tôi có nên xóa các bảng tôi đã tạo trong hàm cài đặt (install function) không?
Tôi có nên clean up các mục tùy chọn (option entries) của mình không?
Và còn điều gì khác cần thực hiện nữa?
redconservatory
Tôi đã lãng phí rất nhiều thời gian để làm cho nó hoạt động. Vấn đề là ở chỗ init hook không hoạt động bên trong của registering hook. Tôi cho rằng không có một hook (action hay filter) nào sẽ hoạt động ngay. Hãy đọc những ghi chú trong link dưới đây.
codex.wordpress.org/Function_Reference/register_activation_hook
Ở đây nó nói rằng: “Việc đăng kí hook trong hook plugins_loaded của bạn sẽ là quá trễ và nó sẽ không chạy! (Thậm chí ngay cả khi nó có vẻ đang hoạt động với register_deactivation_hook cho tới khi the wp_loaded hook. – Anton
Tôi chính là người đã cập nhật codex mà bạn đã đề cập tới, vì vậy câu trả lời ở trên cũng có thể coi như là câu trả lời của tôi. 🙂 – kaiser
Câu trả lời chính xác nhất
Có ba hook khác nhau. Chúng được kích hoạt trong các trường hợp sau:
- Gỡ cài đặt
- Tắt (deactivation)
- Kích hoạt
Cách kích hoạt các hàm một cách an toàn trong các tình huống
Dưới đây là các cách phù hợp để hook một cách an toàn các hàm callback được kích hoạt trong các hoạt động đã được đề cập.
Bạn có thể sử dụng code này trong một plugin có dùng:
- các hàm đơn giản (plain functions),
- một lớp hoặc
- một lớp bên ngoài (external class),
Tôi sẽ cho bạn thấy ba plugin demo khác nhau mà bạn có thể kiểm tra và sau đó triển khai code trong các plugin của riêng bạn.
Lưu ý quan trọng!
Vì chủ đề này cực kỳ khó khăn, rất nhiều chi tiết và có hàng chục trường hợp khác nhau nên câu trả lời này sẽ không bao thể hoàn hảo được. Tôi sẽ tiếp tục cải thiện nó dần dần, vì vậy hãy kiểm tra lại thường xuyên.
(1) Kích hoạt/ Tắt/ Gỡ cài đặt plugin.
Các callback setup plugin được kích hoạt bởi lõi và bạn không thể can thiệp đến cách lõi này thực hiện các công việc này. Có một số điều cần lưu ý:
- Không bao giờ, echo/print bất cứ thứ gì (!) trong suốt setup callback. Điều này sẽ làm xuất hiện thông báo headers already sent và lõi sẽ khuyên bạn nên tắt và xóa plugin của bạn … không hỏi: Tôi biết …
- Bạn sẽ không thấy bất kỳ output trực quan nào. Nhưng tôi đã thêm các câu lệnh exit () vào tất cả các callback khác nhau để bạn có thể hiểu rõ hơn về những gì đang thực sự xảy ra. Chỉ cần bỏ ghi chú để xem mọi thứ hoạt động như thế nào.
- Điều cực kỳ quan trọng là bạn phải kiểm tra xem liệu __FILE__! = WP_PLUGIN_INSTALL và (nếu không: hủy bỏ!) để xem liệu một người có thực sự đang muốn gỡ cài đặt plugin hay không. Tôi muốn khuyên bạn chỉ nên kích hoạt on_deactivation () callbacks trong quá trình development, làm như vậy bạn sẽ tiết kiệm cho mình – khoảng thời gian mà bạn cần để mọi thứ trở lại bình thường. Ít nhất đấy là những gì tôi đã làm.
- Tôi cũng làm một số công cụ bảo mật. Một số cũng được thực hiện bởi cốt lõi, nhưng này! Cẩn thận là không thừa!
- Trước tiên, tôi không cho phép truy cập tệp trực tiếp khi lõi không được tải: defined( ‘ABSPATH’ ) OR exit;
- Sau đó, tôi kiểm tra xem người dùng hiện tại có được phép thực hiện tác vụ này hay không.
- Và nhiệm vụ cuối cùng, tôi kiểm tra liên kết giới thiệu (referrer). Lưu ý: Có thể có kết quả không mong muốn với màn hình wp_die (), nó yêu cầu quyền thích hợp (và nếu bạn muốn thử lại … vâng, chắc chắn rồi), khi bạn gặp lỗi. Điều này xảy ra khi lõi chuyển hướng bạn, khi đó đặt $GLOBALS[‘wp_list_table’] -> current_action (); hiện tại thành error_scrape và sau đó kiểm tra referrer cho check_admin_referer(‘plugin-activation-error_’. $ plugin);, trong đó $plugin là $_REQUEST[‘plugin’]. Vì vậy, chuyển hướng xảy ra ở một nửa page load và bạn sẽ nhận được thanh cuộn có dây (wired scroll bar) này và màn hình tối đen trong hộp thông báo/ tin nhắn (notice/message box) admin màu vàng. Nếu điều này xảy ra: Giữ bình tĩnh và tìm kiếm lỗi với lệnh exit() và gỡ lỗi từng bước một.
(A) Plugin các hàm đơn giản
Hãy nhớ rằng cái này có thể không hoạt động nếu bạn hook các callback trước khi định nghĩa hàm (function definition).
<?php
defined( ‘ABSPATH’ ) OR exit;
/**
* Plugin Name: (WCM) Activate/Deactivate/Uninstall – Functions
* Description: Example Plugin to show activation/deactivation/uninstall callbacks for plain functions.
* Author: Franz Josef Kaiser/wecodemore
* Author URL: http://unserkaiser.com
* Plugin URL: http://wordpress.stackexchange.com/questions/25910/uninstall-activate-deactivate-a-plugin-typical-features-how-to/25979#25979
*/function WCM_Setup_Demo_on_activation()
{
if ( ! current_user_can( ‘activate_plugins’ ) )
return;
$plugin = isset( $_REQUEST[‘plugin’] ) ? $_REQUEST[‘plugin’] : ”;
check_admin_referer( “activate-plugin_{$plugin}” );# Uncomment the following line to see the function in action
# exit( var_dump( $_GET ) );
}function WCM_Setup_Demo_on_deactivation()
{
if ( ! current_user_can( ‘activate_plugins’ ) )
return;
$plugin = isset( $_REQUEST[‘plugin’] ) ? $_REQUEST[‘plugin’] : ”;
check_admin_referer( “deactivate-plugin_{$plugin}” );# Uncomment the following line to see the function in action
# exit( var_dump( $_GET ) );
}function WCM_Setup_Demo_on_uninstall()
{
if ( ! current_user_can( ‘activate_plugins’ ) )
return;
check_admin_referer( ‘bulk-plugins’ );// Important: Check if the file is the one
// that was registered during the uninstall hook.
if ( __FILE__ != WP_UNINSTALL_PLUGIN )
return;# Uncomment the following line to see the function in action
# exit( var_dump( $_GET ) );
}register_activation_hook( __FILE__, ‘WCM_Setup_Demo_on_activation’ );
register_deactivation_hook( __FILE__, ‘WCM_Setup_Demo_on_deactivation’ );
register_uninstall_hook( __FILE__, ‘WCM_Setup_Demo_on_uninstall’ );
(B) Một cấu trúc class based (dựa trên lớp)/OOP
Đây là ví dụ phổ biến nhất trong các plugin hiện nay.
<?php
defined( ‘ABSPATH’ ) OR exit;
/**
* Plugin Name: (WCM) Activate/Deactivate/Uninstall – CLASS
* Description: Example Plugin to show activation/deactivation/uninstall callbacks for classes/objects.
* Author: Franz Josef Kaiser/wecodemore
* Author URL: http://unserkaiser.com
* Plugin URL: http://wordpress.stackexchange.com/questions/25910/uninstall-activate-deactivate-a-plugin-typical-features-how-to/25979#25979
*/register_activation_hook( __FILE__, array( ‘WCM_Setup_Demo_Class’, ‘on_activation’ ) );
register_deactivation_hook( __FILE__, array( ‘WCM_Setup_Demo_Class’, ‘on_deactivation’ ) );
register_uninstall_hook( __FILE__, array( ‘WCM_Setup_Demo_Class’, ‘on_uninstall’ ) );add_action( ‘plugins_loaded’, array( ‘WCM_Setup_Demo_Class’, ‘init’ ) );
class WCM_Setup_Demo_Class
{
protected static $instance;public static function init()
{
is_null( self::$instance ) AND self::$instance = new self;
return self::$instance;
}public static function on_activation()
{
if ( ! current_user_can( ‘activate_plugins’ ) )
return;
$plugin = isset( $_REQUEST[‘plugin’] ) ? $_REQUEST[‘plugin’] : ”;
check_admin_referer( “activate-plugin_{$plugin}” );# Uncomment the following line to see the function in action
# exit( var_dump( $_GET ) );
}public static function on_deactivation()
{
if ( ! current_user_can( ‘activate_plugins’ ) )
return;
$plugin = isset( $_REQUEST[‘plugin’] ) ? $_REQUEST[‘plugin’] : ”;
check_admin_referer( “deactivate-plugin_{$plugin}” );# Uncomment the following line to see the function in action
# exit( var_dump( $_GET ) );
}public static function on_uninstall()
{
if ( ! current_user_can( ‘activate_plugins’ ) )
return;
check_admin_referer( ‘bulk-plugins’ );// Important: Check if the file is the one
// that was registered during the uninstall hook.
if ( __FILE__ != WP_UNINSTALL_PLUGIN )
return;# Uncomment the following line to see the function in action
# exit( var_dump( $_GET ) );
}public function __construct()
{
# INIT the plugin: Hook your callbacks
}
}
(C) Một cấu trúc class based (dựa trên lớp)/OOP với một đối tượng setup bên ngoài
Kịch bản này giả định rằng bạn có một tệp plugin chính và tệp thứ hai có tên là setup.php trong thư mục con của plugin có tên là inc: ~/wp-content/plugins/your_plugin/inc/setup.php. Nó sẽ hoạt động tốt khi thư mục plugin nằm ngoài cấu trúc thư mục WP mặc định, cũng như khi thư mục nội dung được đổi tên hoặc trong trường hợp tệp setup của bạn được đặt tên khác. Chỉ riêng thư mục inc phải có cùng tên và vị trí tương đối với thư mục plugin gốc.
Lưu ý: Bạn có thể chỉ cần thực hiện ba hàm register_*_hook()* và các lớp rồi thả chúng vào plugin của bạn.
File plugin chính:
<?php
defined( ‘ABSPATH’ ) OR exit;
/**
* Plugin Name: (WCM) Activate/Deactivate/Uninstall – FILE/CLASS
* Description: Example Plugin
* Author: Franz Josef Kaiser/wecodemore
* Author URL: http://unserkaiser.com
* Plugin URL: http://wordpress.stackexchange.com/questions/25910/uninstall-activate-deactivate-a-plugin-typical-features-how-to/25979#25979
*/register_activation_hook( __FILE__, array( ‘WCM_Setup_Demo_File_Inc’, ‘on_activation’ ) );
register_deactivation_hook( __FILE__, array( ‘WCM_Setup_Demo_File_Inc’, ‘on_deactivation’ ) );
register_uninstall_hook( __FILE__, array( ‘WCM_Setup_Demo_File_Inc’, ‘on_uninstall’ ) );add_action( ‘plugins_loaded’, array( ‘WCM_Setup_Demo_File’, ‘init’ ) );
class WCM_Setup_Demo_File
{
protected static $instance;public static function init()
{
is_null( self::$instance ) AND self::$instance = new self;
return self::$instance;
}public function __construct()
{
add_action( current_filter(), array( $this, ‘load_files’ ), 30 );
}public function load_files()
{
foreach ( glob( plugin_dir_path( __FILE__ ).’inc/*.php’ ) as $file )
include_once $file;
}
}
File setup:
<?php
defined( ‘ABSPATH’ ) OR exit;class WCM_Setup_Demo_File_Inc
{
public static function on_activation()
{
if ( ! current_user_can( ‘activate_plugins’ ) )
return;
$plugin = isset( $_REQUEST[‘plugin’] ) ? $_REQUEST[‘plugin’] : ”;
check_admin_referer( “activate-plugin_{$plugin}” );# Uncomment the following line to see the function in action
# exit( var_dump( $_GET ) );
}public static function on_deactivation()
{
if ( ! current_user_can( ‘activate_plugins’ ) )
return;
$plugin = isset( $_REQUEST[‘plugin’] ) ? $_REQUEST[‘plugin’] : ”;
check_admin_referer( “deactivate-plugin_{$plugin}” );# Uncomment the following line to see the function in action
# exit( var_dump( $_GET ) );
}public static function on_uninstall()
{
if ( ! current_user_can( ‘activate_plugins’ ) )
return;
check_admin_referer( ‘bulk-plugins’ );// Important: Check if the file is the one
// that was registered during the uninstall hook.
if ( __FILE__ != WP_UNINSTALL_PLUGIN )
return;# Uncomment the following line to see the function in action
# exit( var_dump( $_GET ) );
}
}
(2) Plugin updates
Bếu bạn viết một plugin có bảng DB hoặc option riêng của nó, có thể có một số trường hợp bạn sẽ cần thay đổi hoặc nâng cấp các thứ.
Buồn thay, hiện nay không thể chạy thứ gì đó trên cài đặt plugin/theme hay update/upgrade (cập nhật/nâng cấp). Nhưng lại thật may, có một cách để làm được việc đó: Hook một hàm tùy chỉnh (custom function) vào một tùy chọn tùy chỉnh (custom option).
function prefix_upgrade_plugin()
{
$v = ‘plugin_db_version’;
$update_option = null;
// Upgrade to version 2
if ( 2 !== get_option( $v ) )
{
if ( 2 < get_option( $v ) )
{
// Callback function must return true on success
$update_option = custom_upgrade_cb_fn_v3();// Only update option if it was an success
if ( $update_option )
update_option( $v, 2 );
}
}// Upgrade to version 3, runs just after upgrade to version 2
if ( 3 !== get_option( $v ) )
{
// re-run from beginning if previous update failed
if ( 2 < get_option( $v ) )
return prefix_upgrade_plugin();if ( 3 < get_option( $v ) )
{
// Callback function must return true on success
$update_option = custom_upgrade_cb_fn_v3();// Only update option if it was an success
if ( $update_option )
update_option( $v, 3 );
}
}// Return the result from the update cb fn, so we can test for success/fail/error
if ( $update_option )
return $update_option;return false;
}
add_action(‘admin_init’, ‘prefix_upgrade_plugin’ );
Hàm update này là một ví dụ cũng không thể nói là tuyệt hay là không được tốt. Nhưng có thể nói, đây là một ví dụ và kĩ thuật này hoạt động tốt. Tôi sẽ cải thiện câu trả lời này với một update sau.
Câu trả lời của kaiser
Để kiểm tra hệ thống hiện tại về các tính năng được yêu cầu như phiên bản PHP hoặc phần mở rộng được cài đặt, bạn có thể sử dụng:
<?php # -*- coding: utf-8 -*-
/**
* Plugin Name: T5 Check Plugin Requirements
* Description: Test for PHP version and installed extensions
* Plugin URI:
* Version: 2013.03.31
* Author: Thomas Scholz
* Author URI: http://toscho.de
* Licence: MIT
* License URI: http://opensource.org/licenses/MIT
*//*
* Don’t start on every page, the plugin page is enough.
*/
if ( ! empty ( $GLOBALS[‘pagenow’] ) && ‘plugins.php’ === $GLOBALS[‘pagenow’] )
add_action( ‘admin_notices’, ‘t5_check_admin_notices’, 0 );/**
* Test current system for the features the plugin needs.
*
* @return array Errors or empty array
*/
function t5_check_plugin_requirements()
{
$php_min_version = ‘5.4’;
// see http://www.php.net/manual/en/extensions.alphabetical.php
$extensions = array (
‘iconv’,
‘mbstring’,
‘id3’
);
$errors = array ();$php_current_version = phpversion();
if ( version_compare( $php_min_version, $php_current_version, ‘>’ ) )
$errors[] = “Your server is running PHP version $php_current_version but
this plugin requires at least PHP $php_min_version. Please run an upgrade.”;foreach ( $extensions as $extension )
if ( ! extension_loaded( $extension ) )
$errors[] = “Please install the extension $extension to run this plugin.”;return $errors;
}
/**
* Call t5_check_plugin_requirements() and deactivate this plugin if there are error.
*
* @wp-hook admin_notices
* @return void
*/
function t5_check_admin_notices()
{
$errors = t5_check_plugin_requirements();if ( empty ( $errors ) )
return;// Suppress “Plugin activated” notice.
unset( $_GET[‘activate’] );// this plugin’s name
$name = get_file_data( __FILE__, array ( ‘Plugin Name’ ), ‘plugin’ );printf(
‘<div class=”error”><p>%1$s</p>
<p><i>%2$s</i> has been deactivated.</p></div>’,
join( ‘</p><p>’, $errors ),
$name[0]
);
deactivate_plugins( plugin_basename( __FILE__ ) );
}
Kiểm tra đối với PHP 5.5:
Câu trả lời của fuxia