15 define( 'REST_API_VERSION', '2.0' ); |
15 define( 'REST_API_VERSION', '2.0' ); |
16 |
16 |
17 /** |
17 /** |
18 * Registers a REST API route. |
18 * Registers a REST API route. |
19 * |
19 * |
20 * @since 4.4.0 |
20 * Note: Do not use before the {@see 'rest_api_init'} hook. |
|
21 * |
|
22 * @since 4.4.0 |
|
23 * @since 5.1.0 Added a _doing_it_wrong() notice when not called on or after the rest_api_init hook. |
21 * |
24 * |
22 * @param string $namespace The first URL segment after core prefix. Should be unique to your package/plugin. |
25 * @param string $namespace The first URL segment after core prefix. Should be unique to your package/plugin. |
23 * @param string $route The base URL for route you are adding. |
26 * @param string $route The base URL for route you are adding. |
24 * @param array $args Optional. Either an array of options for the endpoint, or an array of arrays for |
27 * @param array $args Optional. Either an array of options for the endpoint, or an array of arrays for |
25 * multiple methods. Default empty array. |
28 * multiple methods. Default empty array. |
34 * and namespace indexes. If you really need to register a |
37 * and namespace indexes. If you really need to register a |
35 * non-namespaced route, call `WP_REST_Server::register_route` directly. |
38 * non-namespaced route, call `WP_REST_Server::register_route` directly. |
36 */ |
39 */ |
37 _doing_it_wrong( 'register_rest_route', __( 'Routes must be namespaced with plugin or theme name and version.' ), '4.4.0' ); |
40 _doing_it_wrong( 'register_rest_route', __( 'Routes must be namespaced with plugin or theme name and version.' ), '4.4.0' ); |
38 return false; |
41 return false; |
39 } else if ( empty( $route ) ) { |
42 } elseif ( empty( $route ) ) { |
40 _doing_it_wrong( 'register_rest_route', __( 'Route must be specified.' ), '4.4.0' ); |
43 _doing_it_wrong( 'register_rest_route', __( 'Route must be specified.' ), '4.4.0' ); |
41 return false; |
44 return false; |
|
45 } |
|
46 |
|
47 if ( ! did_action( 'rest_api_init' ) ) { |
|
48 _doing_it_wrong( |
|
49 'register_rest_route', |
|
50 sprintf( |
|
51 /* translators: %s: rest_api_init */ |
|
52 __( 'REST API routes must be registered on the %s action.' ), |
|
53 '<code>rest_api_init</code>' |
|
54 ), |
|
55 '5.1.0' |
|
56 ); |
42 } |
57 } |
43 |
58 |
44 if ( isset( $args['args'] ) ) { |
59 if ( isset( $args['args'] ) ) { |
45 $common_args = $args['args']; |
60 $common_args = $args['args']; |
46 unset( $args['args'] ); |
61 unset( $args['args'] ); |
52 // Upgrade a single set to multiple. |
67 // Upgrade a single set to multiple. |
53 $args = array( $args ); |
68 $args = array( $args ); |
54 } |
69 } |
55 |
70 |
56 $defaults = array( |
71 $defaults = array( |
57 'methods' => 'GET', |
72 'methods' => 'GET', |
58 'callback' => null, |
73 'callback' => null, |
59 'args' => array(), |
74 'args' => array(), |
60 ); |
75 ); |
61 foreach ( $args as $key => &$arg_group ) { |
76 foreach ( $args as $key => &$arg_group ) { |
62 if ( ! is_numeric( $key ) ) { |
77 if ( ! is_numeric( $key ) ) { |
63 // Route option, skip here. |
78 // Route option, skip here. |
64 continue; |
79 continue; |
65 } |
80 } |
66 |
81 |
67 $arg_group = array_merge( $defaults, $arg_group ); |
82 $arg_group = array_merge( $defaults, $arg_group ); |
68 $arg_group['args'] = array_merge( $common_args, $arg_group['args'] ); |
83 $arg_group['args'] = array_merge( $common_args, $arg_group['args'] ); |
69 } |
84 } |
70 |
85 |
71 $full_route = '/' . trim( $namespace, '/' ) . '/' . trim( $route, '/' ); |
86 $full_route = '/' . trim( $namespace, '/' ) . '/' . trim( $route, '/' ); |
72 rest_get_server()->register_route( $namespace, $full_route, $args, $override ); |
87 rest_get_server()->register_route( $namespace, $full_route, $args, $override ); |
139 * @global WP_Rewrite $wp_rewrite |
154 * @global WP_Rewrite $wp_rewrite |
140 */ |
155 */ |
141 function rest_api_register_rewrites() { |
156 function rest_api_register_rewrites() { |
142 global $wp_rewrite; |
157 global $wp_rewrite; |
143 |
158 |
144 add_rewrite_rule( '^' . rest_get_url_prefix() . '/?$','index.php?rest_route=/','top' ); |
159 add_rewrite_rule( '^' . rest_get_url_prefix() . '/?$', 'index.php?rest_route=/', 'top' ); |
145 add_rewrite_rule( '^' . rest_get_url_prefix() . '/(.*)?','index.php?rest_route=/$matches[1]','top' ); |
160 add_rewrite_rule( '^' . rest_get_url_prefix() . '/(.*)?', 'index.php?rest_route=/$matches[1]', 'top' ); |
146 add_rewrite_rule( '^' . $wp_rewrite->index . '/' . rest_get_url_prefix() . '/?$','index.php?rest_route=/','top' ); |
161 add_rewrite_rule( '^' . $wp_rewrite->index . '/' . rest_get_url_prefix() . '/?$', 'index.php?rest_route=/', 'top' ); |
147 add_rewrite_rule( '^' . $wp_rewrite->index . '/' . rest_get_url_prefix() . '/(.*)?','index.php?rest_route=/$matches[1]','top' ); |
162 add_rewrite_rule( '^' . $wp_rewrite->index . '/' . rest_get_url_prefix() . '/(.*)?', 'index.php?rest_route=/$matches[1]', 'top' ); |
148 } |
163 } |
149 |
164 |
150 /** |
165 /** |
151 * Registers the default REST API filters. |
166 * Registers the default REST API filters. |
152 * |
167 * |
191 |
206 |
192 if ( post_type_supports( $post_type->name, 'revisions' ) ) { |
207 if ( post_type_supports( $post_type->name, 'revisions' ) ) { |
193 $revisions_controller = new WP_REST_Revisions_Controller( $post_type->name ); |
208 $revisions_controller = new WP_REST_Revisions_Controller( $post_type->name ); |
194 $revisions_controller->register_routes(); |
209 $revisions_controller->register_routes(); |
195 } |
210 } |
|
211 |
|
212 if ( 'attachment' !== $post_type->name ) { |
|
213 $autosaves_controller = new WP_REST_Autosaves_Controller( $post_type->name ); |
|
214 $autosaves_controller->register_routes(); |
|
215 } |
196 } |
216 } |
197 |
217 |
198 // Post types. |
218 // Post types. |
199 $controller = new WP_REST_Post_Types_Controller; |
219 $controller = new WP_REST_Post_Types_Controller; |
200 $controller->register_routes(); |
220 $controller->register_routes(); |
228 |
248 |
229 // Comments. |
249 // Comments. |
230 $controller = new WP_REST_Comments_Controller; |
250 $controller = new WP_REST_Comments_Controller; |
231 $controller->register_routes(); |
251 $controller->register_routes(); |
232 |
252 |
|
253 /** |
|
254 * Filters the search handlers to use in the REST search controller. |
|
255 * |
|
256 * @since 5.0.0 |
|
257 * |
|
258 * @param array $search_handlers List of search handlers to use in the controller. Each search |
|
259 * handler instance must extend the `WP_REST_Search_Handler` class. |
|
260 * Default is only a handler for posts. |
|
261 */ |
|
262 $search_handlers = apply_filters( 'wp_rest_search_handlers', array( new WP_REST_Post_Search_Handler() ) ); |
|
263 |
|
264 $controller = new WP_REST_Search_Controller( $search_handlers ); |
|
265 $controller->register_routes(); |
|
266 |
|
267 // Block Renderer. |
|
268 $controller = new WP_REST_Block_Renderer_Controller; |
|
269 $controller->register_routes(); |
|
270 |
233 // Settings. |
271 // Settings. |
234 $controller = new WP_REST_Settings_Controller; |
272 $controller = new WP_REST_Settings_Controller; |
235 $controller->register_routes(); |
273 $controller->register_routes(); |
|
274 |
|
275 // Themes. |
|
276 $controller = new WP_REST_Themes_Controller; |
|
277 $controller->register_routes(); |
|
278 |
236 } |
279 } |
237 |
280 |
238 /** |
281 /** |
239 * Loads the REST API. |
282 * Loads the REST API. |
240 * |
283 * |
305 function get_rest_url( $blog_id = null, $path = '/', $scheme = 'rest' ) { |
348 function get_rest_url( $blog_id = null, $path = '/', $scheme = 'rest' ) { |
306 if ( empty( $path ) ) { |
349 if ( empty( $path ) ) { |
307 $path = '/'; |
350 $path = '/'; |
308 } |
351 } |
309 |
352 |
|
353 $path = '/' . ltrim( $path, '/' ); |
|
354 |
310 if ( is_multisite() && get_blog_option( $blog_id, 'permalink_structure' ) || get_option( 'permalink_structure' ) ) { |
355 if ( is_multisite() && get_blog_option( $blog_id, 'permalink_structure' ) || get_option( 'permalink_structure' ) ) { |
311 global $wp_rewrite; |
356 global $wp_rewrite; |
312 |
357 |
313 if ( $wp_rewrite->using_index_permalinks() ) { |
358 if ( $wp_rewrite->using_index_permalinks() ) { |
314 $url = get_home_url( $blog_id, $wp_rewrite->index . '/' . rest_get_url_prefix(), $scheme ); |
359 $url = get_home_url( $blog_id, $wp_rewrite->index . '/' . rest_get_url_prefix(), $scheme ); |
315 } else { |
360 } else { |
316 $url = get_home_url( $blog_id, rest_get_url_prefix(), $scheme ); |
361 $url = get_home_url( $blog_id, rest_get_url_prefix(), $scheme ); |
317 } |
362 } |
318 |
363 |
319 $url .= '/' . ltrim( $path, '/' ); |
364 $url .= $path; |
320 } else { |
365 } else { |
321 $url = trailingslashit( get_home_url( $blog_id, '', $scheme ) ); |
366 $url = trailingslashit( get_home_url( $blog_id, '', $scheme ) ); |
322 // nginx only allows HTTP/1.0 methods when redirecting from / to /index.php |
367 // nginx only allows HTTP/1.0 methods when redirecting from / to /index.php |
323 // To work around this, we manually add index.php to the URL, avoiding the redirect. |
368 // To work around this, we manually add index.php to the URL, avoiding the redirect. |
324 if ( 'index.php' !== substr( $url, 9 ) ) { |
369 if ( 'index.php' !== substr( $url, 9 ) ) { |
325 $url .= 'index.php'; |
370 $url .= 'index.php'; |
326 } |
371 } |
327 |
|
328 $path = '/' . ltrim( $path, '/' ); |
|
329 |
372 |
330 $url = add_query_arg( 'rest_route', $path, $url ); |
373 $url = add_query_arg( 'rest_route', $path, $url ); |
331 } |
374 } |
332 |
375 |
333 if ( is_ssl() ) { |
376 if ( is_ssl() ) { |
415 * @since 4.4.0 |
458 * @since 4.4.0 |
416 * |
459 * |
417 * @param string $class_name The name of the server class. Default 'WP_REST_Server'. |
460 * @param string $class_name The name of the server class. Default 'WP_REST_Server'. |
418 */ |
461 */ |
419 $wp_rest_server_class = apply_filters( 'wp_rest_server_class', 'WP_REST_Server' ); |
462 $wp_rest_server_class = apply_filters( 'wp_rest_server_class', 'WP_REST_Server' ); |
420 $wp_rest_server = new $wp_rest_server_class; |
463 $wp_rest_server = new $wp_rest_server_class; |
421 |
464 |
422 /** |
465 /** |
423 * Fires when preparing to serve an API request. |
466 * Fires when preparing to serve an API request. |
424 * |
467 * |
425 * Endpoint objects should be created and register their hooks on this action rather |
468 * Endpoint objects should be created and register their hooks on this action rather |
567 if ( ! empty( $response ) || $request->get_method() !== 'OPTIONS' ) { |
610 if ( ! empty( $response ) || $request->get_method() !== 'OPTIONS' ) { |
568 return $response; |
611 return $response; |
569 } |
612 } |
570 |
613 |
571 $response = new WP_REST_Response(); |
614 $response = new WP_REST_Response(); |
572 $data = array(); |
615 $data = array(); |
573 |
616 |
574 foreach ( $handler->get_routes() as $route => $endpoints ) { |
617 foreach ( $handler->get_routes() as $route => $endpoints ) { |
575 $match = preg_match( '@^' . $route . '$@i', $request->get_route() ); |
618 $match = preg_match( '@^' . $route . '$@i', $request->get_route(), $matches ); |
576 |
619 |
577 if ( ! $match ) { |
620 if ( ! $match ) { |
578 continue; |
621 continue; |
|
622 } |
|
623 |
|
624 $args = array(); |
|
625 foreach ( $matches as $param => $value ) { |
|
626 if ( ! is_int( $param ) ) { |
|
627 $args[ $param ] = $value; |
|
628 } |
|
629 } |
|
630 |
|
631 foreach ( $endpoints as $endpoint ) { |
|
632 // Remove the redundant preg_match argument. |
|
633 unset( $args[0] ); |
|
634 |
|
635 $request->set_url_params( $args ); |
|
636 $request->set_attributes( $endpoint ); |
579 } |
637 } |
580 |
638 |
581 $data = $handler->get_data_for_route( $route, $endpoints, 'help' ); |
639 $data = $handler->get_data_for_route( $route, $endpoints, 'help' ); |
582 $response->set_matched_route( $route ); |
640 $response->set_matched_route( $route ); |
583 break; |
641 break; |
865 // *local* date without a timezone offset) or a UTC date (otherwise). |
923 // *local* date without a timezone offset) or a UTC date (otherwise). |
866 // Timezone conversion needs to be handled differently between these two |
924 // Timezone conversion needs to be handled differently between these two |
867 // cases. |
925 // cases. |
868 if ( ! $is_utc && ! $has_timezone ) { |
926 if ( ! $is_utc && ! $has_timezone ) { |
869 $local = date( 'Y-m-d H:i:s', $date ); |
927 $local = date( 'Y-m-d H:i:s', $date ); |
870 $utc = get_gmt_from_date( $local ); |
928 $utc = get_gmt_from_date( $local ); |
871 } else { |
929 } else { |
872 $utc = date( 'Y-m-d H:i:s', $date ); |
930 $utc = date( 'Y-m-d H:i:s', $date ); |
873 $local = get_date_from_gmt( $utc ); |
931 $local = get_date_from_gmt( $utc ); |
874 } |
932 } |
875 |
933 |
876 return array( $local, $utc ); |
934 return array( $local, $utc ); |
877 } |
935 } |
980 * @param bool|string|int $value The value being evaluated. |
1038 * @param bool|string|int $value The value being evaluated. |
981 * @return boolean Returns the proper associated boolean value. |
1039 * @return boolean Returns the proper associated boolean value. |
982 */ |
1040 */ |
983 function rest_sanitize_boolean( $value ) { |
1041 function rest_sanitize_boolean( $value ) { |
984 // String values are translated to `true`; make sure 'false' is false. |
1042 // String values are translated to `true`; make sure 'false' is false. |
985 if ( is_string( $value ) ) { |
1043 if ( is_string( $value ) ) { |
986 $value = strtolower( $value ); |
1044 $value = strtolower( $value ); |
987 if ( in_array( $value, array( 'false', '0' ), true ) ) { |
1045 if ( in_array( $value, array( 'false', '0' ), true ) ) { |
988 $value = false; |
1046 $value = false; |
989 } |
1047 } |
990 } |
1048 } |
991 |
1049 |
992 // Everything else will map nicely to boolean. |
1050 // Everything else will map nicely to boolean. |
993 return (boolean) $value; |
1051 return (bool) $value; |
994 } |
1052 } |
995 |
1053 |
996 /** |
1054 /** |
997 * Determines if a given value is boolean-like. |
1055 * Determines if a given value is boolean-like. |
998 * |
1056 * |
1079 * @param string $param The parameter name, used in error messages. |
1137 * @param string $param The parameter name, used in error messages. |
1080 * @return true|WP_Error |
1138 * @return true|WP_Error |
1081 */ |
1139 */ |
1082 function rest_validate_value_from_schema( $value, $args, $param = '' ) { |
1140 function rest_validate_value_from_schema( $value, $args, $param = '' ) { |
1083 if ( 'array' === $args['type'] ) { |
1141 if ( 'array' === $args['type'] ) { |
1084 if ( ! is_array( $value ) ) { |
1142 if ( ! is_null( $value ) ) { |
1085 $value = preg_split( '/[\s,]+/', $value ); |
1143 $value = wp_parse_list( $value ); |
1086 } |
1144 } |
1087 if ( ! wp_is_numeric_array( $value ) ) { |
1145 if ( ! wp_is_numeric_array( $value ) ) { |
1088 /* translators: 1: parameter, 2: type name */ |
1146 /* translators: 1: parameter, 2: type name */ |
1089 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %2$s.' ), $param, 'array' ) ); |
1147 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %2$s.' ), $param, 'array' ) ); |
1090 } |
1148 } |
1144 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %2$s.' ), $param, 'string' ) ); |
1202 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %2$s.' ), $param, 'string' ) ); |
1145 } |
1203 } |
1146 |
1204 |
1147 if ( isset( $args['format'] ) ) { |
1205 if ( isset( $args['format'] ) ) { |
1148 switch ( $args['format'] ) { |
1206 switch ( $args['format'] ) { |
1149 case 'date-time' : |
1207 case 'date-time': |
1150 if ( ! rest_parse_date( $value ) ) { |
1208 if ( ! rest_parse_date( $value ) ) { |
1151 return new WP_Error( 'rest_invalid_date', __( 'Invalid date.' ) ); |
1209 return new WP_Error( 'rest_invalid_date', __( 'Invalid date.' ) ); |
1152 } |
1210 } |
1153 break; |
1211 break; |
1154 |
1212 |
1155 case 'email' : |
1213 case 'email': |
1156 if ( ! is_email( $value ) ) { |
1214 if ( ! is_email( $value ) ) { |
1157 return new WP_Error( 'rest_invalid_email', __( 'Invalid email address.' ) ); |
1215 return new WP_Error( 'rest_invalid_email', __( 'Invalid email address.' ) ); |
1158 } |
1216 } |
1159 break; |
1217 break; |
1160 case 'ip' : |
1218 case 'ip': |
1161 if ( ! rest_is_ip_address( $value ) ) { |
1219 if ( ! rest_is_ip_address( $value ) ) { |
1162 /* translators: %s: IP address */ |
1220 /* translators: %s: IP address */ |
1163 return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not a valid IP address.' ), $value ) ); |
1221 return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not a valid IP address.' ), $value ) ); |
1164 } |
1222 } |
1165 break; |
1223 break; |
1223 function rest_sanitize_value_from_schema( $value, $args ) { |
1281 function rest_sanitize_value_from_schema( $value, $args ) { |
1224 if ( 'array' === $args['type'] ) { |
1282 if ( 'array' === $args['type'] ) { |
1225 if ( empty( $args['items'] ) ) { |
1283 if ( empty( $args['items'] ) ) { |
1226 return (array) $value; |
1284 return (array) $value; |
1227 } |
1285 } |
1228 if ( ! is_array( $value ) ) { |
1286 $value = wp_parse_list( $value ); |
1229 $value = preg_split( '/[\s,]+/', $value ); |
|
1230 } |
|
1231 foreach ( $value as $index => $v ) { |
1287 foreach ( $value as $index => $v ) { |
1232 $value[ $index ] = rest_sanitize_value_from_schema( $v, $args['items'] ); |
1288 $value[ $index ] = rest_sanitize_value_from_schema( $v, $args['items'] ); |
1233 } |
1289 } |
1234 // Normalize to numeric array so nothing unexpected |
1290 // Normalize to numeric array so nothing unexpected |
1235 // is in the keys. |
1291 // is in the keys. |
1268 return rest_sanitize_boolean( $value ); |
1324 return rest_sanitize_boolean( $value ); |
1269 } |
1325 } |
1270 |
1326 |
1271 if ( isset( $args['format'] ) ) { |
1327 if ( isset( $args['format'] ) ) { |
1272 switch ( $args['format'] ) { |
1328 switch ( $args['format'] ) { |
1273 case 'date-time' : |
1329 case 'date-time': |
1274 return sanitize_text_field( $value ); |
1330 return sanitize_text_field( $value ); |
1275 |
1331 |
1276 case 'email' : |
1332 case 'email': |
1277 /* |
1333 /* |
1278 * sanitize_email() validates, which would be unexpected. |
1334 * sanitize_email() validates, which would be unexpected. |
1279 */ |
1335 */ |
1280 return sanitize_text_field( $value ); |
1336 return sanitize_text_field( $value ); |
1281 |
1337 |
1282 case 'uri' : |
1338 case 'uri': |
1283 return esc_url_raw( $value ); |
1339 return esc_url_raw( $value ); |
1284 |
1340 |
1285 case 'ip' : |
1341 case 'ip': |
1286 return sanitize_text_field( $value ); |
1342 return sanitize_text_field( $value ); |
1287 } |
1343 } |
1288 } |
1344 } |
1289 |
1345 |
1290 if ( 'string' === $args['type'] ) { |
1346 if ( 'string' === $args['type'] ) { |
1291 return strval( $value ); |
1347 return strval( $value ); |
1292 } |
1348 } |
1293 |
1349 |
1294 return $value; |
1350 return $value; |
1295 } |
1351 } |
|
1352 |
|
1353 /** |
|
1354 * Append result of internal request to REST API for purpose of preloading data to be attached to a page. |
|
1355 * Expected to be called in the context of `array_reduce`. |
|
1356 * |
|
1357 * @since 5.0.0 |
|
1358 * |
|
1359 * @param array $memo Reduce accumulator. |
|
1360 * @param string $path REST API path to preload. |
|
1361 * @return array Modified reduce accumulator. |
|
1362 */ |
|
1363 function rest_preload_api_request( $memo, $path ) { |
|
1364 // array_reduce() doesn't support passing an array in PHP 5.2, so we need to make sure we start with one. |
|
1365 if ( ! is_array( $memo ) ) { |
|
1366 $memo = array(); |
|
1367 } |
|
1368 |
|
1369 if ( empty( $path ) ) { |
|
1370 return $memo; |
|
1371 } |
|
1372 |
|
1373 $method = 'GET'; |
|
1374 if ( is_array( $path ) && 2 === count( $path ) ) { |
|
1375 $method = end( $path ); |
|
1376 $path = reset( $path ); |
|
1377 |
|
1378 if ( ! in_array( $method, array( 'GET', 'OPTIONS' ), true ) ) { |
|
1379 $method = 'GET'; |
|
1380 } |
|
1381 } |
|
1382 |
|
1383 $path_parts = parse_url( $path ); |
|
1384 if ( false === $path_parts ) { |
|
1385 return $memo; |
|
1386 } |
|
1387 |
|
1388 $request = new WP_REST_Request( $method, $path_parts['path'] ); |
|
1389 if ( ! empty( $path_parts['query'] ) ) { |
|
1390 parse_str( $path_parts['query'], $query_params ); |
|
1391 $request->set_query_params( $query_params ); |
|
1392 } |
|
1393 |
|
1394 $response = rest_do_request( $request ); |
|
1395 if ( 200 === $response->status ) { |
|
1396 $server = rest_get_server(); |
|
1397 $data = (array) $response->get_data(); |
|
1398 $links = $server->get_compact_response_links( $response ); |
|
1399 if ( ! empty( $links ) ) { |
|
1400 $data['_links'] = $links; |
|
1401 } |
|
1402 |
|
1403 if ( 'OPTIONS' === $method ) { |
|
1404 $response = rest_send_allow_header( $response, $server, $request ); |
|
1405 |
|
1406 $memo[ $method ][ $path ] = array( |
|
1407 'body' => $data, |
|
1408 'headers' => $response->headers, |
|
1409 ); |
|
1410 } else { |
|
1411 $memo[ $path ] = array( |
|
1412 'body' => $data, |
|
1413 'headers' => $response->headers, |
|
1414 ); |
|
1415 } |
|
1416 } |
|
1417 |
|
1418 return $memo; |
|
1419 } |