/** * Handles serving an API request. * * Matches the current server URI to a route and runs the first matching * callback then outputs a JSON representation of the returned value. * * @since 4.4.0 * @access public * * @see WP_REST_Server::dispatch() * * @param string $path Optional. The request route. If not set, `$_SERVER['PATH_INFO']` will be used. * Default null. * @return false|null Null if not served and a HEAD request, false otherwise. */ public function serve_request($path = null) { $content_type = isset($_GET['_jsonp']) ? 'application/javascript' : 'application/json'; $this->send_header('Content-Type', $content_type . '; charset=' . get_option('blog_charset')); $this->send_header('X-Robots-Tag', 'noindex'); $api_root = get_rest_url(); if (!empty($api_root)) { $this->send_header('Link', '<' . esc_url_raw($api_root) . '>; rel="https://api.w.org/"'); } /* * Mitigate possible JSONP Flash attacks. * * https://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/ */ $this->send_header('X-Content-Type-Options', 'nosniff'); $this->send_header('Access-Control-Expose-Headers', 'X-WP-Total, X-WP-TotalPages'); $this->send_header('Access-Control-Allow-Headers', 'Authorization, Content-Type'); /** * Send nocache headers on authenticated requests. * * @since 4.4.0 * * @param bool $rest_send_nocache_headers Whether to send no-cache headers. */ $send_no_cache_headers = apply_filters('rest_send_nocache_headers', is_user_logged_in()); if ($send_no_cache_headers) { foreach (wp_get_nocache_headers() as $header => $header_value) { $this->send_header($header, $header_value); } } /** * Filters whether the REST API is enabled. * * @since 4.4.0 * @deprecated 4.7.0 Use the rest_authentication_errors filter to restrict access to the API * * @param bool $rest_enabled Whether the REST API is enabled. Default true. */ apply_filters_deprecated('rest_enabled', array(true), '4.7.0', 'rest_authentication_errors', __('The REST API can no longer be completely disabled, the rest_authentication_errors can be used to restrict access to the API, instead.')); /** * Filters whether jsonp is enabled. * * @since 4.4.0 * * @param bool $jsonp_enabled Whether jsonp is enabled. Default true. */ $jsonp_enabled = apply_filters('rest_jsonp_enabled', true); $jsonp_callback = null; if (isset($_GET['_jsonp'])) { if (!$jsonp_enabled) { echo $this->json_error('rest_callback_disabled', __('JSONP support is disabled on this site.'), 400); return false; } $jsonp_callback = $_GET['_jsonp']; if (!wp_check_jsonp_callback($jsonp_callback)) { echo $this->json_error('rest_callback_invalid', __('The JSONP callback function is invalid.'), 400); return false; } } if (empty($path)) { if (isset($_SERVER['PATH_INFO'])) { $path = $_SERVER['PATH_INFO']; } else { $path = '/'; } } $request = new WP_REST_Request($_SERVER['REQUEST_METHOD'], $path); $request->set_query_params(wp_unslash($_GET)); $request->set_body_params(wp_unslash($_POST)); $request->set_file_params($_FILES); $request->set_headers($this->get_headers(wp_unslash($_SERVER))); $request->set_body($this->get_raw_data()); /* * HTTP method override for clients that can't use PUT/PATCH/DELETE. First, we check * $_GET['_method']. If that is not set, we check for the HTTP_X_HTTP_METHOD_OVERRIDE * header. */ if (isset($_GET['_method'])) { $request->set_method($_GET['_method']); } elseif (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) { $request->set_method($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']); } $result = $this->check_authentication(); if (!is_wp_error($result)) { $result = $this->dispatch($request); } // Normalize to either WP_Error or WP_REST_Response... $result = rest_ensure_response($result); // ...then convert WP_Error across. if (is_wp_error($result)) { $result = $this->error_to_response($result); } /** * Filters the API response. * * Allows modification of the response before returning. * * @since 4.4.0 * @since 4.5.0 Applied to embedded responses. * * @param WP_HTTP_Response $result Result to send to the client. Usually a WP_REST_Response. * @param WP_REST_Server $this Server instance. * @param WP_REST_Request $request Request used to generate the response. */ $result = apply_filters('rest_post_dispatch', rest_ensure_response($result), $this, $request); // Wrap the response in an envelope if asked for. if (isset($_GET['_envelope'])) { $result = $this->envelope_response($result, isset($_GET['_embed'])); } // Send extra data from response objects. $headers = $result->get_headers(); $this->send_headers($headers); $code = $result->get_status(); $this->set_status($code); /** * Filters whether the request has already been served. * * Allow sending the request manually - by returning true, the API result * will not be sent to the client. * * @since 4.4.0 * * @param bool $served Whether the request has already been served. * Default false. * @param WP_HTTP_Response $result Result to send to the client. Usually a WP_REST_Response. * @param WP_REST_Request $request Request used to generate the response. * @param WP_REST_Server $this Server instance. */ $served = apply_filters('rest_pre_serve_request', false, $result, $request, $this); if (!$served) { if ('HEAD' === $request->get_method()) { return null; } // Embed links inside the request. $result = $this->response_to_data($result, isset($_GET['_embed'])); $result = wp_json_encode($result); $json_error_message = $this->get_json_last_error(); if ($json_error_message) { $json_error_obj = new WP_Error('rest_encode_error', $json_error_message, array('status' => 500)); $result = $this->error_to_response($json_error_obj); $result = wp_json_encode($result->data[0]); } if ($jsonp_callback) { // Prepend '/**/' to mitigate possible JSONP Flash attacks. // https://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/ echo '/**/' . $jsonp_callback . '(' . $result . ')'; } else { echo $result; } } return null; }
/** * @ticket 10441 */ public function test_apply_filters_deprecated_without_filter() { $val = 'Foobar'; $this->assertSame($val, apply_filters_deprecated('tests_apply_filters_deprecated', array($val), '4.6')); }
/** * Retrieves the details for this site. * * This method is used internally to lazy-load the extended properties of a site. * * @since 4.6.0 * @access private * * @see WP_Site::__get() * * @return stdClass A raw site object with all details included. */ private function get_details() { $details = wp_cache_get($this->blog_id, 'site-details'); if (false === $details) { switch_to_blog($this->blog_id); // Create a raw copy of the object for backwards compatibility with the filter below. $details = new stdClass(); foreach (get_object_vars($this) as $key => $value) { $details->{$key} = $value; } $details->blogname = get_option('blogname'); $details->siteurl = get_option('siteurl'); $details->post_count = get_option('post_count'); $details->home = get_option('home'); restore_current_blog(); $cache_details = true; foreach (array('blogname', 'siteurl', 'post_count', 'home') as $field) { if (false === $details->{$field}) { $cache_details = false; break; } } if ($cache_details) { wp_cache_set($this->blog_id, $details, 'site-details'); } } /** This filter is documented in wp-includes/ms-blogs.php */ $details = apply_filters_deprecated('blog_details', array($details), '4.7.0', 'site_details'); /** * Filters a site's extended properties. * * @since 4.6.0 * * @param stdClass $details The site details. */ $details = apply_filters('site_details', $details); return $details; }
/** * @ticket 10441 * @expectedDeprecated tests_apply_filters_deprecated */ public function test_apply_filters_deprecated_with_multiple_params() { $p1 = 'Foo1'; $p2 = 'Foo2'; add_filter('tests_apply_filters_deprecated', array(__CLASS__, 'deprecated_filter_callback_multiple_params'), 10, 2); $p1 = apply_filters_deprecated('tests_apply_filters_deprecated', array($p1, $p2), '4.6'); remove_filter('tests_apply_filters_deprecated', array(__CLASS__, 'deprecated_filter_callback_multiple_params'), 10, 2); $this->assertSame('Bar1', $p1); // Not passed by reference, so not modified. $this->assertSame('Foo2', $p2); }
/** * Retrieve the details for a blog from the blogs table and blog options. * * @since MU * * @global wpdb $wpdb WordPress database abstraction object. * * @param int|string|array $fields Optional. A blog ID, a blog slug, or an array of fields to query against. * If not specified the current blog ID is used. * @param bool $get_all Whether to retrieve all details or only the details in the blogs table. * Default is true. * @return WP_Site|false Blog details on success. False on failure. */ function get_blog_details($fields = null, $get_all = true) { global $wpdb; if (is_array($fields)) { if (isset($fields['blog_id'])) { $blog_id = $fields['blog_id']; } elseif (isset($fields['domain']) && isset($fields['path'])) { $key = md5($fields['domain'] . $fields['path']); $blog = wp_cache_get($key, 'blog-lookup'); if (false !== $blog) { return $blog; } if (substr($fields['domain'], 0, 4) == 'www.') { $nowww = substr($fields['domain'], 4); $blog = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->blogs} WHERE domain IN (%s,%s) AND path = %s ORDER BY CHAR_LENGTH(domain) DESC", $nowww, $fields['domain'], $fields['path'])); } else { $blog = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->blogs} WHERE domain = %s AND path = %s", $fields['domain'], $fields['path'])); } if ($blog) { wp_cache_set($blog->blog_id . 'short', $blog, 'blog-details'); $blog_id = $blog->blog_id; } else { return false; } } elseif (isset($fields['domain']) && is_subdomain_install()) { $key = md5($fields['domain']); $blog = wp_cache_get($key, 'blog-lookup'); if (false !== $blog) { return $blog; } if (substr($fields['domain'], 0, 4) == 'www.') { $nowww = substr($fields['domain'], 4); $blog = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->blogs} WHERE domain IN (%s,%s) ORDER BY CHAR_LENGTH(domain) DESC", $nowww, $fields['domain'])); } else { $blog = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->blogs} WHERE domain = %s", $fields['domain'])); } if ($blog) { wp_cache_set($blog->blog_id . 'short', $blog, 'blog-details'); $blog_id = $blog->blog_id; } else { return false; } } else { return false; } } else { if (!$fields) { $blog_id = get_current_blog_id(); } elseif (!is_numeric($fields)) { $blog_id = get_id_from_blogname($fields); } else { $blog_id = $fields; } } $blog_id = (int) $blog_id; $all = $get_all == true ? '' : 'short'; $details = wp_cache_get($blog_id . $all, 'blog-details'); if ($details) { if (!is_object($details)) { if ($details == -1) { return false; } else { // Clear old pre-serialized objects. Cache clients do better with that. wp_cache_delete($blog_id . $all, 'blog-details'); unset($details); } } else { return $details; } } // Try the other cache. if ($get_all) { $details = wp_cache_get($blog_id . 'short', 'blog-details'); } else { $details = wp_cache_get($blog_id, 'blog-details'); // If short was requested and full cache is set, we can return. if ($details) { if (!is_object($details)) { if ($details == -1) { return false; } else { // Clear old pre-serialized objects. Cache clients do better with that. wp_cache_delete($blog_id, 'blog-details'); unset($details); } } else { return $details; } } } if (empty($details)) { $details = WP_Site::get_instance($blog_id); if (!$details) { // Set the full cache. wp_cache_set($blog_id, -1, 'blog-details'); return false; } } if (!$details instanceof WP_Site) { $details = new WP_Site($details); } if (!$get_all) { wp_cache_set($blog_id . $all, $details, 'blog-details'); return $details; } switch_to_blog($blog_id); $details->blogname = get_option('blogname'); $details->siteurl = get_option('siteurl'); $details->post_count = get_option('post_count'); $details->home = get_option('home'); restore_current_blog(); /** * Filters a blog's details. * * @since MU * @deprecated 4.7.0 Use site_details * * @param object $details The blog details. */ $details = apply_filters_deprecated('blog_details', array($details), '4.7.0', 'site_details'); wp_cache_set($blog_id . $all, $details, 'blog-details'); $key = md5($details->domain . $details->path); wp_cache_set($key, $details, 'blog-lookup'); return $details; }
/** * Allows developers to use 'github_updater_set_options' hook to set access tokens or other settings. * Saves results of filter hook to self::$options. * * Hook requires return of associative element array. * $key === repo-name and $value === token * e.g. array( 'repo-name' => 'access_token' ); * * @TODO Set `Requires WP: 4.6` and only use current filter and apply_filters_deprecated */ public function set_options_filter() { // Single plugin/theme should not be using both hooks. $config = apply_filters('github_updater_set_options', array()); if (empty($config)) { $config = function_exists('apply_filters_deprecated') ? apply_filters_deprecated('github_updater_token_distribution', array(null), '6.1.0', 'github_updater_set_options') : apply_filters('github_updater_token_distribution', array()); } if (!empty($config)) { $config = Settings::sanitize($config); self::$options = array_merge(get_site_option('github_updater'), $config); update_site_option('github_updater', self::$options); } }