Documentation Writing Bin Scripts

Writing Bin Scripts

Overview #

Occasionally, you may find you need to access or transform data on your WordPress.com site. If it’s more than a dozen posts affected, it’s often more efficient to write what we call a “bin script.” In writing a bin script, you can easily change strings, assign categories, or add post meta across hundreds or thousands of posts. However, with great power comes great responsibility — any small mistake you make with your logic could have negative repercussions across your entire dataset.

Here are some guidelines we’d encourage you to follow when writing a bin script.

↑ Top ↑

Use WP-CLI #

To keep your scripts as lean and mean as possible, we highly encourage you to leverage WP-CLI, an awesome community framework.

When using WP-CLI, all you’ll need to write for your bin script is what’s called a “command.” Your command accepts zero or more arguments and performs a bit of logic. You won’t need to switch to the proper blog, handle arguments, etc., as all of that is done automatically.

Check out the great documentation on how to write a command. When you write commands for VIP, there are a few things to keep in mind:

  • You should extend the WPCOM_VIP_CLI_Command class provided in the development helpers, which includes helper functions like stop_the_insanity(). Do this instead of extending WP_CLI_Command.
  • Make sure you require the file that contains your new command in your functions.php file
  • Make sure you only include the command if WP_CLI is defined and true

Here’s an example of what might be in your functions file:

// CLI scripts
if ( defined( 'WP_CLI' ) && WP_CLI ) {
	if ( false === WPCOM_IS_VIP_ENV ) {
		require_once( WP_CONTENT_DIR . '/themes/vip/plugins/vip-do-not-include-on-wpcom/vip-wp-cli.php' );
		// Development CLI scripts
		require_once MY_THEME_DIR . '/inc/class-testcommand-cli.php';
	}

	// Production CLI scripts
	require_once MY_THEME_DIR . '/inc/class-mycommand1-cli.php';
	require_once MY_THEME_DIR . '/inc/class-mycommand2-cli.php';
}

Once you’ve written your command and tested it throughly in your local environment, you can commit it to your theme.

When you’ve done so, open a ticket with us with explanation of what you’re trying to accomplish. We’ll review, test, and run it.

↑ Top ↑

Best Practices #

Again, it can be easy to make a minor mistake with your script that causes a lot of pain. We encourage you to do the following:

  • Comment well and provide clear usage instructions. It’s important to be very clear about what each part is doing and why — commenting each step of your logic is a good sanity check. Comments are especially helpful when something maybe doesn’t work as intended and we need to debug to figure out why.
  • Allow for varying levels of verbosity. Similarly, provide a summary at the end with all results of the script.
  • It’s a good idea to default your script to do a test run without affecting live data. Add an argument to allow a “live” run. This way, we can compare what the actual impact is versus the expected impact.
  • If you’re modifying lots of data on a live site, make sure to include sleep() in key places. This will help with load associated with cache invalidation and replication.
  • If you’re writing an importer, make sure to define( 'WP_IMPORTING', true ); at the top of your subcommand. This will ensure only the minimum of extra actions are fired.
  • Use core functions as much as possible, except when they might have undesired consequences. For example, WP_Query can be pretty expensive, so a direct query may make more sense. Or, wp_update_post() fires a number of hooks you may not want fired when replacing strings in post_content. In some contexts, a direct SQL query would be a better choice, followed by clean_post_cache().
  • When in doubt, ask us!

↑ Top ↑

FAQ #

How do I modify all the posts?

Without a no-LIMIT query, it can be confusing how you would modify all your posts. The problem is that a no-LIMIT query just won’t work in most situations. If the query takes longer than 30 seconds, it will timeout and fail. The solution is use smaller queries and page through the results.

For example:

$posts_per_page = 100;
$page = 1;

do {
	$posts = get_posts( array(
		'posts_per_page' => $posts_per_page,
		'paged' => $page,
	));

	// Do stuff

	$page++;
} while ( count( $posts ) );