PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 12.0.2
Jetpack – WP Security, Backup, Speed, & Growth v12.0.2
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 4 years ago class.json-api-links.php 4 years ago class.json-api-metadata.php 4 years ago class.json-api-platform-jetpack.php 3 years ago class.json-api-platform.php 3 years ago class.json-api-post-base.php 3 years ago class.json-api-post-jetpack.php 4 years ago class.json-api-site-base.php 3 years ago class.json-api-site-jetpack-base.php 4 years ago class.json-api-site-jetpack.php 3 years ago class.json-api-token.php 4 years ago
class.json-api-links.php
445 lines
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * WPCOM_JSON_API_Links class.
4 *
5 * @package automattic/jetpack
6 */
7
8 require_once __DIR__ . '/../class.json-api.php';
9
10 /**
11 * Base class for WPCOM_JSON_API_Links.
12 */
13 class WPCOM_JSON_API_Links {
14
15 /**
16 * An instance of the WPCOM_JSON_API.
17 *
18 * @var WPCOM_JSON_API
19 */
20 private $api;
21
22 /**
23 * A WPCOM_JSON_API_Links instance.
24 *
25 * @var WPCOM_JSON_API_Links
26 */
27 private static $instance;
28
29 /**
30 * An array of the closest supported version of an endpoint to the current endpoint.
31 *
32 * @var array
33 */
34 private $closest_endpoint_cache_by_version = array();
35
36 /**
37 * An array including the current api endpoint as well as the max versions found if that endpoint doesn't exist.
38 *
39 * @var array
40 */
41 private $matches_by_version = array();
42
43 /**
44 * An array including the cached endpoint path versions.
45 *
46 * @var array
47 */
48 private $cache_result = null;
49
50 /**
51 * Creates a new instance of the WPCOM_JSON_API_Links class.
52 *
53 * @return WPCOM_JSON_API_Links
54 */
55 public static function getInstance() { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
56 if ( null === self::$instance ) {
57 self::$instance = new self();
58 }
59
60 return self::$instance;
61 }
62
63 /**
64 * WPCOM_JSON_API_Links constructor.
65 *
66 * Method protected for singleton.
67 */
68 protected function __construct() {
69 $this->api = WPCOM_JSON_API::init();
70 }
71
72 /**
73 * An empty, private __clone method to prohibit cloning of this instance.
74 */
75 private function __clone() { }
76
77 /**
78 * Overriding PHP's default __wakeup method to prvent unserializing of the instance, and return an error message.
79 */
80 public function __wakeup() {
81 die( "Please don't __wakeup WPCOM_JSON_API_Links" );
82 }
83
84 /**
85 * Generate a URL to an endpoint
86 *
87 * Used to construct meta links in API responses
88 *
89 * @param mixed ...$args Optional arguments to be appended to URL.
90 * @return string Endpoint URL
91 **/
92 public function get_link( ...$args ) {
93 $format = array_shift( $args );
94 $base = WPCOM_JSON_API__BASE;
95
96 $path = array_pop( $args );
97
98 if ( $path ) {
99 $path = '/' . ltrim( $path, '/' );
100 // tack the path onto the end of the format string.
101 // have to escape %'s in the path as %% because
102 // we're about to pass it through sprintf and we don't
103 // want it to see the % as a placeholder.
104 $format .= str_replace( '%', '%%', $path );
105 }
106
107 // Escape any % in args before using sprintf.
108 $escaped_args = array();
109 foreach ( $args as $arg_key => $arg_value ) {
110 $escaped_args[ $arg_key ] = str_replace( '%', '%%', $arg_value );
111 }
112
113 $relative_path = vsprintf( $format, $escaped_args );
114
115 if ( ! wp_startswith( $relative_path, '.' ) ) {
116 // Generic version. Match the requested version as best we can.
117 $api_version = $this->get_closest_version_of_endpoint( $format, $relative_path );
118 $base = substr( $base, 0, - 1 ) . $api_version;
119 }
120
121 // escape any % in the relative path before running it through sprintf again.
122 $relative_path = str_replace( '%', '%%', $relative_path );
123 // http, WPCOM_JSON_API__BASE, ... , path.
124 // %s , %s , $format, %s.
125 return esc_url_raw( sprintf( "https://%s$relative_path", $base ) );
126 }
127
128 /**
129 * Generate the /me prefixed endpoint URL
130 *
131 * Used to construct meta links in API responses, specific to WordPress.com user account pages.
132 *
133 * @param string $path Optional path to be appended to the URL.
134 * @return string /me endpoint URL
135 **/
136 public function get_me_link( $path = '' ) {
137 return $this->get_link( '/me', $path );
138 }
139
140 /**
141 * Generate the endpoint URL for taxonomies
142 *
143 * Used to construct meta links in API responses, specific to taxonomies.
144 *
145 * @param int $blog_id The site's Jetpack blog ID.
146 * @param int $taxonomy_id The taxonomy ID (for example of the category, tag).
147 * @param string $taxonomy_type The taxonomy type (for example category, tag).
148 * @param string $path Optional path to be appended to the URL.
149 * @return string Endpoint URL including taxonomy information.
150 **/
151 public function get_taxonomy_link( $blog_id, $taxonomy_id, $taxonomy_type, $path = '' ) {
152 switch ( $taxonomy_type ) {
153 case 'category':
154 return $this->get_link( '/sites/%d/categories/slug:%s', $blog_id, $taxonomy_id, $path );
155
156 case 'post_tag':
157 return $this->get_link( '/sites/%d/tags/slug:%s', $blog_id, $taxonomy_id, $path );
158
159 default:
160 return $this->get_link( '/sites/%d/taxonomies/%s/terms/slug:%s', $blog_id, $taxonomy_type, $taxonomy_id, $path );
161 }
162 }
163
164 /**
165 * Generate the endpoint URL for media links
166 *
167 * Used to construct meta links in API responses, specific to media links.
168 *
169 * @param int $blog_id The site's Jetpack blog ID.
170 * @param int $media_id The media item ID.
171 * @param string $path Optional path to be appended to the URL.
172 * @return string Endpoint URL including media information.
173 **/
174 public function get_media_link( $blog_id, $media_id, $path = '' ) {
175 return $this->get_link( '/sites/%d/media/%d', $blog_id, $media_id, $path );
176 }
177
178 /**
179 * Generate the site link endpoint URL
180 *
181 * Used to construct meta links in API responses, specific to /site links.
182 *
183 * @param int $blog_id The site's Jetpack blog ID.
184 * @param string $path Optional path to be appended to the URL.
185 * @return string Endpoint URL including site information.
186 **/
187 public function get_site_link( $blog_id, $path = '' ) {
188 return $this->get_link( '/sites/%d', $blog_id, $path );
189 }
190
191 /**
192 * Generate the posts endpoint URL
193 *
194 * Used to construct meta links in API responses, specific to posts links.
195 *
196 * @param int $blog_id The site's Jetpack blog ID.
197 * @param int $post_id The post ID.
198 * @param string $path Optional path to be appended to the URL.
199 * @return string Endpoint URL including post information.
200 **/
201 public function get_post_link( $blog_id, $post_id, $path = '' ) {
202 return $this->get_link( '/sites/%d/posts/%d', $blog_id, $post_id, $path );
203 }
204
205 /**
206 * Generate the comments endpoint URL
207 *
208 * Used to construct meta links in API responses, specific to comments links.
209 *
210 * @param int $blog_id The site's Jetpack blog ID.
211 * @param int $comment_id The comment ID.
212 * @param string $path Optional path to be appended to the URL.
213 * @return string Endpoint URL including comment information.
214 **/
215 public function get_comment_link( $blog_id, $comment_id, $path = '' ) {
216 return $this->get_link( '/sites/%d/comments/%d', $blog_id, $comment_id, $path );
217 }
218
219 /**
220 * Generate the endpoint URL for Publicize connections
221 *
222 * Used to construct meta links in API responses, specific to Publicize connections.
223 *
224 * @param int $blog_id The site's Jetpack blog ID.
225 * @param int $publicize_connection_id The ID of the Publicize connection.
226 * @param string $path Optional path to be appended to the URL.
227 * @return string Endpoint URL including Publicize connection information.
228 **/
229 public function get_publicize_connection_link( $blog_id, $publicize_connection_id, $path = '' ) {
230 return $this->get_link( '.1/sites/%d/publicize-connections/%d', $blog_id, $publicize_connection_id, $path );
231 }
232
233 /**
234 * Generate the endpoint URL for a single Publicize connection including a Keyring connection
235 *
236 * Used to construct meta links in API responses, specific to a single Publicize and Keyring connection.
237 *
238 * @param int $keyring_token_id The ID of the Keyring connection.
239 * @param string $path Optional path to be appended to the URL.
240 * @return string Endpoint URL including specific Keyring connection information for a specific Publicize connection.
241 **/
242 public function get_publicize_connections_link( $keyring_token_id, $path = '' ) {
243 return $this->get_link( '.1/me/publicize-connections/?keyring_connection_ID=%d', $keyring_token_id, $path );
244 }
245
246 /**
247 * Generate the endpoint URL for a single Keyring connection
248 *
249 * Used to construct meta links in API responses, specific to a Keyring connections.
250 *
251 * @param int $keyring_token_id The ID of the Keyring connection.
252 * @param string $path Optional path to be appended to the URL.
253 * @return string Endpoint URL including specific Keyring connection.
254 **/
255 public function get_keyring_connection_link( $keyring_token_id, $path = '' ) {
256 return $this->get_link( '.1/me/keyring-connections/%d', $keyring_token_id, $path );
257 }
258
259 /**
260 * Generate the endpoint URL for an external service that can be integrated with via Keyring
261 *
262 * Used to construct meta links in API responses, specific to an external service.
263 *
264 * @param int $external_service The ID of the external service.
265 * @param string $path Optional path to be appended to the URL.
266 * @return string Endpoint URL including information about an external service that WordPress.com or Jetpack sites can integrate with via keyring.
267 **/
268 public function get_external_service_link( $external_service, $path = '' ) {
269 return $this->get_link( '.1/meta/external-services/%s', $external_service, $path );
270 }
271
272 /**
273 * Try to find the closest supported version of an endpoint to the current endpoint
274 *
275 * For example, if we were looking at the path /animals/panda:
276 * - if the current endpoint is v1.3 and there is a v1.3 of /animals/%s available, we return 1.3
277 * - if the current endpoint is v1.3 and there is no v1.3 of /animals/%s known, we fall back to the
278 * maximum available version of /animals/%s, e.g. 1.1
279 *
280 * This method is used in get_link() to construct meta links for API responses.
281 *
282 * @param string $template_path The generic endpoint path, e.g. /sites/%s .
283 * @param string $path The current endpoint path, relative to the version, e.g. /sites/12345 .
284 * @param string $request_method Request method used to access the endpoint path .
285 * @return string The current version, or otherwise the maximum version available
286 */
287 public function get_closest_version_of_endpoint( $template_path, $path, $request_method = 'GET' ) {
288 $closest_endpoint_cache_by_version = & $this->closest_endpoint_cache_by_version;
289
290 $closest_endpoint_cache = & $closest_endpoint_cache_by_version[ $this->api->version ];
291 if ( ! $closest_endpoint_cache ) {
292 $closest_endpoint_cache_by_version[ $this->api->version ] = array();
293 $closest_endpoint_cache = & $closest_endpoint_cache_by_version[ $this->api->version ];
294 }
295
296 if ( ! isset( $closest_endpoint_cache[ $template_path ] ) ) {
297 $closest_endpoint_cache[ $template_path ] = array();
298 } elseif ( isset( $closest_endpoint_cache[ $template_path ][ $request_method ] ) ) {
299 return $closest_endpoint_cache[ $template_path ][ $request_method ];
300 }
301
302 $path = untrailingslashit( $path );
303
304 // /help is a special case - always use the current request version
305 if ( wp_endswith( $path, '/help' ) ) {
306 $closest_endpoint_cache[ $template_path ][ $request_method ] = $this->api->version;
307 return $this->api->version;
308 }
309
310 $matches_by_version = & $this->matches_by_version;
311
312 // try to match out of saved matches.
313 if ( ! isset( $matches_by_version[ $this->api->version ] ) ) {
314 $matches_by_version[ $this->api->version ] = array();
315 }
316 foreach ( $matches_by_version[ $this->api->version ] as $match ) {
317 $regex = $match->regex;
318 if ( preg_match( "#^$regex\$#", $path ) ) {
319 $closest_endpoint_cache[ $template_path ][ $request_method ] = $match->version;
320 return $match->version;
321 }
322 }
323
324 $endpoint_path_versions = $this->get_endpoint_path_versions();
325 $last_path_segment = $this->get_last_segment_of_relative_path( $path );
326 $max_version_found = null;
327
328 foreach ( $endpoint_path_versions as $endpoint_last_path_segment => $endpoints ) {
329
330 // Does the last part of the path match the path key? (e.g. 'posts')
331 // If the last part contains a placeholder (e.g. %s), we want to carry on.
332 // phpcs:ignore Universal.Operators.StrictComparisons.LooseNotEqual
333 if ( $last_path_segment != $endpoint_last_path_segment && ! strstr( $endpoint_last_path_segment, '%' ) ) {
334 continue;
335 }
336
337 foreach ( $endpoints as $endpoint ) {
338 // Does the request method match?
339 if ( ! in_array( $request_method, $endpoint['request_methods'], true ) ) {
340 continue;
341 }
342
343 $endpoint_path = untrailingslashit( $endpoint['path'] );
344 $endpoint_path_regex = str_replace( array( '%s', '%d' ), array( '([^/?&]+)', '(\d+)' ), $endpoint_path );
345
346 if ( ! preg_match( "#^$endpoint_path_regex\$#", $path ) ) {
347 continue;
348 }
349
350 // Make sure the endpoint exists at the same version.
351 if ( null !== $this->api->version &&
352 version_compare( $this->api->version, $endpoint['min_version'], '>=' ) &&
353 version_compare( $this->api->version, $endpoint['max_version'], '<=' )
354 ) {
355 array_push(
356 $matches_by_version[ $this->api->version ],
357 (object) array(
358 'version' => $this->api->version,
359 'regex' => $endpoint_path_regex,
360 )
361 );
362 $closest_endpoint_cache[ $template_path ][ $request_method ] = $this->api->version;
363 return $this->api->version;
364 }
365
366 // If the endpoint doesn't exist at the same version, record the max version we found.
367 if ( empty( $max_version_found ) || version_compare( $max_version_found['version'], $endpoint['max_version'], '<' ) ) {
368 $max_version_found = array(
369 'version' => $endpoint['max_version'],
370 'regex' => $endpoint_path_regex,
371 );
372 }
373 }
374 }
375
376 // If the endpoint version is less than the requested endpoint version, return the max version found.
377 if ( ! empty( $max_version_found ) ) {
378 array_push(
379 $matches_by_version[ $this->api->version ],
380 (object) $max_version_found
381 );
382 $closest_endpoint_cache[ $template_path ][ $request_method ] = $max_version_found['version'];
383 return $max_version_found['version'];
384 }
385
386 // Otherwise, use the API version of the current request.
387 return $this->api->version;
388 }
389
390 /**
391 * Get an array of endpoint paths with their associated versions
392 *
393 * @return array Array of endpoint paths, min_versions and max_versions, keyed by last segment of path
394 **/
395 protected function get_endpoint_path_versions() {
396
397 if ( ! empty( $this->cache_result ) ) {
398 return $this->cache_result;
399 }
400
401 /*
402 * Create a map of endpoints and their min/max versions keyed by the last segment of the path (e.g. 'posts')
403 * This reduces the search space when finding endpoint matches in get_closest_version_of_endpoint()
404 */
405 $endpoint_path_versions = array();
406
407 foreach ( $this->api->endpoints as $key => $endpoint_objects ) {
408
409 // @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.
410 // The key contains a serialized path, min_version and max_version.
411 list( $path, $min_version, $max_version ) = unserialize( $key ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize -- Legacy, see serialization at class.json-api.php.
412
413 // Grab the last component of the relative path to use as the top-level key.
414 $last_path_segment = $this->get_last_segment_of_relative_path( $path );
415
416 $endpoint_path_versions[ $last_path_segment ][] = array(
417 'path' => $path,
418 'min_version' => $min_version,
419 'max_version' => $max_version,
420 'request_methods' => array_keys( $endpoint_objects ),
421 );
422 }
423
424 $this->cache_result = $endpoint_path_versions;
425
426 return $endpoint_path_versions;
427 }
428
429 /**
430 * Grab the last segment of a relative path
431 *
432 * @param string $path Path.
433 * @return string Last path segment
434 */
435 protected function get_last_segment_of_relative_path( $path ) {
436 $path_parts = array_filter( explode( '/', $path ) );
437
438 if ( empty( $path_parts ) ) {
439 return null;
440 }
441
442 return end( $path_parts );
443 }
444 }
445