Видаліть, активуйте, деактивуйте плагін: типові функції та інструкції


100

Я роблю плагін WordPress. Які типові речі я повинен включати в функцію видалення?

Наприклад, чи слід видалити будь-які таблиці, створені у функції встановлення?

Чи я очищую свої параметри?

Ще щось?


Я витратив стільки часу, намагаючись налагодити це. Проблема полягає в тому, що гачок init не працює всередині реєстраційних гаків. Я припускаю, що не один гачок (дія чи фільтр) не спрацює так рано. Прочитайте примітки за посиланням нижче. codex.wordpress.org/Function_Reference/register_activation_hook У ньому написано: "Реєстрація гака у вашому планку_ завантажений гачок занадто пізно, і він не запуститься! (Навіть коли здається, що він працює для register_deactivation_hook, поки wp_loaded гак.)"
Антон

Я те, що оновив кодекс на те, що ви згадали, тому це розглядається у наведеній вище ↑ відповіді. :)
кайзер

Відповіді:


150

Є три різні гачки. Вони спрацьовують у таких випадках:

  • Видалити
  • Деактивація
  • Активація

Як запустити функції безпечно під час сценаріїв

Нижче показано правильні способи безпечного підключення функцій зворотного виклику, які спрацьовують під час згаданих дій.

Як ви могли використовувати цей код у плагіні, який використовує

  • прості функції,
  • клас або
  • зовнішній клас,

Я покажу три різні демо-плагіни, які ви можете перевірити, а потім пізніше впроваджуйте код у свої власні плагіни.

Важлива примітка наперед!

Оскільки ця тема є надзвичайно складною та дуже детальною і має десяток + крайових випадків, ця відповідь ніколи не буде ідеальною. Я з часом продовжуватиму вдосконалюватись, тому перевіряйте регулярно.

(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;
/**
 * 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) Архітектура на базі класу / OOP

Це найпоширеніший приклад сучасних плагінів.

<?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) Архітектура на основі класу / OOP з зовнішнім об'єктом настройки

Цей сценарій передбачає , що ви отримали головний файл плагіна , і другий файл з ім'ям setup.phpв підкаталозі плагіна під назвою inc: ~/wp-content/plugins/your_plugin/inc/setup.php. Це буде добре працювати, коли папка плагінів знаходиться поза структурою папки WP за замовчуванням, а також, коли вміст dir перейменовано або у випадках, коли ваш файл налаштування названий іншим. Тільки incпапка має мати те саме ім’я та розташування щодо кореневого каталогу плагінів.

Примітка. Ви можете просто взяти три register_*_hook()*функції та класи та залишити їх у свій плагін.

Основний файл плагіна:

<?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;
    }
}

Файл налаштування:

<?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) Оновлення плагінів

Якщо ви пишете плагін, який має власну таблицю або параметри БД, можуть бути сценарії, коли вам потрібно змінити або оновити речі.

На жаль, поки що немає можливості запустити щось із встановлення плагіна / теми чи оновлення / оновлення. Радість, що тут є робота: підключіть спеціальну функцію до спеціальної опції (так, це кульгаво, але вона працює).

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' );

Джерело

Ця функція оновлення є не дуже приємним / добре написаним прикладом, але як сказано: Це приклад, і техніка працює добре. Поліпшимо це з наступним оновленням.


1
Це чудово, Але я дійсно хочу знати, це речі, які я повинен включати в свій метод деактивації ... наприклад, чи слід видалити свої таблиці в базі даних або залишити їх у випадку, якщо користувач передумає та повторно активує плагін. ?
redconservatory

1
Оголошення "АЛЕ": я зазначив, що існує 3 методи. Один для активації, один для тимчасової дезактивації та один для невстановленого. Імхо "видалити" говорить "Видаліть мене та все, що я зробив", а "деактивувати" - тимчасовий стан і може бути перероблений. Але: Дивіться оновлення. Я додав коментарі щодо вашого Q +, доповнив його кількома рекомендаціями щодо розвитку.
кайзер

3
Аха, я зараз розумію. Лише питання, коли вилучається деінсталяція? Коли файли видаляються ??
redconservatory

1
@aendrew Вони використовуються лише збоку check_admin_referer(). Їм не потрібно піддаватися санітарній обробці, тому що ядро ​​не робить це самостійно і все одно порівнюватиме їх з несанітованими $_REQUESTзначеннями. Але якщо вони починають плакати як маленькі дівчатка через це, просто використовуйте filter_var()або esc_attr()на цьому.
кайзер

2
Ви не повинні перевіряти WP_UNINSTALL_PLUGIN у функції зворотного дзвінка, якщо ви використовуєте wp_register_uninstall_hook, лише якщо ви використовуєте uninstall.php
пауль

17

Для тестування поточної системи на необхідні функції, такі як версія PHP або встановлені розширення, ви можете використовувати щось подібне:

<?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__ ) );
}

Тест з чеком на PHP 5.5:

введіть тут опис зображення


Торкніться плутати, тому в основному тут не дзвоніть register_activation_hook- чому б не використати його? Чи буде цей вогонь до або після, register_activation_hookі він буде register_activation_hookгоріти, навіть якщо вищезгадане не пройде?
orionrush

Він запускається після гачка активації лише на сторінці плагіна.
fuxia

Я бачу - але якщо плагін активовано поза сторінки плагіна (скажімо, як частина залежності від теми), то ваші чеки будуть пропущені ні? Тому я спробував перейти add_action( 'admin_notices', 't5_check_admin_notices', 0 );в гачок активації, і плагін активується, не виконуючи перевірок. . .
orionrush

@kaiser пояснив, як працює гачок активації, я хотів показати альтернативу. Якщо плагін не активовано на одній сторінці плагіна, може статися фатальна помилка, так. Цей підхід не може працювати на гачку активації без серйозного переписування, тому що цей гак спрацьовує після admin_notices.
fuxia

На насправді просто наткнувся на легкий шлях: stackoverflow.com/a/13927297/362445
orionrush
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.