@@ -99,6 +99,16 @@ class WP_Ability {
9999 * @param array<string,mixed> $properties An associative array of properties for the ability. This should
100100 * include `label`, `description`, `input_schema`, `output_schema`,
101101 * `execute_callback`, `permission_callback`, and `meta`.
102+ *
103+ * @phpstan-param array{
104+ * label: string,
105+ * description: string,
106+ * input_schema?: array<string,mixed>,
107+ * output_schema?: array<string,mixed>,
108+ * execute_callback: callable( array<string,mixed> $input): (mixed|\WP_Error),
109+ * permission_callback?: ?callable( ?array<string,mixed> $input ): bool,
110+ * meta?: array<string,mixed>,
111+ * } $properties
102112 */
103113 public function __construct ( string $ name , array $ properties ) {
104114 $ this ->name = $ name ;
@@ -180,29 +190,25 @@ public function get_meta(): array {
180190 * @since 0.1.0
181191 *
182192 * @param array<string,mixed> $input Optional. The input data to validate.
183- * @return bool Returns true if valid, false if validation fails.
193+ * @return true|\WP_Error Returns true if valid or the WP_Error object if validation fails.
184194 */
185- protected function validate_input ( array $ input = array () ): bool {
195+ protected function validate_input ( array $ input = array () ) {
186196 $ input_schema = $ this ->get_input_schema ();
187197 if ( empty ( $ input_schema ) ) {
188198 return true ;
189199 }
190200
191- $ valid_input = rest_validate_value_from_schema ( $ input , $ input_schema );
201+ $ valid_input = rest_validate_value_from_schema ( $ input , $ input_schema, ' input ' );
192202 if ( is_wp_error ( $ valid_input ) ) {
193- _doing_it_wrong (
194- __METHOD__ ,
195- esc_html (
196- sprintf (
197- /* translators: %1$s ability name, %2$s error message. */
198- __ ( 'Invalid input provided for ability "%1$s": %2$s. ' ),
199- $ this ->name ,
200- $ valid_input ->get_error_message ()
201- )
202- ),
203- '0.1.0 '
203+ return new \WP_Error (
204+ 'ability_invalid_input ' ,
205+ sprintf (
206+ /* translators: %1$s ability name, %2$s error message. */
207+ __ ( 'Ability "%1$s" has invalid input. Reason: %2$s ' ),
208+ $ this ->name ,
209+ $ valid_input ->get_error_message ()
210+ )
204211 );
205- return false ;
206212 }
207213
208214 return true ;
@@ -216,11 +222,12 @@ protected function validate_input( array $input = array() ): bool {
216222 * @since 0.1.0
217223 *
218224 * @param array<string,mixed> $input Optional. The input data for permission checking.
219- * @return bool Whether the ability has the necessary permission.
225+ * @return true|\WP_Error Whether the ability has the necessary permission.
220226 */
221- public function has_permission ( array $ input = array () ): bool {
222- if ( ! $ this ->validate_input ( $ input ) ) {
223- return false ;
227+ public function has_permission ( array $ input = array () ) {
228+ $ is_valid = $ this ->validate_input ( $ input );
229+ if ( is_wp_error ( $ is_valid ) ) {
230+ return $ is_valid ;
224231 }
225232
226233 if ( ! is_callable ( $ this ->permission_callback ) ) {
@@ -240,16 +247,13 @@ public function has_permission( array $input = array() ): bool {
240247 */
241248 protected function do_execute ( array $ input ) {
242249 if ( ! is_callable ( $ this ->execute_callback ) ) {
243- _doing_it_wrong (
244- __METHOD__ ,
245- esc_html (
246- /* translators: %s ability name. */
247- sprintf ( __ ( 'Ability "%s" does not have a valid execute callback. ' ), $ this ->name )
248- ),
249- '0.1.0 '
250+ return new \WP_Error (
251+ 'ability_invalid_execute_callback ' ,
252+ /* translators: %s ability name. */
253+ sprintf ( __ ( 'Ability "%s" does not have a valid execute callback. ' ), $ this ->name )
250254 );
251- return null ;
252255 }
256+
253257 return call_user_func ( $ this ->execute_callback , $ input );
254258 }
255259
@@ -259,29 +263,25 @@ protected function do_execute( array $input ) {
259263 * @since 0.1.0
260264 *
261265 * @param mixed $output The output data to validate.
262- * @return bool Returns true if valid, false if validation fails.
266+ * @return true|\WP_Error Returns true if valid, or a WP_Error object if validation fails.
263267 */
264- protected function validate_output ( $ output ): bool {
268+ protected function validate_output ( $ output ) {
265269 $ output_schema = $ this ->get_output_schema ();
266270 if ( empty ( $ output_schema ) ) {
267271 return true ;
268272 }
269273
270- $ valid_output = rest_validate_value_from_schema ( $ output , $ output_schema );
274+ $ valid_output = rest_validate_value_from_schema ( $ output , $ output_schema, ' output ' );
271275 if ( is_wp_error ( $ valid_output ) ) {
272- _doing_it_wrong (
273- __METHOD__ ,
274- esc_html (
275- sprintf (
276- /* translators: %1$s ability name, %2$s error message. */
277- __ ( 'Invalid output provided for ability "%1$s": %2$s. ' ),
278- $ this ->name ,
279- $ valid_output ->get_error_message ()
280- )
281- ),
282- '0.1.0 '
276+ return new \WP_Error (
277+ 'ability_invalid_output ' ,
278+ sprintf (
279+ /* translators: %1$s ability name, %2$s error message. */
280+ __ ( 'Ability "%1$s" has invalid output. Reason: %2$s ' ),
281+ $ this ->name ,
282+ $ valid_output ->get_error_message ()
283+ )
283284 );
284- return false ;
285285 }
286286
287287 return true ;
@@ -297,28 +297,35 @@ protected function validate_output( $output ): bool {
297297 * @return mixed|\WP_Error The result of the ability execution, or WP_Error on failure.
298298 */
299299 public function execute ( array $ input = array () ) {
300- if ( ! $ this ->has_permission ( $ input ) ) {
301- _doing_it_wrong (
302- __METHOD__ ,
303- esc_html (
304- /* translators: %s ability name. */
305- sprintf ( __ ( 'Ability "%s" does not have necessary permission. ' ), $ this ->name )
306- ),
307- '0.1.0 '
300+ $ has_permissions = $ this ->has_permission ( $ input );
301+ if ( true !== $ has_permissions ) {
302+ if ( is_wp_error ( $ has_permissions ) ) {
303+ if ( 'ability_invalid_input ' === $ has_permissions ->get_error_code () ) {
304+ return $ has_permissions ;
305+ }
306+ // Don't leak the permission check error to someone without the correct perms.
307+ _doing_it_wrong (
308+ __METHOD__ ,
309+ esc_html ( $ has_permissions ->get_error_message () ),
310+ '0.1.0 '
311+ );
312+ }
313+
314+ return new \WP_Error (
315+ 'ability_invalid_permissions ' ,
316+ /* translators: %s ability name. */
317+ sprintf ( __ ( 'Ability "%s" does not have necessary permission. ' ), $ this ->name )
308318 );
309- return null ;
310319 }
311320
312321 $ result = $ this ->do_execute ( $ input );
313322 if ( is_wp_error ( $ result ) ) {
314323 return $ result ;
315324 }
316325
317- if ( ! $ this ->validate_output ( $ result ) ) {
318- return null ;
319- }
326+ $ is_valid = $ this ->validate_output ( $ result );
320327
321- return $ result ;
328+ return is_wp_error ( $ is_valid ) ? $ is_valid : $ result ;
322329 }
323330
324331 /**
0 commit comments