212 |
229 |
213 return wp_json_encode( $error ); |
230 return wp_json_encode( $error ); |
214 } |
231 } |
215 |
232 |
216 /** |
233 /** |
217 * Handles serving an API request. |
234 * Handles serving a REST API request. |
218 * |
235 * |
219 * Matches the current server URI to a route and runs the first matching |
236 * Matches the current server URI to a route and runs the first matching |
220 * callback then outputs a JSON representation of the returned value. |
237 * callback then outputs a JSON representation of the returned value. |
221 * |
238 * |
222 * @since 4.4.0 |
239 * @since 4.4.0 |
223 * |
240 * |
224 * @see WP_REST_Server::dispatch() |
241 * @see WP_REST_Server::dispatch() |
|
242 * |
|
243 * @global WP_User $current_user The currently authenticated user. |
225 * |
244 * |
226 * @param string $path Optional. The request route. If not set, `$_SERVER['PATH_INFO']` will be used. |
245 * @param string $path Optional. The request route. If not set, `$_SERVER['PATH_INFO']` will be used. |
227 * Default null. |
246 * Default null. |
228 * @return null|false Null if not served and a HEAD request, false otherwise. |
247 * @return null|false Null if not served and a HEAD request, false otherwise. |
229 */ |
248 */ |
230 public function serve_request( $path = null ) { |
249 public function serve_request( $path = null ) { |
231 $content_type = isset( $_GET['_jsonp'] ) ? 'application/javascript' : 'application/json'; |
250 /* @var WP_User|null $current_user */ |
|
251 global $current_user; |
|
252 |
|
253 if ( $current_user instanceof WP_User && ! $current_user->exists() ) { |
|
254 /* |
|
255 * If there is no current user authenticated via other means, clear |
|
256 * the cached lack of user, so that an authenticate check can set it |
|
257 * properly. |
|
258 * |
|
259 * This is done because for authentications such as Application |
|
260 * Passwords, we don't want it to be accepted unless the current HTTP |
|
261 * request is a REST API request, which can't always be identified early |
|
262 * enough in evaluation. |
|
263 */ |
|
264 $current_user = null; |
|
265 } |
|
266 |
|
267 /** |
|
268 * Filters whether JSONP is enabled for the REST API. |
|
269 * |
|
270 * @since 4.4.0 |
|
271 * |
|
272 * @param bool $jsonp_enabled Whether JSONP is enabled. Default true. |
|
273 */ |
|
274 $jsonp_enabled = apply_filters( 'rest_jsonp_enabled', true ); |
|
275 |
|
276 $jsonp_callback = false; |
|
277 if ( isset( $_GET['_jsonp'] ) ) { |
|
278 $jsonp_callback = $_GET['_jsonp']; |
|
279 } |
|
280 |
|
281 $content_type = ( $jsonp_callback && $jsonp_enabled ) ? 'application/javascript' : 'application/json'; |
232 $this->send_header( 'Content-Type', $content_type . '; charset=' . get_option( 'blog_charset' ) ); |
282 $this->send_header( 'Content-Type', $content_type . '; charset=' . get_option( 'blog_charset' ) ); |
233 $this->send_header( 'X-Robots-Tag', 'noindex' ); |
283 $this->send_header( 'X-Robots-Tag', 'noindex' ); |
234 |
284 |
235 $api_root = get_rest_url(); |
285 $api_root = get_rest_url(); |
236 if ( ! empty( $api_root ) ) { |
286 if ( ! empty( $api_root ) ) { |
891 * @param WP_REST_Request $request Request to attempt dispatching. |
935 * @param WP_REST_Request $request Request to attempt dispatching. |
892 * @return WP_REST_Response Response returned by the callback. |
936 * @return WP_REST_Response Response returned by the callback. |
893 */ |
937 */ |
894 public function dispatch( $request ) { |
938 public function dispatch( $request ) { |
895 /** |
939 /** |
896 * Filters the pre-calculated result of a REST dispatch request. |
940 * Filters the pre-calculated result of a REST API dispatch request. |
897 * |
941 * |
898 * Allow hijacking the request before dispatching by returning a non-empty. The returned value |
942 * Allow hijacking the request before dispatching by returning a non-empty. The returned value |
899 * will be used to serve the request instead. |
943 * will be used to serve the request instead. |
900 * |
944 * |
901 * @since 4.4.0 |
945 * @since 4.4.0 |
902 * |
946 * |
903 * @param mixed $result Response to replace the requested version with. Can be anything |
947 * @param mixed $result Response to replace the requested version with. Can be anything |
904 * a normal endpoint can return, or null to not hijack the request. |
948 * a normal endpoint can return, or null to not hijack the request. |
905 * @param WP_REST_Server $this Server instance. |
949 * @param WP_REST_Server $server Server instance. |
906 * @param WP_REST_Request $request Request used to generate the response. |
950 * @param WP_REST_Request $request Request used to generate the response. |
907 */ |
951 */ |
908 $result = apply_filters( 'rest_pre_dispatch', null, $this, $request ); |
952 $result = apply_filters( 'rest_pre_dispatch', null, $this, $request ); |
909 |
953 |
910 if ( ! empty( $result ) ) { |
954 if ( ! empty( $result ) ) { |
911 return $result; |
955 return $result; |
912 } |
956 } |
913 |
957 |
|
958 $error = null; |
|
959 $matched = $this->match_request_to_handler( $request ); |
|
960 |
|
961 if ( is_wp_error( $matched ) ) { |
|
962 return $this->error_to_response( $matched ); |
|
963 } |
|
964 |
|
965 list( $route, $handler ) = $matched; |
|
966 |
|
967 if ( ! is_callable( $handler['callback'] ) ) { |
|
968 $error = new WP_Error( |
|
969 'rest_invalid_handler', |
|
970 __( 'The handler for the route is invalid.' ), |
|
971 array( 'status' => 500 ) |
|
972 ); |
|
973 } |
|
974 |
|
975 if ( ! is_wp_error( $error ) ) { |
|
976 $check_required = $request->has_valid_params(); |
|
977 if ( is_wp_error( $check_required ) ) { |
|
978 $error = $check_required; |
|
979 } else { |
|
980 $check_sanitized = $request->sanitize_params(); |
|
981 if ( is_wp_error( $check_sanitized ) ) { |
|
982 $error = $check_sanitized; |
|
983 } |
|
984 } |
|
985 } |
|
986 |
|
987 return $this->respond_to_request( $request, $route, $handler, $error ); |
|
988 } |
|
989 |
|
990 /** |
|
991 * Matches a request object to its handler. |
|
992 * |
|
993 * @access private |
|
994 * @since 5.6.0 |
|
995 * |
|
996 * @param WP_REST_Request $request The request object. |
|
997 * @return array|WP_Error The route and request handler on success or a WP_Error instance if no handler was found. |
|
998 */ |
|
999 protected function match_request_to_handler( $request ) { |
914 $method = $request->get_method(); |
1000 $method = $request->get_method(); |
915 $path = $request->get_route(); |
1001 $path = $request->get_route(); |
916 |
1002 |
917 $with_namespace = array(); |
1003 $with_namespace = array(); |
918 |
1004 |
955 if ( empty( $handler['methods'][ $checked_method ] ) ) { |
1041 if ( empty( $handler['methods'][ $checked_method ] ) ) { |
956 continue; |
1042 continue; |
957 } |
1043 } |
958 |
1044 |
959 if ( ! is_callable( $callback ) ) { |
1045 if ( ! is_callable( $callback ) ) { |
960 $response = new WP_Error( |
1046 return array( $route, $handler ); |
961 'rest_invalid_handler', |
1047 } |
962 __( 'The handler for the route is invalid' ), |
1048 |
963 array( 'status' => 500 ) |
1049 $request->set_url_params( $args ); |
964 ); |
1050 $request->set_attributes( $handler ); |
965 } |
1051 |
966 |
1052 $defaults = array(); |
967 if ( ! is_wp_error( $response ) ) { |
1053 |
968 // Remove the redundant preg_match argument. |
1054 foreach ( $handler['args'] as $arg => $options ) { |
969 unset( $args[0] ); |
1055 if ( isset( $options['default'] ) ) { |
970 |
1056 $defaults[ $arg ] = $options['default']; |
971 $request->set_url_params( $args ); |
|
972 $request->set_attributes( $handler ); |
|
973 |
|
974 $defaults = array(); |
|
975 |
|
976 foreach ( $handler['args'] as $arg => $options ) { |
|
977 if ( isset( $options['default'] ) ) { |
|
978 $defaults[ $arg ] = $options['default']; |
|
979 } |
|
980 } |
1057 } |
981 |
1058 } |
982 $request->set_default_params( $defaults ); |
1059 |
983 |
1060 $request->set_default_params( $defaults ); |
984 $check_required = $request->has_valid_params(); |
1061 |
985 if ( is_wp_error( $check_required ) ) { |
1062 return array( $route, $handler ); |
986 $response = $check_required; |
1063 } |
987 } else { |
1064 } |
988 $check_sanitized = $request->sanitize_params(); |
1065 |
989 if ( is_wp_error( $check_sanitized ) ) { |
1066 return new WP_Error( |
990 $response = $check_sanitized; |
1067 'rest_no_route', |
991 } |
1068 __( 'No route was found matching the URL and request method.' ), |
992 } |
1069 array( 'status' => 404 ) |
993 } |
|
994 |
|
995 /** |
|
996 * Filters the response before executing any REST API callbacks. |
|
997 * |
|
998 * Allows plugins to perform additional validation after a |
|
999 * request is initialized and matched to a registered route, |
|
1000 * but before it is executed. |
|
1001 * |
|
1002 * Note that this filter will not be called for requests that |
|
1003 * fail to authenticate or match to a registered route. |
|
1004 * |
|
1005 * @since 4.7.0 |
|
1006 * |
|
1007 * @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client. Usually a WP_REST_Response or WP_Error. |
|
1008 * @param array $handler Route handler used for the request. |
|
1009 * @param WP_REST_Request $request Request used to generate the response. |
|
1010 */ |
|
1011 $response = apply_filters( 'rest_request_before_callbacks', $response, $handler, $request ); |
|
1012 |
|
1013 if ( ! is_wp_error( $response ) ) { |
|
1014 // Check permission specified on the route. |
|
1015 if ( ! empty( $handler['permission_callback'] ) ) { |
|
1016 $permission = call_user_func( $handler['permission_callback'], $request ); |
|
1017 |
|
1018 if ( is_wp_error( $permission ) ) { |
|
1019 $response = $permission; |
|
1020 } elseif ( false === $permission || null === $permission ) { |
|
1021 $response = new WP_Error( |
|
1022 'rest_forbidden', |
|
1023 __( 'Sorry, you are not allowed to do that.' ), |
|
1024 array( 'status' => rest_authorization_required_code() ) |
|
1025 ); |
|
1026 } |
|
1027 } |
|
1028 } |
|
1029 |
|
1030 if ( ! is_wp_error( $response ) ) { |
|
1031 /** |
|
1032 * Filters the REST dispatch request result. |
|
1033 * |
|
1034 * Allow plugins to override dispatching the request. |
|
1035 * |
|
1036 * @since 4.4.0 |
|
1037 * @since 4.5.0 Added `$route` and `$handler` parameters. |
|
1038 * |
|
1039 * @param mixed $dispatch_result Dispatch result, will be used if not empty. |
|
1040 * @param WP_REST_Request $request Request used to generate the response. |
|
1041 * @param string $route Route matched for the request. |
|
1042 * @param array $handler Route handler used for the request. |
|
1043 */ |
|
1044 $dispatch_result = apply_filters( 'rest_dispatch_request', null, $request, $route, $handler ); |
|
1045 |
|
1046 // Allow plugins to halt the request via this filter. |
|
1047 if ( null !== $dispatch_result ) { |
|
1048 $response = $dispatch_result; |
|
1049 } else { |
|
1050 $response = call_user_func( $callback, $request ); |
|
1051 } |
|
1052 } |
|
1053 |
|
1054 /** |
|
1055 * Filters the response immediately after executing any REST API |
|
1056 * callbacks. |
|
1057 * |
|
1058 * Allows plugins to perform any needed cleanup, for example, |
|
1059 * to undo changes made during the {@see 'rest_request_before_callbacks'} |
|
1060 * filter. |
|
1061 * |
|
1062 * Note that this filter will not be called for requests that |
|
1063 * fail to authenticate or match to a registered route. |
|
1064 * |
|
1065 * Note that an endpoint's `permission_callback` can still be |
|
1066 * called after this filter - see `rest_send_allow_header()`. |
|
1067 * |
|
1068 * @since 4.7.0 |
|
1069 * |
|
1070 * @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client. Usually a WP_REST_Response or WP_Error. |
|
1071 * @param array $handler Route handler used for the request. |
|
1072 * @param WP_REST_Request $request Request used to generate the response. |
|
1073 */ |
|
1074 $response = apply_filters( 'rest_request_after_callbacks', $response, $handler, $request ); |
|
1075 |
|
1076 if ( is_wp_error( $response ) ) { |
|
1077 $response = $this->error_to_response( $response ); |
|
1078 } else { |
|
1079 $response = rest_ensure_response( $response ); |
|
1080 } |
|
1081 |
|
1082 $response->set_matched_route( $route ); |
|
1083 $response->set_matched_handler( $handler ); |
|
1084 |
|
1085 return $response; |
|
1086 } |
|
1087 } |
|
1088 |
|
1089 return $this->error_to_response( |
|
1090 new WP_Error( |
|
1091 'rest_no_route', |
|
1092 __( 'No route was found matching the URL and request method' ), |
|
1093 array( 'status' => 404 ) |
|
1094 ) |
|
1095 ); |
1070 ); |
|
1071 } |
|
1072 |
|
1073 /** |
|
1074 * Dispatches the request to the callback handler. |
|
1075 * |
|
1076 * @access private |
|
1077 * @since 5.6.0 |
|
1078 * |
|
1079 * @param WP_REST_Request $request The request object. |
|
1080 * @param array $handler The matched route handler. |
|
1081 * @param string $route The matched route regex. |
|
1082 * @param WP_Error|null $response The current error object if any. |
|
1083 * @return WP_REST_Response |
|
1084 */ |
|
1085 protected function respond_to_request( $request, $route, $handler, $response ) { |
|
1086 /** |
|
1087 * Filters the response before executing any REST API callbacks. |
|
1088 * |
|
1089 * Allows plugins to perform additional validation after a |
|
1090 * request is initialized and matched to a registered route, |
|
1091 * but before it is executed. |
|
1092 * |
|
1093 * Note that this filter will not be called for requests that |
|
1094 * fail to authenticate or match to a registered route. |
|
1095 * |
|
1096 * @since 4.7.0 |
|
1097 * |
|
1098 * @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client. |
|
1099 * Usually a WP_REST_Response or WP_Error. |
|
1100 * @param array $handler Route handler used for the request. |
|
1101 * @param WP_REST_Request $request Request used to generate the response. |
|
1102 */ |
|
1103 $response = apply_filters( 'rest_request_before_callbacks', $response, $handler, $request ); |
|
1104 |
|
1105 // Check permission specified on the route. |
|
1106 if ( ! is_wp_error( $response ) && ! empty( $handler['permission_callback'] ) ) { |
|
1107 $permission = call_user_func( $handler['permission_callback'], $request ); |
|
1108 |
|
1109 if ( is_wp_error( $permission ) ) { |
|
1110 $response = $permission; |
|
1111 } elseif ( false === $permission || null === $permission ) { |
|
1112 $response = new WP_Error( |
|
1113 'rest_forbidden', |
|
1114 __( 'Sorry, you are not allowed to do that.' ), |
|
1115 array( 'status' => rest_authorization_required_code() ) |
|
1116 ); |
|
1117 } |
|
1118 } |
|
1119 |
|
1120 if ( ! is_wp_error( $response ) ) { |
|
1121 /** |
|
1122 * Filters the REST API dispatch request result. |
|
1123 * |
|
1124 * Allow plugins to override dispatching the request. |
|
1125 * |
|
1126 * @since 4.4.0 |
|
1127 * @since 4.5.0 Added `$route` and `$handler` parameters. |
|
1128 * |
|
1129 * @param mixed $dispatch_result Dispatch result, will be used if not empty. |
|
1130 * @param WP_REST_Request $request Request used to generate the response. |
|
1131 * @param string $route Route matched for the request. |
|
1132 * @param array $handler Route handler used for the request. |
|
1133 */ |
|
1134 $dispatch_result = apply_filters( 'rest_dispatch_request', null, $request, $route, $handler ); |
|
1135 |
|
1136 // Allow plugins to halt the request via this filter. |
|
1137 if ( null !== $dispatch_result ) { |
|
1138 $response = $dispatch_result; |
|
1139 } else { |
|
1140 $response = call_user_func( $handler['callback'], $request ); |
|
1141 } |
|
1142 } |
|
1143 |
|
1144 /** |
|
1145 * Filters the response immediately after executing any REST API |
|
1146 * callbacks. |
|
1147 * |
|
1148 * Allows plugins to perform any needed cleanup, for example, |
|
1149 * to undo changes made during the {@see 'rest_request_before_callbacks'} |
|
1150 * filter. |
|
1151 * |
|
1152 * Note that this filter will not be called for requests that |
|
1153 * fail to authenticate or match to a registered route. |
|
1154 * |
|
1155 * Note that an endpoint's `permission_callback` can still be |
|
1156 * called after this filter - see `rest_send_allow_header()`. |
|
1157 * |
|
1158 * @since 4.7.0 |
|
1159 * |
|
1160 * @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client. |
|
1161 * Usually a WP_REST_Response or WP_Error. |
|
1162 * @param array $handler Route handler used for the request. |
|
1163 * @param WP_REST_Request $request Request used to generate the response. |
|
1164 */ |
|
1165 $response = apply_filters( 'rest_request_after_callbacks', $response, $handler, $request ); |
|
1166 |
|
1167 if ( is_wp_error( $response ) ) { |
|
1168 $response = $this->error_to_response( $response ); |
|
1169 } else { |
|
1170 $response = rest_ensure_response( $response ); |
|
1171 } |
|
1172 |
|
1173 $response->set_matched_route( $route ); |
|
1174 $response->set_matched_handler( $handler ); |
|
1175 |
|
1176 return $response; |
1096 } |
1177 } |
1097 |
1178 |
1098 /** |
1179 /** |
1099 * Returns if an error occurred during most recent JSON encode/decode. |
1180 * Returns if an error occurred during most recent JSON encode/decode. |
1100 * |
1181 * |
1101 * Strings to be translated will be in format like |
1182 * Strings to be translated will be in format like |
1102 * "Encoding error: Maximum stack depth exceeded". |
1183 * "Encoding error: Maximum stack depth exceeded". |
1103 * |
1184 * |
1104 * @since 4.4.0 |
1185 * @since 4.4.0 |
1105 * |
1186 * |
1106 * @return bool|string Boolean false or string error message. |
1187 * @return false|string Boolean false or string error message. |
1107 */ |
1188 */ |
1108 protected function get_json_last_error() { |
1189 protected function get_json_last_error() { |
1109 $last_error_code = json_last_error(); |
1190 $last_error_code = json_last_error(); |
1110 |
1191 |
1111 if ( JSON_ERROR_NONE === $last_error_code || empty( $last_error_code ) ) { |
1192 if ( JSON_ERROR_NONE === $last_error_code || empty( $last_error_code ) ) { |
1142 'authentication' => array(), |
1223 'authentication' => array(), |
1143 'routes' => $this->get_data_for_routes( $this->get_routes(), $request['context'] ), |
1224 'routes' => $this->get_data_for_routes( $this->get_routes(), $request['context'] ), |
1144 ); |
1225 ); |
1145 |
1226 |
1146 $response = new WP_REST_Response( $available ); |
1227 $response = new WP_REST_Response( $available ); |
1147 |
1228 $response->add_link( 'help', 'https://developer.wordpress.org/rest-api/' ); |
1148 $response->add_link( 'help', 'http://v2.wp-api.org/' ); |
1229 $this->add_active_theme_link_to_index( $response ); |
|
1230 $this->add_site_logo_to_index( $response ); |
1149 |
1231 |
1150 /** |
1232 /** |
1151 * Filters the API root index data. |
1233 * Filters the REST API root index data. |
1152 * |
1234 * |
1153 * This contains the data describing the API. This includes information |
1235 * This contains the data describing the API. This includes information |
1154 * about supported authentication schemes, supported namespaces, routes |
1236 * about supported authentication schemes, supported namespaces, routes |
1155 * available on the API, and a small amount of data about the site. |
1237 * available on the API, and a small amount of data about the site. |
1156 * |
1238 * |
1157 * @since 4.4.0 |
1239 * @since 4.4.0 |
1158 * |
1240 * |
1159 * @param WP_REST_Response $response Response data. |
1241 * @param WP_REST_Response $response Response data. |
1160 */ |
1242 */ |
1161 return apply_filters( 'rest_index', $response ); |
1243 return apply_filters( 'rest_index', $response ); |
|
1244 } |
|
1245 |
|
1246 /** |
|
1247 * Adds a link to the active theme for users who have proper permissions. |
|
1248 * |
|
1249 * @since 5.7.0 |
|
1250 * |
|
1251 * @param WP_REST_Response $response REST API response. |
|
1252 */ |
|
1253 protected function add_active_theme_link_to_index( WP_REST_Response $response ) { |
|
1254 $should_add = current_user_can( 'switch_themes' ) || current_user_can( 'manage_network_themes' ); |
|
1255 |
|
1256 if ( ! $should_add && current_user_can( 'edit_posts' ) ) { |
|
1257 $should_add = true; |
|
1258 } |
|
1259 |
|
1260 if ( ! $should_add ) { |
|
1261 foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) { |
|
1262 if ( current_user_can( $post_type->cap->edit_posts ) ) { |
|
1263 $should_add = true; |
|
1264 break; |
|
1265 } |
|
1266 } |
|
1267 } |
|
1268 |
|
1269 if ( $should_add ) { |
|
1270 $theme = wp_get_theme(); |
|
1271 $response->add_link( 'https://api.w.org/active-theme', rest_url( 'wp/v2/themes/' . $theme->get_stylesheet() ) ); |
|
1272 } |
|
1273 } |
|
1274 |
|
1275 /** |
|
1276 * Exposes the site logo through the WordPress REST API. |
|
1277 * This is used for fetching this information when user has no rights |
|
1278 * to update settings. |
|
1279 * |
|
1280 * @since 5.8.0 |
|
1281 * |
|
1282 * @param WP_REST_Response $response REST API response. |
|
1283 */ |
|
1284 protected function add_site_logo_to_index( WP_REST_Response $response ) { |
|
1285 $site_logo_id = get_theme_mod( 'custom_logo' ); |
|
1286 $response->data['site_logo'] = $site_logo_id; |
|
1287 if ( $site_logo_id ) { |
|
1288 $response->add_link( |
|
1289 'https://api.w.org/featuredmedia', |
|
1290 rest_url( 'wp/v2/media/' . $site_logo_id ), |
|
1291 array( |
|
1292 'embeddable' => true, |
|
1293 ) |
|
1294 ); |
|
1295 } |
1162 } |
1296 } |
1163 |
1297 |
1164 /** |
1298 /** |
1165 * Retrieves the index for a namespace. |
1299 * Retrieves the index for a namespace. |
1166 * |
1300 * |
1337 // No methods supported, hide the route. |
1458 // No methods supported, hide the route. |
1338 return null; |
1459 return null; |
1339 } |
1460 } |
1340 |
1461 |
1341 return $data; |
1462 return $data; |
|
1463 } |
|
1464 |
|
1465 /** |
|
1466 * Gets the maximum number of requests that can be included in a batch. |
|
1467 * |
|
1468 * @since 5.6.0 |
|
1469 * |
|
1470 * @return int The maximum requests. |
|
1471 */ |
|
1472 protected function get_max_batch_size() { |
|
1473 /** |
|
1474 * Filters the maximum number of REST API requests that can be included in a batch. |
|
1475 * |
|
1476 * @since 5.6.0 |
|
1477 * |
|
1478 * @param int $max_size The maximum size. |
|
1479 */ |
|
1480 return apply_filters( 'rest_get_max_batch_size', 25 ); |
|
1481 } |
|
1482 |
|
1483 /** |
|
1484 * Serves the batch/v1 request. |
|
1485 * |
|
1486 * @since 5.6.0 |
|
1487 * |
|
1488 * @param WP_REST_Request $batch_request The batch request object. |
|
1489 * @return WP_REST_Response The generated response object. |
|
1490 */ |
|
1491 public function serve_batch_request_v1( WP_REST_Request $batch_request ) { |
|
1492 $requests = array(); |
|
1493 |
|
1494 foreach ( $batch_request['requests'] as $args ) { |
|
1495 $parsed_url = wp_parse_url( $args['path'] ); |
|
1496 |
|
1497 if ( false === $parsed_url ) { |
|
1498 $requests[] = new WP_Error( 'parse_path_failed', __( 'Could not parse the path.' ), array( 'status' => 400 ) ); |
|
1499 |
|
1500 continue; |
|
1501 } |
|
1502 |
|
1503 $single_request = new WP_REST_Request( isset( $args['method'] ) ? $args['method'] : 'POST', $parsed_url['path'] ); |
|
1504 |
|
1505 if ( ! empty( $parsed_url['query'] ) ) { |
|
1506 $query_args = null; // Satisfy linter. |
|
1507 wp_parse_str( $parsed_url['query'], $query_args ); |
|
1508 $single_request->set_query_params( $query_args ); |
|
1509 } |
|
1510 |
|
1511 if ( ! empty( $args['body'] ) ) { |
|
1512 $single_request->set_body_params( $args['body'] ); |
|
1513 } |
|
1514 |
|
1515 if ( ! empty( $args['headers'] ) ) { |
|
1516 $single_request->set_headers( $args['headers'] ); |
|
1517 } |
|
1518 |
|
1519 $requests[] = $single_request; |
|
1520 } |
|
1521 |
|
1522 $matches = array(); |
|
1523 $validation = array(); |
|
1524 $has_error = false; |
|
1525 |
|
1526 foreach ( $requests as $single_request ) { |
|
1527 $match = $this->match_request_to_handler( $single_request ); |
|
1528 $matches[] = $match; |
|
1529 $error = null; |
|
1530 |
|
1531 if ( is_wp_error( $match ) ) { |
|
1532 $error = $match; |
|
1533 } |
|
1534 |
|
1535 if ( ! $error ) { |
|
1536 list( $route, $handler ) = $match; |
|
1537 |
|
1538 if ( isset( $handler['allow_batch'] ) ) { |
|
1539 $allow_batch = $handler['allow_batch']; |
|
1540 } else { |
|
1541 $route_options = $this->get_route_options( $route ); |
|
1542 $allow_batch = isset( $route_options['allow_batch'] ) ? $route_options['allow_batch'] : false; |
|
1543 } |
|
1544 |
|
1545 if ( ! is_array( $allow_batch ) || empty( $allow_batch['v1'] ) ) { |
|
1546 $error = new WP_Error( |
|
1547 'rest_batch_not_allowed', |
|
1548 __( 'The requested route does not support batch requests.' ), |
|
1549 array( 'status' => 400 ) |
|
1550 ); |
|
1551 } |
|
1552 } |
|
1553 |
|
1554 if ( ! $error ) { |
|
1555 $check_required = $single_request->has_valid_params(); |
|
1556 if ( is_wp_error( $check_required ) ) { |
|
1557 $error = $check_required; |
|
1558 } |
|
1559 } |
|
1560 |
|
1561 if ( ! $error ) { |
|
1562 $check_sanitized = $single_request->sanitize_params(); |
|
1563 if ( is_wp_error( $check_sanitized ) ) { |
|
1564 $error = $check_sanitized; |
|
1565 } |
|
1566 } |
|
1567 |
|
1568 if ( $error ) { |
|
1569 $has_error = true; |
|
1570 $validation[] = $error; |
|
1571 } else { |
|
1572 $validation[] = true; |
|
1573 } |
|
1574 } |
|
1575 |
|
1576 $responses = array(); |
|
1577 |
|
1578 if ( $has_error && 'require-all-validate' === $batch_request['validation'] ) { |
|
1579 foreach ( $validation as $valid ) { |
|
1580 if ( is_wp_error( $valid ) ) { |
|
1581 $responses[] = $this->envelope_response( $this->error_to_response( $valid ), false )->get_data(); |
|
1582 } else { |
|
1583 $responses[] = null; |
|
1584 } |
|
1585 } |
|
1586 |
|
1587 return new WP_REST_Response( |
|
1588 array( |
|
1589 'failed' => 'validation', |
|
1590 'responses' => $responses, |
|
1591 ), |
|
1592 WP_Http::MULTI_STATUS |
|
1593 ); |
|
1594 } |
|
1595 |
|
1596 foreach ( $requests as $i => $single_request ) { |
|
1597 $clean_request = clone $single_request; |
|
1598 $clean_request->set_url_params( array() ); |
|
1599 $clean_request->set_attributes( array() ); |
|
1600 $clean_request->set_default_params( array() ); |
|
1601 |
|
1602 /** This filter is documented in wp-includes/rest-api/class-wp-rest-server.php */ |
|
1603 $result = apply_filters( 'rest_pre_dispatch', null, $this, $clean_request ); |
|
1604 |
|
1605 if ( empty( $result ) ) { |
|
1606 $match = $matches[ $i ]; |
|
1607 $error = null; |
|
1608 |
|
1609 if ( is_wp_error( $validation[ $i ] ) ) { |
|
1610 $error = $validation[ $i ]; |
|
1611 } |
|
1612 |
|
1613 if ( is_wp_error( $match ) ) { |
|
1614 $result = $this->error_to_response( $match ); |
|
1615 } else { |
|
1616 list( $route, $handler ) = $match; |
|
1617 |
|
1618 if ( ! $error && ! is_callable( $handler['callback'] ) ) { |
|
1619 $error = new WP_Error( |
|
1620 'rest_invalid_handler', |
|
1621 __( 'The handler for the route is invalid' ), |
|
1622 array( 'status' => 500 ) |
|
1623 ); |
|
1624 } |
|
1625 |
|
1626 $result = $this->respond_to_request( $single_request, $route, $handler, $error ); |
|
1627 } |
|
1628 } |
|
1629 |
|
1630 /** This filter is documented in wp-includes/rest-api/class-wp-rest-server.php */ |
|
1631 $result = apply_filters( 'rest_post_dispatch', rest_ensure_response( $result ), $this, $single_request ); |
|
1632 |
|
1633 $responses[] = $this->envelope_response( $result, false )->get_data(); |
|
1634 } |
|
1635 |
|
1636 return new WP_REST_Response( array( 'responses' => $responses ), WP_Http::MULTI_STATUS ); |
1342 } |
1637 } |
1343 |
1638 |
1344 /** |
1639 /** |
1345 * Sends an HTTP status code. |
1640 * Sends an HTTP status code. |
1346 * |
1641 * |