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