Odinstaluj, aktywuj i dezaktywuj wtyczkę: typowe funkcje i instrukcje

100

Tworzę wtyczkę Wordpress. Jakie są typowe rzeczy, które powinienem uwzględnić w funkcji odinstalowywania?

Na przykład, czy powinienem usunąć tabele utworzone w funkcji instalacji?

Czy usuwam wpisy opcji?

Coś jeszcze?

redconservatory
źródło
Zmarnowałem tyle czasu, próbując go uruchomić. Problem polega na tym, że haczyk init nie działa wewnątrz rejestrowania haków. Przypuszczam, że ani jeden haczyk (akcja lub filtr) nie zadziała tak wcześnie. Przeczytaj uwagi linkiem poniżej. codex.wordpress.org/Function_Reference/register_activation_hook Mówi: „Rejestracja haka wewnątrz haka ładowanego przez pluginy jest za późna i nie będzie działać! (Nawet jeśli wydaje się, że działa dla haka register_deactivation_hook aż do haka wp_loaded.)”
Anton,
To ja zaktualizowałem kodeks do tego, co wspomniałeś, więc zostało to uwzględnione w powyższej odpowiedzi ↑. :)
kaiser

Odpowiedzi:

150

Istnieją trzy różne haki. Wyzwalają w następujących przypadkach:

  • Odinstaluj
  • Dezaktywacja
  • Aktywacja

Jak uruchomić wyzwalacz, działa bezpiecznie podczas scenariuszy

Poniżej pokazano właściwe sposoby bezpiecznego przechwytywania funkcji zwrotnych uruchamianych podczas wymienionych działań.

Jak możesz użyć tego kodu we wtyczce, która używa

  • proste funkcje,
  • klasa lub
  • klasa zewnętrzna,

Pokażę trzy różne wtyczki demonstracyjne , które możesz sprawdzić, a następnie zaimplementuj kod we własnych wtyczkach.

Ważna uwaga z góry!

Ponieważ ten temat jest niezwykle trudny i bardzo szczegółowy i zawiera ponad tuzin skrzynek, odpowiedź ta nigdy nie będzie idealna. Z czasem będę go poprawiać, więc sprawdzaj regularnie.

(1) Aktywuj / dezaktywuj / odinstaluj wtyczki.

Do wywołania zwrotne instalacji wtyczki są wyzwalane przez rdzeń i masz żadnego wpływu na sposób rdzeń to robi. Należy pamiętać o kilku rzeczach:

  • Nigdy , nigdy echo/printnic (!) Podczas wywołań zwrotnych instalacji. Doprowadzi to do headers already sentwiadomości, a rdzeń zaleci dezaktywację i usunięcie wtyczki ... nie pytaj: wiem ...
  • Nie zobaczysz żadnych wyników wizualnych. Ale dodałem exit()oświadczenia do wszystkich różnych wywołań zwrotnych, abyś mógł uzyskać wgląd w to, co naprawdę się dzieje. Po prostu odkomentuj je, aby zobaczyć, jak działają.
  • Niezwykle ważne jest sprawdzenie, czy __FILE__ != WP_PLUGIN_INSTALLi (jeśli nie: przerwanie!), Aby naprawdę odinstalować wtyczkę. Polecam po prostu wywoływać on_deactivation()wywołania zwrotne podczas programowania, abyś zaoszczędził czas na odzyskanie wszystkiego. Przynajmniej tak robię.
  • Równie dobrze robię zabezpieczenia. Niektóre są również wykonywane przez rdzeń, ale hej! Lepiej dmuchać na zimne! .
    • Najpierw nie zezwalam na bezpośredni dostęp do plików, gdy rdzeń nie jest załadowany: defined( 'ABSPATH' ) OR exit;
    • Następnie sprawdzam, czy bieżący użytkownik może wykonać to zadanie.
    • Jako ostatnie zadanie sprawdzam polecającego. Uwaga: mogą pojawić się nieoczekiwane wyniki z wp_die()ekranem z prośbą o odpowiednie uprawnienia (i jeśli chcesz spróbować ponownie ... tak, pewnie ), gdy pojawi się błąd. Dzieje się tak, gdy rdzeń przekierowuje cię, ustawia prąd $GLOBALS['wp_list_table']->current_action();na, error_scrapea następnie sprawdza polecającego check_admin_referer('plugin-activation-error_' . $plugin);, gdzie $pluginjest $_REQUEST['plugin']. Przekierowanie odbywa się więc w połowie ładowania strony, a ten przewodowy pasek przewijania i ekran matrycy pozwalają wyświetlić żółte okno powiadomienia / komunikatu administratora. Jeśli tak się stanie: zachowaj spokój i po prostu wyszukaj błąd przy niektórych exit()i debugowaniu krok po kroku.

(A) Wtyczka funkcji zwykłych

Pamiętaj, że może to nie zadziałać, jeśli odhaczysz wywołania zwrotne przed definicją funkcji.

<?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) Architektura oparta na klasie / OOP

Jest to najczęstszy przykład we współczesnych wtyczkach.

<?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) Architektura oparta na klasie / OOP z zewnętrznym obiektem instalacji

Ten scenariusz zakłada, że masz główny plik wtyczki i drugi plik o nazwie setup.phpw podkatalogu plugin o nazwie inc: ~/wp-content/plugins/your_plugin/inc/setup.php. Działa to również wtedy, gdy folder wtyczki znajduje się poza domyślną strukturą folderów WP, a także gdy nazwa katalogu zawartości zostanie zmieniona lub w przypadkach, gdy plik instalacyjny ma inną nazwę. Tylko incfolder musi mieć tę samą nazwę i lokalizację względem katalogu głównego wtyczek.

Uwaga: Możesz po prostu wziąć trzy register_*_hook()*funkcje i klasy i upuścić je we wtyczce.

Główny plik wtyczki:

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

Plik instalacyjny:

<?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) Aktualizacje wtyczek

Jeśli napiszesz wtyczkę, która ma własną tabelę DB lub opcje, mogą istnieć scenariusze, w których musisz coś zmienić lub uaktualnić.

Niestety do tej pory nie ma możliwości uruchomienia czegoś na instalacji wtyczki / motywu lub aktualizacji / aktualizacji. Chętnie obejdziemy to: Podłącz niestandardową funkcję do niestandardowej opcji (tak, jest kiepska - ale działa).

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

Źródło

Ta funkcja aktualizacji nie jest tak ładnym / dobrze napisanym przykładem, ale jak powiedziano: To przykład, a technika działa dobrze. Poprawi to w późniejszej aktualizacji.

kajzer
źródło
1
To jest świetne, ALE tak naprawdę chcę wiedzieć, co powinienem uwzględnić w mojej metodzie dezaktywacji ... na przykład powinienem usunąć tabele z bazy danych lub zostawić je na wypadek, gdyby użytkownik zmienił zdanie i ponownie aktywował wtyczkę ?
Redconservatory
1
Reklama „ALE”: Wspomniałem, że istnieją 3 metody. Jeden do aktywacji, jeden do tymczasowej dezaktywacji i jeden do odinstalowania. Imho „odinstaluj” mówi „Usuń mnie i wszystko, co zrobiłem”, podczas gdy „dezaktywuj” jest stanem tymczasowym i może zostać ponownie wykonany. Ale: Zobacz aktualizację. Dodałem komentarze na temat twojego Q +, rozszerzyłem go o kilka rekomendacji programistycznych.
kaiser
3
Ach, teraz rozumiem. Tylko pytanie, kiedy zostanie odinstalowane odinstalowane? Kiedy pliki zostaną usunięte?
Redconservatory
1
@aendrew Są używane tylko z boku check_admin_referer(). Nie muszą się dezynfekować, ponieważ rdzeń nie robi tego sam, i tak czy inaczej porównałby to z niezauczonymi $_REQUESTwartościami. Ale jeśli z tego powodu zaczną płakać jak małe dziewczynki, po prostu użyj filter_var()lub esc_attr()na nim.
kaiser
2
Nie powinieneś sprawdzać WP_UNINSTALL_PLUGIN w funkcji wywołania zwrotnego, jeśli używasz wp_register_uninstall_hook, tylko jeśli używasz uninstall.php
Paul
17

Aby przetestować obecny system pod kątem wymaganych funkcji, takich jak wersja PHP lub zainstalowane rozszerzenia, możesz użyć czegoś takiego:

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

Przetestuj z czekiem dla PHP 5.5:

wprowadź opis zdjęcia tutaj

fuxia
źródło
Dotyk jest zdezorientowany, więc w zasadzie nie ma tu żadnego wezwania register_activation_hook- dlaczego go nie użyć? Czy ten ogień będzie strzelał przed czy po register_activation_hooki będzie register_activation_hookstrzelał, nawet jeśli powyższe nie przejdzie?
orionrush
Działa po haku aktywacyjnym tylko na stronie wtyczki.
fuxia
Rozumiem - ale jeśli wtyczka zostanie aktywowana poza stroną wtyczki (powiedzmy, że jest to część zależności motywu), wtedy twoje testy zostaną pominięte? Próbowałem więc przejść add_action( 'admin_notices', 't5_check_admin_notices', 0 );do haka aktywacyjnego, a wtyczka aktywuje się bez przeprowadzania kontroli. . .
orionrush
@kaiser wyjaśnił, jak działa hak aktywacyjny, chciałem pokazać alternatywę. Jeśli wtyczka nie zostanie aktywowana na stronie wtyczki, może wystąpić błąd krytyczny, tak. To podejście nie może działać na haku aktywacyjnym bez poważnego przepisania, ponieważ ten hak uruchamia się później admin_notices.
fuxia
Właściwie po prostu
natknąłem