wp/wp-includes/rest-api/class-wp-rest-server.php
changeset 9 177826044cd9
parent 7 cf61fcea0001
child 16 a86126ab1dd4
equal deleted inserted replaced
8:c7c34916027a 9:177826044cd9
    86 	public function __construct() {
    86 	public function __construct() {
    87 		$this->endpoints = array(
    87 		$this->endpoints = array(
    88 			// Meta endpoints.
    88 			// Meta endpoints.
    89 			'/' => array(
    89 			'/' => array(
    90 				'callback' => array( $this, 'get_index' ),
    90 				'callback' => array( $this, 'get_index' ),
    91 				'methods' => 'GET',
    91 				'methods'  => 'GET',
    92 				'args' => array(
    92 				'args'     => array(
    93 					'context' => array(
    93 					'context' => array(
    94 						'default' => 'view',
    94 						'default' => 'view',
    95 					),
    95 					),
    96 				),
    96 				),
    97 			),
    97 			),
   158 
   158 
   159 		$errors = array();
   159 		$errors = array();
   160 
   160 
   161 		foreach ( (array) $error->errors as $code => $messages ) {
   161 		foreach ( (array) $error->errors as $code => $messages ) {
   162 			foreach ( (array) $messages as $message ) {
   162 			foreach ( (array) $messages as $message ) {
   163 				$errors[] = array( 'code' => $code, 'message' => $message, 'data' => $error->get_error_data( $code ) );
   163 				$errors[] = array(
       
   164 					'code'    => $code,
       
   165 					'message' => $message,
       
   166 					'data'    => $error->get_error_data( $code ),
       
   167 				);
   164 			}
   168 			}
   165 		}
   169 		}
   166 
   170 
   167 		$data = $errors[0];
   171 		$data = $errors[0];
   168 		if ( count( $errors ) > 1 ) {
   172 		if ( count( $errors ) > 1 ) {
   258 		 * @since 4.4.0
   262 		 * @since 4.4.0
   259 		 * @deprecated 4.7.0 Use the rest_authentication_errors filter to restrict access to the API
   263 		 * @deprecated 4.7.0 Use the rest_authentication_errors filter to restrict access to the API
   260 		 *
   264 		 *
   261 		 * @param bool $rest_enabled Whether the REST API is enabled. Default true.
   265 		 * @param bool $rest_enabled Whether the REST API is enabled. Default true.
   262 		 */
   266 		 */
   263 		apply_filters_deprecated( 'rest_enabled', array( true ), '4.7.0', 'rest_authentication_errors',
   267 		apply_filters_deprecated(
       
   268 			'rest_enabled',
       
   269 			array( true ),
       
   270 			'4.7.0',
       
   271 			'rest_authentication_errors',
   264 			__( 'The REST API can no longer be completely disabled, the rest_authentication_errors filter can be used to restrict access to the API, instead.' )
   272 			__( 'The REST API can no longer be completely disabled, the rest_authentication_errors filter can be used to restrict access to the API, instead.' )
   265 		);
   273 		);
   266 
   274 
   267 		/**
   275 		/**
   268 		 * Filters whether jsonp is enabled.
   276 		 * Filters whether jsonp is enabled.
   396 			$result = wp_json_encode( $result );
   404 			$result = wp_json_encode( $result );
   397 
   405 
   398 			$json_error_message = $this->get_json_last_error();
   406 			$json_error_message = $this->get_json_last_error();
   399 			if ( $json_error_message ) {
   407 			if ( $json_error_message ) {
   400 				$json_error_obj = new WP_Error( 'rest_encode_error', $json_error_message, array( 'status' => 500 ) );
   408 				$json_error_obj = new WP_Error( 'rest_encode_error', $json_error_message, array( 'status' => 500 ) );
   401 				$result = $this->error_to_response( $json_error_obj );
   409 				$result         = $this->error_to_response( $json_error_obj );
   402 				$result = wp_json_encode( $result->data[0] );
   410 				$result         = wp_json_encode( $result->data[0] );
   403 			}
   411 			}
   404 
   412 
   405 			if ( $jsonp_callback ) {
   413 			if ( $jsonp_callback ) {
   406 				// Prepend '/**/' to mitigate possible JSONP Flash attacks.
   414 				// Prepend '/**/' to mitigate possible JSONP Flash attacks.
   407 				// https://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/
   415 				// https://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/
   452 	 *
   460 	 *
   453 	 * Extracts the links from a response into a structured hash, suitable for
   461 	 * Extracts the links from a response into a structured hash, suitable for
   454 	 * direct output.
   462 	 * direct output.
   455 	 *
   463 	 *
   456 	 * @since 4.4.0
   464 	 * @since 4.4.0
   457 	 * @static
       
   458 	 *
   465 	 *
   459 	 * @param WP_REST_Response $response Response to extract links from.
   466 	 * @param WP_REST_Response $response Response to extract links from.
   460 	 * @return array Map of link relation to list of link hashes.
   467 	 * @return array Map of link relation to list of link hashes.
   461 	 */
   468 	 */
   462 	public static function get_response_links( $response ) {
   469 	public static function get_response_links( $response ) {
   469 		$data = array();
   476 		$data = array();
   470 		foreach ( $links as $rel => $items ) {
   477 		foreach ( $links as $rel => $items ) {
   471 			$data[ $rel ] = array();
   478 			$data[ $rel ] = array();
   472 
   479 
   473 			foreach ( $items as $item ) {
   480 			foreach ( $items as $item ) {
   474 				$attributes = $item['attributes'];
   481 				$attributes         = $item['attributes'];
   475 				$attributes['href'] = $item['href'];
   482 				$attributes['href'] = $item['href'];
   476 				$data[ $rel ][] = $attributes;
   483 				$data[ $rel ][]     = $attributes;
   477 			}
   484 			}
   478 		}
   485 		}
   479 
   486 
   480 		return $data;
   487 		return $data;
   481 	}
   488 	}
   485 	 *
   492 	 *
   486 	 * Extracts the links from a response into a structured hash, suitable for
   493 	 * Extracts the links from a response into a structured hash, suitable for
   487 	 * direct output.
   494 	 * direct output.
   488 	 *
   495 	 *
   489 	 * @since 4.5.0
   496 	 * @since 4.5.0
   490 	 * @static
       
   491 	 *
   497 	 *
   492 	 * @param WP_REST_Response $response Response to extract links from.
   498 	 * @param WP_REST_Response $response Response to extract links from.
   493 	 * @return array Map of link relation to list of link hashes.
   499 	 * @return array Map of link relation to list of link hashes.
   494 	 */
   500 	 */
   495 	public static function get_compact_response_links( $response ) {
   501 	public static function get_compact_response_links( $response ) {
   497 
   503 
   498 		if ( empty( $links ) ) {
   504 		if ( empty( $links ) ) {
   499 			return array();
   505 			return array();
   500 		}
   506 		}
   501 
   507 
   502 		$curies = $response->get_curies();
   508 		$curies      = $response->get_curies();
   503 		$used_curies = array();
   509 		$used_curies = array();
   504 
   510 
   505 		foreach ( $links as $rel => $items ) {
   511 		foreach ( $links as $rel => $items ) {
   506 
   512 
   507 			// Convert $rel URIs to their compact versions if they exist.
   513 			// Convert $rel URIs to their compact versions if they exist.
   513 
   519 
   514 				// Relation now changes from '$uri' to '$curie:$relation'.
   520 				// Relation now changes from '$uri' to '$curie:$relation'.
   515 				$rel_regex = str_replace( '\{rel\}', '(.+)', preg_quote( $curie['href'], '!' ) );
   521 				$rel_regex = str_replace( '\{rel\}', '(.+)', preg_quote( $curie['href'], '!' ) );
   516 				preg_match( '!' . $rel_regex . '!', $rel, $matches );
   522 				preg_match( '!' . $rel_regex . '!', $rel, $matches );
   517 				if ( $matches ) {
   523 				if ( $matches ) {
   518 					$new_rel = $curie['name'] . ':' . $matches[1];
   524 					$new_rel                       = $curie['name'] . ':' . $matches[1];
   519 					$used_curies[ $curie['name'] ] = $curie;
   525 					$used_curies[ $curie['name'] ] = $curie;
   520 					$links[ $new_rel ] = $items;
   526 					$links[ $new_rel ]             = $items;
   521 					unset( $links[ $rel ] );
   527 					unset( $links[ $rel ] );
   522 					break;
   528 					break;
   523 				}
   529 				}
   524 			}
   530 			}
   525 		}
   531 		}
   650 	 */
   656 	 */
   651 	public function register_route( $namespace, $route, $route_args, $override = false ) {
   657 	public function register_route( $namespace, $route, $route_args, $override = false ) {
   652 		if ( ! isset( $this->namespaces[ $namespace ] ) ) {
   658 		if ( ! isset( $this->namespaces[ $namespace ] ) ) {
   653 			$this->namespaces[ $namespace ] = array();
   659 			$this->namespaces[ $namespace ] = array();
   654 
   660 
   655 			$this->register_route( $namespace, '/' . $namespace, array(
   661 			$this->register_route(
       
   662 				$namespace,
       
   663 				'/' . $namespace,
   656 				array(
   664 				array(
   657 					'methods' => self::READABLE,
   665 					array(
   658 					'callback' => array( $this, 'get_namespace_index' ),
   666 						'methods'  => self::READABLE,
   659 					'args' => array(
   667 						'callback' => array( $this, 'get_namespace_index' ),
   660 						'namespace' => array(
   668 						'args'     => array(
   661 							'default' => $namespace,
   669 							'namespace' => array(
   662 						),
   670 								'default' => $namespace,
   663 						'context' => array(
   671 							),
   664 							'default' => 'view',
   672 							'context'   => array(
       
   673 								'default' => 'view',
       
   674 							),
   665 						),
   675 						),
   666 					),
   676 					),
   667 				),
   677 				)
   668 			) );
   678 			);
   669 		}
   679 		}
   670 
   680 
   671 		// Associative to avoid double-registration.
   681 		// Associative to avoid double-registration.
   672 		$this->namespaces[ $namespace ][ $route ] = true;
   682 		$this->namespaces[ $namespace ][ $route ] = true;
   673 		$route_args['namespace'] = $namespace;
   683 		$route_args['namespace']                  = $namespace;
   674 
   684 
   675 		if ( $override || empty( $this->endpoints[ $route ] ) ) {
   685 		if ( $override || empty( $this->endpoints[ $route ] ) ) {
   676 			$this->endpoints[ $route ] = $route_args;
   686 			$this->endpoints[ $route ] = $route_args;
   677 		} else {
   687 		} else {
   678 			$this->endpoints[ $route ] = array_merge( $this->endpoints[ $route ], $route_args );
   688 			$this->endpoints[ $route ] = array_merge( $this->endpoints[ $route ], $route_args );
   754 				}
   764 				}
   755 
   765 
   756 				$handler['methods'] = array();
   766 				$handler['methods'] = array();
   757 
   767 
   758 				foreach ( $methods as $method ) {
   768 				foreach ( $methods as $method ) {
   759 					$method = strtoupper( trim( $method ) );
   769 					$method                        = strtoupper( trim( $method ) );
   760 					$handler['methods'][ $method ] = true;
   770 					$handler['methods'][ $method ] = true;
   761 				}
   771 				}
   762 			}
   772 			}
   763 		}
   773 		}
   764 
   774 
   836 					$args[ $param ] = $value;
   846 					$args[ $param ] = $value;
   837 				}
   847 				}
   838 			}
   848 			}
   839 
   849 
   840 			foreach ( $handlers as $handler ) {
   850 			foreach ( $handlers as $handler ) {
   841 				$callback  = $handler['callback'];
   851 				$callback = $handler['callback'];
   842 				$response = null;
   852 				$response = null;
   843 
   853 
   844 				// Fallback to GET method if no HEAD method is registered.
   854 				// Fallback to GET method if no HEAD method is registered.
   845 				$checked_method = $method;
   855 				$checked_method = $method;
   846 				if ( 'HEAD' === $method && empty( $handler['methods']['HEAD'] ) ) {
   856 				if ( 'HEAD' === $method && empty( $handler['methods']['HEAD'] ) ) {
   892 				 * Note that this filter will not be called for requests that
   902 				 * Note that this filter will not be called for requests that
   893 				 * fail to authenticate or match to a registered route.
   903 				 * fail to authenticate or match to a registered route.
   894 				 *
   904 				 *
   895 				 * @since 4.7.0
   905 				 * @since 4.7.0
   896 				 *
   906 				 *
   897 				 * @param WP_HTTP_Response $response Result to send to the client. Usually a WP_REST_Response.
   907 				 * @param WP_HTTP_Response|WP_Error $response Result to send to the client. Usually a WP_REST_Response or WP_Error.
   898 				 * @param WP_REST_Server   $handler  ResponseHandler instance (usually WP_REST_Server).
   908 				 * @param array                     $handler  Route handler used for the request.
   899 				 * @param WP_REST_Request  $request  Request used to generate the response.
   909 				 * @param WP_REST_Request           $request  Request used to generate the response.
   900 				 */
   910 				 */
   901 				$response = apply_filters( 'rest_request_before_callbacks', $response, $handler, $request );
   911 				$response = apply_filters( 'rest_request_before_callbacks', $response, $handler, $request );
   902 
   912 
   903 				if ( ! is_wp_error( $response ) ) {
   913 				if ( ! is_wp_error( $response ) ) {
   904 					// Check permission specified on the route.
   914 					// Check permission specified on the route.
   920 					 * Allow plugins to override dispatching the request.
   930 					 * Allow plugins to override dispatching the request.
   921 					 *
   931 					 *
   922 					 * @since 4.4.0
   932 					 * @since 4.4.0
   923 					 * @since 4.5.0 Added `$route` and `$handler` parameters.
   933 					 * @since 4.5.0 Added `$route` and `$handler` parameters.
   924 					 *
   934 					 *
   925 					 * @param bool            $dispatch_result Dispatch result, will be used if not empty.
   935 					 * @param mixed           $dispatch_result Dispatch result, will be used if not empty.
   926 					 * @param WP_REST_Request $request         Request used to generate the response.
   936 					 * @param WP_REST_Request $request         Request used to generate the response.
   927 					 * @param string          $route           Route matched for the request.
   937 					 * @param string          $route           Route matched for the request.
   928 					 * @param array           $handler         Route handler used for the request.
   938 					 * @param array           $handler         Route handler used for the request.
   929 					 */
   939 					 */
   930 					$dispatch_result = apply_filters( 'rest_dispatch_request', null, $request, $route, $handler );
   940 					$dispatch_result = apply_filters( 'rest_dispatch_request', null, $request, $route, $handler );
   951 				 * Note that an endpoint's `permission_callback` can still be
   961 				 * Note that an endpoint's `permission_callback` can still be
   952 				 * called after this filter - see `rest_send_allow_header()`.
   962 				 * called after this filter - see `rest_send_allow_header()`.
   953 				 *
   963 				 *
   954 				 * @since 4.7.0
   964 				 * @since 4.7.0
   955 				 *
   965 				 *
   956 				 * @param WP_HTTP_Response $response Result to send to the client. Usually a WP_REST_Response.
   966 				 * @param WP_HTTP_Response|WP_Error $response Result to send to the client. Usually a WP_REST_Response or WP_Error.
   957 				 * @param WP_REST_Server   $handler  ResponseHandler instance (usually WP_REST_Server).
   967 				 * @param array                     $handler  Route handler used for the request.
   958 				 * @param WP_REST_Request  $request  Request used to generate the response.
   968 				 * @param WP_REST_Request           $request  Request used to generate the response.
   959 				 */
   969 				 */
   960 				$response = apply_filters( 'rest_request_after_callbacks', $response, $handler, $request );
   970 				$response = apply_filters( 'rest_request_after_callbacks', $response, $handler, $request );
   961 
   971 
   962 				if ( is_wp_error( $response ) ) {
   972 				if ( is_wp_error( $response ) ) {
   963 					$response = $this->error_to_response( $response );
   973 					$response = $this->error_to_response( $response );
  1060 
  1070 
  1061 		if ( ! isset( $this->namespaces[ $namespace ] ) ) {
  1071 		if ( ! isset( $this->namespaces[ $namespace ] ) ) {
  1062 			return new WP_Error( 'rest_invalid_namespace', __( 'The specified namespace could not be found.' ), array( 'status' => 404 ) );
  1072 			return new WP_Error( 'rest_invalid_namespace', __( 'The specified namespace could not be found.' ), array( 'status' => 404 ) );
  1063 		}
  1073 		}
  1064 
  1074 
  1065 		$routes = $this->namespaces[ $namespace ];
  1075 		$routes    = $this->namespaces[ $namespace ];
  1066 		$endpoints = array_intersect_key( $this->get_routes(), $routes );
  1076 		$endpoints = array_intersect_key( $this->get_routes(), $routes );
  1067 
  1077 
  1068 		$data = array(
  1078 		$data     = array(
  1069 			'namespace' => $namespace,
  1079 			'namespace' => $namespace,
  1070 			'routes' => $this->get_data_for_routes( $endpoints, $request['context'] ),
  1080 			'routes'    => $this->get_data_for_routes( $endpoints, $request['context'] ),
  1071 		);
  1081 		);
  1072 		$response = rest_ensure_response( $data );
  1082 		$response = rest_ensure_response( $data );
  1073 
  1083 
  1074 		// Link to the root index.
  1084 		// Link to the root index.
  1075 		$response->add_link( 'up', rest_url( '/' ) );
  1085 		$response->add_link( 'up', rest_url( '/' ) );
  1143 	 * @return array|null Data for the route, or null if no publicly-visible data.
  1153 	 * @return array|null Data for the route, or null if no publicly-visible data.
  1144 	 */
  1154 	 */
  1145 	public function get_data_for_route( $route, $callbacks, $context = 'view' ) {
  1155 	public function get_data_for_route( $route, $callbacks, $context = 'view' ) {
  1146 		$data = array(
  1156 		$data = array(
  1147 			'namespace' => '',
  1157 			'namespace' => '',
  1148 			'methods' => array(),
  1158 			'methods'   => array(),
  1149 			'endpoints' => array(),
  1159 			'endpoints' => array(),
  1150 		);
  1160 		);
  1151 
  1161 
  1152 		if ( isset( $this->route_options[ $route ] ) ) {
  1162 		if ( isset( $this->route_options[ $route ] ) ) {
  1153 			$options = $this->route_options[ $route ];
  1163 			$options = $this->route_options[ $route ];
  1168 			if ( empty( $callback['show_in_index'] ) ) {
  1178 			if ( empty( $callback['show_in_index'] ) ) {
  1169 				continue;
  1179 				continue;
  1170 			}
  1180 			}
  1171 
  1181 
  1172 			$data['methods'] = array_merge( $data['methods'], array_keys( $callback['methods'] ) );
  1182 			$data['methods'] = array_merge( $data['methods'], array_keys( $callback['methods'] ) );
  1173 			$endpoint_data = array(
  1183 			$endpoint_data   = array(
  1174 				'methods' => array_keys( $callback['methods'] ),
  1184 				'methods' => array_keys( $callback['methods'] ),
  1175 			);
  1185 			);
  1176 
  1186 
  1177 			if ( isset( $callback['args'] ) ) {
  1187 			if ( isset( $callback['args'] ) ) {
  1178 				$endpoint_data['args'] = array();
  1188 				$endpoint_data['args'] = array();
  1317 	 */
  1327 	 */
  1318 	public function get_headers( $server ) {
  1328 	public function get_headers( $server ) {
  1319 		$headers = array();
  1329 		$headers = array();
  1320 
  1330 
  1321 		// CONTENT_* headers are not prefixed with HTTP_.
  1331 		// CONTENT_* headers are not prefixed with HTTP_.
  1322 		$additional = array( 'CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true );
  1332 		$additional = array(
       
  1333 			'CONTENT_LENGTH' => true,
       
  1334 			'CONTENT_MD5'    => true,
       
  1335 			'CONTENT_TYPE'   => true,
       
  1336 		);
  1323 
  1337 
  1324 		foreach ( $server as $key => $value ) {
  1338 		foreach ( $server as $key => $value ) {
  1325 			if ( strpos( $key, 'HTTP_' ) === 0 ) {
  1339 			if ( strpos( $key, 'HTTP_' ) === 0 ) {
  1326 				$headers[ substr( $key, 5 ) ] = $value;
  1340 				$headers[ substr( $key, 5 ) ] = $value;
  1327 			} elseif ( isset( $additional[ $key ] ) ) {
  1341 			} elseif ( isset( $additional[ $key ] ) ) {