PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 15.8-beta
Jetpack – WP Security, Backup, Speed, & Growth v15.8-beta
15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / sal / class.json-api-links.php
jetpack / sal Last commit date
class.json-api-date.php 6 months ago class.json-api-links.php 6 months ago class.json-api-metadata.php 6 months ago class.json-api-platform-jetpack.php 6 months ago class.json-api-platform.php 6 months ago class.json-api-post-base.php 1 month ago class.json-api-post-jetpack.php 6 months ago class.json-api-site-base.php 4 weeks ago class.json-api-site-jetpack-base.php 6 months ago class.json-api-site-jetpack.php 6 months ago class.json-api-token.php 6 months ago
class.json-api-links.php
452 lines
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * WPCOM_JSON_API_Links class.
4 *
5 * @package automattic/jetpack
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit( 0 );
10 }
11
12 require_once __DIR__ . '/../class.json-api.php';
13
14 /**
15 * Base class for WPCOM_JSON_API_Links.
16 */
17 class WPCOM_JSON_API_Links {
18
19 /**
20 * An instance of the WPCOM_JSON_API.
21 *
22 * @var WPCOM_JSON_API
23 */
24 private $api;
25
26 /**
27 * A WPCOM_JSON_API_Links instance.
28 *
29 * @var WPCOM_JSON_API_Links
30 */
31 private static $instance;
32
33 /**
34 * An array of the closest supported version of an endpoint to the current endpoint.
35 *
36 * @var array
37 */
38 private $closest_endpoint_cache_by_version = array();
39
40 /**
41 * An array including the current api endpoint as well as the max versions found if that endpoint doesn't exist.
42 *
43 * @var array
44 */
45 private $matches_by_version = array();
46
47 /**
48 * An array including the cached endpoint path versions.
49 *
50 * @var array
51 */
52 private $cache_result = null;
53
54 /**
55 * Creates a new instance of the WPCOM_JSON_API_Links class.
56 *
57 * @return WPCOM_JSON_API_Links
58 */
59 public static function getInstance() { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
60 if ( null === self::$instance ) {
61 self::$instance = new self();
62 }
63
64 return self::$instance;
65 }
66
67 /**
68 * WPCOM_JSON_API_Links constructor.
69 *
70 * Method protected for singleton.
71 */
72 protected function __construct() {
73 $this->api = WPCOM_JSON_API::init();
74 }
75
76 /**
77 * An empty, private __clone method to prohibit cloning of this instance.
78 */
79 private function __clone() { }
80
81 /**
82 * Overriding PHP's default __wakeup method to prvent unserializing of the instance, and return an error message.
83 *
84 * @return never
85 */
86 public function __wakeup() {
87 die( "Please don't __wakeup WPCOM_JSON_API_Links" );
88 }
89
90 /**
91 * Generate a URL to an endpoint
92 *
93 * Used to construct meta links in API responses
94 *
95 * @param mixed ...$args Optional arguments to be appended to URL.
96 * @return string Endpoint URL
97 **/
98 public function get_link( ...$args ) {
99 $format = array_shift( $args );
100 $base = WPCOM_JSON_API__BASE;
101
102 $path = array_pop( $args );
103
104 if ( $path ) {
105 $path = '/' . ltrim( $path, '/' );
106 // tack the path onto the end of the format string.
107 // have to escape %'s in the path as %% because
108 // we're about to pass it through sprintf and we don't
109 // want it to see the % as a placeholder.
110 $format .= str_replace( '%', '%%', $path );
111 }
112
113 // Escape any % in args before using sprintf.
114 $escaped_args = array();
115 foreach ( $args as $arg_key => $arg_value ) {
116 $escaped_args[ $arg_key ] = str_replace( '%', '%%', $arg_value );
117 }
118
119 $relative_path = vsprintf( $format, $escaped_args );
120
121 if ( ! wp_startswith( $relative_path, '.' ) ) {
122 // Generic version. Match the requested version as best we can.
123 $api_version = $this->get_closest_version_of_endpoint( $format, $relative_path );
124 $base = substr( $base, 0, - 1 ) . $api_version;
125 }
126
127 // escape any % in the relative path before running it through sprintf again.
128 $relative_path = str_replace( '%', '%%', $relative_path );
129 // http, WPCOM_JSON_API__BASE, ... , path.
130 // %s , %s , $format, %s.
131 return esc_url_raw( sprintf( "https://%s$relative_path", $base ) );
132 }
133
134 /**
135 * Generate the /me prefixed endpoint URL
136 *
137 * Used to construct meta links in API responses, specific to WordPress.com user account pages.
138 *
139 * @param string $path Optional path to be appended to the URL.
140 * @return string /me endpoint URL
141 **/
142 public function get_me_link( $path = '' ) {
143 return $this->get_link( '/me', $path );
144 }
145
146 /**
147 * Generate the endpoint URL for taxonomies
148 *
149 * Used to construct meta links in API responses, specific to taxonomies.
150 *
151 * @param int $blog_id The site's Jetpack blog ID.
152 * @param int $taxonomy_id The taxonomy ID (for example of the category, tag).
153 * @param string $taxonomy_type The taxonomy type (for example category, tag).
154 * @param string $path Optional path to be appended to the URL.
155 * @return string Endpoint URL including taxonomy information.
156 **/
157 public function get_taxonomy_link( $blog_id, $taxonomy_id, $taxonomy_type, $path = '' ) {
158 switch ( $taxonomy_type ) {
159 case 'category':
160 return $this->get_link( '/sites/%d/categories/slug:%s', $blog_id, $taxonomy_id, $path );
161
162 case 'post_tag':
163 return $this->get_link( '/sites/%d/tags/slug:%s', $blog_id, $taxonomy_id, $path );
164
165 default:
166 return $this->get_link( '/sites/%d/taxonomies/%s/terms/slug:%s', $blog_id, $taxonomy_type, $taxonomy_id, $path );
167 }
168 }
169
170 /**
171 * Generate the endpoint URL for media links
172 *
173 * Used to construct meta links in API responses, specific to media links.
174 *
175 * @param int $blog_id The site's Jetpack blog ID.
176 * @param int $media_id The media item ID.
177 * @param string $path Optional path to be appended to the URL.
178 * @return string Endpoint URL including media information.
179 **/
180 public function get_media_link( $blog_id, $media_id, $path = '' ) {
181 return $this->get_link( '/sites/%d/media/%d', $blog_id, $media_id, $path );
182 }
183
184 /**
185 * Generate the site link endpoint URL
186 *
187 * Used to construct meta links in API responses, specific to /site links.
188 *
189 * @param int $blog_id The site's Jetpack blog ID.
190 * @param string $path Optional path to be appended to the URL.
191 * @return string Endpoint URL including site information.
192 **/
193 public function get_site_link( $blog_id, $path = '' ) {
194 return $this->get_link( '/sites/%d', $blog_id, $path );
195 }
196
197 /**
198 * Generate the posts endpoint URL
199 *
200 * Used to construct meta links in API responses, specific to posts links.
201 *
202 * @param int $blog_id The site's Jetpack blog ID.
203 * @param int $post_id The post ID.
204 * @param string $path Optional path to be appended to the URL.
205 * @return string Endpoint URL including post information.
206 **/
207 public function get_post_link( $blog_id, $post_id, $path = '' ) {
208 return $this->get_link( '/sites/%d/posts/%d', $blog_id, $post_id, $path );
209 }
210
211 /**
212 * Generate the comments endpoint URL
213 *
214 * Used to construct meta links in API responses, specific to comments links.
215 *
216 * @param int $blog_id The site's Jetpack blog ID.
217 * @param int $comment_id The comment ID.
218 * @param string $path Optional path to be appended to the URL.
219 * @return string Endpoint URL including comment information.
220 **/
221 public function get_comment_link( $blog_id, $comment_id, $path = '' ) {
222 return $this->get_link( '/sites/%d/comments/%d', $blog_id, $comment_id, $path );
223 }
224
225 /**
226 * Generate the endpoint URL for Publicize connections
227 *
228 * Used to construct meta links in API responses, specific to Publicize connections.
229 *
230 * @param int $blog_id The site's Jetpack blog ID.
231 * @param int $publicize_connection_id The ID of the Publicize connection.
232 * @param string $path Optional path to be appended to the URL.
233 * @return string Endpoint URL including Publicize connection information.
234 **/
235 public function get_publicize_connection_link( $blog_id, $publicize_connection_id, $path = '' ) {
236 return $this->get_link( '.1/sites/%d/publicize-connections/%d', $blog_id, $publicize_connection_id, $path );
237 }
238
239 /**
240 * Generate the endpoint URL for a single Publicize connection including a Keyring connection
241 *
242 * Used to construct meta links in API responses, specific to a single Publicize and Keyring connection.
243 *
244 * @param int $keyring_token_id The ID of the Keyring connection.
245 * @param string $path Optional path to be appended to the URL.
246 * @return string Endpoint URL including specific Keyring connection information for a specific Publicize connection.
247 **/
248 public function get_publicize_connections_link( $keyring_token_id, $path = '' ) {
249 return $this->get_link( '.1/me/publicize-connections/?keyring_connection_ID=%d', $keyring_token_id, $path );
250 }
251
252 /**
253 * Generate the endpoint URL for a single Keyring connection
254 *
255 * Used to construct meta links in API responses, specific to a Keyring connections.
256 *
257 * @param int $keyring_token_id The ID of the Keyring connection.
258 * @param string $path Optional path to be appended to the URL.
259 * @return string Endpoint URL including specific Keyring connection.
260 **/
261 public function get_keyring_connection_link( $keyring_token_id, $path = '' ) {
262 return $this->get_link( '.1/me/keyring-connections/%d', $keyring_token_id, $path );
263 }
264
265 /**
266 * Generate the endpoint URL for an external service that can be integrated with via Keyring
267 *
268 * Used to construct meta links in API responses, specific to an external service.
269 *
270 * @param int $external_service The ID of the external service.
271 * @param string $path Optional path to be appended to the URL.
272 * @return string Endpoint URL including information about an external service that WordPress.com or Jetpack sites can integrate with via keyring.
273 **/
274 public function get_external_service_link( $external_service, $path = '' ) {
275 return $this->get_link( '.1/meta/external-services/%s', $external_service, $path );
276 }
277
278 /**
279 * Try to find the closest supported version of an endpoint to the current endpoint
280 *
281 * For example, if we were looking at the path /animals/panda:
282 * - if the current endpoint is v1.3 and there is a v1.3 of /animals/%s available, we return 1.3
283 * - if the current endpoint is v1.3 and there is no v1.3 of /animals/%s known, we fall back to the
284 * maximum available version of /animals/%s, e.g. 1.1
285 *
286 * This method is used in get_link() to construct meta links for API responses.
287 *
288 * @param string $template_path The generic endpoint path, e.g. /sites/%s .
289 * @param string $path The current endpoint path, relative to the version, e.g. /sites/12345 .
290 * @param string $request_method Request method used to access the endpoint path .
291 * @return string The current version, or otherwise the maximum version available
292 */
293 public function get_closest_version_of_endpoint( $template_path, $path, $request_method = 'GET' ) {
294 $closest_endpoint_cache_by_version = & $this->closest_endpoint_cache_by_version;
295
296 $api_version = $this->api->version ?? '';
297 $closest_endpoint_cache = & $closest_endpoint_cache_by_version[ $api_version ];
298 if ( ! $closest_endpoint_cache ) {
299 $closest_endpoint_cache_by_version[ $api_version ] = array();
300 $closest_endpoint_cache = & $closest_endpoint_cache_by_version[ $api_version ];
301 }
302
303 if ( ! isset( $closest_endpoint_cache[ $template_path ] ) ) {
304 $closest_endpoint_cache[ $template_path ] = array();
305 } elseif ( isset( $closest_endpoint_cache[ $template_path ][ $request_method ] ) ) {
306 return $closest_endpoint_cache[ $template_path ][ $request_method ];
307 }
308
309 $path = untrailingslashit( $path );
310
311 // /help is a special case - always use the current request version
312 if ( wp_endswith( $path, '/help' ) ) {
313 $closest_endpoint_cache[ $template_path ][ $request_method ] = $this->api->version;
314 return $this->api->version;
315 }
316
317 $matches_by_version = & $this->matches_by_version;
318
319 // try to match out of saved matches.
320 if ( ! isset( $matches_by_version[ $api_version ] ) ) {
321 $matches_by_version[ $api_version ] = array();
322 }
323 foreach ( $matches_by_version[ $api_version ] as $match ) {
324 $regex = $match->regex;
325 if ( preg_match( "#^$regex\$#", $path ) ) {
326 $closest_endpoint_cache[ $template_path ][ $request_method ] = $match->version;
327 return $match->version;
328 }
329 }
330
331 $endpoint_path_versions = $this->get_endpoint_path_versions();
332 $last_path_segment = $this->get_last_segment_of_relative_path( $path );
333 $max_version_found = null;
334
335 foreach ( $endpoint_path_versions as $endpoint_last_path_segment => $endpoints ) {
336
337 // Does the last part of the path match the path key? (e.g. 'posts')
338 // If the last part contains a placeholder (e.g. %s), we want to carry on.
339 // phpcs:ignore Universal.Operators.StrictComparisons.LooseNotEqual
340 if ( $last_path_segment != $endpoint_last_path_segment && ! strstr( $endpoint_last_path_segment, '%' ) ) {
341 continue;
342 }
343
344 foreach ( $endpoints as $endpoint ) {
345 // Does the request method match?
346 if ( ! in_array( $request_method, $endpoint['request_methods'], true ) ) {
347 continue;
348 }
349
350 $endpoint_path = untrailingslashit( (string) $endpoint['path'] );
351 $endpoint_path_regex = str_replace( array( '%s', '%d' ), array( '([^/?&]+)', '(\d+)' ), $endpoint_path );
352
353 if ( ! preg_match( "#^$endpoint_path_regex\$#", $path ) ) {
354 continue;
355 }
356
357 // Make sure the endpoint exists at the same version.
358 if ( null !== $this->api->version &&
359 version_compare( $this->api->version, $endpoint['min_version'], '>=' ) &&
360 version_compare( $this->api->version, $endpoint['max_version'], '<=' )
361 ) {
362 array_push(
363 $matches_by_version[ $this->api->version ],
364 (object) array(
365 'version' => $this->api->version,
366 'regex' => $endpoint_path_regex,
367 )
368 );
369 $closest_endpoint_cache[ $template_path ][ $request_method ] = $this->api->version;
370 return $this->api->version;
371 }
372
373 // If the endpoint doesn't exist at the same version, record the max version we found.
374 if ( empty( $max_version_found ) || version_compare( $max_version_found['version'], $endpoint['max_version'], '<' ) ) {
375 $max_version_found = array(
376 'version' => $endpoint['max_version'],
377 'regex' => $endpoint_path_regex,
378 );
379 }
380 }
381 }
382
383 // If the endpoint version is less than the requested endpoint version, return the max version found.
384 if ( ! empty( $max_version_found ) ) {
385 array_push(
386 $matches_by_version[ $api_version ],
387 (object) $max_version_found
388 );
389 $closest_endpoint_cache[ $template_path ][ $request_method ] = $max_version_found['version'];
390 return $max_version_found['version'];
391 }
392
393 // Otherwise, use the API version of the current request.
394 return $this->api->version;
395 }
396
397 /**
398 * Get an array of endpoint paths with their associated versions
399 *
400 * @return array Array of endpoint paths, min_versions and max_versions, keyed by last segment of path
401 **/
402 protected function get_endpoint_path_versions() {
403
404 if ( ! empty( $this->cache_result ) ) {
405 return $this->cache_result;
406 }
407
408 /*
409 * Create a map of endpoints and their min/max versions keyed by the last segment of the path (e.g. 'posts')
410 * This reduces the search space when finding endpoint matches in get_closest_version_of_endpoint()
411 */
412 $endpoint_path_versions = array();
413
414 foreach ( $this->api->endpoints as $key => $endpoint_objects ) {
415
416 // @todo As with the todo in class.json-api.php, we need to determine if anything depends on this being serialized and hence unserialized, rather than e.g. JSON.
417 // The key contains a serialized path, min_version and max_version.
418 list( $path, $min_version, $max_version ) = unserialize( $key ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize -- Legacy, see serialization at class.json-api.php.
419
420 // Grab the last component of the relative path to use as the top-level key.
421 $last_path_segment = $this->get_last_segment_of_relative_path( $path ) ?? '';
422
423 $endpoint_path_versions[ $last_path_segment ][] = array(
424 'path' => $path,
425 'min_version' => $min_version,
426 'max_version' => $max_version,
427 'request_methods' => array_keys( $endpoint_objects ),
428 );
429 }
430
431 $this->cache_result = $endpoint_path_versions;
432
433 return $endpoint_path_versions;
434 }
435
436 /**
437 * Grab the last segment of a relative path
438 *
439 * @param string $path Path.
440 * @return string Last path segment
441 */
442 protected function get_last_segment_of_relative_path( $path ) {
443 $path_parts = array_filter( explode( '/', $path ) );
444
445 if ( empty( $path_parts ) ) {
446 return null;
447 }
448
449 return end( $path_parts );
450 }
451 }
452