A library to assist with running performant bulk tasks against WordPress posts.

v0.2.1 2024-01-19 00:08 UTC

This package is auto-updated.

Last update: 2024-02-13 15:32:22 UTC


Readme Standard Spec Badge

A library to assist with running performant bulk tasks against WordPress posts.


This package provides a library to make it easier to run bulk tasks against a WordPress database in a performant way. It includes functionality to search through a WordPress database for posts using WP_Query-style arguments and keeps a cursor of its location within the database in case it is interrupted and needs to start again.


This package is released via Packagist for installation via Composer. It follows semantic versioning conventions.


Requires Composer and PHP >= 8.0.


Install this package via Composer:

composer require alleyinteractive/wp-bulk-task

Ensure that the Composer autoloader is loaded into your project:

require_once __DIR__ . '/vendor/autoload.php';

Then use the class in your custom CLI command:

class My_Custom_CLI_Command extends WP_CLI_Command {
	use Bulk_Task_Side_Effects;

	 * Replace all instances of 'apple' with 'banana' in post content.
	 * ## OPTIONS
	 * [--dry-run]
	 * : If present, no updates will be made.
	 * [--rewind]
	 * : Resets the cursor so the next time the command is run it will start from the beginning.
	 *     # Bananaify links.
	 *     $ wp my-custom-cli-command bananaify
	public function bananaify( $args, $assoc_args ) {
		$bulk_task = new \Alley\WP_Bulk_Task\Bulk_Task(
			new \Alley\WP_Bulk_Task\Progress\PHP_CLI_Progress_Bar(
				__( 'Bulk Task: remove_broken_links', 'my-textdomain' )

		// Handle rewind requests.
		if ( ! empty( $assoc_args['rewind'] ) ) {
			WP_CLI::log( __( 'Rewound the cursor. Run again without the --rewind flag to process posts.', 'my-textdomain' ) );


		// Set up and run the bulk task.
		$dry_run = ! empty( $assoc_args['dry-run'] );
				'post_status' => 'publish',
				'post_type'   => 'post',
				'tax_query'   => [
						'field'    => 'slug',
						'taxonomy' => 'category',
						'terms'    => 'fruit',
			function( $post ) use ( $dry_run ) {
				if ( false !== strpos( $post->post_content, 'apple' ) ) {
					$new_value = str_replace( 'apple', 'banana', $post->post_content );
					if ( $dry_run ) {
						WP_CLI::log( 'Old post_content: ' . $post->post_content );
						WP_CLI::log( 'New post_content: ' . $new_value );
					} else {
						$post->post_content = $new_value;
						wp_update_post( $post );


For more information on usage, visit the wiki.

From Source

To work on this project locally, first add the repository to your project's composer.json:

	"repositories": [
			"type": "path",
			"url": "../path/to/wp-bulk-task",
			"options": {
				"symlink": true

Next, add the local development files to the require section of composer.json:

	"require": {
		"alleyinteractive/wp-bulk-task": "@dev"

Finally, update composer to use the local copy of the package:

composer update alleyinteractive/wp-bulk-task --prefer-source


This project keeps a changelog.

Development Process

See instructions above on installing from source. Pull requests are welcome from the community and will be considered for inclusion. Releases follow semantic versioning and are shipped on an as-needed basis.


See our contributor guidelines for instructions on how to contribute to this open source project.

Project Structure

This is a Composer package that is published to Packagist.

Classes must be autoloadable using alleyinteractive/composer-wordpress-autoloader and live in the src directory, following standard WordPress naming conventions for classes.

Related Efforts


Alley logo


Thanks to all of the contributors to this project.


This project is licensed under the GNU Public License (GPL) version 2 or later.