set_http_method(); $this->set_current_route( $request ); $this->build_supported_routes(); $this->set_url_params(); $this->set_object_types(); } /** * Magic getter for accessing read-only properties. Should we ever need to enforce a getter method, we can do so here. * * @param string $name The desired property name. * @return string|null */ public function __get( $name ) { if ( in_array( $name, $this->readonly_props ) ) { return $this->$name; } return null; } /** * Get a URL parameter if found on the request URL. * * @param $param * @return mixed|null */ public function get_url_param( $param ) { return isset( $this->url_params[ $param ] ) ? $this->url_params[ $param ] : null; } /** * Determine the HTTP method of the current request. */ private function set_http_method() { $this->http_method = 'GET'; if ( ! empty( $_SERVER['REQUEST_METHOD'] ) ) { $this->http_method = strtoupper( sanitize_text_field( $_SERVER['REQUEST_METHOD'] ) ); } // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Verified elsewhere. // HTTP method override for clients that can't use PUT/PATCH/DELETE. This is identical to WordPress' // handling in \WP_REST_Server::serve_request(). This block of code should always be identical to that // in core. if ( isset( $_GET['_method'] ) ) { $this->http_method = strtoupper( sanitize_text_field( $_GET['_method'] ) ); } elseif ( isset( $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] ) ) { $this->http_method = strtoupper( sanitize_text_field( $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] ) ); } // phpcs:enable WordPress.Security.NonceVerification.Recommended } /** * Get the current REST route as determined by WordPress. */ private function set_current_route( $request ) { if ( $request ) { $this->current_route = $request->get_route(); } else { $this->current_route = empty( $GLOBALS['wp']->query_vars['rest_route'] ) ? null : $GLOBALS['wp']->query_vars['rest_route']; } } /** * Build an array of route match patterns that we handle. These are the same as WordPress' core patterns except * we are also matching the object type here as well. */ private function build_supported_routes() { // Add post type routes for all post types configured to show in REST. /** @var WP_Post_Type $post_type */ foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) { $rest_base = acf_get_object_type_rest_base( $post_type ); $this->supported_routes[] = "/wp/v2/(?P{$rest_base})"; $this->supported_routes[] = "/wp/v2/(?P{$rest_base})/(?P[\d]+)"; if ( post_type_supports( $post_type->name, 'revisions' ) ) { $this->supported_routes[] = "/wp/v2/(?P{$rest_base})/(?P[\d]+)/(?Previsions)"; $this->supported_routes[] = "/wp/v2/(?P{$rest_base})/(?P[\d]+)/(?Previsions)/(?P[\d]+)"; } if ( 'attachment' !== $post_type->name ) { $this->supported_routes[] = "/wp/v2/(?P{$rest_base})/(?P[\d]+)/(?Pautosaves)"; $this->supported_routes[] = "/wp/v2/(?P{$rest_base})/(?P[\d]+)/(?Pautosaves)/(?P[\d]+)"; } } // Add taxonomy routes all taxonomies configured to show in REST. /** @var WP_Taxonomy $taxonomy */ foreach ( get_taxonomies( array( 'show_in_rest' => true ), 'object' ) as $taxonomy ) { $rest_base = acf_get_object_type_rest_base( $taxonomy ); $this->supported_routes[] = "/wp/v2/(?P{$rest_base})"; $this->supported_routes[] = "/wp/v2/(?P{$rest_base})/(?P[\d]+)"; } // Add user routes. $this->supported_routes[] = '/wp/v2/(?Pusers)'; $this->supported_routes[] = '/wp/v2/(?Pusers)/(?P[\d]+)'; $this->supported_routes[] = '/wp/v2/(?Pusers)/me'; // Add comment routes. $this->supported_routes[] = '/wp/v2/(?Pcomments)'; $this->supported_routes[] = '/wp/v2/(?Pcomments)/(?P[\d]+)'; } /** * Loop through supported routes to find matching pattern. Use matching pattern to determine any URL parameters. */ private function set_url_params() { if ( ! $this->supported_routes || ! is_string( $this->current_route ) ) { return; } // Determine query args passed within the URL. foreach ( $this->supported_routes as $route ) { $match = preg_match( '@^' . $route . '$@i', $this->current_route, $matches ); if ( ! $match ) { continue; } foreach ( $matches as $param => $value ) { if ( ! is_int( $param ) ) { $this->url_params[ $param ] = $value; } } } } /** * Determine the object type and sub type from the requested route. We need to know both the underlying WordPress * object type as well as post type or taxonomy in order to provide the right context when getting/updating fields. */ private function set_object_types() { $base = $this->get_url_param( 'rest_base' ); $child_base = $this->get_url_param( 'child_rest_base' ); // We need a matched rest base to proceed here. If we haven't matched one while parsing the request, bail. if ( is_null( $base ) ) { return; } // Determine the matching object type from the rest base. Start with users as that is simple. From there, // check post types then check taxonomies if a matching post type cannot be found. if ( $base === 'users' ) { $this->object_type = $this->object_sub_type = 'user'; } elseif ( $base === 'comments' ) { $this->object_type = $this->object_sub_type = 'comment'; } elseif ( $post_type = $this->get_post_type_by_rest_base( $base ) ) { $this->object_type = 'post'; $this->object_sub_type = $post_type->name; // Autosaves and revisions are mostly handled the same by WP, and share the same schema. if ( in_array( $this->get_url_param( 'child_rest_base' ), array( 'revisions', 'autosaves' ) ) ) { $this->child_object_type = $this->object_sub_type . '-revision'; } } elseif ( $taxonomy = $this->get_taxonomy_by_rest_base( $base ) ) { $this->object_type = 'term'; $this->object_sub_type = $taxonomy->name; } } /** * Find the REST enabled post type object that matches the given REST base. * * @param string $rest_base * @return WP_Post_Type|null */ private function get_post_type_by_rest_base( $rest_base ) { $types = get_post_types( array( 'show_in_rest' => true ), 'objects' ); foreach ( $types as $type ) { if ( acf_get_object_type_rest_base( $type ) === $rest_base ) { return $type; } } return null; } /** * Find the REST enabled taxonomy object that matches the given REST base. * * @param $rest_base * @return WP_Taxonomy|null */ private function get_taxonomy_by_rest_base( $rest_base ) { $taxonomies = get_taxonomies( array( 'show_in_rest' => true ), 'objects' ); foreach ( $taxonomies as $taxonomy ) { if ( acf_get_object_type_rest_base( $taxonomy ) === $rest_base ) { return $taxonomy; } } return null; } }