Deinstallieren, Aktivieren, Deaktivieren eines Plugins: typische Funktionen und Anleitungen

100

Ich mache ein WordPress-Plugin. Was sind typische Dinge, die ich in die Deinstallationsfunktion aufnehmen sollte?

Soll ich beispielsweise Tabellen löschen, die ich in der Installationsfunktion erstellt habe?

Bereinige ich meine Optionseinträge?

Noch etwas?

Rotkonservatorium
quelle
Ich habe so viel Zeit damit verschwendet, es zum Laufen zu bringen. Das Problem ist, dass der init-Hook innerhalb der registrierten Hooks nicht funktioniert. Ich nehme an, dass nicht ein Haken (Aktion oder Filter) so früh nicht funktioniert. Lesen Sie die Notizen per Link unten. codex.wordpress.org/Function_Reference/register_activation_hook Es heißt: "Das Registrieren des Hooks in Ihrem plugins_loaded-Hook ist zu spät und wird nicht ausgeführt! (Auch wenn es für register_deactivation_hook bis zum wp_loaded-Hook zu funktionieren scheint.)"
Anton,
Ich bin derjenige, der den Codex auf das aktualisiert hat, was Sie erwähnt haben, daher wird dies in der obigen ↑ Antwort berücksichtigt. :)
Kaiser

Antworten:

150

Es gibt drei verschiedene Haken. Sie lösen in folgenden Fällen aus:

  • Deinstallieren
  • Deaktivierung
  • Aktivierung

Sicheres Auslösen von Funktionen während der Szenarien

Im Folgenden werden die richtigen Methoden zum sicheren Verknüpfen von Rückruffunktionen gezeigt, die während der genannten Aktionen ausgelöst werden.

Da könntest du diesen Code in einem Plugin verwenden, das benutzt

  • einfache funktionen,
  • eine Klasse oder
  • eine externe Klasse,

Ich werde drei verschiedene Demo-Plugins zeigen , die Sie überprüfen und später in Ihren eigenen Plugins implementieren können.

Wichtiger Hinweis vorab!

Da dieses Thema extrem schwierig und sehr detailliert ist und über ein Dutzend Randfälle verfügt, wird diese Antwort niemals perfekt sein. Ich werde es im Laufe der Zeit weiter verbessern, schauen Sie also regelmäßig vorbei.

(1) Plugins aktivieren / deaktivieren / deinstallieren.

Die Setup-Callbacks des Plugins werden von core ausgelöst und Sie haben keinen Einfluss darauf, wie core dies tut. Es gibt einige Dinge zu beachten:

  • Nie , nie echo/printetwas (!) Während der Installation Rückrufe. Dies wird zu einer headers already sentMeldung führen und Core wird empfehlen, Ihr Plugin zu deaktivieren und zu löschen ... fragen Sie nicht: Ich weiß ...
  • Sie sehen keine visuelle Ausgabe. Aber ich exit()habe allen verschiedenen Rückrufen Anweisungen hinzugefügt , damit Sie einige Einblicke in das bekommen, was wirklich passiert. Kommentieren Sie sie einfach aus, um zu sehen, wie etwas funktioniert.
  • Es ist äußerst wichtig, dass Sie prüfen, ob __FILE__ != WP_PLUGIN_INSTALLund (falls nicht: Abbrechen!), Ob das Plugin wirklich deinstalliert wird. Ich würde empfehlen, on_deactivation()während der Entwicklung einfach Rückrufe auszulösen , damit Sie sich die Zeit sparen, die Sie benötigen, um alles wieder in den Griff zu bekommen. Zumindest ist es das, was ich tue.
  • Ich mache auch ein paar Sicherheitsaufgaben. Einige werden auch vom Kern erledigt, aber hey! Sicher ist sicher! .
    • Zuerst erlaube ich keinen direkten Dateizugriff, wenn der Core nicht geladen ist: defined( 'ABSPATH' ) OR exit;
    • Dann überprüfe ich, ob der aktuelle Benutzer diese Aufgabe ausführen darf.
    • Als letzte Aufgabe überprüfe ich den Referrer. Hinweis: Es kann zu unerwarteten Ergebnissen kommen, wp_die()wenn ein Bildschirm nach den richtigen Berechtigungen fragt (und wenn Sie es erneut versuchen möchten ... ja, sicher ), wenn ein Fehler auftritt. Dies geschieht als Kern leitet Sie, setzt die aktuelle $GLOBALS['wp_list_table']->current_action();zu error_scrapeund überprüft dann die Referrer für check_admin_referer('plugin-activation-error_' . $plugin);, wo $pluginist $_REQUEST['plugin']. Die Weiterleitung erfolgt also bei der Hälfte des Seitenaufwands, und Sie erhalten diese verdrahtete Bildlaufleiste und auf dem Bildschirm einen Einblick in das gelbe Benachrichtigungs- / Meldungsfeld für Administratoren. Wenn dies passiert: Bleiben Sie ruhig und suchen Sie einfach nach dem Fehler mit einigem exit()und schrittweisem Debuggen.

(A) Einfaches Funktions-Plugin

Beachten Sie, dass dies möglicherweise nicht funktioniert, wenn Sie die Rückrufe vor der Funktionsdefinition einbinden.

<?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) Eine klassenbasierte / OOP-Architektur

Dies ist das häufigste Beispiel für heutige Plugins.

<?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) Eine klassenbasierte / OOP-Architektur mit einem externen Setup-Objekt

Dieses Szenario geht davon aus, dass Sie eine Haupt-Plugin - Datei und eine zweite Datei mit dem Namen bekamen setup.phpin einem Unterverzeichnis des Plugins genannt inc: ~/wp-content/plugins/your_plugin/inc/setup.php. Dies funktioniert auch, wenn sich der Plugin-Ordner außerhalb der standardmäßigen WP-Ordnerstruktur befindet, wenn das Inhaltsverzeichnis umbenannt wird oder wenn Ihre Setup-Datei einen anderen Namen hat. Nur der incOrdner muss den gleichen Namen und Speicherort im Stammverzeichnis des Plugins haben.

Hinweis: Sie können die drei register_*_hook()*Funktionen und Klassen einfach übernehmen und in Ihr Plugin einfügen.

Die Haupt-Plugin-Datei:

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

Die Setup-Datei:

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

Wenn Sie ein Plugin mit einer eigenen DB-Tabelle oder eigenen Optionen schreiben, müssen Sie möglicherweise Änderungen oder Upgrades vornehmen.

Leider gibt es bisher keine Möglichkeit, etwas über Plugin / Theme-Installation oder Update / Upgrade auszuführen. Gerne gibt es eine Abhilfe: Verknüpfen Sie eine benutzerdefinierte Funktion mit einer benutzerdefinierten Option (ja, es ist lahm - aber es funktioniert).

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

Quelle

Diese Update-Funktion ist ein nicht so schönes / gut geschriebenes Beispiel, aber wie gesagt: Es ist ein Beispiel und die Technik funktioniert gut. Wird das mit einem späteren Update verbessern.

Kaiser
quelle
1
Das ist großartig, ABER was ich wirklich wissen möchte, sind Dinge, die ich in meine Deaktivierungsmethode einbeziehen sollte ... Zum Beispiel sollte ich meine Tabellen in der Datenbank löschen oder sie lassen, falls der Benutzer seine Meinung ändert und das Plugin wieder aktiviert ?
Redconservatory
1
Ad "ABER": Ich erwähnte, dass es 3 Methoden gibt. Eine für die Aktivierung, eine für die vorübergehende Deaktivierung und eine für die Deinstallation. Imho "uninstall" sagt "Entferne mich und alles, was ich getan habe", während "disable" ein vorübergehender Zustand ist und möglicherweise wiederholt wird. Aber: Siehe Update. Ich habe Kommentare zu Ihrem Q + hinzugefügt und es um einige Entwicklungsempfehlungen erweitert.
Kaiser
3
Ah, ich verstehe jetzt. Nur eine Frage, wann wird deinstalliert aufgerufen? Wann werden die Dateien gelöscht ??
Redconservatory
1
@aendrew Sie werden nur in der Seite verwendet check_admin_referer(). Sie müssen nicht desinfiziert werden, da der Core dies nicht selbst tut und es sowieso mit nicht desinfizierten $_REQUESTWerten vergleicht. Aber wenn sie deswegen anfangen zu weinen wie kleine Mädchen, benutze filter_var()oder benutze esc_attr()es einfach .
Kaiser
2
Sie sollten in der Rückruffunktion nicht nach WP_UNINSTALL_PLUGIN suchen, wenn Sie wp_register_uninstall_hook verwenden, sondern nur, wenn Sie uninstall.php
paul am
17

Um das aktuelle System auf erforderliche Features wie PHP-Version oder installierte Erweiterungen zu testen, können Sie Folgendes verwenden:

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

Test mit einem Check für PHP 5.5:

Bildbeschreibung hier eingeben

fuxia
quelle
Berührung verwirrt, daher gibt es hier keinen Anruf register_activation_hook- warum nicht? Wird dieses Feuer auch vorher oder nachher register_activation_hookausgelöst und wird es register_activation_hookauch dann ausgelöst, wenn die oben genannten Bedingungen nicht erfüllt werden?
orionrush
Es wird nur nach dem Aktivierungshook auf der Pluginseite ausgeführt.
Fuxia
Ich verstehe - aber wenn das Plugin außerhalb der Plugin-Seite aktiviert ist (z. B. als Teil einer Theme-Abhängigkeit), werden Ihre Checks übersprungen. Nein? Also habe ich versucht, add_action( 'admin_notices', 't5_check_admin_notices', 0 );in einen Aktivierungs-Hook zu wechseln und das Plugin wird aktiviert, ohne die Überprüfungen durchzuführen. . .
orionrush
@kaiser hat erklärt, wie der Aktivierungshaken funktioniert, ich wollte eine Alternative zeigen. Wenn das Plugin nicht per Plugin-Seite aktiviert wird, kann ein schwerwiegender Fehler auftreten, ja. Dieser Ansatz kann bei einem Aktivierungs-Hook nicht ohne ernsthaftes Umschreiben funktionieren, da dieser Hook nach dem Start ausgelöst wird admin_notices.
Fuxia
Eigentlich nur auf die einfache Art gestolpert: stackoverflow.com/a/13927297/362445
orionrush