Skip to content

Commit 9e2c2ff

Browse files
committed
Sync changes from Abilities API targeted for v0.2.0
1 parent bf04acb commit 9e2c2ff

File tree

10 files changed

+783
-146
lines changed

10 files changed

+783
-146
lines changed

src/wp-includes/abilities-api.php

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* Defines functions for managing abilities in WordPress.
66
*
77
* @package WordPress
8-
* @subpackage Abilities API
8+
* @subpackage Abilities_API
99
* @since 0.1.0
1010
*/
1111

@@ -16,31 +16,31 @@
1616
*
1717
* Note: Do not use before the {@see 'abilities_api_init'} hook.
1818
*
19-
* @see WP_Abilities_Registry::register()
20-
*
2119
* @since 0.1.0
2220
*
23-
* @param string $name The name of the ability. The name must be a string containing a namespace
24-
* prefix, i.e. `my-plugin/my-ability`. It can only contain lowercase
25-
* alphanumeric characters, dashes and the forward slash.
26-
* @param array<string,mixed> $properties An associative array of properties for the ability. This should include
27-
* `label`, `description`, `input_schema`, `output_schema`, `execute_callback`,
28-
* `permission_callback`, `meta`, and `ability_class`.
21+
* @see WP_Abilities_Registry::register()
22+
*
23+
* @param string $name The name of the ability. The name must be a string containing a namespace
24+
* prefix, i.e. `my-plugin/my-ability`. It can only contain lowercase
25+
* alphanumeric characters, dashes and the forward slash.
26+
* @param array<string,mixed> $args An associative array of arguments for the ability. This should include
27+
* `label`, `description`, `input_schema`, `output_schema`, `execute_callback`,
28+
* `permission_callback`, `meta`, and `ability_class`.
2929
* @return ?\WP_Ability An instance of registered ability on success, null on failure.
3030
*
3131
* @phpstan-param array{
3232
* label?: string,
3333
* description?: string,
34+
* execute_callback?: callable( mixed $input= ): (mixed|\WP_Error),
35+
* permission_callback?: callable( mixed $input= ): (bool|\WP_Error),
3436
* input_schema?: array<string,mixed>,
3537
* output_schema?: array<string,mixed>,
36-
* execute_callback?: callable( array<string,mixed> $input): (mixed|\WP_Error),
37-
* permission_callback?: callable( array<string,mixed> $input ): (bool|\WP_Error),
3838
* meta?: array<string,mixed>,
3939
* ability_class?: class-string<\WP_Ability>,
4040
* ...<string, mixed>
41-
* } $properties
41+
* } $args
4242
*/
43-
function wp_register_ability( string $name, array $properties = array() ): ?WP_Ability {
43+
function wp_register_ability( string $name, array $args ): ?WP_Ability {
4444
if ( ! did_action( 'abilities_api_init' ) ) {
4545
_doing_it_wrong(
4646
__FUNCTION__,
@@ -55,16 +55,16 @@ function wp_register_ability( string $name, array $properties = array() ): ?WP_A
5555
return null;
5656
}
5757

58-
return WP_Abilities_Registry::get_instance()->register( $name, $properties );
58+
return WP_Abilities_Registry::get_instance()->register( $name, $args );
5959
}
6060

6161
/**
6262
* Unregisters an ability using Abilities API.
6363
*
64-
* @see WP_Abilities_Registry::unregister()
65-
*
6664
* @since 0.1.0
6765
*
66+
* @see WP_Abilities_Registry::unregister()
67+
*
6868
* @param string $name The name of the registered ability, with its namespace.
6969
* @return ?\WP_Ability The unregistered ability instance on success, null on failure.
7070
*/
@@ -75,10 +75,10 @@ function wp_unregister_ability( string $name ): ?WP_Ability {
7575
/**
7676
* Retrieves a registered ability using Abilities API.
7777
*
78-
* @see WP_Abilities_Registry::get_registered()
79-
*
8078
* @since 0.1.0
8179
*
80+
* @see WP_Abilities_Registry::get_registered()
81+
*
8282
* @param string $name The name of the registered ability, with its namespace.
8383
* @return ?\WP_Ability The registered ability instance, or null if it is not registered.
8484
*/
@@ -89,10 +89,10 @@ function wp_get_ability( string $name ): ?WP_Ability {
8989
/**
9090
* Retrieves all registered abilities using Abilities API.
9191
*
92-
* @see WP_Abilities_Registry::get_all_registered()
93-
*
9492
* @since 0.1.0
9593
*
94+
* @see WP_Abilities_Registry::get_all_registered()
95+
*
9696
* @return \WP_Ability[] The array of registered abilities.
9797
*/
9898
function wp_get_abilities(): array {

src/wp-includes/abilities-api/class-wp-abilities-registry.php

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,17 @@ final class WP_Abilities_Registry {
4646
* @param string $name The name of the ability. The name must be a string containing a namespace
4747
* prefix, i.e. `my-plugin/my-ability`. It can only contain lowercase
4848
* alphanumeric characters, dashes and the forward slash.
49-
* @param array<string,mixed> $args An associative array of arguments for the ability. This should include
50-
* `label`, `description`, `input_schema`, `output_schema`,
51-
* `execute_callback`, `permission_callback`, `meta`, and ability_class.
49+
* @param array<string,mixed> $args An associative array of arguments for the ability. See wp_register_ability() for
50+
* details.
5251
* @return ?\WP_Ability The registered ability instance on success, null on failure.
5352
*
5453
* @phpstan-param array{
5554
* label?: string,
5655
* description?: string,
56+
* execute_callback?: callable( mixed $input= ): (mixed|\WP_Error),
57+
* permission_callback?: callable( mixed $input= ): (bool|\WP_Error),
5758
* input_schema?: array<string,mixed>,
5859
* output_schema?: array<string,mixed>,
59-
* execute_callback?: callable( array<string,mixed> $input): (mixed|\WP_Error),
60-
* permission_callback?: ?callable( array<string,mixed> $input ): (bool|\WP_Error),
6160
* meta?: array<string,mixed>,
6261
* ability_class?: class-string<\WP_Ability>,
6362
* ...<string, mixed>
@@ -85,6 +84,16 @@ public function register( string $name, array $args ): ?WP_Ability {
8584
return null;
8685
}
8786

87+
/**
88+
* Filters the ability arguments before they are validated and used to instantiate the ability.
89+
*
90+
* @since 0.2.0
91+
*
92+
* @param array<string,mixed> $args The arguments used to instantiate the ability.
93+
* @param string $name The name of the ability, with its namespace.
94+
*/
95+
$args = apply_filters( 'register_ability_args', $args, $name );
96+
8897
// The class is only used to instantiate the ability, and is not a property of the ability itself.
8998
if ( isset( $args['ability_class'] ) && ! is_a( $args['ability_class'], WP_Ability::class, true ) ) {
9099
_doing_it_wrong(
@@ -94,6 +103,8 @@ public function register( string $name, array $args ): ?WP_Ability {
94103
);
95104
return null;
96105
}
106+
107+
/** @var class-string<\WP_Ability> */
97108
$ability_class = $args['ability_class'] ?? WP_Ability::class;
98109
unset( $args['ability_class'] );
99110

src/wp-includes/abilities-api/class-wp-ability.php

Lines changed: 68 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,17 @@ class WP_Ability {
6565
* The ability execute callback.
6666
*
6767
* @since 0.1.0
68-
* @var callable( array<string,mixed> $input): (mixed|\WP_Error)
68+
* @var callable( mixed $input= ): (mixed|\WP_Error)
6969
*/
7070
protected $execute_callback;
7171

7272
/**
7373
* The optional ability permission callback.
7474
*
7575
* @since 0.1.0
76-
* @var ?callable( array<string,mixed> $input ): (bool|\WP_Error)
76+
* @var callable( mixed $input= ): (bool|\WP_Error)
7777
*/
78-
protected $permission_callback = null;
78+
protected $permission_callback;
7979

8080
/**
8181
* The optional ability metadata.
@@ -143,15 +143,16 @@ public function __construct( string $name, array $args ) {
143143
* @phpstan-return array{
144144
* label: string,
145145
* description: string,
146+
* execute_callback: callable( mixed $input= ): (mixed|\WP_Error),
147+
* permission_callback: callable( mixed $input= ): (bool|\WP_Error),
146148
* input_schema?: array<string,mixed>,
147149
* output_schema?: array<string,mixed>,
148-
* execute_callback: callable( array<string,mixed> $input): (mixed|\WP_Error),
149-
* permission_callback?: ?callable( array<string,mixed> $input ): (bool|\WP_Error),
150150
* meta?: array<string,mixed>,
151151
* ...<string, mixed>,
152152
* } $args
153153
*/
154154
protected function prepare_properties( array $args ): array {
155+
// Required args must be present and of the correct type.
155156
if ( empty( $args['label'] ) || ! is_string( $args['label'] ) ) {
156157
throw new \InvalidArgumentException(
157158
esc_html__( 'The ability properties must contain a `label` string.' )
@@ -164,27 +165,28 @@ protected function prepare_properties( array $args ): array {
164165
);
165166
}
166167

167-
if ( isset( $args['input_schema'] ) && ! is_array( $args['input_schema'] ) ) {
168+
if ( empty( $args['execute_callback'] ) || ! is_callable( $args['execute_callback'] ) ) {
168169
throw new \InvalidArgumentException(
169-
esc_html__( 'The ability properties should provide a valid `input_schema` definition.' )
170+
esc_html__( 'The ability properties must contain a valid `execute_callback` function.' )
170171
);
171172
}
172173

173-
if ( isset( $args['output_schema'] ) && ! is_array( $args['output_schema'] ) ) {
174+
if ( empty( $args['permission_callback'] ) || ! is_callable( $args['permission_callback'] ) ) {
174175
throw new \InvalidArgumentException(
175-
esc_html__( 'The ability properties should provide a valid `output_schema` definition.' )
176+
esc_html__( 'The ability properties must provide a valid `permission_callback` function.' )
176177
);
177178
}
178179

179-
if ( empty( $args['execute_callback'] ) || ! is_callable( $args['execute_callback'] ) ) {
180+
// Optional args only need to be of the correct type if they are present.
181+
if ( isset( $args['input_schema'] ) && ! is_array( $args['input_schema'] ) ) {
180182
throw new \InvalidArgumentException(
181-
esc_html__( 'The ability properties must contain a valid `execute_callback` function.' )
183+
esc_html__( 'The ability properties should provide a valid `input_schema` definition.' )
182184
);
183185
}
184186

185-
if ( isset( $args['permission_callback'] ) && ! is_callable( $args['permission_callback'] ) ) {
187+
if ( isset( $args['output_schema'] ) && ! is_array( $args['output_schema'] ) ) {
186188
throw new \InvalidArgumentException(
187-
esc_html__( 'The ability properties should provide a valid `permission_callback` function.' )
189+
esc_html__( 'The ability properties should provide a valid `output_schema` definition.' )
188190
);
189191
}
190192

@@ -269,13 +271,24 @@ public function get_meta(): array {
269271
*
270272
* @since 0.1.0
271273
*
272-
* @param array<string,mixed> $input Optional. The input data to validate.
274+
* @param mixed $input Optional. The input data to validate. Default `null`.
273275
* @return true|\WP_Error Returns true if valid or the WP_Error object if validation fails.
274276
*/
275-
protected function validate_input( array $input = array() ) {
277+
protected function validate_input( $input = null ) {
276278
$input_schema = $this->get_input_schema();
277279
if ( empty( $input_schema ) ) {
278-
return true;
280+
if ( null === $input ) {
281+
return true;
282+
}
283+
284+
return new \WP_Error(
285+
'ability_missing_input_schema',
286+
sprintf(
287+
/* translators: %s ability name. */
288+
__( 'Ability "%s" does not define an input schema required to validate the provided input.' ),
289+
$this->name
290+
)
291+
);
279292
}
280293

281294
$valid_input = rest_validate_value_from_schema( $input, $input_schema, 'input' );
@@ -296,22 +309,22 @@ protected function validate_input( array $input = array() ) {
296309

297310
/**
298311
* Checks whether the ability has the necessary permissions.
299-
* If the permission callback is not set, the default behavior is to allow access
300-
* when the input provided passes validation.
312+
*
313+
* The input is validated against the input schema before it is passed to to permission callback.
301314
*
302315
* @since 0.1.0
303316
*
304-
* @param array<string,mixed> $input Optional. The input data for permission checking.
317+
* @param mixed $input Optional. The input data for permission checking. Default `null`.
305318
* @return bool|\WP_Error Whether the ability has the necessary permission.
306319
*/
307-
public function has_permission( array $input = array() ) {
320+
public function has_permission( $input = null ) {
308321
$is_valid = $this->validate_input( $input );
309322
if ( is_wp_error( $is_valid ) ) {
310323
return $is_valid;
311324
}
312325

313-
if ( ! is_callable( $this->permission_callback ) ) {
314-
return true;
326+
if ( empty( $this->get_input_schema() ) ) {
327+
return call_user_func( $this->permission_callback );
315328
}
316329

317330
return call_user_func( $this->permission_callback, $input );
@@ -322,10 +335,10 @@ public function has_permission( array $input = array() ) {
322335
*
323336
* @since 0.1.0
324337
*
325-
* @param array<string,mixed> $input The input data for the ability.
338+
* @param mixed $input Optional. The input data for the ability. Default `null`.
326339
* @return mixed|\WP_Error The result of the ability execution, or WP_Error on failure.
327340
*/
328-
protected function do_execute( array $input ) {
341+
protected function do_execute( $input = null ) {
329342
if ( ! is_callable( $this->execute_callback ) ) {
330343
return new \WP_Error(
331344
'ability_invalid_execute_callback',
@@ -334,6 +347,10 @@ protected function do_execute( array $input ) {
334347
);
335348
}
336349

350+
if ( empty( $this->get_input_schema() ) ) {
351+
return call_user_func( $this->execute_callback );
352+
}
353+
337354
return call_user_func( $this->execute_callback, $input );
338355
}
339356

@@ -373,10 +390,10 @@ protected function validate_output( $output ) {
373390
*
374391
* @since 0.1.0
375392
*
376-
* @param array<string,mixed> $input Optional. The input data for the ability.
393+
* @param mixed $input Optional. The input data for the ability. Default `null`.
377394
* @return mixed|\WP_Error The result of the ability execution, or WP_Error on failure.
378395
*/
379-
public function execute( array $input = array() ) {
396+
public function execute( $input = null ) {
380397
$has_permissions = $this->has_permission( $input );
381398
if ( true !== $has_permissions ) {
382399
if ( is_wp_error( $has_permissions ) ) {
@@ -398,14 +415,38 @@ public function execute( array $input = array() ) {
398415
);
399416
}
400417

418+
/**
419+
* Fires before an ability gets executed.
420+
*
421+
* @since 0.2.0
422+
*
423+
* @param string $ability_name The name of the ability.
424+
* @param mixed $input The input data for the ability.
425+
*/
426+
do_action( 'before_execute_ability', $this->name, $input );
427+
401428
$result = $this->do_execute( $input );
402429
if ( is_wp_error( $result ) ) {
403430
return $result;
404431
}
405432

406433
$is_valid = $this->validate_output( $result );
434+
if ( is_wp_error( $is_valid ) ) {
435+
return $is_valid;
436+
}
407437

408-
return is_wp_error( $is_valid ) ? $is_valid : $result;
438+
/**
439+
* Fires immediately after an ability finished executing.
440+
*
441+
* @since 0.2.0
442+
*
443+
* @param string $ability_name The name of the ability.
444+
* @param mixed $input The input data for the ability.
445+
* @param mixed $result The result of the ability execution.
446+
*/
447+
do_action( 'after_execute_ability', $this->name, $input, $result );
448+
449+
return $result;
409450
}
410451

411452
/**

0 commit comments

Comments
 (0)