What is your best practice to execute one-time scripts?

The Problem

We all have been in a situation like this, and a lot of questions on this site need a solution like this. You either have to update a database, insert a lot of data automatically, convert meta_keys, or something similar.

Of course, in a running system based on best practises this should not happen.

Read More

But as it does, I would love to hear your personal solution to this problem, and why you chose yours.

The Question

How do you implement one-time scripts in your (running) WordPress install?

The issue here is mainly because of the following reasons:

  • Scripts that insert data should not run more than one time
  • Scripts that require a lot of ressources should not run at a time when they can not be monitored
  • They should not be run by accident

The Reason I ask

I have got my own practise, I am going to post it in the answers. As I do not know if it is the best solution out there, I’d like to know about yours. Also, this is a question that is asked a lot of times in context of other questions, and it would be great to have a ressource collecting the ideas.

looking forward to learning from you 🙂

Related posts

12 comments

  1. I for myself use a combination of:

    • one file dedicated to the one-time script
    • using a transient to stop the script from accidentally running more than once
    • using capability-management or user-control to ensure the script is just run by me.

    Structure

    I use a file (onetime.php) in my include-folder inc, which is included in the functions.php, and deleted from there after the use.

    include( 'inc/onetime.php' );
    

    The file for the script itself

    In my onetime.php my function f711_my_onetime_function() is placed. As it could be any function. I assume your script is tested and works correctly.

    To achieve the control over the execution of the script, I use both

    Capability control

    To stop other users from accidentally executioning my script:

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

    or

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

    a transient

    to stop myself from accidentally executing the script more than once.

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

    The file for executing the script in my function f711_my_onetime_function() would look like that:

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

    The reason I set the transient immediately after the check if it exists is that I want the function to be executed after the script has been locked from beeing used twice.

    If I need any output from my function, I either print it out as a comment in the footer, or sometimes I even filter the content.

    The lockouttime is set to 10 Minutes, but can be adjusted to your needs.

    Cleanup

    After the successful execution of my script I delete the include from the functions.php, and remove the onetime.php from the server. As I used a timeout for the transient, I do not need to clean the database, but of course you could also delete the transient after you removed the file.

  2. You can also do this:

    run onetime.php and rename it after execution.

    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');
    }
    
  3. I created a command line Phing script for this, it’s nothing special other than loading an external script to run. The reason I used it via the CLI is because:

    • I don’t want it to load by mistake (need to type a command)
    • It’s secure since it can be run outside the web root, in other words it can effect WP but WP cannot reach the script in any way.
    • It does not add any code to WP or the DB itself.

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

    So you can use Phing, or the PHP CLI and sleep at night. The WP-CLI is also a good alternative though I forget if you can use it outside the web root.

    Since this is a popular post here is an example of the script: https://github.com/wycks/WordPhing (run.php)

  4. Under ideal conditions I’d ssh into the server and execute the function myself using wp-cli.

    This often isn’t possible, though, so I tend to set a $_GET variable and hook it onto ‘init’, for instance:

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

    then hit

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

    and disable the hook when it’s done.

  5. Another pretty simple way of running a one-time script is to do this by means of an MU plugin.

    Put the code in some PHP file (e.g., one-time.php) that you upload to your MU plugins’ folder (by default /wp-content/mu-plugins), adjust the file permissions, run the plugin (i.e., according to your chosen hook, you basically just have to visit the frontend/backend), and you’re all done.

    Here is a 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);
    

    Without the comments and stuff, it just looks like this:

    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);
    
  6. Definitely you can, Just create your one time code as a plugin.

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

    Problem how do I activate this plugin without clicking Activate link?

    just add activate_plugins('onetime/index.php'); in functions.php

    or
    Use must use plugins, http://codex.wordpress.org/Must_Use_Plugins

    Try with different actions as when you want to execute onetime plugin,

    1. admin_init – after admin init

    2. init – wordpress init

    3. wp – when wordpress loaded

  7. Sometimes I used a function hooked on plugin deactivation.

    See here Update Old Links To Pretty Permalinks Custom Post Type

    Once only admins can activate plugins, there a capability check as a side effect.

    There is no need to delete file once deactivated it will not included by wordress.
    In addiction if you want to run again it you can. Activating and deactivating again.

    And sometimes I used transient used like in @fischi answer. E.g. here query to create woocommerce products from images or here Delete/replace img tags in post content for auto published posts

    A combination of both can be an alternative.

  8. Another way is to set a global wp_option when the work is done and check for that option every time the init hook is executed.

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

    Naturally you don’t need to have this code forever (even if it is a simple read from the database), so you can probably remove the code when the work is done.
    Also you can manually change this option value to 0 if you need to re-execute the code.

  9. My approach is a little different on this. I like to add my one-time script as a function in my theme’s function.php and run it on a specific GET query.

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

    To run this, just visit the URL “www.sitename.com/?linkupdate”

    This is working fine for me till now…

    Does this method have any drawbacks? Just wondering…

  10. I just use a single custom product template page I’m not using and isn’t connected to anything on the public server.

    Like if I have a testimonial page that isn’t live (in draft mode, or whatever), but connected to a single page template, for example single-testimonial.php – I can place functions in there, load the page via a preview and the function or whatever is launched once. It’s also very easy to make modifications to the function in case of debugging.

    Is really easy and I prefer it over using init because I have more control over when and how its launched. Just my preference.

  11. Just in case it helps, this is what I did and it works well:

    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
    }
    
  12. Using wp-cli eval-file is great. You can even do it on a remote system (using an ‘@‘ ssh alias) with a local script.

    If you have your code in one-time.php in the wordpress base directory and you have a command line on the system you want to run against you can do:

    wp eval-file one-time.php
    

    If you have the file one-time.php locally and you want to run it against a remote wordpress using @ it looks like this:

    wp @remote eval-file - < one-time.php
    

Comments are closed.