Disinstalla, Attiva, Disattiva un plug-in: funzionalità tipiche e istruzioni


100

Sto realizzando un plugin per wordpress. Quali sono le cose tipiche che dovrei includere nella funzione di disinstallazione?

Ad esempio, devo eliminare tutte le tabelle che ho creato nella funzione di installazione?

Pulisco le voci delle mie opzioni?

Qualunque altra cosa?


Ho perso così tanto tempo cercando di farlo funzionare. Il problema è che l'inizial hook non funziona all'interno dei hook di registrazione. Suppongo che nessun gancio (azione o filtro) non funzionerà così presto. Leggi le note tramite link qui sotto. codex.wordpress.org/Function_Reference/register_activation_hook Dice: "Registrare l'hook all'interno del tuo hook plugins_loaded è troppo tardi e non funzionerà! (Anche quando sembra funzionare per register_deactivation_hook fino al gancio wp_loaded)"
Anton

Sono quello che ha aggiornato il codice con quello che hai menzionato, quindi questo è considerato nella risposta ↑ sopra. :)
Kaiser

Risposte:


150

Esistono tre diversi ganci. Si innescano nei seguenti casi:

  • Disinstallare
  • Disattivazione
  • Attivazione

Come attivare le funzioni in modo sicuro durante gli scenari

Quanto segue mostra i modi giusti per agganciare in modo sicuro le funzioni di callback che vengono attivate durante le azioni menzionate.

Come è possibile utilizzare questo codice in un plug-in che utilizza

  • funzioni semplici,
  • una classe o
  • una classe esterna,

Mostrerò tre diversi plug-in demo che è possibile controllare e successivamente implementare il codice nei propri plug-in.

Nota importante in anticipo!

Poiché questo argomento è estremamente difficile e molto dettagliato e ha una dozzina di casi limite, questa risposta non sarà mai perfetta. Continuerò a migliorarlo nel tempo, quindi ricontrolla su base regolare.

(1) Attiva / Disattiva / Disinstalla plugin.

I callback di installazione del plug-in sono attivati ​​da core e non si ha alcuna influenza su come core fa questo. Ci sono alcune cose da tenere a mente:

  • Mai e poi mai echo/printniente (!) Durante i callback di installazione. Questo porterà a un headers already sentmessaggio e core raccomanderà di disattivare ed eliminare il plugin ... non chiedere: lo so ...
  • Non vedrai alcun output visivo. Ma ho aggiunto delle exit()dichiarazioni a tutti i diversi callback in modo da poter ottenere alcune informazioni su ciò che sta realmente accadendo. Basta decommentarli per vedere le cose che funzionano.
  • È estremamente importante controllare se __FILE__ != WP_PLUGIN_INSTALLe (in caso contrario: interrompere!) Per vedere se si sta realmente disinstallando il plugin. Consiglio di attivare semplicemente i on_deactivation()callback durante lo sviluppo, così ti risparmierai il tempo necessario per riavere tutto. Almeno questo è quello che faccio.
  • Anch'io faccio alcune cose di sicurezza. Alcuni vengono eseguiti anche dal core, ma hey! Meglio prevenire che curare! .
    • Per prima cosa non posso accedere direttamente ai file quando il core non è caricato: defined( 'ABSPATH' ) OR exit;
    • Quindi controllo se l'utente corrente è autorizzato a svolgere questa attività.
    • Come ultima attività, controllo il referrer. Nota: ci possono essere risultati imprevisti con una wp_die()schermata che richiede le autorizzazioni appropriate (e se si desidera riprovare ... sì, certo ), quando si riceve un errore. Questo accade quando il core ti reindirizza, imposta la corrente $GLOBALS['wp_list_table']->current_action();su error_scrapee quindi controlla il referrer per check_admin_referer('plugin-activation-error_' . $plugin);dove si $plugintrova $_REQUEST['plugin']. Quindi il reindirizzamento avviene a metà del caricamento della pagina e ottieni questa barra di scorrimento cablata e la schermata del dado visualizza la finestra di avviso / messaggio gialla dell'amministratore. Se ciò accade: mantieni la calma e cerca l'errore con alcuni exit()e debug passo-passo.

(A) Plugin di funzioni semplici

Ricordare che questo potrebbe non funzionare se si agganciano i callback prima della definizione della funzione.

<?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) Un'architettura basata su classe / OOP

Questo è l'esempio più comune nei plugin di oggi.

<?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) Un'architettura basata su classe / OOP con un oggetto di installazione esterno

Questo scenario presuppone che hai un file plugin principale e un secondo file denominato setup.phpin una sottodirectory del plugin di nome inc: ~/wp-content/plugins/your_plugin/inc/setup.php. Funzionerà anche quando la cartella del plug-in si trova al di fuori della struttura di cartelle WP predefinita, nonché quando la directory del contenuto viene rinominata o nei casi in cui il file di installazione ha un nome diverso. Solo la inccartella deve avere lo stesso nome e posizione relativi dalla directory principale dei plugin.

Nota: puoi semplicemente prendere le tre register_*_hook()*funzioni e le classi e rilasciarle nel tuo plugin.

Il file del plugin principale:

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

Il file di installazione:

<?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) Aggiornamenti plug-in

Se si scrive un plug-in che ha una propria tabella DB o opzioni, potrebbero esserci scenari in cui è necessario modificare o aggiornare le cose.

Purtroppo finora non c'è possibilità di eseguire qualcosa sull'installazione di plugin / temi o sull'aggiornamento / aggiornamento. Volentieri c'è una soluzione: agganciare una funzione personalizzata a un'opzione personalizzata (sì, è zoppa - ma funziona).

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

fonte

Questa funzione di aggiornamento è un esempio non così bello / ben scritto, ma come detto: è un esempio e la tecnica funziona bene. Lo migliorerà con un aggiornamento successivo.


1
Questo è fantastico MA quello che voglio davvero sapere sono cose che dovrei includere nel mio metodo di disattivazione ... ad esempio, dovrei eliminare le mie tabelle nel database o lasciarle nel caso in cui l'utente cambi idea e riattivi il plugin ?
redconservatory il

1
Annuncio "MA": ho detto che ci sono 3 metodi. Uno per l'attivazione, uno per la disattivazione temporanea e uno per unstall. Imho "disinstalla" dice "Rimuovi me e tutto ciò che ho fatto", mentre "Disattiva" è uno stato temporaneo e potrebbe essere rifatto. Ma: vedi aggiornamento. Ho aggiunto commenti sul tuo Q + esteso con alcuni consigli di sviluppo.
Kaiser,

3
Ah adesso capisco. Solo una domanda, quando viene chiamata la disinstallazione? Quando i file vengono eliminati ??
redconservatory il

1
@aendrew Sono usati solo di lato check_admin_referer(). Non hanno bisogno di essere disinfettati perché il core non lo fa da solo e lo confronterebbe comunque con i $_REQUESTvalori non autentizzati . Ma se iniziano a piangere come bambine per questo, basta usare filter_var()o esc_attr()su di esso.
Kaiser

2
Non dovresti controllare WP_UNINSTALL_PLUGIN nella funzione di callback se usi wp_register_uninstall_hook, solo se usi uninstall.php
paul

17

Per testare l'attuale sistema per le caratteristiche richieste come la versione di PHP o le estensioni installate puoi usare qualcosa del genere:

<?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 con un controllo per PHP 5.5:

inserisci qui la descrizione dell'immagine


Tocco confuso, quindi sostanzialmente non c'è una chiamata a register_activation_hookqui - perché non usarlo? Inoltre questo fuoco prima o dopo register_activation_hooke register_activation_hooksparerà anche se quanto sopra non passa?
Orionrush,

Funziona dopo l'hook di attivazione solo sulla pagina del plugin.
fuxia

Vedo - ma se il plug-in viene attivato al di fuori della pagina del plug-in (ad esempio come parte di una dipendenza dal tema), i tuoi controlli verranno saltati no? Quindi ho provato a passare add_action( 'admin_notices', 't5_check_admin_notices', 0 );ad un hook di attivazione e il plugin si attiva senza eseguire i controlli. . .
Orionrush,

@kaiser ha spiegato come funziona l'hook di attivazione, volevo mostrare un'alternativa. Se il plug-in non è attivato per pagina del plug-in, potrebbe verificarsi un errore irreversibile, sì. Questo approccio non può funzionare su un hook di attivazione senza una riscrittura seria, perché quel hook si attiva dopo admin_notices.
fuxia

In realtà solo inciampato in modo semplice: stackoverflow.com/a/13927297/362445
orionrush
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.