Qual é a sua melhor prática para executar scripts únicos?

32

O problema

Todos nós já passamos por uma situação como essa e muitas perguntas neste site precisam de uma solução como essa. Você precisa atualizar um banco de dados, inserir muitos dados automaticamente, converter meta_keysou algo semelhante.

Obviamente, em um sistema em execução baseado nas melhores práticas, isso não deve acontecer.

Mas, como isso acontece, eu adoraria ouvir sua solução pessoal para esse problema e por que você escolheu a sua.

A questão

Como você implementa scripts únicos na sua instalação do WordPress (em execução)?

O problema aqui é principalmente devido aos seguintes motivos:

  • Os scripts que inserem dados não devem ser executados mais de uma vez
  • Os scripts que requerem muitos recursos não devem ser executados no momento em que não podem ser monitorados
  • Eles não devem ser executados por acidente

A razão pela qual pergunto

Eu tenho minha própria prática, vou postar nas respostas. Como não sei se é a melhor solução existente, gostaria de saber sobre a sua. Além disso, essa é uma pergunta que é feita muitas vezes no contexto de outras perguntas, e seria ótimo ter um recurso para coletar as idéias.

ansioso para aprender com você :)

fischi
fonte
2
Se for realmente um contrato único, escrevo o script, o executo e o excluo. Ninguém pode executá-lo novamente depois disso. Como tudo, o código é passageiro. ;)
Otto
1
O problema é que estou preocupado que um script possa ser chamado uma segunda vez, por coincidência. mas eu fiz sua abordagem inúmeras vezes;)
fischi
Execute-o na página de administração de um plugin, sempre funcionou para mim. Você pode adicionar verificações de autenticação na parte superior da página para garantir que seja você, se necessário.
Andrew Bartel
mas você não está falando sobre execução agendada uma vez, apenas manual ?
birgire
1
Sim, estou falando apenas de operações manuais pontuais, como scripts de migração etc., não de wp-croneventos agendados.
fischi

Respostas:

17

Eu, pessoalmente, uso uma combinação de:

  • um arquivo dedicado ao script único
  • usando um transitório para impedir que o script seja executado acidentalmente mais de uma vez
  • usando gerenciamento de capacidade ou controle de usuário para garantir que o script seja executado apenas por mim.

Estrutura

Eu uso um arquivo ( onetime.php) na minha pasta de inclusão inc, que é incluída no functions.phpe excluída de lá após o uso.

include( 'inc/onetime.php' );

O arquivo para o próprio script

Na minha onetime.phpfunção f711_my_onetime_function()é colocada. Como poderia ser qualquer função. Presumo que seu script foi testado e funciona corretamente.

Para obter o controle sobre a execução do script, eu uso os dois

Controle de capacidade

Para impedir que outros usuários executem acidentalmente meu script:

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

ou

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

um transitório

para me impedir de executar o script acidentalmente mais de uma vez.

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

O arquivo para executar o script na minha função f711_my_onetime_function()ficaria assim:

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

A razão pela qual defini o transitório imediatamente após a verificação, se ela existe, é que desejo que a função seja executada após o bloqueio do script de ser usado duas vezes.

Se precisar de alguma saída da minha função, imprimo-a como um comentário no rodapé ou às vezes até filtre o conteúdo.

O tempo de bloqueio é definido como 10 minutos, mas pode ser ajustado às suas necessidades.

Limpar

Após a execução bem-sucedida do meu script eu apagar o includedo functions.phpe remova a onetime.phppartir do servidor. Como usei um tempo limite para o transitório, não preciso limpar o banco de dados, mas é claro que você também pode excluir o transitório depois de remover o arquivo.

fischi
fonte
Pensei em adicionar minha resposta a isso, mas depois de ler sua listagem na parte superior desta resposta ... não vou mais, pois minha abordagem parece quase exatamente a mesma. Então, +1 para isso - também para os pensamentos detalhados sobre isso.
tfrommen
14

Você também pode fazer isso:

execute onetime.phpe renomeie-o após a execução.

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
fonte
É isso que fazemos; é praticamente garantido para ser à prova de idiotas.
Qix
7

Eu criei um script Phing da linha de comando para isso, não é nada especial além de carregar um script externo para executar. O motivo pelo qual eu o usei via CLI é porque:

  • Não quero que seja carregado por engano (é necessário digitar um comando)
  • É seguro, pois pode ser executado fora da raiz da web; em outras palavras, pode afetar o WP, mas o WP não pode acessar o script de nenhuma maneira.
  • Ele não adiciona nenhum código ao WP ou ao próprio banco de dados.

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

Assim, você pode usar o Phing ou a CLI do PHP e dormir à noite. O WP-CLI também é uma boa alternativa, embora eu esqueça que você pode usá-lo fora da raiz da web.

Como esta é uma publicação popular, aqui está um exemplo do script: https://github.com/wycks/WordPhing (run.php)

Wyck
fonte
Parece bom e simples, além de seguro. Você também abordou uma das minhas principais preocupações (eu executando duas vezes por acidente) em grande parte usando a linha de comando. Boa ideia!
fischi
5

Outra maneira bastante simples de executar um script único é fazer isso por meio de um plug-in MU.

Coloque o código em algum arquivo PHP (por exemplo, one-time.php) que você carrega na pasta dos plugins do MU (por padrão /wp-content/mu-plugins), ajuste as permissões do arquivo, execute o plug-in (ou seja, de acordo com o gancho escolhido, você basicamente só precisa visitar o frontend / back-end) e pronto.

Aqui está um boilerplate:

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

Sem os comentários e outras coisas, fica assim:

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

Sob condições ideais, eu ssh no servidor e executaria a função usando wp-cli.

No entanto, isso geralmente não é possível, então eu tendem a definir uma variável $ _GET e conectá-la ao 'init', por exemplo:

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

então aperte

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

e desative o gancho quando terminar.

djb
fonte
4

Às vezes, usei uma função ligada à desativação de plugins.

Veja aqui Atualizar links antigos para Pretty Permalinks Tipo de postagem personalizada

Uma vez que apenas os administradores podem ativar plug-ins, há uma verificação de capacidade como efeito colateral.

Não há necessidade de excluir o arquivo, uma vez desativado, ele não será incluído pelo wordress. Além disso, se você quiser correr de novo, pode. Ativando e desativando novamente.

E às vezes eu usei transitória como na resposta @fischi. Por exemplo, consulta aqui para criar produtos de woocommerce a partir de imagens ou aqui Excluir / substituir tags img no conteúdo da postagem para postagens publicadas automaticamente

Uma combinação de ambos pode ser uma alternativa.

gmazzap
fonte
Essa também é uma ótima idéia. Se ficar chato sempre ter que ativar para desativá-lo novamente, você também pode conectar a mesma função à ativação do plugin, certo?
fischi
Sim, se você quiser. No entanto, acho que 2 cliques não são um grande esforço para executar um script único. Qualquer outra solução que envolva comando da CLI ou manipulação de arquivos (renomeação, exclusão) precisa de mais "trabalho". Além disso, toda vez que você depende de ganchos, depende de variáveis ​​globais, adicionando uma camada adicional de possíveis problemas relacionados à segurança / previsibilidade do código. @fischi
gmazzap
Eu não acho que os dois cliques é muito também, só queria perguntar :)
fischi
3

Definitivamente você pode, basta criar seu código único como um plugin.

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

Problema: como ativo este plug-in sem clicar no link Ativar?

basta adicionar activate_plugins('onetime/index.php');emfunctions.php

ou Use deve usar plugins, http://codex.wordpress.org/Must_Use_Plugins

Tente com ações diferentes, como quando você deseja executar o plug-in onetime,

  1. admin_init - depois do admin init

  2. init - inic. wordpress

  3. wp - quando o wordpress é carregado

Amit Sukapure
fonte
2

Outra maneira é definir uma opção wp_option global quando o trabalho estiver concluído e verificar essa opção sempre que o gancho init for executado.

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, você não precisa ter esse código para sempre (mesmo que seja uma simples leitura do banco de dados); portanto, você provavelmente poderá remover o código quando o trabalho estiver concluído. Além disso, você pode alterar manualmente esse valor da opção para 0 se precisar executar novamente o código.

Mauro Mascia
fonte
1

Minha abordagem é um pouco diferente nisso. Eu gosto de adicionar meu script único como uma função no function.php do meu tema e executá-lo em uma consulta GET específica.

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

Para executar isso, basta visitar o URL "www.sitename.com/?linkupdate"

Isso está funcionando bem para mim até agora ...

Este método tem alguma desvantagem? Apenas me perguntando...

Sid
fonte
1

Acabei de usar uma única página de modelo de produto personalizado que não estou usando e não está conectada a nada no servidor público.

Por exemplo, se eu tiver uma página de testemunho que não esteja ativa (no modo rascunho, ou o que for), mas conectada a um único modelo de página, por exemplo single-testimonial.php- eu posso colocar funções lá, carregar a página por meio de uma previewfunção e o que for lançado uma vez. Também é muito fácil fazer modificações na função em caso de depuração.

É realmente fácil e eu prefiro usá-lo initporque tenho mais controle sobre quando e como ele será lançado. Apenas minha preferência.

bbruman
fonte
0

Caso isso ajude, foi o que eu fiz e funciona bem:

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
fonte