ถอนการติดตั้งเปิดใช้งานปิดใช้งานปลั๊กอิน: คุณสมบัติทั่วไปและวิธีการ


100

ฉันกำลังสร้างปลั๊กอินเวิร์ดเพรส อะไรคือสิ่งที่ฉันควรรวมไว้ในคุณสมบัติถอนการติดตั้ง

ตัวอย่างเช่นฉันควรลบตารางใด ๆ ที่ฉันสร้างในฟังก์ชั่นติดตั้งหรือไม่

ฉันจะล้างรายการตัวเลือกของฉันหรือไม่?

มีอะไรอีกไหม


ฉันเสียเวลามากกับการพยายามทำให้มันใช้งานได้ ปัญหาคือตะขอของ init ไม่ทำงานภายในการลงทะเบียน hooks ฉันคิดว่าไม่ใช่หนึ่ง hook (action หรือ filter) จะไม่ทำงานเร็วนัก อ่านหมายเหตุตามลิงค์ด้านล่าง codex.wordpress.org/Function_Reference/register_activation_hookมันพูดว่า: "การลงทะเบียน hook ภายใน plugins_loaded hook ของคุณสายเกินไปและจะไม่ทำงาน! (แม้เมื่อดูเหมือนว่าจะทำงานกับ register_deactivation_hook จนกระทั่งตะขอ wp_loaded)"
Anton

ฉันเป็นผู้ปรับปรุง Codex ให้เป็นสิ่งที่คุณพูดถึงดังนั้นสิ่งนี้จึงได้รับการพิจารณาในคำตอบด้านบน :)
ไกเซอร์

คำตอบ:


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']ดังนั้นการเปลี่ยนเส้นทางจึงเกิดขึ้นเพียงครึ่งหนึ่งของการโหลดหน้าเว็บและคุณจะได้รับแถบเลื่อนแบบใช้สายนี้และหน้าจอ die จะมีข้อมูลเชิงลึกเกี่ยวกับกล่อง / การแจ้งเตือนของผู้ดูแลระบบสีเหลือง หากสิ่งนี้เกิดขึ้น: อยู่ในความสงบและเพียงค้นหาข้อผิดพลาดด้วยการ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 คลาสพร้อมกับวัตถุการตั้งค่าภายนอก

สถานการณ์นี้อนุมานว่าคุณมีไฟล์ plugin หลักและไฟล์ที่สองชื่อsetup.phpในไดเรกทอรีย่อยของปลั๊กอินชื่อ:inc ~/wp-content/plugins/your_plugin/inc/setup.phpสิ่งนี้จะทำงานได้ดีเมื่อโฟลเดอร์ปลั๊กอินอยู่นอกโครงสร้างโฟลเดอร์ WP เริ่มต้นและเมื่อเนื้อหาถูกเปลี่ยนชื่อหรือในกรณีที่ไฟล์ติดตั้งของคุณชื่อแตกต่างกัน มีเพียง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 วิธี หนึ่งสำหรับการเปิดใช้งานหนึ่งสำหรับการปิดการใช้งานชั่วคราวและหนึ่งสำหรับการยกเลิกการติดตั้ง Imho "ถอนการติดตั้ง" กล่าวว่า "ลบฉันและทุกอย่างที่ฉันทำ" ในขณะที่ "ปิดการใช้งาน" เป็นสถานะชั่วคราวและอาจทำซ้ำ แต่: ดูการอัปเดต ฉันได้เพิ่มความคิดเห็นเกี่ยวกับ Q + ของคุณเพิ่มเติมด้วยคำแนะนำในการพัฒนา
ไกเซอร์

3
อ่าฉันเข้าใจแล้ว แค่คำถามถอนการติดตั้งจะถูกเรียกเมื่อใด เมื่อไฟล์ถูกลบ?
redconservatory

1
@aendrew พวกเขาจะใช้ในด้านcheck_admin_referer()เท่านั้น พวกเขาไม่จำเป็นต้องได้รับการฆ่าเชื้อเพราะแกนไม่ได้ทำเองและจะเปรียบเทียบมันกับ$_REQUESTค่าที่ไม่ถูกทำลาย แต่ถ้าพวกเขาเริ่มร้องไห้เหมือนเด็กผู้หญิงตัวเล็ก ๆ ด้วยเหตุผลนั้นเพียงแค่ใช้filter_var()หรือทำesc_attr()มัน
ไกเซอร์

2
คุณไม่ควรตรวจสอบ WP_UNINSTALL_PLUGIN ในฟังก์ชั่นการโทรกลับหากใช้ wp_register_uninstall_hook เฉพาะในกรณีที่คุณใช้ uninstall.php
paul

17

หากต้องการทดสอบระบบปัจจุบันสำหรับ featurs ที่ต้องการเช่นเวอร์ชัน 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.