¿Cuál es tu mejor práctica para ejecutar scripts de una sola vez?

29 ene 2014, 18:24:47
Vistas: 21.4K
Votos: 42

El Problema

Todos nos hemos encontrado en una situación así, y muchas preguntas en este sitio necesitan una solución como esta. Ya sea que tengas que actualizar una base de datos, insertar muchos datos automáticamente, convertir meta_keys, o algo similar.

Por supuesto, en un sistema en funcionamiento basado en mejores prácticas esto no debería suceder.

Pero como sucede, me encantaría escuchar tu solución personal a este problema y por qué elegiste la tuya.

La Pregunta

¿Cómo implementas scripts de una sola vez en tu instalación de WordPress (en funcionamiento)?

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

  • Los scripts que insertan datos no deberían ejecutarse más de una vez
  • Los scripts que requieren muchos recursos no deberían ejecutarse en un momento en que no puedan ser monitoreados
  • No deberían ejecutarse por accidente

La Razón por la que Pregunto

Tengo mi propia práctica, la voy a publicar en las respuestas. Como no sé si es la mejor solución que existe, me gustaría conocer la tuya. 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 recopile las ideas.

¡espero aprender de ustedes! :)

8
Comentarios

Si es realmente algo que solo se hará una vez, entonces escribo el script, lo ejecuto y luego lo elimino. Nadie podrá ejecutarlo de nuevo después de eso. Como todas las cosas, el código es efímero. ;)

Otto Otto
29 ene 2014 20:44:20

La cosa es que me preocupa que un script pueda llamarse una segunda vez, por coincidencia. Pero he usado tu enfoque innumerables veces ;)

fischi fischi
29 ene 2014 22:31:21

Ejecútalo en una página de administración de un plugin, siempre me ha funcionado. 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 Andrew Bartel
29 ene 2014 23:14:15

pero no estás hablando de ejecución programada de una sola vez, solo de ejecución manual?

birgire birgire
3 feb 2014 13:08:42

Sí, solo estoy hablando de operaciones manuales puntuales, como scripts de migración, etc., no eventos programados con wp-cron.

fischi fischi
3 feb 2014 14:10:32

Supongo que no entiendo por qué querrías hacer esto. Si quieres cambiar el nombre de un meta_key que se usa tanto en la base de datos como en tu código, ¿realmente existe un script de una sola ejecución que lo haga todo? Incluso si no tuvieras que cambiar la base de datos y solo necesitaras cambiar el código, si incluyeras un script via include, ¿no permanecería estático hasta que se llame a la página? Supongo que no estoy entendiendo el punto :/

Howdy_McGee Howdy_McGee
5 feb 2014 23:00:55

El punto es, por ejemplo, convertir la información de SEO de un plugin a otro, sin perder información, o insertar mucho contenido, lo cual debe ejecutarse solo una vez. Y con las funciones integradas de WordPress eres mucho más rápido y seguro que ejecutando consultas directamente en tu base de datos.

fischi fischi
6 feb 2014 09:11:51

No era mi objetivo hacer esto, pero después de la votación elegiré mi respuesta, basándome únicamente en los resultados.

fischi fischi
11 feb 2014 08:22:55
Mostrar los 3 comentarios restantes
Todas las respuestas a la pregunta 12
1
26

Yo personalmente uso una combinación de:

  • un archivo dedicado al script de una sola ejecución
  • usar un transient para evitar que el script se ejecute accidentalmente más de una vez
  • usar gestión de capacidades o control de usuario para asegurar que solo yo ejecute el script.

Estructura

Uso un archivo (onetime.php) en mi carpeta de includes inc, el cual se incluye en el functions.php, y se elimina de ahí después de su uso.

include( 'inc/onetime.php' );

El archivo para el script en sí

En mi onetime.php coloco mi función f711_my_onetime_function(). Podría ser cualquier función. Asumo que tu script está probado y funciona correctamente.

Para tener control sobre la ejecución del script, uso ambos

Control de capacidades

Para evitar que otros usuarios ejecuten accidentalmente mi script:

if ( current_user_can( 'manage_options' ) ) // verificar permisos de administrador

o

if ( get_current_user_id() == 711 ) // verificar si soy yo - prefiero restringir la ejecución solo a mí, no a todos los admins.

Un transient

para evitar que yo mismo ejecute accidentalmente el script más de una vez.

$transient = 'f711_my_onetime_check';
if ( !get_transient( $transient ) ) // verificar si la función no se ha ejecutado.

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

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

    set_transient( $transient, 'locked', 600 ); // bloquear función por 10 minutos
    add_action( 'wp_footer', 'f711_my_onetime_function' ); // ejecutar mi función en el hook deseado.

}

function f711_my_onetime_function() {
    // toda mi gloriosa magia de una sola vez.
}

La razón por la que establezco el transient inmediatamente después de verificar si existe es porque quiero que la función se ejecute después de que el script haya sido bloqueado para evitar usos dobles.

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

El tiempo de bloqueo está configurado a 10 minutos, pero puede ajustarse según tus necesidades.

Limpieza

Después de la ejecución exitosa de mi script, elimino el include del functions.php, y quito el onetime.php del servidor. Como usé un tiempo de espera para el transient, no necesito limpiar la base de datos, pero por supuesto también podrías eliminar el transient después de quitar el archivo.

29 ene 2014 18:24:47
Comentarios

Pensé en agregar mi respuesta a esto, pero después de haber leído tu listado justo en la parte superior de esta respuesta... ya no lo haré ya que mi enfoque se ve casi exactamente igual. Así que +1 por esto, también por los pensamientos detallados sobre este tema.

tfrommen tfrommen
5 feb 2014 22:58:38
2
20

También puedes hacer esto:

ejecuta onetime.php y renómbralo después de la ejecución.

if ( current_user_can( 'manage_options' ) ) {

    if( ! file_exists( '/ruta/a/onetime.php' ) )
      return;
    add_action( 'wp_footer', 'ravs_my_onetime_function' ); // ejecuta mi función en el hook deseado.

}

function ravs_my_onetime_function() {

    // toda mi gloriosa magia de una sola vez.
    include( '/ruta/a/onetime.php' );

   // después de toda la ejecución renombra tu archivo;
   rename( '/ruta/a/onetime.php', '/ruta/a/onetime-backup.php');
}
5 feb 2014 21:58:56
Comentarios

Esto es lo que hacemos; está prácticamente garantizado que es a prueba de errores.

Qix - MONICA WAS MISTREATED Qix - MONICA WAS MISTREATED
6 ene 2015 23:07:52

Perfecto, no sé por qué esta idea no vino a mi mente, gracias :)

Shahin Shahin
11 feb 2020 09:01:46
1

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

  • No quiero que se cargue por error (necesita escribir un comando)
  • Es seguro ya que se puede ejecutar fuera del directorio web, en otras palabras, puede afectar a WP pero WP no puede acceder al script de ninguna manera.
  • No añade ningún código a WP o a la base de datos en sí.

require('..ruta a ../wp-blog-header.php');
//un montón de variables globales de WP
define('WP_USE_THEMES', false);
//código personalizado

Así que puedes usar Phing, o la CLI de PHP y dormir tranquilo. WP-CLI también es una buena alternativa, aunque olvido si puedes usarlo fuera del directorio web.

Ya que este es un post popular, aquí hay un ejemplo del script: https://github.com/wycks/WordPhing (run.php)

6 feb 2014 04:54:58
Comentarios

Esto se ve agradable y simple, además de seguro. También abordaste una de mis principales preocupaciones (ejecutarlo dos veces por accidente) en gran medida al usar la línea de comandos. ¡Buena idea!

fischi fischi
6 feb 2014 09:18:15
0

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

Aunque esto no siempre es posible, así que suelo establecer una variable $_GET y engancharla 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 acceder a

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

y desactivar el hook una vez que haya terminado.

5 feb 2014 21:38:38
0

Otra forma bastante simple de ejecutar un script de una sola vez es hacerlo mediante un plugin MU.

Coloca el código en algún archivo PHP (por ejemplo, one-time.php) que subas a la carpeta de plugins MU (por defecto /wp-content/mu-plugins), ajusta los permisos del archivo, ejecuta el plugin (es decir, según el hook que hayas elegido, básicamente solo tienes que visitar el frontend/backend), y ya estarás listo.

Aquí tienes una plantilla:

/**
* Clase principal (y única).
*/
class OneTimeScript {

    /**
     * Hook para la función del plugin.
     *
     * @type    string
     */
    public static $hook = 'init';


    /**
     * Prioridad de la función del plugin.
     *
     * @type    int
     */
    public static $priority = 0;


    /**
     * Ejecuta el script de una sola vez.
     *
     * @hook    self::$hook
     * @return  void
     */
    public static function run() {
        // acción de una sola vez va aquí...

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


    /**
     * Elimina el archivo.
     *
     * @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 demás, simplemente se ve así:

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

    public static function run() {
        // acción de una sola vez va aquí...
        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);
6 feb 2014 00:01:47
0

Definitivamente puedes hacerlo, solo crea tu código de un solo uso como un plugin.

add_action('admin_init', 'one_time_call');
function one_time_call()
{
    /* TUS SCRIPTS */
    deactivate_plugins('onetime/index.php'); //desactivar el plugin actual
}

El problema es ¿cómo activo este plugin sin hacer clic en el enlace Activar?

Simplemente agrega activate_plugins('onetime/index.php'); en functions.php

o Usa plugins obligatorios, http://codex.wordpress.org/Must_Use_Plugins

Prueba con diferentes acciones según cuándo quieras ejecutar el plugin de un solo uso:

  1. admin_init - después de la inicialización del admin

  2. init - inicialización de WordPress

  3. wp - cuando WordPress esté cargado

8 feb 2014 18:28:52
3

A veces he utilizado una función enganchada a la desactivación de un plugin.

Mira aquí Actualizar enlaces antiguos a permalinks bonitos para Custom Post Types

Como solo los administradores pueden activar plugins, hay una comprobación de capacidades como efecto secundario.

No hay necesidad de borrar el archivo una vez desactivado, WordPress no lo incluirá. Además, si quieres ejecutarlo de nuevo puedes hacerlo. Activando y desactivando nuevamente.

Y a veces he utilizado transitorios como en la respuesta de @fischi. Por ejemplo aquí consulta para crear productos de WooCommerce desde imágenes o aquí Eliminar/reemplazar etiquetas img en el contenido de posts para publicaciones automáticas

Una combinación de ambos puede ser una alternativa.

5 feb 2014 22:14:57
Comentarios

Esta también es una muy buena idea. Si resulta molesto tener que activarlo y desactivarlo constantemente, también podrías enganchar la misma función a la activación del plugin, ¿verdad?

fischi fischi
6 feb 2014 09:20:32

Sí, si lo deseas. Sin embargo, creo que hacer 2 clics no es un gran esfuerzo para ejecutar un script de una sola vez. Cualquier otra solución que implique comandos CLI o manipulación de archivos (renombrar, eliminar) requiere más "trabajo". Además, cada vez que dependes de hooks, estás confiando en variables globales, añadiendo una capa adicional de posibles problemas relacionados con la seguridad/predictibilidad del código. @fischi

gmazzap gmazzap
6 feb 2014 13:00:52

Yo tampoco creo que dos clics sean demasiado, solo quería preguntar :)

fischi fischi
6 feb 2014 14:44:25
0

Otra forma es establecer una opción global de wp_option cuando el trabajo esté hecho y verificar esa opción cada vez que se ejecute el hook init.

function my_one_time_function() {
    // Salir si el trabajo ya se ha realizado.
    if ( get_option( 'my_one_time_function', '0' ) == '1' ) {
        return;
    }

    /***** REALIZA TU TRABAJO UNA SOLA VEZ *****/

    // Añadir o actualizar la opción wp_option
    update_option( 'my_one_time_function', '1' );
}
add_action( 'init', 'my_one_time_function' );

Naturalmente, no necesitas mantener este código para siempre (aunque sea solo una simple lectura de la base de datos), por lo que probablemente puedas eliminarlo cuando el trabajo esté hecho. También puedes cambiar manualmente el valor de esta opción a 0 si necesitas volver a ejecutar el código.

29 dic 2014 13:22:11
0

Usar wp-cli eval-file es excelente. Incluso puedes hacerlo en un sistema remoto (usando un alias ssh con '@') con un script local.

Si tienes tu código en one-time.php en el directorio base de WordPress y tienes acceso por línea de comandos al sistema donde quieres ejecutarlo, puedes hacer:

wp eval-file one-time.php

Si tienes el archivo one-time.php localmente y quieres ejecutarlo contra un WordPress remoto usando @, se vería así:

wp @remote eval-file - < one-time.php
25 jun 2020 21:00:16
0

Mi enfoque es un poco diferente en esto. Me gusta añadir mis scripts de una sola vez como una función en el function.php de mi tema y ejecutarlos con una consulta GET específica.

if ( isset($_GET['linkupdate']) ) {
    add_action('init', 'link_update', 10);
}
function link_update() {
  // Script de una sola ejecución
   die;
}

Para ejecutar esto, simplemente visita la URL "www.sitename.com/?linkupdate"

Hasta ahora, este método me ha funcionado bien...

¿Tiene este método algún inconveniente? Solo por curiosidad...

16 jun 2017 09:10:33
0

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

Por ejemplo, si tengo una página de testimonios que no está en vivo (en modo borrador o similar), pero está conectada a una plantilla de página única, como single-testimonial.php, puedo colocar funciones ahí, cargar la página mediante un preview y la función o lo que sea se ejecuta una vez. También es muy fácil hacer modificaciones a la función en caso de depuración.

Es realmente sencillo y lo prefiero sobre usar init porque tengo más control sobre cuándo y cómo se ejecuta. Simplemente es mi preferencia.

9 jul 2017 01:13:25
0

Por si ayuda, esto es lo que hice y funciona bien:

add_action( 'init', 'upsubscriptions_setup');

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

    // Si no hay versión registrada aún en la base de datos
    if (!$version) {
        add_option('upsubscriptions_setup_version', '0.1');
        $version = get_option('upsubscriptions_setup_version');
    }

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

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

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

    // etc
}
19 nov 2018 17:25:30