Was ist Ihre bewährte Methode zum Ausführen von einmaligen Skripten?


32

Das Problem

Wir waren alle in einer solchen Situation und viele Fragen auf dieser Website erfordern eine solche Lösung. Sie müssen entweder eine Datenbank aktualisieren, viele Daten automatisch einfügen, konvertieren meta_keysoder ähnliches.

In einem laufenden System, das auf Best Practices basiert, sollte dies natürlich nicht passieren.

Aber ich würde gerne Ihre persönliche Lösung für dieses Problem erfahren und wissen, warum Sie sich für Ihre entschieden haben.

Die Frage

Wie implementieren Sie einmalige Skripte in Ihre (laufende) WordPress-Installation?

Das Problem ist hauptsächlich auf folgende Gründe zurückzuführen:

  • Skripte, die Daten einfügen, sollten nicht mehrmals ausgeführt werden
  • Skripte, die viele Ressourcen erfordern, sollten nicht zu einem Zeitpunkt ausgeführt werden, an dem sie nicht überwacht werden können
  • Sie sollten nicht versehentlich ausgeführt werden

Den Grund frage ich

Ich habe meine eigene Praxis, ich werde sie in den Antworten veröffentlichen. Da ich nicht weiß, ob es die beste Lösung ist, würde ich gerne etwas über Ihre erfahren. Dies ist auch eine Frage, die häufig im Zusammenhang mit anderen Fragen gestellt wird, und es wäre großartig, wenn eine Ressource die Ideen sammelt.

Ich freue mich darauf, von dir zu lernen :)


2
Wenn es wirklich ein einmaliger Deal ist, schreibe ich das Skript, führe es aus und lösche es. Danach kann es niemand mehr ausführen. Wie alle Dinge ist Code flüchtig. ;)
Otto

1
Die Sache ist, dass ich mir Sorgen mache, dass ein Skript zufällig ein zweites Mal aufgerufen wird. aber ich habe deinen
anflug

Führen Sie es auf einer Admin-Seite eines Plugins aus, das immer für mich funktioniert hat. Sie können oben auf der Seite Authentifizierungsprüfungen hinzufügen, um sicherzustellen, dass Sie es sind, falls erforderlich.
Andrew Bartel

Sie sprechen aber nicht von einer geplanten einmaligen Ausführung, sondern nur von einer manuellen ?
Birgire

1
Ja, ich spreche nur von manuellen Einschaltvorgängen wie Migrationsskripten usw., nicht von wp-crongeplanten Ereignissen.
Fischi

Antworten:


17

Ich benutze für mich eine Kombination aus:

  • Eine Datei für das einmalige Skript
  • Verwenden eines Transienten, um zu verhindern, dass das Skript versehentlich mehr als einmal ausgeführt wird
  • Verwenden des Capability-Managements oder der Benutzerkontrolle, um sicherzustellen, dass das Skript nur von mir ausgeführt wird.

Struktur

Ich verwende eine Datei ( onetime.php) in meinem Include-Ordner inc, die in der enthalten functions.phpist und von dort nach der Verwendung gelöscht wird.

include( 'inc/onetime.php' );

Die Datei für das Skript selbst

In meinem ist onetime.phpmeine Funktion f711_my_onetime_function()platziert. Da könnte es jede Funktion sein. Ich gehe davon aus, dass Ihr Skript getestet wurde und ordnungsgemäß funktioniert.

Um die Kontrolle über die Ausführung des Skripts zu erlangen, verwende ich beide

Fähigkeitskontrolle

So verhindern Sie, dass andere Benutzer versehentlich mein Skript ausführen:

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

oder

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

eine vorübergehende

um mich davon abzuhalten, das Skript versehentlich mehr als einmal auszuführen.

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

Die Datei zum Ausführen des Skripts in meiner Funktion f711_my_onetime_function()würde folgendermaßen aussehen:

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

Der Grund, warum ich den Transienten unmittelbar nach der Prüfung auf Vorhandensein gesetzt habe, ist, dass die Funktion ausgeführt werden soll, nachdem das Skript für die zweimalige Verwendung gesperrt wurde.

Wenn ich eine Ausgabe von meiner Funktion benötige, drucke ich sie entweder als Kommentar in der Fußzeile aus oder filtere manchmal sogar den Inhalt.

Die Sperrzeit ist auf 10 Minuten eingestellt, kann aber an Ihre Bedürfnisse angepasst werden.

Aufräumen

Nach der erfolgreichen Ausführung meines Skripts lösche ich das includeaus functions.phpund entferne das onetime.phpvom Server. Da ich für den Übergang eine Zeitüberschreitung verwendet habe, muss die Datenbank nicht bereinigt werden. Natürlich können Sie den Übergang auch löschen, nachdem Sie die Datei entfernt haben.


Ich habe darüber nachgedacht, meine Antwort hinzuzufügen, aber nachdem ich Ihren Eintrag ganz oben in dieser Antwort gelesen habe ... werde ich nicht mehr, da mein Ansatz fast genauso aussieht. Also +1 dafür - auch für die ausführlichen Gedanken dazu.
5.

14

Sie können dies auch tun:

laufen onetime.phpund benennen Sie sie nach der Ausführung.

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

Das ist was wir machen; Es ist so gut wie garantiert narrensicher.
Qix

7

Ich habe ein Kommandozeilen-Phing-Skript dafür erstellt. Es ist nichts Besonderes, als ein externes Skript zu laden, das ausgeführt werden soll. Der Grund, warum ich es über die CLI verwendet habe, ist folgender:

  • Ich möchte nicht, dass es versehentlich geladen wird (ich muss einen Befehl eingeben)
  • Es ist sicher, da es außerhalb des Webroot ausgeführt werden kann, mit anderen Worten, es kann sich auf WP auswirken, aber WP kann das Skript in keiner Weise erreichen.
  • WP oder der DB selbst wird kein Code hinzugefügt.

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

Sie können also Phing oder die PHP-CLI verwenden und nachts schlafen. Die WP-CLI ist auch eine gute Alternative, obwohl ich vergesse, ob Sie sie außerhalb des Webroot verwenden können.

Da dies ein beliebter Beitrag ist, ist hier ein Beispiel für das Skript: https://github.com/wycks/WordPhing (run.php)


Das sieht einfach und sicher aus. Sie haben auch eines meiner Hauptprobleme (ich wurde zweimal versehentlich ausgeführt) ausführlich über die Befehlszeile behandelt. Gute Idee!
Fischi

5

Eine andere ziemlich einfache Möglichkeit, ein einmaliges Skript auszuführen, ist die Verwendung eines MU-Plugins.

Fügen Sie den Code in eine PHP-Datei ein (z. B. one-time.php), die Sie in den Ordner Ihrer MU-Plugins hochgeladen haben (standardmäßig /wp-content/mu-plugins), passen Sie die Dateiberechtigungen an und führen Sie das Plugin aus (dh Sie müssen entsprechend Ihrem gewählten Hook nur das Frontend besuchen) / backend) und du bist fertig.

Hier ist ein Kesselschild:

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

Ohne die Kommentare und das Zeug sieht es einfach so aus:

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

Unter idealen Bedingungen würde ich in den Server ssh und die Funktion selbst mit wp-cli ausführen.

Dies ist jedoch oft nicht möglich. Daher setze ich normalerweise eine $ _GET-Variable und hänge sie an 'init' an, zum Beispiel:

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

dann schlagen

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

und deaktivieren Sie den Haken, wenn es fertig ist.


4

Manchmal habe ich eine Funktion verwendet, die bei der Deaktivierung des Plugins aktiviert ist.

Siehe hier Aktualisiere alte Links zu hübschen Permalinks

Sobald nur noch Administratoren Plugins aktivieren können, erfolgt als Nebeneffekt eine Funktionsprüfung.

Es ist nicht nötig, die Datei zu löschen, wenn sie deaktiviert ist. Sie wird nicht von wordress aufgenommen. In der Sucht, wenn Sie es wieder ausführen möchten, können Sie. Aktivieren und wieder deaktivieren.

Und manchmal habe ich transient verwendet wie in @fischi answer. ZB hier abfragen, um Woocommerce-Produkte aus Bildern zu erstellen oder hier img-Tags im Post-Inhalt für automatisch veröffentlichte Posts löschen / ersetzen

Eine Kombination von beiden kann eine Alternative sein.


Das ist auch eine wirklich gute Idee. Wenn es ärgerlich wird, immer wieder aktivieren zu müssen, um es wieder zu deaktivieren, könnten Sie dieselbe Funktion auch mit der Aktivierung des Plugins verbinden, oder?
Fischi

Ja, wenn du willst. Ich denke jedoch, dass 2 Klicks keine große Anstrengung sind, um ein einmaliges Skript auszuführen. Jede andere Lösung, die CLI-Befehle oder die Dateiverwaltung (Umbenennen, Löschen) umfasst, erfordert mehr "Arbeit". Darüber hinaus verlassen Sie sich jedes Mal, wenn Sie sich auf Hooks verlassen, auf globale Variablen, was eine zusätzliche Ebene potenzieller Probleme in Bezug auf die Sicherheit / Vorhersagbarkeit des Codes hinzufügt. @fischi
gmazzap

Ich denke nicht, dass zwei Klicks zu viel sind, wollte nur fragen :)
fischi

3

Auf jeden Fall können Sie, erstellen Sie einfach Ihren einmaligen Zeitcode als Plugin.

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

Problem, wie kann ich dieses Plugin aktivieren, ohne auf den Link Aktivieren zu klicken?

fügen Sie einfach activate_plugins('onetime/index.php');infunctions.php

oder Use muss Plugins verwenden, http://codex.wordpress.org/Must_Use_Plugins

Versuchen Sie es mit verschiedenen Aktionen, als wenn Sie einmaliges Plugin ausführen möchten,

  1. admin_init - nach admin init

  2. Init - WordPress Init

  3. wp - wenn WordPress geladen


2

Eine andere Möglichkeit besteht darin, eine globale wp_option festzulegen, wenn die Arbeit abgeschlossen ist, und diese Option bei jeder Ausführung des init-Hooks zu überprüfen.

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

Natürlich müssen Sie diesen Code nicht für immer haben (auch wenn es sich um ein einfaches Lesen aus der Datenbank handelt), sodass Sie den Code wahrscheinlich entfernen können, wenn die Arbeit erledigt ist. Sie können diesen Optionswert auch manuell in 0 ändern, wenn Sie den Code erneut ausführen müssen.


1

Mein Ansatz ist diesbezüglich etwas anders. Ich füge mein einmaliges Skript gerne als Funktion in die function.php meines Themas ein und führe es auf einer bestimmten GET-Abfrage aus.

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

Um dies auszuführen, besuchen Sie einfach die URL "www.sitename.com/?linkupdate"

Das funktioniert bis jetzt gut für mich ...

Hat diese Methode irgendwelche Nachteile? Ich frage mich nur ...


1

Ich verwende nur eine einzelne benutzerdefinierte Produktvorlagenseite, die ich nicht verwende und die mit nichts auf dem öffentlichen Server verbunden ist.

Zum Beispiel, wenn ich eine Testimonial-Seite habe, die nicht online ist (im Entwurfsmodus oder was auch immer), sondern mit einer einzelnen Seitenvorlage verbunden ist single-testimonial.php- ich kann dort Funktionen platzieren, die Seite über a laden previewund die Funktion oder was auch immer einmal gestartet. Es ist auch sehr einfach, die Funktion beim Debuggen zu ändern.

Ist wirklich einfach und ich bevorzuge es gegenüber der Verwendung, initweil ich mehr Kontrolle darüber habe, wann und wie es gestartet wird. Nur meine Präferenz.


0

Nur für den Fall, dass es hilft, habe ich das getan und es funktioniert gut:

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
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.