是否清理选项条目? ?
还有其他吗?
#1 楼
有三个不同的钩子。它们在以下情况下触发:卸载
停用
激活
如何在方案中安全触发功能
以下显示了正确挂接在上述操作期间触发的回调函数的正确方法。
您可以在使用
普通函数,
一个类或
外部类,
我将展示三个不同的演示插件,您可以检查它们,然后在以后实现代码在您自己的插件中。
重要的事前通知!
由于该主题非常困难且非常详细,并且具有十几种极端情况,因此此答案永远不会是完美的。我会随着时间的推移不断改进它,因此请定期检查。
(1)激活/停用/卸载插件。
插件设置回调由触发核心,您对核心的执行方式没有任何影响。有一些事情要记住:
在设置回调期间,永远不要
echo/print
any(!)。这将导致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架构
此场景假设您在名为
setup.php
的插件的子目录:inc
中获得了一个主插件文件和另一个名为~/wp-content/plugins/your_plugin/inc/setup.php
的文件。当plugin文件夹不在默认WP文件夹结构之外时,以及在重命名目录dir或安装文件的名称不同的情况下,此方法也将起作用。在插件根目录中,只有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 )插件更新
如果编写的插件具有自己的数据库表或选项,则可能需要更改或升级内容。
遗憾的是,到目前为止,无法在插件/主题安装或更新/升级上运行某些东西。很高兴有一种解决方法:将自定义函数挂接到自定义选项(是的,虽然很la脚-但却可以使用)。
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' );
源
这个更新功能不是一个很好/写得很好的例子,但是如前所述:这是一个例子,该技术效果很好。将在以后的更新中对此进行改进。
评论
很棒,但是我真正想知道的是我应该在停用方法中包含的东西...例如,如果我删除数据库中的表,还是应该保留它们以防用户改变主意并重新激活插件, ?
–红色音乐学院
2011年8月15日在21:25
广告“但”:我提到了3种方法。一种用于激活,一种用于临时停用,另一种用于失速。 Imho的“卸载”说“删除我和我所做的一切”,而“停用”是一个临时状态,可能会重做。但是:请参阅更新。我添加了有关您的Q +的注释,并扩展了一些开发建议。
– kaiser
2011年8月15日在21:55
啊,我现在明白了。只是一个问题,何时卸载会被调用?什么时候删除文件?
–红色音乐学院
11年8月16日在22:40
@aendrew它们仅在侧面check_admin_referer()中使用。他们不需要进行清理,因为core本身不会这样做,并且无论如何都会将其与未消毒的$ _REQUEST值进行比较。但是,如果他们因此而像小女孩一样哭泣,只需在上面使用filter_var()或esc_attr()即可。
– kaiser
2014年8月14日在1:09
如果使用wp_register_uninstall_hook,则不应该在回调函数中检查WP_UNINSTALL_PLUGIN,仅当您使用uninstall.php时才应如此。
–保罗
15年1月21日在19:13
#2 楼
要测试当前系统是否具有所需的功能(例如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
13年7月31日在12:52
它仅在插件页面上的激活挂钩之后运行。
– fuxia♦
13年7月31日在12:54
我知道-但是,如果插件在插件页面之外被激活(例如作为主题依赖项的一部分),那么您的检查将被跳过,否?所以我尝试移动add_action('admin_notices','t5_check_admin_notices',0);进入激活钩,插件激活而不执行检查。 。 。
– Orionrush
13年7月31日在13:30
@kaiser解释了激活挂钩的工作原理,我想展示一个替代方法。如果未按插件页面激活插件,则可能会发生致命错误,是的。如果不认真重写,此方法无法在激活挂钩上使用,因为该挂钩会在admin_notices之后触发。
– fuxia♦
13年7月31日在13:46
实际上只是偶然发现了一个简单的方法:stackoverflow.com/a/13927297/362445
– Orionrush
13年7月31日在13:50
评论
我浪费了很多时间试图使它工作。问题是init钩子在注册钩子内部不起作用。我想没有一个钩子(动作或过滤器)不会这么早起作用。阅读以下链接的注释。 codex.wordpress.org/Function_Reference/register_activation_hook它说:“在您的plugins_loaded钩子中注册该钩子为时已晚,它将无法运行!(即使直到wp_loaded钩子似乎对register_deactivation_hook都起作用。)”我是将编解码器更新为您提到的内容的人,因此在以上↑答案中对此进行了考虑。 :)