@@ -62,8 +62,8 @@ public function register_routes(): void {
6262 // This was the same issue that we ended up seeing with the Feature API.
6363 array (
6464 'methods ' => WP_REST_Server::ALLMETHODS ,
65- 'callback ' => array ( $ this , 'run_ability_with_method_check ' ),
66- 'permission_callback ' => array ( $ this , 'run_ability_permissions_check ' ),
65+ 'callback ' => array ( $ this , 'execute_ability ' ),
66+ 'permission_callback ' => array ( $ this , 'check_ability_permissions ' ),
6767 'args ' => $ this ->get_run_args (),
6868 ),
6969 'schema ' => array ( $ this , 'get_run_schema ' ),
@@ -72,16 +72,15 @@ public function register_routes(): void {
7272 }
7373
7474 /**
75- * Executes an ability with HTTP method validation .
75+ * Executes an ability.
7676 *
7777 * @since 6.9.0
7878 *
7979 * @param WP_REST_Request<array<string, mixed>> $request Full details about the request.
8080 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
8181 */
82- public function run_ability_with_method_check ( $ request ) {
82+ public function execute_ability ( $ request ) {
8383 $ ability = wp_get_ability ( $ request ->get_param ( 'name ' ) );
84-
8584 if ( ! $ ability ) {
8685 return new WP_Error (
8786 'rest_ability_not_found ' ,
@@ -90,60 +89,50 @@ public function run_ability_with_method_check( $request ) {
9089 );
9190 }
9291
93- // Check if the HTTP method matches the ability annotations.
94- $ annotations = $ ability ->get_meta_item ( 'annotations ' );
95- $ expected_method = 'POST ' ;
96- if ( ! empty ( $ annotations ['readonly ' ] ) ) {
97- $ expected_method = 'GET ' ;
98- } elseif ( ! empty ( $ annotations ['destructive ' ] ) && ! empty ( $ annotations ['idempotent ' ] ) ) {
99- $ expected_method = 'DELETE ' ;
100- }
101-
102- if ( $ expected_method !== $ request ->get_method () ) {
103- $ error_message = __ ( 'Abilities that perform updates require POST method. ' );
104- if ( 'GET ' === $ expected_method ) {
105- $ error_message = __ ( 'Read-only abilities require GET method. ' );
106- } elseif ( 'DELETE ' === $ expected_method ) {
107- $ error_message = __ ( 'Abilities that perform destructive actions require DELETE method. ' );
92+ $ input = $ this ->get_input_from_request ( $ request );
93+ $ result = $ ability ->execute ( $ input );
94+ if ( is_wp_error ( $ result ) ) {
95+ if ( 'ability_invalid_input ' === $ result ->get_error_code () ) {
96+ $ result ->add_data ( array ( 'status ' => 400 ) );
10897 }
109- return new WP_Error (
110- 'rest_ability_invalid_method ' ,
111- $ error_message ,
112- array ( 'status ' => 405 )
113- );
98+ return $ result ;
11499 }
115100
116- return $ this -> run_ability ( $ request );
101+ return rest_ensure_response ( $ result );
117102 }
118103
119104 /**
120- * Executes an ability.
105+ * Validates if the HTTP method matches the expected method for the ability based on its annotations .
121106 *
122107 * @since 6.9.0
123108 *
124- * @param WP_REST_Request<array<string, mixed>> $request Full details about the request.
125- * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
109+ * @param string $request_method The HTTP method of the request.
110+ * @param array<string, (null|bool)> $annotations The ability annotations.
111+ * @return true|WP_Error True on success, or WP_Error object on failure.
126112 */
127- public function run_ability ( $ request ) {
128- $ ability = wp_get_ability ( $ request ->get_param ( 'name ' ) );
129- if ( ! $ ability ) {
130- return new WP_Error (
131- 'rest_ability_not_found ' ,
132- __ ( 'Ability not found. ' ),
133- array ( 'status ' => 404 )
134- );
113+ public function validate_request_method ( string $ request_method , array $ annotations ) {
114+ $ expected_method = 'POST ' ;
115+ if ( ! empty ( $ annotations ['readonly ' ] ) ) {
116+ $ expected_method = 'GET ' ;
117+ } elseif ( ! empty ( $ annotations ['destructive ' ] ) && ! empty ( $ annotations ['idempotent ' ] ) ) {
118+ $ expected_method = 'DELETE ' ;
135119 }
136120
137- $ input = $ this ->get_input_from_request ( $ request );
138- $ result = $ ability ->execute ( $ input );
139- if ( is_wp_error ( $ result ) ) {
140- if ( 'ability_invalid_input ' === $ result ->get_error_code () ) {
141- $ result ->add_data ( array ( 'status ' => 400 ) );
142- }
143- return $ result ;
121+ if ( $ expected_method === $ request_method ) {
122+ return true ;
144123 }
145124
146- return rest_ensure_response ( $ result );
125+ $ error_message = __ ( 'Abilities that perform updates require POST method. ' );
126+ if ( 'GET ' === $ expected_method ) {
127+ $ error_message = __ ( 'Read-only abilities require GET method. ' );
128+ } elseif ( 'DELETE ' === $ expected_method ) {
129+ $ error_message = __ ( 'Abilities that perform destructive actions require DELETE method. ' );
130+ }
131+ return new WP_Error (
132+ 'rest_ability_invalid_method ' ,
133+ $ error_message ,
134+ array ( 'status ' => 405 )
135+ );
147136 }
148137
149138 /**
@@ -154,7 +143,7 @@ public function run_ability( $request ) {
154143 * @param WP_REST_Request<array<string, mixed>> $request Full details about the request.
155144 * @return true|WP_Error True if the request has execution permission, WP_Error object otherwise.
156145 */
157- public function run_ability_permissions_check ( $ request ) {
146+ public function check_ability_permissions ( $ request ) {
158147 $ ability = wp_get_ability ( $ request ->get_param ( 'name ' ) );
159148 if ( ! $ ability || ! $ ability ->get_meta_item ( 'show_in_rest ' ) ) {
160149 return new WP_Error (
@@ -164,8 +153,23 @@ public function run_ability_permissions_check( $request ) {
164153 );
165154 }
166155
167- $ input = $ this ->get_input_from_request ( $ request );
168- if ( ! $ ability ->check_permissions ( $ input ) ) {
156+ $ is_valid = $ this ->validate_request_method (
157+ $ request ->get_method (),
158+ $ ability ->get_meta_item ( 'annotations ' )
159+ );
160+ if ( is_wp_error ( $ is_valid ) ) {
161+ return $ is_valid ;
162+ }
163+
164+ $ input = $ this ->get_input_from_request ( $ request );
165+ $ result = $ ability ->check_permissions ( $ input );
166+ if ( is_wp_error ( $ result ) ) {
167+ if ( 'ability_invalid_input ' === $ result ->get_error_code () ) {
168+ $ result ->add_data ( array ( 'status ' => 400 ) );
169+ }
170+ return $ result ;
171+ }
172+ if ( ! $ result ) {
169173 return new WP_Error (
170174 'rest_ability_cannot_execute ' ,
171175 __ ( 'Sorry, you are not allowed to execute this ability. ' ),
@@ -185,7 +189,7 @@ public function run_ability_permissions_check( $request ) {
185189 * @return mixed|null The input parameters.
186190 */
187191 private function get_input_from_request ( $ request ) {
188- if ( in_array ( $ request ->get_method (), array ( 'GET ' , 'DELETE ' ) ) ) {
192+ if ( in_array ( $ request ->get_method (), array ( 'GET ' , 'DELETE ' ), true ) ) {
189193 // For GET and DELETE requests, look for 'input' query parameter.
190194 $ query_params = $ request ->get_query_params ();
191195 return $ query_params ['input ' ] ?? null ;
0 commit comments