- Вопрос или проблема
- Существует три разных хуков. Они срабатывают в следующих случаях:
- Как безопасно вызывать функции в этих сценариях
- Важное замечание заранее!
- (1) Активация/Деактивация/Удаление плагинов.
- (A) Плагин с обычными функциями
- (B) Архитектура, основанная на классах/ООП
- (C) Архитектура, основанная на классах/ООП с внешним объектом установки
- (2) Обновления плагинов
- Ответ или решение
- Типичные функции для активации, деактивации и удаления плагина
- Как реализовать эти функции
- 1. Активация плагина
- 2. Деактивация плагина
- 3. Удаление плагина
- Соображения безопасности
- Заключение
Вопрос или проблема
Я создаю плагин для WordPress. Какие типичные вещи я должен включить в функцию удаления?
Следует ли, например, удалять любые таблицы, которые я создал в функции установки?
Должен ли я очищать свои записи опций?
Что-нибудь еще?
Существует три разных хуков. Они срабатывают в следующих случаях:
- Удаление
- Деактивация
- Активация
Как безопасно вызывать функции в этих сценариях
Следующее показывает правильные способы безопасно подключать обратные функции, которые срабатывают в упомянутых действиях.
Вы можете использовать этот код в плагине, который использует
- обычные функции,
- классы или
- внешние классы,
Я покажу три разных демо-плагина, которые вы можете изучить, а затем реализовать код в своем собственном плагине(ах).
Важное замечание заранее!
Поскольку эта тема чрезвычайно сложная и очень детальная и имеет более десятка краевых случаев, этот ответ никогда не будет идеальным. Я буду по мере возможности его улучшать, поэтому проверяйте его регулярно.
(1) Активация/Деактивация/Удаление плагинов.
Обратные вызовы настройки плагина срабатывают ядром, и вы не можете повлиять на то, как это происходит. Есть несколько моментов, которые нужно держать в голове:
- Никогда не выводите
echo/print
ничего(!) во время обратных вызовов настройки. Это приведет к сообщениюheaders already sent
, и ядро предложит деактивировать и удалить ваш плагин… не спрашивайте: я знаю… - Вы не увидите никакого визуального вывода. Но я добавил
exit()
в разные обратные вызовы, чтобы вы могли понять, что на самом деле происходит. Просто раскомментируйте их, чтобы увидеть, как все работает. - Крайне важно проверить, если
__FILE__ != WP_PLUGIN_INSTALL
и (если нет: завершить!) чтобы узнать, действительно ли происходит удаление плагина. Я бы рекомендовал просто вызыватьon_deactivation()
во время разработки, чтобы сэкономить время, необходимое для восстановления всего. По крайней мере, это то, что делаю я. - Я также выполняю некоторые меры безопасности. Некоторые из них обеспечиваются ядром, но, эй! Лучше быть в безопасности, чем потом жалеть!.
- Сначала я запрещаю прямой доступ к файлам, если ядро не загружено:
defined( 'ABSPATH' ) OR exit;
- Затем я проверяю, имеет ли текущий пользователь право выполнять эту задачу.
- В последнюю очередь я проверяю реферера. Замечание: могут быть неожиданные результаты с экраном
wp_die()
, спрашивающим о правильных разрешениях (и если вы хотите попробовать снова … да, конечно), когда вы получили ошибку. Это происходит, так как ядро перенаправляет вас, устанавливает текущее$GLOBALS['wp_list_table']->current_action();
наerror_scrape
и затем проверяет реферера наcheck_admin_referer('plugin-activation-error_' . $plugin);
, где$plugin
это$_REQUEST['plugin']
. Поэтому перенаправление происходит наполовину загрузки страницы, и вы получаете этот странный скроллбар и экран смерти внутри желтого административного уведомления/сообщения. Если это произойдет: оставайтесь спокойными и просто ищите ошибку с помощьюexit()
и пошаговой отладки.
- Сначала я запрещаю прямой доступ к файлам, если ядро не загружено:
(A) Плагин с обычными функциями
Помните, что это может не сработать, если вы подключите обратные вызовы до определения функции.
<?php
defined( 'ABSPATH' ) OR exit;
/**
* Название плагина: (WCM) Активация/Деактивация/Удаление - Функции
* Описание: Пример плагина для демонстрации обратных вызовов активации/деактивации/удаления для обычных функций.
* Автор: Franz Josef Kaiser/wecodemore
* URL автора: http://unserkaiser.com
* 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}" );
# Раcкомментируйте следующую строку, чтобы увидеть функцию в действии
# 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}" );
# Раcкомментируйте следующую строку, чтобы увидеть функцию в действии
# exit( var_dump( $_GET ) );
}
function WCM_Setup_Demo_on_uninstall()
{
if ( ! current_user_can( 'activate_plugins' ) )
return;
check_admin_referer( 'bulk-plugins' );
// Важно: Проверьте, является ли файл тем
// который был зарегистрирован во время хуков удаления.
if ( __FILE__ != WP_UNINSTALL_PLUGIN )
return;
# Раcкомментируйте следующую строку, чтобы увидеть функцию в действии
# 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) Архитектура, основанная на классах/ООП
Это самый распространенный пример в современных плагинах.
<?php
defined( 'ABSPATH' ) OR exit;
/**
* Название плагина: (WCM) Активация/Деактивация/Удаление - КЛАСС
* Описание: Пример плагина для демонстрации обратных вызовов активации/деактивации/удаления для классов/объектов.
* Автор: Franz Josef Kaiser/wecodemore
* URL автора: http://unserkaiser.com
* 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}" );
# Раcкомментируйте следующую строку, чтобы увидеть функцию в действии
# 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}" );
# Раcкомментируйте следующую строку, чтобы увидеть функцию в действии
# exit( var_dump( $_GET ) );
}
public static function on_uninstall()
{
if ( ! current_user_can( 'activate_plugins' ) )
return;
check_admin_referer( 'bulk-plugins' );
// Важно: Проверьте, является ли файл тем
// который был зарегистрирован во время хуков удаления.
if ( __FILE__ != WP_UNINSTALL_PLUGIN )
return;
# Раcкомментируйте следующую строку, чтобы увидеть функцию в действии
# exit( var_dump( $_GET ) );
}
public function __construct()
{
# Инициализируйте плагин: Подключите ваши обратные вызовы
}
}
(C) Архитектура, основанная на классах/ООП с внешним объектом установки
Этот сценарий предполагает, что у вас есть основной файл плагина и второй файл с именем setup.php
в подкаталоге плагина с именем inc
: ~/wp-content/plugins/your_plugin/inc/setup.php
. Это должно работать, если папка плагина находится вне структуры папок WP по умолчанию, а также если каталог контента переименован или в случаях, когда ваш файл установки называется иначе. Только папка inc
должна иметь то же имя и расположение относительно корневого каталога плагина.
Замечание: вы можете просто взять три register_*_hook()*
функции и классы и вставить их в свой плагин.
Основной файл плагина:
<?php
defined( 'ABSPATH' ) OR exit;
/**
* Название плагина: (WCM) Активация/Деактивация/Удаление - ФАЙЛ/КЛАСС
* Описание: Пример плагина
* Автор: Franz Josef Kaiser/wecodemore
* URL автора: http://unserkaiser.com
* 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;
}
}
Файл установки:
<?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}" );
# Раcкомментируйте следующую строку, чтобы увидеть функцию в действии
# 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}" );
# Раcкомментируйте следующую строку, чтобы увидеть функцию в действии
# exit( var_dump( $_GET ) );
}
public static function on_uninstall()
{
if ( ! current_user_can( 'activate_plugins' ) )
return;
check_admin_referer( 'bulk-plugins' );
// Важно: Проверьте, является ли файл тем
// который был зарегистрирован во время хуков удаления.
if ( __FILE__ != WP_UNINSTALL_PLUGIN )
return;
# Раcкомментируйте следующую строку, чтобы увидеть функцию в действии
# exit( var_dump( $_GET ) );
}
}
(2) Обновления плагинов
Если вы пишете плагин, который имеет свою собственную таблицу БД или параметры, могут быть сценарии, когда вам нужно изменить или обновить некоторые вещи.
К сожалению, на данный момент нет возможности запускать что-то при установке или обновлении плагина/темы. К счастью, есть обходной путь: подключите пользовательскую функцию к пользовательской опции (да, это глупо – но это работает).
function prefix_upgrade_plugin()
{
$v = 'plugin_db_version';
$update_option = null;
// Обновление до версии 2
if ( 2 !== get_option( $v ) )
{
if ( 2 < get_option( $v ) )
{
// Обратная функция должна возвращать true при успехе
$update_option = custom_upgrade_cb_fn_v3();
// Обновить опцию только в случае успеха
if ( $update_option )
update_option( $v, 2 );
}
}
// Обновление до версии 3, запускается сразу после обновления до версии 2
if ( 3 !== get_option( $v ) )
{
// начать с начала, если предыдущее обновление не удалось
if ( 2 < get_option( $v ) )
return prefix_upgrade_plugin();
if ( 3 < get_option( $v ) )
{
// Обратная функция должна возвращать true при успехе
$update_option = custom_upgrade_cb_fn_v3();
// Обновить опцию только в случае успеха
if ( $update_option )
update_option( $v, 3 );
}
}
// Вернуть результат из обратной функции обновления, чтобы мы могли проверить успех/неудачу/ошибку
if ( $update_option )
return $update_option;
return false;
}
add_action('admin_init', 'prefix_upgrade_plugin' );
Эта функция обновления является не самым красивым/хорошо написанным примером, но, как уже говорилось: это пример, и техника работает неплохо. Я улучшу это в следующем обновлении.
Чтобы проверить текущую систему на наличие необходимых функций, таких как версия PHP или установленные расширения, вы можете использовать что-то подобное:
<?php # -*- coding: utf-8 -*-
/**
* Название плагина: T5 Проверить требования плагина
* Описание: Проверка версии PHP и установленных расширений
* URI плагина:
* Версия: 2013.03.31
* Автор: Fuxia Scholz
* Лицензия: MIT
* URI лицензии: http://opensource.org/licenses/MIT
*/
/*
* Не запускайте на каждой странице, страницу плагина достаточно.
*/
if ( ! empty ( $GLOBALS['pagenow'] ) && 'plugins.php' === $GLOBALS['pagenow'] )
add_action( 'admin_notices', 't5_check_admin_notices', 0 );
/**
* Проверка текущей системы на наличие функций, необходимых плагину.
*
* @return array Ошибки или пустой массив
*/
function t5_check_plugin_requirements()
{
$php_min_version = '5.4';
// смотрите 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[] = "Ваш сервер работает на версии PHP $php_current_version, но
этот плагин требует как минимум PHP $php_min_version. Пожалуйста, выполните обновление.";
foreach ( $extensions as $extension )
if ( ! extension_loaded( $extension ) )
$errors[] = "Пожалуйста, установите расширение $extension для работы этого плагина.";
return $errors;
}
/**
* Вызовите t5_check_plugin_requirements() и деактивируйте этот плагин, если есть ошибки.
*
* @wp-hook admin_notices
* @return void
*/
function t5_check_admin_notices()
{
$errors = t5_check_plugin_requirements();
if ( empty ( $errors ) )
return;
// Подавить уведомление "Плагин активирован".
unset( $_GET['activate'] );
// имя этого плагина
$name = get_file_data( __FILE__, array ( 'Plugin Name' ), 'plugin' );
printf(
'<div class="error"><p>%1$s</p>
<p><i>%2$s</i> был деактивирован.</p></div>',
join( '</p><p>', $errors ),
$name[0]
);
deactivate_plugins( plugin_basename( __FILE__ ) );
}
Проверка с учетом PHP 5.5:
Ответ или решение
Когда вы разрабатываете плагин для WordPress, создание функций для его установки, активации, деактивации и удаления является критически важным для обеспечения корректной работы плагина. В этом ответе мы рассмотрим типичные функции, которые следует включить в сценарии активации, деактивации и удаления плагина, а также предоставим пошаговые инструкции.
Типичные функции для активации, деактивации и удаления плагина
-
Активация плагина:
- Создание необходимых баз данных (таблиц).
- Установка записей в настройки (options).
- Начальная настройка значений плагина.
-
Деактивация плагина:
- Настройка поведения при отключении плагина, например, отключение зарегистрированных хуков.
- Сохранение данных, если это необходимо, для последующей активации.
-
Удаление плагина:
- Полное удаление всех созданных таблиц.
- Очистка всех записей в настройках, связанных с плагином.
- Удаление других настроек или данных, которые были добавлены плагином.
Как реализовать эти функции
1. Активация плагина
Для активации плагина используйте register_activation_hook()
. Вот пример реализации:
register_activation_hook(__FILE__, 'my_plugin_activate');
function my_plugin_activate() {
global $wpdb;
// Пример создания таблицы
$table_name = $wpdb->prefix . 'my_custom_table';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE $table_name (
id mediumint(9) NOT NULL AUTO_INCREMENT,
name tinytext NOT NULL,
PRIMARY KEY (id)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
// Установка начальных значений настроек
add_option('my_plugin_option', 'default_value');
}
2. Деактивация плагина
Для деактивации плагина используйте register_deactivation_hook()
. Пример:
register_deactivation_hook(__FILE__, 'my_plugin_deactivate');
function my_plugin_deactivate() {
// При необходимости, можно выполнять действия перед отключением плагина.
// Например, отключить хуки, которые были добавлены в активации.
}
3. Удаление плагина
Для реализации функции удаления плагина используйте register_uninstall_hook()
. Пример:
register_uninstall_hook(__FILE__, 'my_plugin_uninstall');
function my_plugin_uninstall() {
global $wpdb;
// Удаление таблицы
$table_name = $wpdb->prefix . 'my_custom_table';
$wpdb->query("DROP TABLE IF EXISTS $table_name");
// Очистка настроек
delete_option('my_plugin_option');
// Удалите любые другие записи или данные, которые необходимо очистить.
}
Соображения безопасности
При реализации вышеуказанных функций важно учитывать безопасность:
- Используйте
current_user_can()
для проверки прав доступа пользователя перед выполнением действий. - Используйте
check_admin_referer()
для проверки подлинности перед выполнением действий, связанных с активацией или удалением, для предотвращения CSRF-атак.
Заключение
Эти функции обеспечивают корректную работу вашего плагина и поддерживают чистоту базы данных WordPress. Настройте их в соответствии с вашей функциональностью и логикой работы плагина, чтобы улучшить пользовательский опыт. Более того, при разработке плагинов всегда старайтесь следовать передовым практикам безопасности и оптимизации кода.