Какова ваша лучшая практика для выполнения одноразовых сценариев?

32

Проблема

Мы все были в такой ситуации, и многие вопросы на этом сайте нуждаются в подобном решении. Вы должны либо обновить базу данных, либо автоматически вставить много данных, конвертировать meta_keysили что-то подобное.

Конечно, в работающей системе, основанной на лучших практиках, этого не должно происходить.

Но как бы то ни было, я хотел бы услышать ваше личное решение этой проблемы и почему вы выбрали свое.

Вопрос

Как реализовать одноразовые скрипты в вашей (работающей) установке WordPress?

Проблема здесь в основном из-за следующих причин:

  • Скрипты, вставляющие данные, не должны запускаться более одного раза
  • Скрипты, которые требуют большого количества ресурсов, не должны запускаться в то время, когда их нельзя контролировать
  • Они не должны быть запущены случайно

Причина, которую я спрашиваю

У меня есть своя практика, я собираюсь опубликовать ее в ответах. Поскольку я не знаю, является ли это лучшим решением, я бы хотел узнать о вашем. Кроме того, это вопрос, который часто задают в контексте других вопросов, и было бы здорово иметь ресурс, собирающий идеи.

с нетерпением жду учиться у вас :)

fischi
источник
2
Если это действительно одноразовая сделка, я пишу сценарий, запускаю его и удаляю. После этого никто не сможет запустить его снова. Как и все вещи, код мимолетный. ;)
Отто
1
Дело в том, что меня беспокоит, что сценарий может быть вызван второй раз, по совпадению. но я делал твой подход бесчисленное количество раз;)
fischi
Запустите его на странице администратора плагина, всегда работал для меня. Вы можете добавить проверки подлинности вверху страницы, чтобы убедиться, что это вы, если это необходимо.
Эндрю Бартел
но вы не говорите о запланированном однократном исполнении, только ручном ?
Birgire
1
Да, я говорю только о ручных операциях вовремя, таких как сценарии миграции и т. Д., А не о wp-cronзапланированных событиях.
fischi

Ответы:

17

Я для себя использую комбинацию из:

  • один файл, посвященный одноразовому сценарию
  • использование переходного процесса, чтобы остановить сценарий от случайного запуска более одного раза
  • используя управление возможностями или пользовательский контроль, чтобы скрипт был запущен мной.

Состав

Я использую файл ( onetime.php) в моей include-папке 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с сервера. Поскольку я использовал время ожидания для переходного процесса, мне не нужно очищать базу данных, но, конечно, вы также можете удалить переходный процесс после удаления файла.

fischi
источник
Я думал добавить свой ответ к этому, но после прочтения вашего списка в верхней части этого ответа ... я больше не буду, так как мой подход выглядит почти так же. +1 за это - и за подробные мысли по этому поводу.
tfrommen
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, заключается в следующем:

  • Я не хочу, чтобы он загружался по ошибке (нужно ввести команду)
  • Это безопасно, поскольку его можно запускать вне корневого веб-узла, другими словами, это может повлиять на 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 также является хорошей альтернативой, хотя я забыл, если вы можете использовать его вне веб-корня.

Поскольку это популярный пост, вот пример скрипта: https://github.com/wycks/WordPhing (run.php)

Уик
источник
Это выглядит красиво и просто, а также безопасно. Вы также охватили одну из моих главных проблем (я выполнял дважды случайно) с помощью командной строки. Хорошая идея!
fischi
5

Еще один довольно простой способ запуска одноразового скрипта - сделать это с помощью плагина MU.

Поместите код в некоторый файл PHP (например, one-time.php), который вы загружаете в папку ваших плагинов MU (по умолчанию /wp-content/mu-plugins), настройте права доступа к файлу, запустите плагин (то есть, в соответствии с выбранным вами хуком, вам просто нужно посетить веб-интерфейс / 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);
tfrommen
источник
4

В идеальных условиях я бы зашёл на сервер и сам выполнил функцию с помощью 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

и отключите крючок, когда это будет сделано.

DJB
источник
4

Иногда я использовал функцию, подключенную к деактивации плагина.

Смотрите здесь Обновление старых ссылок на довольно постоянные ссылки Пользовательский тип сообщения

Как только плагины могут активировать только администраторы, возможен побочный эффект.

Нет необходимости удалять файл после деактивации, он не будет включен в wordress. В зависимости, если вы хотите запустить его снова, вы можете. Активация и деактивация снова.

И иногда я использовал переходный процесс, как в ответе @fischi. Например, запрос здесь для создания продуктов woocommerce из изображений или здесь Удаление / замена тегов img в содержимом сообщений для автоматически публикуемых сообщений.

Комбинация обоих может быть альтернативой.

Gmazzap
источник
Это тоже действительно хорошая идея. Если постоянно раздражает необходимость активировать его, чтобы деактивировать снова, вы также можете подключить ту же функцию к активации плагина, верно?
fischi
Да, если хочешь. Тем не менее, я думаю, что 2 клика - это не большое усилие для запуска одноразового скрипта. Любое другое решение, которое включает команду CLI или обработку файла (переименование, удаление), требует больше «работы». Кроме того, каждый раз, когда вы полагаетесь на хуки, вы полагаетесь на глобальные переменные, добавляя дополнительный уровень потенциальных проблем, связанных с безопасностью / предсказуемостью кода. @fischi
gmazzap
Я не думаю, что два клика это слишком много, просто хотел спросить :)
fischi
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 - после init admin

  2. init - wordpress init

  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"

До сих пор это работает нормально для меня ...

Есть ли у этого метода недостатки? Просто интересуюсь...

Sid
источник
1

Я просто использую одну пользовательскую страницу шаблона продукта, которую я не использую и не подключена ни к чему на общедоступном сервере.

Например, если у меня есть страница отзыва, которая не является активной (в черновом режиме и т. Д.), Но подключена, например, к одному шаблону страницы single-testimonial.php- я могу разместить там функции, загрузить страницу с помощью функции a previewи т. Д. запущен один раз. Также очень легко вносить изменения в функцию в случае отладки.

Это действительно легко, и я предпочитаю его использовать, initпотому что у меня больше контроля над тем, когда и как он был запущен. Просто мои предпочтения.

bbruman
источник
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
}
Адам Мосс
источник