Jaka jest Twoja najlepsza praktyka wykonywania skryptów jednorazowych?

32

Problem

Wszyscy znaleźliśmy się w takiej sytuacji i wiele pytań na tej stronie potrzebuje takiego rozwiązania. Musisz albo zaktualizować bazę danych, automatycznie wstawić dużo danych, przekonwertować meta_keyslub coś podobnego.

Oczywiście w działającym systemie opartym na najlepszych praktykach tak się nie powinno stać.

Ale tak się stanie, chciałbym usłyszeć twoje osobiste rozwiązanie tego problemu i dlaczego wybrałeś swoje.

Pytanie

Jak wdrożyć jednorazowe skrypty w (działającej) instalacji WordPress?

Problem dotyczy głównie następujących powodów:

  • Skrypty wstawiające dane nie powinny być uruchamiane więcej niż jeden raz
  • Skrypty wymagające dużej ilości zasobów nie powinny być uruchamiane w czasie, gdy nie można ich monitorować
  • Nie powinny być uruchamiane przypadkowo

Powód, o który pytam

Mam swoją własną praktykę, zamierzam zamieścić ją w odpowiedziach. Ponieważ nie wiem, czy jest to najlepsze rozwiązanie, chciałbym wiedzieć o twoim. Jest to również pytanie, które jest zadawane wiele razy w kontekście innych pytań, i byłoby wspaniale mieć zasoby gromadzące pomysły.

czekam na naukę od ciebie :)

fischi
źródło
2
Jeśli to naprawdę jednorazowa umowa, to piszę skrypt, uruchamiam go, a następnie usuwam. Po tym nikt nie może uruchomić go ponownie. Jak wszystkie rzeczy, kod jest ulotny. ;)
Otto
1
Chodzi mi o to, że martwię się, że skrypt może zostać wywołany po raz drugi, przypadkowo. ale podchodziłem do ciebie niezliczoną ilość razy;)
fischi
Uruchom go na stronie administratora wtyczki, zawsze działała dla mnie. Możesz dodać kontrole uwierzytelnienia u góry strony, aby upewnić się, że to Ty, jeśli to konieczne.
Andrew Bartel
ale nie mówisz o planowanym jednorazowym wykonaniu, tylko manualnie ?
birgire
1
Tak, mówię tylko o ręcznych operacjach na czas, takich jak migracja skryptów itp., A nie o wp-cronzaplanowanych zdarzeniach.
fischi

Odpowiedzi:

17

Dla siebie używam kombinacji:

  • jeden plik poświęcony jednorazowemu skryptowi
  • za pomocą przejściowego, aby zatrzymać przypadkowe uruchomienie skryptu więcej niż jeden raz
  • za pomocą zarządzania możliwościami lub kontroli użytkownika, aby skrypt był uruchamiany przeze mnie.

Struktura

Korzystam z pliku ( onetime.php) w moim folderze incdołączania, który jest zawarty functions.phpi usunięty stamtąd po użyciu.

include( 'inc/onetime.php' );

Plik dla samego skryptu

W mojej onetime.phpfunkcji f711_my_onetime_function()jest umieszczona. Jak może to być dowolna funkcja. Zakładam, że twój skrypt jest przetestowany i działa poprawnie.

Aby uzyskać kontrolę nad wykonywaniem skryptu, używam obu

Kontrola zdolności

Aby powstrzymać innych użytkowników przed przypadkowym uruchomieniem mojego skryptu:

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

lub

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

przemijający

aby powstrzymać się od przypadkowego wykonania skryptu więcej niż raz.

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

Plik do wykonania skryptu w mojej funkcji f711_my_onetime_function()wyglądałby tak:

$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.
}

Powodem, dla którego ustawiam stan przejściowy natychmiast po sprawdzeniu, czy istnieje, jest to, że chcę, aby funkcja była wykonywana po dwukrotnym użyciu skryptu przed zablokowaniem.

Jeśli potrzebuję danych wyjściowych z mojej funkcji, albo drukuję to jako komentarz w stopce, albo czasami filtruję zawartość.

Czas blokady jest ustawiony na 10 minut, ale można go dostosować do własnych potrzeb.

Sprzątać

Po pomyślnym wykonaniu mojego skryptu usunę includez functions.php, i usunąć onetime.phpz serwera. Ponieważ użyłem limitu czasu dla stanu przejściowego, nie muszę czyścić bazy danych, ale oczywiście można również usunąć stan przejściowy po usunięciu pliku.

fischi
źródło
Myślałem o dodaniu do tego mojej odpowiedzi, ale po przeczytaniu twojego wpisu na samej górze tej odpowiedzi ... nie będę już więcej, ponieważ moje podejście wygląda prawie dokładnie tak samo. Więc +1 za to - także za szczegółowe przemyślenia na ten temat.
tfrommen
14

Możesz także to zrobić:

uruchom onetime.phpi zmień nazwę po wykonaniu.

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');
}
Ravinder Kumar
źródło
Oto co robimy; jest prawie na pewno głupi.
Qix
7

Stworzyłem w tym celu skrypt Phing z wiersza poleceń, nie jest to nic innego jak ładowanie zewnętrznego skryptu do uruchomienia. Powodem, dla którego użyłem go za pośrednictwem CLI, jest to, że:

  • Nie chcę, aby ładował się przez pomyłkę (trzeba wpisać polecenie)
  • Jest bezpieczny, ponieważ można go uruchomić poza katalogiem głównym, innymi słowy, może wpływać na WP, ale WP nie może w żaden sposób dotrzeć do skryptu.
  • Nie dodaje żadnego kodu do WP ani do samej DB.

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

Możesz więc użyć Phing lub CLI PHP i spać w nocy. WP-CLI jest również dobrą alternatywą, choć zapominam, czy można go używać poza katalogiem głównym.

Ponieważ jest to popularny post, tutaj jest przykład skryptu: https://github.com/wycks/WordPhing (run.php)

Wyck
źródło
To wygląda ładnie i prosto, a także bezpiecznie. Zajmowałeś się także jednym z moich głównych problemów (wykonywaniem przez przypadek dwukrotnie) przy użyciu wiersza poleceń. Dobry pomysł!
fischi
5

Innym dość prostym sposobem uruchomienia skryptu jednorazowego jest zrobienie tego za pomocą wtyczki MU.

Umieść kod w jakimś pliku PHP (np. one-time.php), Który przesyłasz do folderu wtyczek MU (domyślnie /wp-content/mu-plugins), dostosuj uprawnienia do pliku, uruchom wtyczkę (tj. Zgodnie z wybranym hakiem, po prostu musisz odwiedzić frontend / backend) i gotowe.

Oto płyta grzewcza:

/**
* 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);

Bez komentarzy i innych rzeczy wygląda to tak:

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
źródło
4

W idealnych warunkach ssh na serwer i sam wykonuję tę funkcję za pomocą wp-cli.

Często nie jest to jednak możliwe, więc zwykle ustawiam zmienną $ _GET i podpinam ją do 'init', na przykład:

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

następnie uderzył

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

i wyłącz hak po zakończeniu.

djb
źródło
4

Czasami korzystałem z funkcji uzależnionej od dezaktywacji wtyczek.

Zobacz tutaj Zaktualizuj stare linki do ładnego, niestandardowego typu posta

Gdy tylko administratorzy mogą aktywować wtyczki, pojawia się sprawdzenie zdolności jako efekt uboczny.

Po dezaktywacji nie ma potrzeby usuwania pliku, nie będzie on uwzględniany przez Wordress. Uzależniony, jeśli chcesz ponownie uruchomić, możesz. Ponowna aktywacja i dezaktywacja.

A czasami używałem przejściowego używanego jak w odpowiedzi @fischi. Np. Tutaj zapytanie o tworzenie produktów woocommerce ze zdjęć lub tutaj Usuń / zamień tagi img w treści postu dla automatycznie publikowanych postów

Połączenie obu może być alternatywą.

gmazzap
źródło
To także naprawdę dobry pomysł. Jeśli zawsze denerwuje Cię konieczność ponownej aktywacji, możesz również podłączyć tę samą funkcję do aktywacji wtyczki, prawda?
fischi
Tak, jeśli chcesz. Myślę jednak, że 2 kliknięcia nie są wielkim wysiłkiem, aby uruchomić skrypt jednorazowy. Każde inne rozwiązanie obejmujące komendę CLI lub obsługę plików (zmiana nazwy, usuwanie) wymaga więcej „pracy”. Ponadto za każdym razem, gdy korzystasz z haków, polegasz na zmiennych globalnych, dodając dodatkową warstwę potencjalnych problemów dotyczących bezpieczeństwa / przewidywalności kodu. @fischi
gmazzap
Nie sądzę, że dwa kliknięcia to też za dużo, chciałem tylko zapytać :)
fischi
3

Zdecydowanie możesz, po prostu stwórz swój jednorazowy kod jako wtyczkę.

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

Problem jak aktywować tę wtyczkę bez kliknięcia Aktywuj link?

wystarczy dodać activate_plugins('onetime/index.php');wfunctions.php

lub Użyj musi używać wtyczek, http://codex.wordpress.org/Must_Use_Plugins

Spróbuj wykonać inne czynności, jak wtedy, gdy chcesz uruchomić jednorazową wtyczkę,

  1. admin_init - po admin init

  2. init - wordpress init

  3. wp - po załadowaniu wordpress

Amit Sukapure
źródło
2

Innym sposobem jest ustawienie globalnej opcji wp_opt po zakończeniu pracy i sprawdzenie tej opcji za każdym razem, gdy wykonywane jest przechwycenie init.

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' );

Oczywiście nie musisz mieć tego kodu na zawsze (nawet jeśli jest to prosty odczyt z bazy danych), więc prawdopodobnie możesz go usunąć po zakończeniu pracy. Możesz także ręcznie zmienić tę wartość opcji na 0, jeśli chcesz ponownie wykonać kod.

Mauro Mascia
źródło
1

Moje podejście jest nieco inne. Lubię dodawać mój jednorazowy skrypt jako funkcję w pliku function.php mojego motywu i uruchamiać go dla określonego zapytania GET.

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

Aby to uruchomić, po prostu odwiedź adres URL „www.sitename.com/?linkupdate”

Do tej pory działa mi to dobrze ...

Czy ta metoda ma jakieś wady? Zastanawiam się ...

Sid
źródło
1

Używam tylko jednej niestandardowej strony szablonu produktu, z której nie korzystam i nie jestem połączony z niczym na serwerze publicznym.

Na przykład, jeśli mam stronę referencyjną, która nie jest aktywna (w trybie roboczym itp.), Ale jest połączona na przykład z szablonem pojedynczej strony single-testimonial.php- mogę tam umieścić funkcje, załadować stronę za pomocą a previewi funkcji lub cokolwiek innego uruchomiony raz. Bardzo łatwo jest również modyfikować funkcję w przypadku debugowania.

Jest naprawdę łatwy i wolę go niż używać, initponieważ mam większą kontrolę nad tym, kiedy i jak został uruchomiony. Po prostu moje preferencje.

bbruman
źródło
0

Na wszelki wypadek to właśnie zrobiłem i działa dobrze:

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
}
Adam Moss
źródło