Skip to content

Commit 09f4b09

Browse files
author
Guido W. Pettinari
committed
✨ Hook support
before_run_command, before_invoke, after_invoke
1 parent fa4c103 commit 09f4b09

File tree

3 files changed

+137
-11
lines changed

3 files changed

+137
-11
lines changed

README.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ Simple class to create WP-CLI commands with validation with little effort.
22

33
# Features
44

5-
- Create a command by extending a simple class
6-
- Advanced validation
7-
- Support for sub-commands
5+
- Create a WP CLI command by extending a simple class
6+
- Easily hook into the [command lifecycle](https://make.wordpress.org/cli/handbook/references/internal-api/wp-cli-add-hook/#notes) by extending methods `before_run_command`, `before_invoke`, `after_invoke`
7+
- Advanced validation by extending method `validate`
8+
- Support for sub-commands by defining public methods
89
- Support for [PHPDoc validation](https://make.wordpress.org/cli/handbook/guides/commands-cookbook/#annotating-with-phpdoc)
910

1011
# Quick start
@@ -17,13 +18,22 @@ Simple class to create WP-CLI commands with validation with little effort.
1718
3. Register your command with `MyCommand::init( 'my-command' );`
1819
4. Run the command with `wp my-command`.
1920

21+
# Available hooks
22+
23+
In oder of execution:
24+
25+
- `before_run_command` > Just before the command is found and executed
26+
- `before_invoke` > Just before a command is invoked
27+
- `after_invoke` > Just after a command is invoked
28+
2029
# Example
2130

2231
- A simple command from [WP-CLI docs](https://make.wordpress.org/cli/handbook/guides/commands-cookbook/#annotating-with-phpdoc) with an additional layer of validation: [examples/SimpleCommand.php](./examples/SimpleCommand.php)
2332
- An example with two sub-commands, each with its own validation > TODO!
2433

2534
# To do
2635

36+
- Make hook methods aware of the specific command being executed, in case the class contains more than one. Probably, we will need to switch away from static, in order to save $args in the class.
2737
- Behat tests with [`wp scaffold package`](https://github.com/wp-cli/scaffold-package-command)
2838
- Example with subcommands
2939
- Find a way to execute PHPDoc validation before custom validation (maybe invoking the command and the exiting with `before_invoke:{$cmd}`)?

examples/SimpleCommand.php

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,11 @@ public function __invoke( array $args, array $assoc_args )
3737
}
3838

3939
/**
40-
* Restrict the <name> parameter to be longer than 3
41-
* characters
40+
* Restrict the <name> parameter to be longer than 3 characters.
41+
*
42+
* @param string[] $args
43+
* @param array<string,string> $assoc_args
44+
* @param array<string,mixed> $options
4245
*/
4346
public static function validate(array $args, array $assoc_args, array $options): bool
4447
{
@@ -48,4 +51,32 @@ public static function validate(array $args, array $assoc_args, array $options):
4851
}
4952
return true;
5053
}
54+
55+
/**
56+
* Tell the user what's going on, as soon as WP CLI starts
57+
* thinking about executin a command
58+
*
59+
* @param string[] $args
60+
* @param array<string,string> $assoc_args
61+
* @param array<string,mixed> $options
62+
*/
63+
public static function before_run_command( array $args, array $assoc_args, array $options ): void
64+
{
65+
WP_CLI::line( "About to run command `wp " . join( " ", $args ) . "`" );
66+
}
67+
/**
68+
* Tell the user what's going on, before the command is invoked
69+
*/
70+
public static function before_invoke(): void
71+
{
72+
WP_CLI::line( "Invoking command..." );
73+
}
74+
75+
/**
76+
* Tell the user what's going on, after the command is invoked
77+
*/
78+
public static function after_invoke(): void
79+
{
80+
WP_CLI::line( "All done 💪" );
81+
}
5182
}

src/Command.php

Lines changed: 91 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
use WP_CLI;
55

66
/**
7-
* A command to be launched from WP CLI
7+
* Register new WP CLI commands.
8+
*
9+
* Override methods to setup hooks and custom validation.
10+
* List of available hooks in README.
811
*/
912
abstract class Command
1013
{
@@ -14,6 +17,16 @@ abstract class Command
1417
*/
1518
protected static string $usage = 'Wrong arguments';
1619

20+
/**
21+
* Number of times before_invoke is run
22+
*/
23+
protected static int $count_before_invoke = 0;
24+
25+
/**
26+
* Number of times after_invoke is run
27+
*/
28+
protected static int $count_after_invoke = 0;
29+
1730
/**
1831
* Register the command with WP-CLI
1932
*
@@ -34,20 +47,33 @@ public static function init( string $command ): void
3447
protected static function register( string $command ): void
3548
{
3649
// Register the command
37-
WP_CLI::add_command( $command, static::class );
38-
// Validate arguments
50+
WP_CLI::add_command(
51+
$command,
52+
static::class,
53+
[
54+
'before_invoke' => [ static::class, '_before_invoke' ],
55+
'after_invoke' => [ static::class, '_after_invoke' ]
56+
]
57+
);
58+
59+
// Allow to do stuff just before the command is executed
3960
WP_CLI::add_hook(
4061
'before_run_command',
4162
function( array $args, array $assoc_args, array $options ) use ( $command )
4263
{
43-
// Temp vars
64+
// The before_run_command hook in WP-CLI is run for all commands.
65+
// Here we restrict the scope to the commands defined in this class,
66+
// using the fact that $all_args includes the command being invoked
4467
$args_string = join( " ", $args );
4568
$command_tokens = explode( " ", $command );
4669
$actual_args = array_slice( $args, count( $command_tokens ) );
4770
// Ignore commands defined elsewhere
4871
if ( strpos( $args_string, $command ) !== 0 ) {
4972
return;
5073
}
74+
// Allow the user to hook into before_run_command
75+
WP_CLI::debug("About to execute before_run_command hook", "idearia");
76+
static::before_run_command( $args, $assoc_args, $options );
5177
// Do nothing if args are valid
5278
WP_CLI::debug("Starting custom validation", "idearia");
5379
if ( static::validate( $actual_args, $assoc_args, $options ) ) {
@@ -68,6 +94,37 @@ protected static function isCliRunning(): bool
6894
return defined( 'WP_CLI' ) && WP_CLI;
6995
}
7096

97+
/**
98+
* Override to inject code just before any command in
99+
* the class is found (runs before before_invoked)
100+
*
101+
* @param string[] $args
102+
* @param array<string,string> $assoc_args
103+
* @param array<string,mixed> $options
104+
*/
105+
public static function before_run_command( array $args, array $assoc_args, array $options ): void
106+
{
107+
WP_CLI::debug("Skipping before_run_command hook", "idearia");
108+
}
109+
110+
/**
111+
* Override to inject code just before any command in the
112+
* class is invoked
113+
*/
114+
public static function before_invoke(): void
115+
{
116+
WP_CLI::debug("Skipping before_invoke hook", "idearia");
117+
}
118+
119+
/**
120+
* Override to inject code just after any command in the
121+
* class is invoked
122+
*/
123+
public static function after_invoke(): void
124+
{
125+
WP_CLI::debug("Skipping after_invoke hook", "idearia");
126+
}
127+
71128
/**
72129
* Custom validation for the command's arguments; returning
73130
* false will skip command execution.
@@ -78,7 +135,8 @@ protected static function isCliRunning(): bool
78135
* cookbook.
79136
*
80137
* Please note that:
81-
* - The method will run *before* PHPDoc validation.
138+
* - The method will run *before* PHPDoc validation and *after*
139+
* $this->before_run_command()
82140
* - If the class has sub-commands, $args will contain the
83141
* sub-command bing run in the first position.
84142
*
@@ -89,7 +147,34 @@ protected static function isCliRunning(): bool
89147
*/
90148
public static function validate( array $args, array $assoc_args, array $options ): bool
91149
{
92-
WP_CLI::debug("Will skip custom validation", "idearia");
150+
WP_CLI::debug("Skipping custom validation hook", "idearia");
93151
return true;
94152
}
153+
154+
/**
155+
* Wrapper to avoid running before_invoke twice, due to how
156+
* WP-CLI works (it runs both for the parent comand and the
157+
* subcommand)
158+
*/
159+
public static function _before_invoke(): void
160+
{
161+
if ( static::$count_before_invoke == 0 ) {
162+
static::before_invoke();
163+
}
164+
static::$count_before_invoke++;
165+
}
166+
167+
/**
168+
* Wrapper to avoid running after_invoke twice, due to how
169+
* WP-CLI works (it runs both for the parent comand and the
170+
* subcommand)
171+
*/
172+
public static function _after_invoke(): void
173+
{
174+
if ( static::$count_after_invoke == 0 ) {
175+
static::after_invoke();
176+
}
177+
static::$count_after_invoke++;
178+
}
179+
95180
}

0 commit comments

Comments
 (0)