Server IP : 104.21.14.48 / Your IP : 3.147.13.252 [ Web Server : Apache System : Linux b70eb322-3aee-0c53-7c82-0db91281f2c6.secureserver.net 6.1.90-1.el9.elrepo.x86_64 #1 SMP PREEMPT_DYNAMIC Thu May 2 12:09:22 EDT 2024 x86_64 User : root ( 0) PHP Version : 8.0.30.2 Disable Function : NONE Domains : 0 Domains MySQL : ON | cURL : ON | WGET : ON | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /var/www/wp-content/mu-plugins/object-cache-pro/src/Plugin/Api/ |
Upload File : |
<?php /** * Copyright © 2019-2024 Rhubarb Tech Inc. All Rights Reserved. * * The Object Cache Pro Software and its related materials are property and confidential * information of Rhubarb Tech Inc. Any reproduction, use, distribution, or exploitation * of the Object Cache Pro Software and its related materials, in whole or in part, * is strictly forbidden unless prior permission is obtained from Rhubarb Tech Inc. * * In addition, any reproduction, use, distribution, or exploitation of the Object Cache Pro * Software and its related materials, in whole or in part, is subject to the End-User License * Agreement accessible in the included `LICENSE` file, or at: https://objectcache.pro/eula */ declare(strict_types=1); namespace RedisCachePro\Plugin\Api; use WP_Error; use WP_REST_Server; use WP_REST_Controller; use RedisCachePro\Plugin; use RedisCachePro\ObjectCaches\ObjectCacheInterface; use RedisCachePro\ObjectCaches\MeasuredObjectCacheInterface; use RedisCachePro\Metrics\RedisMetrics; use RedisCachePro\Metrics\RelayMetrics; use RedisCachePro\Metrics\WordPressMetrics; class Analytics extends WP_REST_Controller { /** * The resource name of this controller's route. * * @var string */ protected $resource_name; /** * The default interval, in seconds. * * @var int */ protected static $interval = 60; /** * The default intervals. * * The keys represent the resolution in seconds * and the values are the number of intervals. * * @var array<int, int> */ protected static $intervals = [ 10 => 30, 60 => 30, 300 => 24, ]; /** * Create a new instance. * * @return void */ public function __construct() { $this->namespace = 'objectcache/v1'; $this->resource_name = 'analytics'; } /** * Returns the default interval, in seconds. * * @return int */ public static function interval() { /** * Filters the default interval for object cache analytics. * * @param int $interval The interval, in seconds. */ return (int) apply_filters('objectcache_analytics_interval', static::$interval); } /** * Returns the supported intervals. * * @return array<int, int> */ public static function intervals() { /** * Filters the intervals for object cache analytics. * * The array keys represent the resolution in seconds * and the values are the number of intervals. * * @param array $intervals The intervals. */ return (array) apply_filters('objectcache_analytics_intervals', static::$intervals); } /** * Register all REST API routes. * * @return void */ public function register_routes() { register_rest_route($this->namespace, "/{$this->resource_name}", [ [ 'methods' => WP_REST_Server::READABLE, 'callback' => [$this, 'get_items'], 'permission_callback' => [$this, 'get_items_permissions_check'], 'args' => $this->get_collection_params(), ], 'schema' => [$this, 'get_public_item_schema'], ]); } /** * The permission callback for the endpoint. * * @param \WP_REST_Request $request * @return true|\WP_Error */ public function get_items_permissions_check($request) { /** * Filter the capability required to access REST API endpoints. * * @param string $capability The capability name. */ $capability = (string) apply_filters('objectcache_rest_capability', Plugin::Capability); if (current_user_can($capability)) { return true; } return new WP_Error( 'rest_forbidden', 'Sorry, you are not allowed to do that.', ['status' => rest_authorization_required_code()] ); } /** * Retrieves the query params for the posts collection. * * @return array<string, mixed> */ public function get_collection_params() { $params = parent::get_collection_params(); $params['per_page']['default'] = 30; $params['context']['default'] = 'compute'; unset($params['search']); $params['interval'] = [ 'description' => 'The interval in seconds.', 'type' => 'integer', 'required' => false, 'minimum' => 1, 'default' => static::interval(), ]; return $params; } /** * Returns the REST API response for the request. * * @param \WP_REST_Request $request * @return \WP_REST_Response|\WP_Error */ public function get_items($request) { global $wp_object_cache; if (! $wp_object_cache instanceof ObjectCacheInterface) { return new WP_Error( 'objectcache_not_supported', 'The object cache is not supported.', ['status' => 400] ); } if (! $wp_object_cache instanceof MeasuredObjectCacheInterface) { return new WP_Error( 'objectcache_analytics_unsupported', 'The object cache does not support analytics.', ['status' => 400] ); } if (! $wp_object_cache->connection()) { return new WP_Error( 'objectcache_not_connected', 'The object cache is not connected.', ['status' => 400] ); } if (! $wp_object_cache->config()->analytics->enabled) { return new WP_Error( 'objectcache_analytics_disabled', 'Object cache analytics are disabled.', ['status' => 400] ); } $page = $request->get_param('page'); $per_page = $request->get_param('per_page'); $interval = $request->get_param('interval'); $now = microtime(true); $min = (int) $now - ($interval * $per_page * $page); $min = $min - $min % $interval; $max = (int) $now - ($interval * $per_page * ($page - 1)); $intervals = $wp_object_cache->measurements((string) $min, (string) $max) ->intervals($interval); $range = array_slice(array_reverse(range($min, $max, $interval)), 0, 30, true); $collection = array_map(function ($timestamp) use ($intervals, $request) { return $this->prepare_item_for_response([ 'timestamp' => $timestamp, 'measurements' => $intervals[$timestamp] ?? null, ], $request); }, $range); /** @var \WP_REST_Response $response */ $response = rest_ensure_response($collection); $response->header('Cache-Control', 'no-store'); return $response; } /** * Prepares a single interval output for response. * * @param array $item * @param \WP_REST_Request $request * @return array */ public function prepare_item_for_response($item, $request) // @phpstan-ignore-line { $fields = $this->get_fields_for_response($request); if (rest_is_field_included('count', $fields)) { $item['count'] = count($item['measurements'] ?? []); } $rfc3339 = 'Y-m-d\TH:i:s'; if (rest_is_field_included('date', $fields)) { $item['date'] = get_date_from_gmt("@{$item['timestamp']}", $rfc3339); } if (rest_is_field_included('date_gmt', $fields)) { $item['date_gmt'] = date($rfc3339, $item['timestamp']); } if (rest_is_field_included('date_display', $fields)) { $item['date_display'] = [ 'date' => wp_date('D jS', $item['timestamp']), 'time' => sprintf( '%s - %s %s', wp_date('H:i', $item['timestamp']), wp_date('H:i', $item['timestamp'] + $request->get_param('interval')), current_datetime()->format('T') ), ]; } $hasMeasurements = ! empty($item['measurements']); foreach ($this->get_metrics() as $id => $metric) { foreach ($metric['computations'] as $computation) { $name = $metric['group'] === 'wp' ? $id : str_replace($metric['group'], '', $id); if (rest_is_field_included("{$id}.{$computation}", $fields)) { $item[$id][$computation] = $hasMeasurements ? $item['measurements']->{$computation}("{$metric['group']}->{$name}") : null; } } } if (rest_is_field_included('measurements', $fields)) { $item['measurements'] = array_map(static function ($measurement) { return $measurement->toArray(); }, $item['measurements'] ? $item['measurements']->all() : []); } $item = $this->add_additional_fields_to_object($item, $request); $item = $this->filter_response_by_context($item, $request['context']); return $item; } /** * Returns the metrics for each group and their supported computations. * * @return array<string, mixed> */ protected function get_metrics() { $metrics = array_merge( WordPressMetrics::schema(), RedisMetrics::schema(), RelayMetrics::schema() ); return array_map(static function ($metric) { $metric['computations'] = [ 'max', 'mean', 'median', 'p90', 'p95', 'p99', ]; return $metric; }, $metrics); } /** * Retrieves the endpoint's schema, conforming to JSON Schema. * * @return array<string, mixed> */ public function get_item_schema() { if ($this->schema) { return $this->add_additional_fields_schema($this->schema); } $properties = [ 'max' => ['type' => ['integer', 'float', 'null']], 'mean' => ['type' => ['integer', 'float', 'null']], 'median' => ['type' => ['integer', 'float', 'null']], 'p90' => ['type' => ['integer', 'float', 'null']], 'p95' => ['type' => ['integer', 'float', 'null']], 'p99' => ['type' => ['integer', 'float', 'null']], ]; $schema = [ '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'objectcache_analytics', 'type' => 'object', 'properties' => [ 'timestamp' => [ 'description' => 'The timestamp of the interval.', 'type' => 'integer', 'context' => ['raw', 'compute'], ], 'date' => [ 'description' => "The date of the interval, in the site's timezone.", 'type' => 'string', 'format' => 'date-time', 'context' => ['compute'], ], 'date_gmt' => [ 'description' => 'The date of the interval, as GMT.', 'type' => 'string', 'format' => 'date-time', 'context' => ['compute'], ], 'date_display' => [ 'description' => 'The displayable date of the interval.', 'type' => 'object', 'context' => ['compute'], ], 'count' => [ 'description' => 'The amount of measurements taken in the interval.', 'type' => 'integer', 'context' => ['raw', 'compute'], ], 'measurements' => [ 'description' => 'The measurements taken in the interval.', 'type' => 'array', 'context' => ['raw'], ], ], ]; foreach ($this->get_metrics() as $id => $metric) { $schema['properties'][$id] = [ 'title' => $metric['title'], 'description' => $metric['description'], 'type' => 'object', 'context' => ['compute'], 'properties' => $properties, ]; } $this->schema = $schema; return $this->add_additional_fields_schema($this->schema); } }