Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
690d135
feat: introduces a run method
dpanta94 Dec 16, 2025
8665d98
docs: update documentation to reflect the run method
dpanta94 Dec 16, 2025
6e84b2e
tests: add test coverage
dpanta94 Dec 16, 2025
135fb25
Merge 6e84b2e065def65548ff7077a44285037c9d19d3 into 0af4cfc2a1242e795…
dpanta94 Dec 16, 2025
a08f383
chore: autopublish 2025-12-16T17:16:52Z
github-actions[bot] Dec 16, 2025
5fbee64
feat: clean up memory every 10 tasks
dpanta94 Dec 16, 2025
fcb8428
Merge 5fbee64759e4f46b6a24a3170f6e8a76907b6ec6 into 0af4cfc2a1242e795…
dpanta94 Dec 16, 2025
b243f11
chore: autopublish 2025-12-16T17:25:18Z
github-actions[bot] Dec 16, 2025
5cfb45f
tweak: provide control of number of tasks before clean up
dpanta94 Dec 16, 2025
a52af14
chrore: fix phpcs issues
dpanta94 Dec 16, 2025
51aa3c0
chore: fix static analysis
dpanta94 Dec 16, 2025
a962da6
chore: update AS workflow
dpanta94 Dec 16, 2025
8067f44
chore: update as workflow once again
dpanta94 Dec 16, 2025
cc6d0f3
tweak: remove on_error method
dpanta94 Dec 16, 2025
17ad10b
Merge cc6d0f394f0121c61147907b12915c1cc11c7215 into 0af4cfc2a1242e795…
dpanta94 Dec 16, 2025
48965e6
chore: autopublish 2025-12-16T17:58:23Z
github-actions[bot] Dec 16, 2025
4401ca2
chore: trigger php tests
dpanta94 Dec 16, 2025
c06a4b2
Merge remote-tracking branch 'origin/feat/introduce-a-run-method' int…
dpanta94 Dec 16, 2025
9024591
tweak: amend CR comments
dpanta94 Dec 17, 2025
033cc8b
tweak: fix static analysis
dpanta94 Dec 17, 2025
c2adecb
tweak: ensure possible exceptions inside callables are caught
dpanta94 Dec 17, 2025
0262946
chore: add test coverage and update docs
dpanta94 Dec 17, 2025
3243774
tests: fix broken test
dpanta94 Dec 17, 2025
bad6d9b
tweak: remove exception and catch only throwables
dpanta94 Dec 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/update_as.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ jobs:
run: |
${SLIC_BIN} up wordpress
${SLIC_BIN} wp core version
${SLIC_BIN} wp core update --force --version=6.7
${SLIC_BIN} wp core update --force --version=latest
${SLIC_BIN} wp core version
${SLIC_BIN} use shepherd

Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

All notable changes to this project will be documented in this file. This project adhere to the [Semantic Versioning](http://semver.org/) standard.

## [0.1.0] 2025-12-17

* Feature - Introduces a method `run` to the Regulator class which enables running a set of tasks synchronously.

[0.0.9]: https://github.com/stellarwp/shepherd/releases/tag/0.0.9

## [0.0.9] 2025-11-17

* Tweak - Add a filter `shepherd_{prefix}_dispatch_handler` to allow for custom dispatch handlers.
Expand Down
11 changes: 11 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Shepherd is a lightweight background processing library for WordPress built on t
## Key Features

- **Background Task Processing**: Offload time-consuming operations to background processes
- **Synchronous Task Execution**: Run tasks immediately with lifecycle callbacks via `run()` method (since 0.1.0)
- **Automatic Retries**: Configurable retry mechanism with exponential backoff
- **Task Debouncing**: Prevents tasks from running too frequently with customizable delays
- **Unique Task Enforcement**: Prevents duplicate tasks from being scheduled
Expand Down Expand Up @@ -105,6 +106,16 @@ shepherd()->dispatch(new My_Task($arg1, $arg2));
// Dispatch with delay (in seconds)
shepherd()->dispatch(new My_Task($arg1, $arg2), 300); // 5 minutes

// Run tasks synchronously with lifecycle callbacks (since 0.1.0)
shepherd()->run(
[ new My_Task($arg1, $arg2), new Another_Task() ],
[
'before' => function( $task ) { /* called before each task */ },
'after' => function( $task ) { /* called after each task */ },
'always' => function( $tasks ) { /* called after all tasks complete successfully */ },
]
);

// Retrieve task logs
use StellarWP\Shepherd\Contracts\Logger;
use StellarWP\Shepherd\Provider;
Expand Down
148 changes: 148 additions & 0 deletions docs/advanced-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,154 @@ The task tables include indexes on:
- **Indexed Queries**: All cleanup queries use indexed columns for optimal performance
- **Minimal Overhead**: Action deletion hooks add minimal overhead to Action Scheduler operations

## Synchronous Task Execution (Since 0.1.0)

The `run()` method allows you to execute tasks synchronously with full control over the execution lifecycle. This is useful for CLI commands, REST API endpoints, or any scenario where you need tasks to execute immediately.

### Basic Usage

```php
use function StellarWP\Shepherd\shepherd;

// Run a single task immediately
shepherd()->run( [ new My_Task() ] );

// Run multiple tasks in sequence
$tasks = [
new Process_Image_Task( $image_id ),
new Generate_Thumbnail_Task( $image_id ),
new Update_Metadata_Task( $image_id ),
];

shepherd()->run( $tasks );
```

### Lifecycle Callbacks

The `run()` method accepts an optional array of callbacks for fine-grained control:

```php
shepherd()->run( $tasks, [
// Called before each task runs
'before' => function( Task $task ): void {
error_log( 'Starting task: ' . get_class( $task ) );
},

// Called after each task completes successfully
'after' => function( Task $task ): void {
error_log( 'Completed task: ' . get_class( $task ) );
},

// Called after all tasks complete (even on error)
'always' => function( array $tasks ): void {
error_log( 'Finished processing ' . count( $tasks ) . ' tasks' );
},
] );
```

### CLI Command Example

```php
use WP_CLI;
use function StellarWP\Shepherd\shepherd;

WP_CLI::add_command( 'myapp process-images', function( $args, $assoc_args ) {
$image_ids = get_unprocessed_image_ids();
$tasks = array_map(
fn( $id ) => new Process_Image_Task( $id ),
$image_ids
);

$processed = 0;
$failed = 0;

shepherd()->run( $tasks, [
'before' => function( Task $task ) {
WP_CLI::log( 'Processing image...' );
},
'after' => function( Task $task ) use ( &$processed ) {
$processed++;
WP_CLI::success( 'Image processed!' );
},
'always' => function( array $tasks ) use ( &$processed, &$failed ) {
WP_CLI::line( "Processed: {$processed}, Failed: {$failed}" );
},
] );
} );
```

### REST API Example

```php
use function StellarWP\Shepherd\shepherd;

register_rest_route( 'myapp/v1', '/process', [
'methods' => 'POST',
'callback' => function( WP_REST_Request $request ) {
$items = $request->get_param( 'items' );
$tasks = array_map(
fn( $item ) => new Process_Item_Task( $item ),
$items
);

$results = [
'processed' => [],
'failed' => [],
];

shepherd()->run( $tasks, [
'after' => function( Task $task ) use ( &$results ) {
$results['processed'][] = $task->get_args()[0];
},
] );

return new WP_REST_Response( $results, 200 );
},
'permission_callback' => fn() => current_user_can( 'manage_options' ),
] );
```

### Behavior Notes

- **Already scheduled tasks**: If a task was previously dispatched via `dispatch()`, `run()` will execute it without re-dispatching
- **Fallback mode**: When Shepherd's database tables are not registered, tasks execute immediately via `process()` without Action Scheduler
- **Context detection**: Shepherd automatically detects CLI and REST contexts for proper logging
- **Exception handling**: Exceptions or Throwables thrown inside callables (`before`, `after`, `always`) are caught and trigger the `tasks_run_failed` action

### WordPress Hooks

Monitor synchronous task execution using WordPress actions:

```php
$prefix = Config::get_hook_prefix();

// Fired before each task runs
add_action( "shepherd_{$prefix}_task_before_run", function( Task $task ) {
// Prepare for task execution
}, 10, 1 );

// Fired after each task completes
add_action( "shepherd_{$prefix}_task_after_run", function( Task $task ) {
// Post-task cleanup or notifications
}, 10, 1 );

// Fired when any task or callable fails (catches Exception and Throwable)
add_action( "shepherd_{$prefix}_tasks_run_failed", function( array $tasks, Throwable $e ) {
// Handle batch failure - receives all tasks and the exception/error
error_log( 'Tasks failed: ' . $e->getMessage() );
}, 10, 2 );

// Fired after all tasks complete successfully
add_action( "shepherd_{$prefix}_tasks_finished", function( array $tasks ) {
// Batch completion handling
}, 10, 1 );

// Fired when tables aren't registered (fallback mode)
add_action( "shepherd_{$prefix}_task_run_sync", function( Task $task ) {
// Track fallback executions
}, 10, 1 );
```

## Custom Dispatch Handlers

**Since 0.0.9**, you can completely override Shepherd's default dispatch behavior by providing a custom handler via a filter. This is useful for advanced scenarios where you need full control over how tasks are dispatched.
Expand Down
41 changes: 41 additions & 0 deletions docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,32 @@ Schedules a task for execution.
- `shepherd_{prefix}_task_scheduling_failed`
- `shepherd_{prefix}_task_already_exists`

##### `run( array $tasks, array $callables = [] ): void`

Runs a set of tasks synchronously with optional lifecycle callbacks.

- **Parameters:**
- `$tasks` - Array of Task instances to run
- `$callables` - Optional array of lifecycle callbacks:
- `'before'` - `function( Task $task ): void` - Called before each task runs
- `'after'` - `function( Task $task ): void` - Called after each task completes
- `'always'` - `function( array $tasks ): void` - Called after all tasks complete (even on error)
- **Since:** 0.1.0
- **Behavior:**
- When tables are registered: Dispatches tasks if not already scheduled, then processes them immediately using Action Scheduler's queue runner
- When tables are NOT registered: Processes tasks immediately in a synchronous manner (fallback)
- Tasks already scheduled (via `dispatch()`) will be executed without re-dispatching
- **Actions Fired:**
- `shepherd_{prefix}_task_run_sync` - When tables are not registered and task runs synchronously
- `shepherd_{prefix}_task_before_run` - Before each task is processed
- `shepherd_{prefix}_task_after_run` - After each task completes successfully
- `shepherd_{prefix}_tasks_run_failed` - When a task fails during the run
- `shepherd_{prefix}_tasks_finished` - After all tasks have been processed
- **Use Cases:**
- CLI commands that need immediate task execution
- REST API endpoints that need synchronous task processing
- Testing scenarios requiring controlled task execution

##### `get_last_scheduled_task_id(): ?int`

Returns the ID of the most recently scheduled task.
Expand Down Expand Up @@ -526,6 +552,21 @@ Table name: `shepherd_{prefix}_task_logs`
- `shepherd_{prefix}_http_request_processed` - Fired after successful HTTP request
- Parameters: `$task` (HTTP_Request instance), `$response` (wp_remote_request response array)

- `shepherd_{prefix}_task_run_sync` - Fired when a task is run synchronously via `run()` when tables are not registered (since 0.1.0)
- Parameters: `$task` (Task instance)

- `shepherd_{prefix}_task_before_run` - Fired before a task is processed via `run()` (since 0.1.0)
- Parameters: `$task` (Task instance)

- `shepherd_{prefix}_task_after_run` - Fired after a task completes via `run()` (since 0.1.0)
- Parameters: `$task` (Task instance)

- `shepherd_{prefix}_tasks_run_failed` - Fired when a task or callable fails during `run()` (since 0.1.0)
- Parameters: `$tasks` (array of Task instances), `$exception` (Exception or Throwable)

- `shepherd_{prefix}_tasks_finished` - Fired after all tasks have been processed via `run()` (since 0.1.0)
- Parameters: `$tasks` (array of Task instances)

### Filters

- `shepherd_{prefix}_should_log` - Filter to control whether logging should occur (since 0.0.5)
Expand Down
3 changes: 3 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ shepherd()->dispatch( $my_task );

// Or dispatch with a delay (in seconds)
shepherd()->dispatch( $my_task, 5 * MINUTE_IN_SECONDS ); // Execute after 5 minutes

// Run tasks immediately (synchronous execution - since 0.1.0)
shepherd()->run( [ $my_task ] );
```

### What Happens Next?
Expand Down
2 changes: 1 addition & 1 deletion shepherd.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* @wordpress-plugin
* Plugin Name: Shepherd
* Description: A library for offloading tasks to background processes.
* Version: 0.0.9
* Version: 0.1.0
* Author: StellarWP
* Author URI: https://stellarwp.com
* License: GPL-2.0-or-later
Expand Down
Loading
Loading