¿Cuál es su mejor práctica para ejecutar scripts únicos?

32

El problema

Todos hemos estado en una situación como esta, y muchas preguntas en este sitio necesitan una solución como esta. Tienes que actualizar una base de datos, insertar muchos datos automáticamente, convertir meta_keyso algo similar.

Por supuesto, en un sistema en ejecución basado en las mejores prácticas esto no debería suceder.

Pero mientras lo hace, me encantaría escuchar su solución personal a este problema y por qué eligió la suya.

La pregunta

¿Cómo implementa scripts únicos en su instalación de WordPress (en ejecución)?

El problema aquí se debe principalmente a las siguientes razones:

  • Las secuencias de comandos que insertan datos no deben ejecutarse más de una vez
  • Los scripts que requieren muchos recursos no deben ejecutarse en un momento en que no se puedan monitorear
  • No deben ejecutarse por accidente.

La razón que pido

Tengo mi propia práctica, voy a publicarla en las respuestas. Como no sé si es la mejor solución, me gustaría saber cuál es la suya. Además, esta es una pregunta que se hace muchas veces en el contexto de otras preguntas, y sería genial tener un recurso que recolecte las ideas.

deseando aprender de ti :)

fischi
fuente
2
Si realmente es un trato único, entonces escribo el script, lo ejecuto y luego lo borro. Nadie puede ejecutarlo de nuevo después de eso. Como todas las cosas, el código es fugaz. ;)
Otto
1
La cuestión es que me preocupa que un guión pueda llamarse por segunda vez, por coincidencia. pero hice tu enfoque innumerables veces;)
fischi
Ejecútelo en la página de administración de un complemento, siempre funcionó para mí. Puedes agregar verificaciones de autenticación en la parte superior de la página para asegurarte de que eres tú si es necesario.
Andrew Bartel
pero no estás hablando de la ejecución programada de una sola vez, solo manual ?
Birgire
1
Sí, solo estoy hablando de operaciones manuales a tiempo, como migrar scripts, etc., no de wp-croneventos programados.
fischi

Respuestas:

17

Yo mismo uso una combinación de:

  • un archivo dedicado al script de una sola vez
  • usando un transitorio para evitar que el script se ejecute accidentalmente más de una vez
  • usando la gestión de capacidades o el control de usuario para garantizar que solo ejecute el script.

Estructura

Uso un archivo ( onetime.php) en mi carpeta de inclusión inc, que se incluye en el functions.php, y se elimina de allí después del uso.

include( 'inc/onetime.php' );

El archivo para el script en sí

En mi se coloca onetime.phpmi función f711_my_onetime_function(). Como podría ser cualquier función. Supongo que su secuencia de comandos se prueba y funciona correctamente.

Para lograr el control sobre la ejecución del script, utilizo ambos

Control de la capacidad

Para evitar que otros usuarios ejecuten accidentalmente mi script:

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

o

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

un transitorio

para evitar ejecutar accidentalmente el script más de una vez.

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

El archivo para ejecutar el script en mi función f711_my_onetime_function()se vería así:

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

La razón por la que configuré el transitorio inmediatamente después de la verificación, si es que existe, es porque quiero que la función se ejecute después de que el script haya sido bloqueado para que no se use dos veces.

Si necesito algún resultado de mi función, lo imprimo como un comentario en el pie de página o, a veces, incluso filtro el contenido.

El tiempo de bloqueo se establece en 10 minutos, pero se puede ajustar a sus necesidades.

Limpiar

Después de la ejecución exitosa de mi script, elimino el includedel functions.php, y elimino el onetime.phpdel servidor. Como utilicé un tiempo de espera para el transitorio, no necesito limpiar la base de datos, pero, por supuesto, también podría eliminar el transitorio después de eliminar el archivo.

fischi
fuente
Pensé en agregar mi respuesta a esto, pero después de haber leído su lista justo en la parte superior de esta respuesta ... ya no lo haré, ya que mi enfoque se ve casi exactamente igual. Entonces +1 para esto, también para los pensamientos detallados sobre esto.
tfrommen
14

También puedes hacer esto:

ejecutar onetime.phpy renombrarlo después de la ejecución.

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
fuente
Esto es lo que hacemos; está prácticamente garantizado para ser infalible.
Qix
7

Creé un script Phing de línea de comando para esto, no es nada especial aparte de cargar un script externo para ejecutar. La razón por la que lo usé a través de la CLI es porque:

  • No quiero que se cargue por error (necesito escribir un comando)
  • Es seguro, ya que puede ejecutarse fuera de la raíz web, en otras palabras, puede afectar a WP pero WP no puede acceder al script de ninguna manera.
  • No agrega ningún código a WP o al DB en sí.

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

Entonces puedes usar Phing, o la CLI de PHP y dormir por la noche. El WP-CLI también es una buena alternativa, aunque se me olvida si puede usarlo fuera de la raíz web.

Como esta es una publicación popular, aquí hay un ejemplo del script: https://github.com/wycks/WordPhing (run.php)

Wyck
fuente
Esto se ve agradable y simple, así como seguro. También cubriste una de mis principales preocupaciones (yo ejecuté dos veces por accidente) en gran medida utilizando la línea de comando. ¡Buena idea!
fischi
5

Otra forma bastante sencilla de ejecutar un script de una sola vez es hacerlo mediante un complemento MU.

Coloque el código en algún archivo PHP (p. Ej. one-time.php) Que cargue en la carpeta de complementos de MU (de forma predeterminada/wp-content/mu-plugins ), ajuste los permisos del archivo, ejecute el complemento (es decir, de acuerdo con el enlace elegido, básicamente solo tiene que visitar la interfaz / backend), y ya está todo listo.

Aquí hay un repetitivo:

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

Sin los comentarios y esas cosas, se ve así:

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
fuente
4

En condiciones ideales, me enviaría al servidor y ejecutaría la función yo mismo usando wp-cli.

Sin embargo, esto a menudo no es posible, por lo que tiendo a establecer una variable $ _GET y la engancho a 'init', por ejemplo:

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

luego golpear

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

y deshabilitar el gancho cuando haya terminado.

djb
fuente
4

A veces utilicé una función enganchada en la desactivación del complemento.

Vea aquí Actualizar enlaces antiguos a enlaces permanentes tipo de publicación personalizada

Una vez que solo los administradores pueden activar los complementos, hay una verificación de capacidad como efecto secundario.

No es necesario eliminar el archivo una vez desactivado, no lo incluirá wordress. Además, si quieres volver a correr, puedes hacerlo. Activando y desactivando nuevamente.

Y a veces utilicé transitorios como en la respuesta de @fischi. Por ejemplo, consulta aquí para crear productos de comercio electrónico a partir de imágenes o aquí elimina / reemplaza etiquetas img en el contenido de la publicación para publicaciones publicadas automáticamente

Una combinación de ambos puede ser una alternativa.

gmazzap
fuente
Esta también es una muy buena idea. Si se vuelve molesto tener que activarlo para desactivarlo nuevamente, también podría conectar la misma función a la activación del complemento, ¿verdad?
fischi
Sí, si tu quieres. Sin embargo, creo que 2 clics no es un gran esfuerzo para ejecutar un script de una sola vez. Cualquier otra solución que implique el comando CLI o el manejo de archivos (cambio de nombre, eliminación) necesita más "trabajo". Además, cada vez que confía en los ganchos, confía en variables globales, agregando una capa adicional de posibles problemas con respecto a la seguridad / previsibilidad del código. @fischi
gmazzap
No creo que dos clics sean demasiado, solo quería preguntar :)
fischi
3

Definitivamente puedes, solo crea tu código de una vez como un complemento.

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

¿Problema, cómo activo este complemento sin hacer clic en el enlace Activar?

sólo tiene que añadir activate_plugins('onetime/index.php');enfunctions.php

o Use debe usar complementos, http://codex.wordpress.org/Must_Use_Plugins

Intente con diferentes acciones como cuando desea ejecutar el complemento de una vez,

  1. admin_init: después de admin init

  2. init - wordpress init

  3. wp - cuando se carga wordpress

Amit Sukapure
fuente
2

Otra forma es establecer una opción global wp_option cuando el trabajo está hecho y verificar esa opción cada vez que se ejecuta el enlace de inicio.

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

Naturalmente, no necesita tener este código para siempre (incluso si se trata de una simple lectura de la base de datos), por lo que probablemente pueda eliminar el código cuando termine el trabajo. También puede cambiar manualmente el valor de esta opción a 0 si necesita volver a ejecutar el código.

Mauro Mascia
fuente
1

Mi enfoque es un poco diferente en esto. Me gusta agregar mi script de una sola vez como una función en function.php de mi tema y ejecutarlo en una consulta GET específica.

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

Para ejecutar esto, solo visite la URL "www.sitename.com/?linkupdate"

Esto está funcionando bien para mí hasta ahora ...

¿Este método tiene algún inconveniente? Sólo me preguntaba...

Sid
fuente
1

Solo uso una sola página de plantilla de producto personalizada que no estoy usando y no está conectada a nada en el servidor público.

Por ejemplo single-testimonial.php, si tengo una página de testimonios que no está en vivo (en modo borrador, o lo que sea), pero que está conectada a una sola plantilla de página, por ejemplo , puedo colocar funciones allí, cargar la página a través de unpreview la función o lo que sea lanzado una vez. También es muy fácil hacer modificaciones a la función en caso de depuración.

Es realmente fácil y prefiero usarlo initporque tengo más control sobre cuándo y cómo se inicia. Solo mi preferencia.

bbruman
fuente
0

En caso de que ayude, esto es lo que hice y funciona bien:

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
fuente