WPCOM_JSON_API_Links

The WordPress Core WPCOM JSON API Links class.

Defined (1)

The class is defined in the following location(s).

/sal/class.json-api-links.php  
  1. class WPCOM_JSON_API_Links { 
  2. private $api; 
  3. private static $instance; 
  4.  
  5. public static function getInstance() { 
  6. if ( null === self::$instance ) { 
  7. self::$instance = new self(); 
  8.  
  9. return self::$instance; 
  10.  
  11. // protect these methods for singleton 
  12. protected function __construct() {  
  13. $this->api = WPCOM_JSON_API::init(); 
  14. private function __clone() { } 
  15. private function __wakeup() { } 
  16.  
  17. /** 
  18. * Generate a URL to an endpoint 
  19. * Used to construct meta links in API responses 
  20. * @param mixed $args Optional arguments to be appended to URL 
  21. * @return string Endpoint URL 
  22. **/ 
  23. function get_link() { 
  24. $args = func_get_args(); 
  25. $format = array_shift( $args ); 
  26. $base = WPCOM_JSON_API__BASE; 
  27.  
  28. $path = array_pop( $args ); 
  29.  
  30. if ( $path ) { 
  31. $path = '/' . ltrim( $path, '/' ); 
  32.  
  33. $args[] = $path; 
  34.  
  35. // Escape any % in args before using sprintf 
  36. $escaped_args = array(); 
  37. foreach ( $args as $arg_key => $arg_value ) { 
  38. $escaped_args[ $arg_key ] = str_replace( '%', '%%', $arg_value ); 
  39.  
  40. $relative_path = vsprintf( "$format%s", $escaped_args ); 
  41.  
  42. if ( ! wp_startswith( $relative_path, '.' ) ) { 
  43. // Generic version. Match the requested version as best we can 
  44. $api_version = $this->get_closest_version_of_endpoint( $format, $relative_path ); 
  45. $base = substr( $base, 0, - 1 ) . $api_version; 
  46.  
  47. // escape any % in the relative path before running it through sprintf again 
  48. $relative_path = str_replace( '%', '%%', $relative_path ); 
  49. // http, WPCOM_JSON_API__BASE, ... , path 
  50. // %s , %s , $format, %s 
  51. return esc_url_raw( sprintf( "https://%s$relative_path", $base ) ); 
  52.  
  53. function get_me_link( $path = '' ) { 
  54. return $this->get_link( '/me', $path ); 
  55.  
  56. function get_taxonomy_link( $blog_id, $taxonomy_id, $taxonomy_type, $path = '' ) { 
  57. switch ( $taxonomy_type ) { 
  58. case 'category': 
  59. return $this->get_link( '/sites/%d/categories/slug:%s', $blog_id, $taxonomy_id, $path ); 
  60.  
  61. case 'post_tag': 
  62. return $this->get_link( '/sites/%d/tags/slug:%s', $blog_id, $taxonomy_id, $path ); 
  63.  
  64. default: 
  65. return $this->get_link( '/sites/%d/taxonomies/%s/terms/slug:%s', $blog_id, $taxonomy_type, $taxonomy_id, $path ); 
  66.  
  67. function get_media_link( $blog_id, $media_id, $path = '' ) { 
  68. return $this->get_link( '/sites/%d/media/%d', $blog_id, $media_id, $path ); 
  69.  
  70. function get_site_link( $blog_id, $path = '' ) { 
  71. return $this->get_link( '/sites/%d', $blog_id, $path ); 
  72.  
  73. function get_post_link( $blog_id, $post_id, $path = '' ) { 
  74. return $this->get_link( '/sites/%d/posts/%d', $blog_id, $post_id, $path ); 
  75.  
  76. function get_comment_link( $blog_id, $comment_id, $path = '' ) { 
  77. return $this->get_link( '/sites/%d/comments/%d', $blog_id, $comment_id, $path ); 
  78.  
  79. function get_publicize_connection_link( $blog_id, $publicize_connection_id, $path = '' ) { 
  80. return $this->get_link( '.1/sites/%d/publicize-connections/%d', $blog_id, $publicize_connection_id, $path ); 
  81.  
  82. function get_publicize_connections_link( $keyring_token_id, $path = '' ) { 
  83. return $this->get_link( '.1/me/publicize-connections/?keyring_connection_ID=%d', $keyring_token_id, $path ); 
  84.  
  85. function get_keyring_connection_link( $keyring_token_id, $path = '' ) { 
  86. return $this->get_link( '.1/me/keyring-connections/%d', $keyring_token_id, $path ); 
  87.  
  88. function get_external_service_link( $external_service, $path = '' ) { 
  89. return $this->get_link( '.1/meta/external-services/%s', $external_service, $path ); 
  90.  
  91. /** 
  92. * Try to find the closest supported version of an endpoint to the current endpoint 
  93. * For example, if we were looking at the path /animals/panda: 
  94. * - if the current endpoint is v1.3 and there is a v1.3 of /animals/%s available, we return 1.3 
  95. * - if the current endpoint is v1.3 and there is no v1.3 of /animals/%s known, we fall back to the 
  96. * maximum available version of /animals/%s, e.g. 1.1 
  97. * This method is used in get_link() to construct meta links for API responses. 
  98. *  
  99. * @param $template_path The generic endpoint path, e.g. /sites/%s 
  100. * @param $path string The current endpoint path, relative to the version, e.g. /sites/12345 
  101. * @param $method string Request method used to access the endpoint path 
  102. * @return string The current version, or otherwise the maximum version available 
  103. */ 
  104. function get_closest_version_of_endpoint( $template_path, $path, $request_method = 'GET' ) { 
  105. static $closest_endpoint_cache; 
  106.  
  107. if ( ! $closest_endpoint_cache ) { 
  108. $closest_endpoint_cache = array(); 
  109.  
  110. if ( ! isset( $closest_endpoint_cache[ $template_path ] ) ) { 
  111. $closest_endpoint_cache[ $template_path ] = array(); 
  112. } elseif ( isset( $closest_endpoint_cache[ $template_path ][ $request_method ] ) ) { 
  113. return $closest_endpoint_cache[ $template_path ][ $request_method ];  
  114.  
  115. $path = untrailingslashit( $path ); 
  116.  
  117. // /help is a special case - always use the current request version 
  118. if ( wp_endswith( $path, '/help' ) ) { 
  119. return $closest_endpoint_cache[ $template_path ][ $request_method ] = $this->api->version; 
  120.  
  121. static $matches; 
  122. if ( empty( $matches ) ) { 
  123. $matches = array(); 
  124. } else { 
  125. // try to match out of saved matches 
  126. foreach( $matches as $match ) { 
  127. $regex = $match->regex; 
  128. if ( preg_match( "#^$regex\$#", $path ) ) { 
  129. return $closest_endpoint_cache[ $template_path ][ $request_method ] = $match->version; 
  130.  
  131. $endpoint_path_versions = $this->get_endpoint_path_versions(); 
  132. $last_path_segment = $this->get_last_segment_of_relative_path( $path ); 
  133. $max_version_found = null; 
  134.  
  135. foreach ( $endpoint_path_versions as $endpoint_last_path_segment => $endpoints ) { 
  136.  
  137. // Does the last part of the path match the path key? (e.g. 'posts') 
  138. // If the last part contains a placeholder (e.g. %s), we want to carry on 
  139. if ( $last_path_segment != $endpoint_last_path_segment && ! strstr( $endpoint_last_path_segment, '%' ) ) { 
  140. continue; 
  141.  
  142. foreach ( $endpoints as $endpoint ) { 
  143. // Does the request method match? 
  144. if ( ! in_array( $request_method, $endpoint['request_methods'] ) ) { 
  145. continue; 
  146.  
  147. $endpoint_path = untrailingslashit( $endpoint['path'] ); 
  148. $endpoint_path_regex = str_replace( array( '%s', '%d' ), array( '([^/?&]+)', '(\d+)' ), $endpoint_path ); 
  149.  
  150. if ( ! preg_match( "#^$endpoint_path_regex\$#", $path ) ) { 
  151. continue; 
  152.  
  153. // Make sure the endpoint exists at the same version 
  154. if ( version_compare( $this->api->version, $endpoint['min_version'], '>=') && 
  155. version_compare( $this->api->version, $endpoint['max_version'], '<=') ) { 
  156. array_push( $matches, (object) array( 'version' => $this->api->version, 'regex' => $endpoint_path_regex ) ); 
  157. return $closest_endpoint_cache[ $template_path ][ $request_method ] = $this->api->version; 
  158.  
  159. // If the endpoint doesn't exist at the same version, record the max version we found 
  160. if ( empty( $max_version_found ) || version_compare( $max_version_found['version'], $endpoint['max_version'], '<' ) ) { 
  161. $max_version_found = array( 'version' => $endpoint['max_version'], 'regex' => $endpoint_path_regex ); 
  162.  
  163. // If the endpoint version is less than the requested endpoint version, return the max version found 
  164. if ( ! empty( $max_version_found ) ) { 
  165. array_push( $matches, (object) $max_version_found ); 
  166. return $max_version_found['version']; 
  167.  
  168. // Otherwise, use the API version of the current request 
  169. return $this->api->version; 
  170.  
  171. /** 
  172. * Get an array of endpoint paths with their associated versions 
  173. * The result is cached for 30 minutes. 
  174. * @return array Array of endpoint paths, min_versions and max_versions, keyed by last segment of path 
  175. **/ 
  176. protected function get_endpoint_path_versions() { 
  177.  
  178. static $cache_result; 
  179.  
  180. if ( ! empty ( $cache_result ) ) { 
  181. return $cache_result; 
  182.  
  183. /** 
  184. * Create a map of endpoints and their min/max versions keyed by the last segment of the path (e.g. 'posts') 
  185. * This reduces the search space when finding endpoint matches in get_closest_version_of_endpoint() 
  186. */ 
  187. $endpoint_path_versions = array(); 
  188.  
  189. foreach ( $this->api->endpoints as $key => $endpoint_objects ) { 
  190.  
  191. // The key contains a serialized path, min_version and max_version 
  192. list( $path, $min_version, $max_version ) = unserialize( $key ); 
  193.  
  194. // Grab the last component of the relative path to use as the top-level key 
  195. $last_path_segment = $this->get_last_segment_of_relative_path( $path ); 
  196.  
  197. $endpoint_path_versions[ $last_path_segment ][] = array( 
  198. 'path' => $path,  
  199. 'min_version' => $min_version,  
  200. 'max_version' => $max_version,  
  201. 'request_methods' => array_keys( $endpoint_objects ) 
  202. ); 
  203.  
  204. $cache_result = $endpoint_path_versions; 
  205.  
  206. return $endpoint_path_versions; 
  207.  
  208. /** 
  209. * Grab the last segment of a relative path 
  210. * @param string $path Path 
  211. * @return string Last path segment 
  212. */ 
  213. protected function get_last_segment_of_relative_path( $path) { 
  214. $path_parts = array_filter( explode( '/', $path ) ); 
  215.  
  216. if ( empty( $path_parts ) ) { 
  217. return null; 
  218.  
  219. return end( $path_parts );