Skip to content

Commit 769de9e

Browse files
justlevinejustlevinegziolofelixarntz
authored
dev!: handle property registration inside WP_Ability. (#54)
Co-authored-by: Greg Ziółkowski <[email protected]> Co-authored-by: justlevine <[email protected]> Co-authored-by: gziolo <[email protected]> Co-authored-by: felixarntz <[email protected]>
1 parent 1b75e1d commit 769de9e

File tree

3 files changed

+102
-77
lines changed

3 files changed

+102
-77
lines changed

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

Lines changed: 12 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -85,87 +85,33 @@ public function register( string $name, array $properties = array() ): ?WP_Abili
8585
return null;
8686
}
8787

88-
if ( empty( $properties['label'] ) || ! is_string( $properties['label'] ) ) {
89-
_doing_it_wrong(
90-
__METHOD__,
91-
esc_html__( 'The ability properties must contain a `label` string.' ),
92-
'0.1.0'
93-
);
94-
return null;
95-
}
96-
97-
if ( empty( $properties['description'] ) || ! is_string( $properties['description'] ) ) {
98-
_doing_it_wrong(
99-
__METHOD__,
100-
esc_html__( 'The ability properties must contain a `description` string.' ),
101-
'0.1.0'
102-
);
103-
return null;
104-
}
105-
106-
if ( isset( $properties['input_schema'] ) && ! is_array( $properties['input_schema'] ) ) {
107-
_doing_it_wrong(
108-
__METHOD__,
109-
esc_html__( 'The ability properties should provide a valid `input_schema` definition.' ),
110-
'0.1.0'
111-
);
112-
return null;
113-
}
114-
115-
if ( isset( $properties['output_schema'] ) && ! is_array( $properties['output_schema'] ) ) {
116-
_doing_it_wrong(
117-
__METHOD__,
118-
esc_html__( 'The ability properties should provide a valid `output_schema` definition.' ),
119-
'0.1.0'
120-
);
121-
return null;
122-
}
123-
124-
if ( empty( $properties['execute_callback'] ) || ! is_callable( $properties['execute_callback'] ) ) {
125-
_doing_it_wrong(
126-
__METHOD__,
127-
esc_html__( 'The ability properties must contain a valid `execute_callback` function.' ),
128-
'0.1.0'
129-
);
130-
return null;
131-
}
132-
133-
if ( isset( $properties['permission_callback'] ) && ! is_callable( $properties['permission_callback'] ) ) {
88+
// The class is only used to instantiate the ability, and is not a property of the ability itself.
89+
if ( isset( $properties['ability_class'] ) && ! is_a( $properties['ability_class'], WP_Ability::class, true ) ) {
13490
_doing_it_wrong(
13591
__METHOD__,
136-
esc_html__( 'The ability properties should provide a valid `permission_callback` function.' ),
92+
esc_html__( 'The ability properties should provide a valid `ability_class` that extends WP_Ability.' ),
13793
'0.1.0'
13894
);
13995
return null;
14096
}
97+
$ability_class = $properties['ability_class'] ?? WP_Ability::class;
98+
unset( $properties['ability_class'] );
14199

142-
if ( isset( $properties['meta'] ) && ! is_array( $properties['meta'] ) ) {
143-
_doing_it_wrong(
144-
__METHOD__,
145-
esc_html__( 'The ability properties should provide a valid `meta` array.' ),
146-
'0.1.0'
100+
try {
101+
// WP_Ability::validate_properties() will throw an exception if the properties are invalid.
102+
$ability = new $ability_class(
103+
$name,
104+
$properties
147105
);
148-
return null;
149-
}
150-
151-
if ( isset( $properties['ability_class'] ) && ! is_a( $properties['ability_class'], WP_Ability::class, true ) ) {
106+
} catch ( \InvalidArgumentException $e ) {
152107
_doing_it_wrong(
153108
__METHOD__,
154-
esc_html__( 'The ability properties should provide a valid `ability_class` that extends WP_Ability.' ),
109+
esc_html( $e->getMessage() ),
155110
'0.1.0'
156111
);
157112
return null;
158113
}
159114

160-
// The class is only used to instantiate the ability, and is not a property of the ability itself.
161-
$ability_class = $properties['ability_class'] ?? WP_Ability::class;
162-
unset( $properties['ability_class'] );
163-
164-
$ability = new $ability_class(
165-
$name,
166-
$properties
167-
);
168-
169115
$this->registered_abilities[ $name ] = $ability;
170116
return $ability;
171117
}

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

Lines changed: 72 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -100,21 +100,12 @@ class WP_Ability {
100100
* @param array<string,mixed> $properties An associative array of properties for the ability. This should
101101
* include `label`, `description`, `input_schema`, `output_schema`,
102102
* `execute_callback`, `permission_callback`, and `meta`.
103-
*
104-
* @phpstan-param array{
105-
* label: string,
106-
* description: string,
107-
* input_schema?: array<string,mixed>,
108-
* output_schema?: array<string,mixed>,
109-
* execute_callback: callable( array<string,mixed> $input): (mixed|\WP_Error),
110-
* permission_callback?: ?callable( array<string,mixed> $input ): (bool|\WP_Error),
111-
* meta?: array<string,mixed>,
112-
* ...<string, mixed>,
113-
* } $properties
114103
*/
115104
public function __construct( string $name, array $properties ) {
116105
$this->name = $name;
117106

107+
$this->validate_properties( $properties );
108+
118109
foreach ( $properties as $property_name => $property_value ) {
119110
if ( ! property_exists( $this, $property_name ) ) {
120111
_doing_it_wrong(
@@ -202,6 +193,76 @@ public function get_meta(): array {
202193
return $this->meta;
203194
}
204195

196+
/**
197+
* Validates the properties used to instantiate the ability.
198+
*
199+
* Errors are thrown as exceptions instead of \WP_Errors to allow for simpler handling and overloading. They are then
200+
* caught and converted to a WP_Error when by WP_Abilities_Registry::register().
201+
*
202+
* @since n.e.x.t
203+
*
204+
* @see WP_Abilities_Registry::register()
205+
*
206+
* @param array<string,mixed> $properties An associative array of properties to validate.
207+
*
208+
* @return void
209+
* @throws \InvalidArgumentException if the properties are invalid.
210+
*
211+
* @phpstan-assert array{
212+
* label: string,
213+
* description: string,
214+
* input_schema?: array<string,mixed>,
215+
* output_schema?: array<string,mixed>,
216+
* execute_callback: callable( array<string,mixed> $input): (mixed|\WP_Error),
217+
* permission_callback?: ?callable( array<string,mixed> $input ): (bool|\WP_Error),
218+
* meta?: array<string,mixed>,
219+
* ...<string, mixed>,
220+
* } $properties
221+
*/
222+
protected function validate_properties( array $properties ) {
223+
if ( empty( $properties['label'] ) || ! is_string( $properties['label'] ) ) {
224+
throw new \InvalidArgumentException(
225+
esc_html__( 'The ability properties must contain a `label` string.' )
226+
);
227+
}
228+
229+
if ( empty( $properties['description'] ) || ! is_string( $properties['description'] ) ) {
230+
throw new \InvalidArgumentException(
231+
esc_html__( 'The ability properties must contain a `description` string.' )
232+
);
233+
}
234+
235+
if ( isset( $properties['input_schema'] ) && ! is_array( $properties['input_schema'] ) ) {
236+
throw new \InvalidArgumentException(
237+
esc_html__( 'The ability properties should provide a valid `input_schema` definition.' )
238+
);
239+
}
240+
241+
if ( isset( $properties['output_schema'] ) && ! is_array( $properties['output_schema'] ) ) {
242+
throw new \InvalidArgumentException(
243+
esc_html__( 'The ability properties should provide a valid `output_schema` definition.' )
244+
);
245+
}
246+
247+
if ( empty( $properties['execute_callback'] ) || ! is_callable( $properties['execute_callback'] ) ) {
248+
throw new \InvalidArgumentException(
249+
esc_html__( 'The ability properties must contain a valid `execute_callback` function.' )
250+
);
251+
}
252+
253+
if ( isset( $properties['permission_callback'] ) && ! is_callable( $properties['permission_callback'] ) ) {
254+
throw new \InvalidArgumentException(
255+
esc_html__( 'The ability properties should provide a valid `permission_callback` function.' )
256+
);
257+
}
258+
259+
if ( isset( $properties['meta'] ) && ! is_array( $properties['meta'] ) ) {
260+
throw new \InvalidArgumentException(
261+
esc_html__( 'The ability properties should provide a valid `meta` array.' )
262+
);
263+
}
264+
}
265+
205266
/**
206267
* Validates input data against the input schema.
207268
*

tests/unit/abilities-api/wpAbilitiesRegistry.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,4 +383,22 @@ public function test_get_all_registered() {
383383
$this->assertSame( $ability_two_name, $result[ $ability_two_name ]->get_name() );
384384
$this->assertSame( $ability_three_name, $result[ $ability_three_name ]->get_name() );
385385
}
386+
387+
/**
388+
* Direct instantiation of WP_Ability with invalid properties should throw an exception.
389+
*
390+
* @covers WP_Ability::__construct
391+
* @covers WP_Ability::validate_properties
392+
*/
393+
public function test_wp_ability_invalid_properties_throws_exception() {
394+
$this->expectException( \InvalidArgumentException::class );
395+
new WP_Ability(
396+
'test/invalid',
397+
array(
398+
'label' => '',
399+
'description' => '',
400+
'execute_callback' => null,
401+
)
402+
);
403+
}
386404
}

0 commit comments

Comments
 (0)