您执行一次性脚本的最佳实践是什么?


32

问题

我们所有人都处在这样的情况下,这个站点上的许多问题都需要这样的解决方案。您要么必须更新数据库,自动插入大量数据,进行convert meta_keys或类似的操作。

当然,在基于最佳实践的运行系统中,这不应发生。

但是,这样做的话,我很想听听您对这个问题的个人解决方案,以及您为什么选择自己的解决方案。

问题

如何在(运行的)WordPress安装中实现一次性脚本?

这里的问题主要是由于以下原因:

  • 插入数据的脚本不应运行多次
  • 需要大量资源的脚本不应在无法监视的时候运行
  • 他们不应该无意中运行

我问的原因

我有自己的见解,我将其张贴在答案中。因为我不知道这是否是最好的解决方案,所以我想了解您的解决方案。同样,这是一个在其他问题中被问过很多次的问题,拥有一个收集想法的资源非常好。

期待向您学习:)


2
如果这确实是一次性交易,那么我编写脚本,运行它,然后删除它。此后没有人不能再次运行它。像所有事物一样,代码瞬息万变。;)
奥托(Otto

1
我担心的是,每次碰巧都可能再次调用脚本。但我无数次采用了您的方法;)
fischi 2014年

在插件的管理页面上运行它,始终对我有用。您可以在页面顶部添加身份验证检查,以确保在必要时使用身份验证。
Andrew Bartel 2014年

但是您不是在谈论计划的一次执行,而是手动
birgire 2014年

1
是的,我只是在谈论手动准时操作(例如迁移脚本等),而不是wp-cron计划中的事件。
fischi 2014年

Answers:


17

我自己使用以下组合:

  • 一个专门用于一次性脚本的文件
  • 使用瞬态来阻止脚本意外运行超过一次
  • 使用功能管理或用户控制来确保脚本仅由我运行。

结构体

onetime.php在include-folder中使用了文件()inc,该文件包含在中functions.php,并在使用后从那里删除。

include( 'inc/onetime.php' );

脚本本身的文件

在我的onetime.php职能f711_my_onetime_function()中。因为它可以是任何功能。我认为您的脚本已经过测试并且可以正常工作。

为了实现对脚本执行的控制,我同时使用了

能力控制

要阻止其他用户意外执行我的脚本,请执行以下操作:

if ( current_user_can( 'manage_options' ) ) // check for administrator rights

要么

if ( get_current_user_id() == 711 ) // check if it is me - I prefer restricting the execution to me, not to all admins.

短暂的

以防止自己不止一次地意外执行脚本。

$transient = 'f711_my_onetime_check';
if ( !get_transient( $transient ) ) // check if the function was not executed.

在我的函数中用于执行脚本的文件f711_my_onetime_function()如下所示:

$transient = 'f711_my_onetime_check';
if ( get_current_user_id() == 711 && !get_transient( $transient ) ) {

    set_transient( $transient, 'locked', 600 ); // lock function for 10 Minutes
    add_action( 'wp_footer', 'f711_my_onetime_function' ); // execute my function on the desired hook.

}

function f711_my_onetime_function() {
    // all my glorious one-time-magic.
}

我在检查瞬态是否存在后立即设置瞬态的原因是,我希望函数在脚本被锁定不被蜂鸣两次使用才能执行。

如果我需要函数的任何输出,我可以将其作为注释打印在页脚中,有时甚至可以过滤内容。

锁定时间设置为10分钟,但可以根据需要进行调整。

清理

成功执行脚本后,我include从中删除functions.php,然后onetime.php从服务器中删除该。当我为过渡使用超时时,我不需要清理数据库,但是当然您也可以在删除文件后删除过渡。


我本来打算添加我的答案,但是在阅读完该答案顶部的清单之后,我将不再...因为我的方法看起来几乎完全相同。因此+1-对此也有详细的想法。
tfrommen 2014年

14

您也可以这样做:

运行onetime.php并在执行后重命名。

if ( current_user_can( 'manage_options' ) ) {

    if( ! file_exists( '/path/to/onetime.php' ) )
      return;
    add_action( 'wp_footer', 'ravs_my_onetime_function' ); // execute my function on the desired hook.

}

function ravs_my_onetime_function() {

    // all my glorious one-time-magic.
    include( '/path/to/onetime.php' );

   // after all execution rename your file;
   rename( '/path/to/onetime.php', '/path/to/onetime-backup.php');
}

这就是我们要做的;它几乎可以保证是万无一失的。
Qix

7

我为此创建了一个命令行Phing脚本,除了加载要运行的外部脚本外,没有什么特别的。我通过CLI使用它的原因是:

  • 我不希望它被错误加载(需要输入命令)
  • 它可以在Web根目录外部运行,因此很安全,换句话说,它可以影响WP,但是WP无法以任何方式到达脚本。
  • 它不会向WP或数据库本身添加任何代码。

require('..path to ../wp-blog-header.php');
//bunch of WP globals
define('WP_USE_THEMES', false);
//custom code

因此,您可以使用Phing或PHP CLI并在晚上入睡。WP-CLI也是一个不错的选择,尽管我忘记了是否可以在Web根目录之外使用它。

由于这是一个受欢迎的帖子,因此这里是脚本示例:https : //github.com/wycks/WordPhing(run.php)


这看起来很简单,很安全。您还使用命令行覆盖了我的主要担忧之一(我偶然执行了两次)。好主意!
fischi 2014年

5

运行一次性脚本的另一种非常简单的方法是通过MU插件来执行此操作。

将代码放在one-time.php您上传到MU插件的文件夹(默认情况下/wp-content/mu-plugins)的某个PHP文件(例如)中,调整文件权限,运行插件(即,根据您选择的钩子,基本上您只需要访问前端/ backend),一切都完成了。

这是样板:

/**
* Main (and only) class.
*/
class OneTimeScript {

    /**
     * Plugin function hook.
     *
     * @type    string
     */
    public static $hook = 'init';


    /**
     * Plugin function priority.
     *
     * @type    int
     */
    public static $priority = 0;


    /**
     * Run the one-time script.
     *
     * @hook    self::$hook
     * @return  void
     */
    public static function run() {
        // one-time action goes here...

        // clean up
        add_action('shutdown', array(__CLASS__, 'unlink'), PHP_INT_MAX);
    } // function run


    /**
     * Remove the file.
     *
     * @hook    shutdown
     * @return  void
     */
    public static function unlink() {
        unlink(__FILE__);
    } // function unlink

} // class OneTimeScript

add_action(OneTimeScript::$hook, array('OneTimeScript', 'run'), OneTimeScript::$priority);

没有评论和内容,它看起来像这样:

class OneTimeScript {
    public static $hook = 'init';
    public static $priority = 0;

    public static function run() {
        // one-time action goes here...
        add_action('shutdown', array(__CLASS__, 'unlink'), PHP_INT_MAX);
    } // function run

    public static function unlink() {
        unlink(__FILE__);
    } // function unlink
} // class OneTimeScript
add_action(OneTimeScript::$hook, array('OneTimeScript', 'run'), OneTimeScript::$priority);

4

在理想的条件下,我将ssh进入服务器并使用wp-cli自己执行该功能。

但是,这通常是不可能的,因此我倾向于设置$ _GET变量并将其挂接到'init'上,例如:

add_action( 'init', function() {
    if( isset( $_GET['one_time'] ) && $_GET['one_time'] == 'an_unlikely_string' ) {
        do_the_one_time_thing();
    }
});

然后打

http://my_blog.com/?one_time=an_unlikely_string

并在完成后禁用该挂钩。


4

有时,我使用了与插件停用相关的函数。

参见此处将旧链接更新为漂亮的永久链接自定义帖子类型

只有管​​理员可以激活插件后,才能进行功能检查。

一旦被停用,就无需删除文件,否则它将不会被女巫所包括。如果您想再次上瘾,可以。再次激活和停用。

有时我会使用@fischi答案中的瞬态。例如,在此处查询以从图像创建woocommerce产品,或在此处删除/替换自动发布的帖子内容中的img标签

可以将两者结合使用。


这也是一个非常好的主意。如果它变得烦人,总是必须再次激活才能将其停用,那么您也可以将相同的功能与插件的激活挂钩,对吗?
fischi 2014年

是的,如果你想。但是,我认为两次单击并不是运行一次性脚本的巨大努力。任何其他涉及CLI命令或文件处理(重新命名,删除)的解决方案都需要更多的“工作”。此外,每次依赖钩子时,都依赖于全局变量,从而增加了与代码的安全性/可预测性有关的潜在问题。@fischi
gmazzap

我认为两次单击也不算太多,只是想问一下:)
fischi 2014年

3

绝对可以,只需将您的一次性代码创建为插件即可。

add_action('admin_init', 'one_time_call');
function one_time_call()
{
    /* YOUR SCRIPTS */
    deactivate_plugins('onetime/index.php'); //deactivate current plugin
}

问题如何在不单击“激活”链接的情况下激活该插件?

只需添加activate_plugins('onetime/index.php');functions.php

或使用必须使用插件,http://codex.wordpress.org/Must_Use_Plugins

尝试执行其他操作,例如要执行一次插件,

  1. admin_init-管理员初始化后

  2. 初始化-wordpress初始化

  3. wp-加载wordpress时


2

另一种方法是在工作完成后设置全局wp_option,并在每次执行初始化挂钩时检查该选项。

function my_one_time_function() {
    // Exit if the work has already been done.
    if ( get_option( 'my_one_time_function', '0' ) == '1' ) {
        return;
    }

    /***** DO YOUR ONE TIME WORK *****/

    // Add or update the wp_option
    update_option( 'my_one_time_function', '1' );
}
add_action( 'init', 'my_one_time_function' );

自然,您不需要永远拥有此代码(即使它是从数据库中简单读取的),因此您可以在完成工作后删除代码。如果需要重新执行代码,也可以手动将此选项值更改为0。


1

我的方法与此有所不同。我想将一次性脚本作为一个函数添加到主题的function.php中,并在特定的GET查询上运行它。

if ( isset($_GET['linkupdate']) ) {
    add_action('init', 'link_update', 10);
}
function link_update() {
  // One Time Script
   die;
}

要运行此程序,只需访问URL“ www.sitename.com/?linkupdate”

到目前为止,这对我来说还不错...

这种方法有什么缺点吗?就是想...


1

我只使用了一个未使用的自定义产品模板页面,并且未连接到公共服务器上的任何内容。

例如,如果我有一个不在使用中的推荐页面(处于草稿模式等),但是连接到单个页面模板,例如single-testimonial.php-我可以在其中放置函数,通过preview和函数加载页面,或者发射一次。在调试的情况下,对功能进行修改也非常容易。

这真的很容易,我更喜欢使用它,init因为我可以更好地控制其启动时间和方式。只是我的偏爱。


0

以防万一,这就是我所做的,并且效果很好:

add_action( 'init', 'upsubscriptions_setup');

function upsubscriptions_setup()
{
    $version = get_option('upsubscriptions_setup_version');

    // If no version is recorded yet in the DB
    if (!$version) {
        add_option('upsubscriptions_setup_version', '0.1');
        $version = get_option('upsubscriptions_setup_version');
    }

    if (version_compare($version, "0.1") <= 0) {
        // do stuff
        update_option('upsubscriptions_setup_version', '0.2');
    }

    if (version_compare($version, "0.2") <= 0) {
        // do stuff
        update_option('upsubscriptions_setup_version', '0.3');
    }

    if (version_compare($version, "0.3") <= 0) {
        // do stuff
        update_option('upsubscriptions_setup_version', '0.4');
    }

    // etc
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.