Удаление, активация, деактивация плагина: типичные функции и руководство по использованию

Вопрос или проблема

Я создаю плагин для 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:

enter image description here

Ответ или решение

Когда вы разрабатываете плагин для WordPress, создание функций для его установки, активации, деактивации и удаления является критически важным для обеспечения корректной работы плагина. В этом ответе мы рассмотрим типичные функции, которые следует включить в сценарии активации, деактивации и удаления плагина, а также предоставим пошаговые инструкции.

Типичные функции для активации, деактивации и удаления плагина

  1. Активация плагина:

    • Создание необходимых баз данных (таблиц).
    • Установка записей в настройки (options).
    • Начальная настройка значений плагина.
  2. Деактивация плагина:

    • Настройка поведения при отключении плагина, например, отключение зарегистрированных хуков.
    • Сохранение данных, если это необходимо, для последующей активации.
  3. Удаление плагина:

    • Полное удаление всех созданных таблиц.
    • Очистка всех записей в настройках, связанных с плагином.
    • Удаление других настроек или данных, которые были добавлены плагином.

Как реализовать эти функции

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. Настройте их в соответствии с вашей функциональностью и логикой работы плагина, чтобы улучшить пользовательский опыт. Более того, при разработке плагинов всегда старайтесь следовать передовым практикам безопасности и оптимизации кода.

Оцените материал
Добавить комментарий

Капча загружается...