プラグインのアンインストール、アクティブ化、非アクティブ化:典型的な機能と方法


100

私はワードプレスのプラグインを作っています。アンインストール機能に含めるべき典型的なものは何ですか?

たとえば、インストール機能で作成したテーブルを削除する必要がありますか?

オプションエントリをクリーンアップしますか?

他に何か?


私はそれを機能させるために多くの時間を無駄にしました。問題は、登録フックの内部でinitフックが機能しないことです。1つのフック(アクションまたはフィルター)がそれほど早く機能しないと思います。以下のリンクからメモをお読みください。codex.wordpress.org/Function_Reference/register_activation_hook「plugins_loadedフック内にフックを登録するのは遅すぎて実行されません!(wp_loadedフックまでregister_deactivation_hookで動作しているように見える場合でも)」
アントン

私はあなたが言及したものにコーデックスを更新したので、これは上記の↑の回答で考慮されます。:)
kaiser

回答:


150

3つの異なるフックがあります。次の場合にトリガーします。

  • アンインストール
  • 無効化
  • アクティベーション

シナリオ中に機能を安全にトリガーする方法

以下に、上記のアクション中にトリガーされるコールバック関数を安全にフックする正しい方法を示します。

あなたが使用するプラグインでこのコードを使用できるように

  • プレーン関数、
  • クラスまたは
  • 外部クラス、

検証できる3つの異なるデモプラグインを示し、後で独自のプラグインにコードを実装します。

重要なメモを前もって!

このトピックは非常に難しく、非常に詳細であり、多数のエッジケースがあるため、この答えは決して完璧ではありません。今後も改善していきますので、定期的にご確認ください。

(1)プラグインを有効化/無効化/アンインストールします。

プラグインのセットアップコールバックはコアによってトリガーされ、コアがこれを行う方法に影響はありません。留意すべきことがいくつかあります。

  • 決して、これまでのecho/printセットアップコールバック時に何も(!)。これによりheaders already sentメッセージが表示され、コアはプラグインを非アクティブ化および削除することを推奨します...尋ねないでください:わかっています...
  • 視覚的な出力は表示されません。しかし、exit()実際に何が起こっているかについての洞察を得ることができるように、すべての異なるコールバックにステートメントを追加しました。コメントを外して、機能するものを確認してください。
  • __FILE__ != WP_PLUGIN_INSTALLプラグインが実際にアンインストールされているかどうかを確認するためにand をチェックすることが非常に重要です(そうでない場合:abort!)。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フォルダー構造の外にある場合、およびコンテンツディレクトリの名前が変更された場合、またはセットアップファイルの名前が異なる場合にも機能します。incフォルダーのみが、プラグインのルートディレクトリからの相対的な同じ名前と場所を持っている必要があります。

注:3つの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)プラグインの更新

独自のDBテーブルまたはオプションを持つプラグインを作成する場合、物事を変更またはアップグレードする必要があるシナリオがあります。

悲しいことに今のところ、プラグイン/テーマのインストールまたは更新/アップグレードで何かを実行する可能性はありません。喜んで回避策があります:カスタム関数をカスタムオプションにフックします(そう、それはラメです-しかしそれは動作します)。

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つの方法があると述べました。1つはアクティブ化、1つは一時的な非アクティブ化、もう1つは失速です。Imhoの「アンインストール」は「私とすべてを削除します」と表示されますが、「非アクティブ化」は一時的な状態であり、やり直される可能性があります。しかし:更新を参照してください。Q +についてのコメントを追加し、いくつかの開発に関する推奨事項を追加しました。
カイザー

3
ああ、わかった。質問ですが、アンインストールはいつ呼び出されますか?ファイルが削除されたとき??
-redconservatory

1
@aendrewこれらはsideでのみ使用されcheck_admin_referer()ます。コアはそれ自体を実行せず、とにかく非サニタイズされた$_REQUEST値と比較するため、サニタイズする必要はありません。しかし、彼らがそのために小さな女の子のように泣き始めたら、ただそれを使用するfilter_var()esc_attr()、それで。
カイザー

2
uninstall.phpを使用する場合にのみ、wp_register_uninstall_hookを使用する場合は、コールバック関数でWP_UNINSTALL_PLUGINを確認しないでください。
paul

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_hookregister_activation_hook発火し、上記に合格しなくても発火しますか?
オリオンラッシュ

プラグインページのアクティベーションフックの後にのみ実行されます。
FUXIAの

わかりました-しかし、プラグインがプラグインページの外で(テーマの依存関係の一部としてなど)アクティブ化された場合、チェックはスキップされませんか?だから私はadd_action( 'admin_notices', 't5_check_admin_notices', 0 );アクティベーションフックに移動しようとしましたが、プラグインはチェックを実行せずにアクティベートします。。。
オリオンラッシュ

@kaiserはアクティベーションフックがどのように機能するかを説明しました。代わりのものを見せたかったのです。プラグインがプラグインページごとにアクティブ化されない場合、致命的なエラーが発生する可能性があります、はい。このアプローチは、重大なリライトなしではアクティベーションフックでは機能しませんadmin_notices。そのフックは後に起動するためです。
FUXIAの

実際には簡単な方法でつまずいた:stackoverflow.com/a/13927297/362445
orionrush
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.