Jetpack_Sync_WP_Replicastore

An implementation of iJetpack_Sync_Replicastore which returns data stored in a WordPress.org DB.

Defined (1)

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

/sync/class.jetpack-sync-wp-replicastore.php  
  1. class Jetpack_Sync_WP_Replicastore implements iJetpack_Sync_Replicastore { 
  2.  
  3.  
  4. public function reset() { 
  5. global $wpdb; 
  6.  
  7. $wpdb->query( "DELETE FROM $wpdb->posts" ); 
  8. $wpdb->query( "DELETE FROM $wpdb->comments" ); 
  9.  
  10. // also need to delete terms from cache 
  11. $term_ids = $wpdb->get_col( "SELECT term_id FROM $wpdb->terms" ); 
  12. foreach ( $term_ids as $term_id ) { 
  13. wp_cache_delete( $term_id, 'terms' ); 
  14.  
  15. $wpdb->query( "DELETE FROM $wpdb->terms" ); 
  16.  
  17. $wpdb->query( "DELETE FROM $wpdb->term_taxonomy" ); 
  18. $wpdb->query( "DELETE FROM $wpdb->term_relationships" ); 
  19.  
  20. // callables and constants 
  21. $wpdb->query( "DELETE FROM $wpdb->options WHERE option_name LIKE 'jetpack_%'" ); 
  22. $wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_key NOT LIKE '\_%'" ); 
  23.  
  24. function full_sync_start( $config ) { 
  25. $this->reset(); 
  26.  
  27. function full_sync_end( $checksum ) { 
  28. // noop right now 
  29.  
  30. public function post_count( $status = null, $min_id = null, $max_id = null ) { 
  31. global $wpdb; 
  32.  
  33. $where = ''; 
  34.  
  35. if ( $status ) { 
  36. $where = "post_status = '" . esc_sql( $status ) . "'"; 
  37. } else { 
  38. $where = '1=1'; 
  39.  
  40. if ( null != $min_id ) { 
  41. $where .= ' AND ID >= ' . intval( $min_id ); 
  42.  
  43. if ( null != $max_id ) { 
  44. $where .= ' AND ID <= ' . intval( $max_id ); 
  45.  
  46. return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts WHERE $where" ); 
  47.  
  48. // TODO: actually use max_id/min_id 
  49. public function get_posts( $status = null, $min_id = null, $max_id = null ) { 
  50. $args = array( 'orderby' => 'ID', 'posts_per_page' => -1 ); 
  51.  
  52. if ( $status ) { 
  53. $args['post_status'] = $status; 
  54. } else { 
  55. $args['post_status'] = 'any'; 
  56.  
  57. return get_posts( $args ); 
  58.  
  59. public function get_post( $id ) { 
  60. return get_post( $id ); 
  61.  
  62. public function upsert_post( $post, $silent = false ) { 
  63. global $wpdb; 
  64.  
  65. // reject the post if it's not a WP_Post 
  66. if ( ! $post instanceof WP_Post ) { 
  67. return; 
  68.  
  69. $post = $post->to_array(); 
  70.  
  71. // reject posts without an ID 
  72. if ( ! isset( $post['ID'] ) ) { 
  73. return; 
  74.  
  75. $now = current_time( 'mysql' ); 
  76. $now_gmt = get_gmt_from_date( $now ); 
  77.  
  78. $defaults = array( 
  79. 'ID' => 0,  
  80. 'post_author' => '0',  
  81. 'post_content' => '',  
  82. 'post_content_filtered' => '',  
  83. 'post_title' => '',  
  84. 'post_name' => '',  
  85. 'post_excerpt' => '',  
  86. 'post_status' => 'draft',  
  87. 'post_type' => 'post',  
  88. 'comment_status' => 'closed',  
  89. 'comment_count' => '0',  
  90. 'ping_status' => '',  
  91. 'post_password' => '',  
  92. 'to_ping' => '',  
  93. 'pinged' => '',  
  94. 'post_parent' => 0,  
  95. 'menu_order' => 0,  
  96. 'guid' => '',  
  97. 'post_date' => $now,  
  98. 'post_date_gmt' => $now_gmt,  
  99. 'post_modified' => $now,  
  100. 'post_modified_gmt' => $now_gmt,  
  101. ); 
  102.  
  103. $post = array_intersect_key( $post, $defaults ); 
  104.  
  105. $post = sanitize_post( $post, 'db' ); 
  106.  
  107. unset( $post['filter'] ); 
  108.  
  109. $exists = $wpdb->get_var( $wpdb->prepare( "SELECT EXISTS( SELECT 1 FROM $wpdb->posts WHERE ID = %d )", $post['ID'] ) ); 
  110.  
  111. if ( $exists ) { 
  112. $wpdb->update( $wpdb->posts, $post, array( 'ID' => $post['ID'] ) ); 
  113. } else { 
  114. $wpdb->insert( $wpdb->posts, $post ); 
  115.  
  116. clean_post_cache( $post['ID'] ); 
  117.  
  118. public function delete_post( $post_id ) { 
  119. wp_delete_post( $post_id, true ); 
  120.  
  121. public function posts_checksum( $min_id = null, $max_id = null ) { 
  122. global $wpdb; 
  123. return $this->table_checksum( $wpdb->posts, Jetpack_Sync_Defaults::$default_post_checksum_columns , 'ID', Jetpack_Sync_Settings::get_blacklisted_post_types_sql(), $min_id, $max_id ); 
  124.  
  125. public function post_meta_checksum( $min_id = null, $max_id = null ) { 
  126. global $wpdb; 
  127. return $this->table_checksum( $wpdb->postmeta, Jetpack_Sync_Defaults::$default_post_meta_checksum_columns , 'meta_id', Jetpack_Sync_Settings::get_whitelisted_post_meta_sql(), $min_id, $max_id ); 
  128.  
  129. public function comment_count( $status = null, $min_id = null, $max_id = null ) { 
  130. global $wpdb; 
  131.  
  132. $comment_approved = $this->comment_status_to_approval_value( $status ); 
  133.  
  134. if ( $comment_approved !== false ) { 
  135. $where = "comment_approved = '" . esc_sql( $comment_approved ) . "'"; 
  136. } else { 
  137. $where = '1=1'; 
  138.  
  139. if ( $min_id != null ) { 
  140. $where .= ' AND comment_ID >= ' . intval( $min_id ); 
  141.  
  142. if ( $max_id != null ) { 
  143. $where .= ' AND comment_ID <= ' . intval( $max_id ); 
  144.  
  145. return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->comments WHERE $where" ); 
  146.  
  147. private function comment_status_to_approval_value( $status ) { 
  148. switch ( $status ) { 
  149. case 'approve': 
  150. return '1'; 
  151. case 'hold': 
  152. return '0'; 
  153. case 'spam': 
  154. return 'spam'; 
  155. case 'trash': 
  156. return 'trash'; 
  157. case 'any': 
  158. return false; 
  159. case 'all': 
  160. return false; 
  161. default: 
  162. return false; 
  163.  
  164. // TODO: actually use max_id/min_id 
  165. public function get_comments( $status = null, $min_id = null, $max_id = null ) { 
  166. $args = array( 'orderby' => 'ID', 'status' => 'all' ); 
  167.  
  168. if ( $status ) { 
  169. $args['status'] = $status; 
  170.  
  171. return get_comments( $args ); 
  172.  
  173. public function get_comment( $id ) { 
  174. return WP_Comment::get_instance( $id ); 
  175.  
  176. public function upsert_comment( $comment ) { 
  177. global $wpdb, $wp_version; 
  178.  
  179. if ( version_compare( $wp_version, '4.4', '<' ) ) { 
  180. $comment = (array) $comment; 
  181. } else { 
  182. // WP 4.4 introduced the WP_Comment Class 
  183. $comment = $comment->to_array(); 
  184.  
  185. // filter by fields on comment table 
  186. $comment_fields_whitelist = array( 
  187. 'comment_ID',  
  188. 'comment_post_ID',  
  189. 'comment_author',  
  190. 'comment_author_email',  
  191. 'comment_author_url',  
  192. 'comment_author_IP',  
  193. 'comment_date',  
  194. 'comment_date_gmt',  
  195. 'comment_content',  
  196. 'comment_karma',  
  197. 'comment_approved',  
  198. 'comment_agent',  
  199. 'comment_type',  
  200. 'comment_parent',  
  201. 'user_id',  
  202. ); 
  203.  
  204. foreach ( $comment as $key => $value ) { 
  205. if ( ! in_array( $key, $comment_fields_whitelist ) ) { 
  206. unset( $comment[ $key ] ); 
  207.  
  208. $exists = $wpdb->get_var( 
  209. $wpdb->prepare( 
  210. "SELECT EXISTS( SELECT 1 FROM $wpdb->comments WHERE comment_ID = %d )",  
  211. $comment['comment_ID'] 
  212. ); 
  213.  
  214. if ( $exists ) { 
  215. $wpdb->update( $wpdb->comments, $comment, array( 'comment_ID' => $comment['comment_ID'] ) ); 
  216. } else { 
  217. $wpdb->insert( $wpdb->comments, $comment ); 
  218.  
  219. wp_update_comment_count( $comment['comment_post_ID'] ); 
  220.  
  221. public function trash_comment( $comment_id ) { 
  222. wp_delete_comment( $comment_id ); 
  223.  
  224. public function delete_comment( $comment_id ) { 
  225. wp_delete_comment( $comment_id, true ); 
  226.  
  227. public function spam_comment( $comment_id ) { 
  228. wp_spam_comment( $comment_id ); 
  229.  
  230. public function trashed_post_comments( $post_id, $statuses ) { 
  231. wp_trash_post_comments( $post_id ); 
  232.  
  233. public function untrashed_post_comments( $post_id ) { 
  234. wp_untrash_post_comments( $post_id ); 
  235.  
  236. public function comments_checksum( $min_id = null, $max_id = null ) { 
  237. global $wpdb; 
  238. return $this->table_checksum( $wpdb->comments, Jetpack_Sync_Defaults::$default_comment_checksum_columns, 'comment_ID', Jetpack_Sync_Settings::get_comments_filter_sql(), $min_id, $max_id ); 
  239.  
  240. public function comment_meta_checksum( $min_id = null, $max_id = null ) { 
  241. global $wpdb; 
  242. return $this->table_checksum( $wpdb->commentmeta, Jetpack_Sync_Defaults::$default_comment_meta_checksum_columns , 'meta_id', Jetpack_Sync_Settings::get_whitelisted_comment_meta_sql(), $min_id, $max_id ); 
  243.  
  244. public function options_checksum() { 
  245. global $wpdb; 
  246.  
  247. $options_whitelist = "'" . implode( "', '", Jetpack_Sync_Defaults::$default_options_whitelist ) . "'"; 
  248. $where_sql = "option_name IN ( $options_whitelist )"; 
  249.  
  250. return $this->table_checksum( $wpdb->options, Jetpack_Sync_Defaults::$default_option_checksum_columns, null, $where_sql, null, null ); 
  251.  
  252.  
  253. public function update_option( $option, $value ) { 
  254. return update_option( $option, $value ); 
  255.  
  256. public function get_option( $option, $default = false ) { 
  257. return get_option( $option, $default ); 
  258.  
  259. public function delete_option( $option ) { 
  260. return delete_option( $option ); 
  261.  
  262. public function set_theme_support( $theme_support ) { 
  263. // noop 
  264.  
  265. public function current_theme_supports( $feature ) { 
  266. return current_theme_supports( $feature ); 
  267.  
  268. public function get_metadata( $type, $object_id, $meta_key = '', $single = false ) { 
  269. return get_metadata( $type, $object_id, $meta_key, $single ); 
  270.  
  271. /** 
  272. * Stores remote meta key/values alongside an ID mapping key 
  273. * @param $type 
  274. * @param $object_id 
  275. * @param $meta_key 
  276. * @param $meta_value 
  277. * @param $meta_id 
  278. * @return bool 
  279. */ 
  280. public function upsert_metadata( $type, $object_id, $meta_key, $meta_value, $meta_id ) { 
  281.  
  282. $table = _get_meta_table( $type ); 
  283. if ( ! $table ) { 
  284. return false; 
  285.  
  286. global $wpdb; 
  287.  
  288. $exists = $wpdb->get_var( $wpdb->prepare( 
  289. "SELECT EXISTS( SELECT 1 FROM $table WHERE meta_id = %d )",  
  290. $meta_id 
  291. ) ); 
  292.  
  293. if ( $exists ) { 
  294. $wpdb->update( $table, array( 
  295. 'meta_key' => $meta_key,  
  296. 'meta_value' => maybe_serialize( $meta_value ),  
  297. ), array( 'meta_id' => $meta_id ) ); 
  298. } else { 
  299. $object_id_field = $type . '_id'; 
  300. $wpdb->insert( $table, array( 
  301. 'meta_id' => $meta_id,  
  302. $object_id_field => $object_id,  
  303. 'meta_key' => $meta_key,  
  304. 'meta_value' => maybe_serialize( $meta_value ),  
  305. ) ); 
  306.  
  307. wp_cache_delete( $object_id, $type . '_meta' ); 
  308.  
  309. return true; 
  310.  
  311. public function delete_metadata( $type, $object_id, $meta_ids ) { 
  312. global $wpdb; 
  313.  
  314. $table = _get_meta_table( $type ); 
  315. if ( ! $table ) { 
  316. return false; 
  317.  
  318. foreach ( $meta_ids as $meta_id ) { 
  319. $wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE meta_id = %d", $meta_id ) ); 
  320.  
  321. // if we don't have an object ID what do we do - invalidate ALL meta? 
  322. if ( $object_id ) { 
  323. wp_cache_delete( $object_id, $type . '_meta' ); 
  324.  
  325. // constants 
  326. public function get_constant( $constant ) { 
  327. $value = get_option( 'jetpack_constant_' . $constant ); 
  328.  
  329. if ( $value ) { 
  330. return $value; 
  331.  
  332. return null; 
  333.  
  334. public function set_constant( $constant, $value ) { 
  335. update_option( 'jetpack_constant_' . $constant, $value ); 
  336.  
  337. public function get_updates( $type ) { 
  338. $all_updates = get_option( 'jetpack_updates', array() ); 
  339.  
  340. if ( isset( $all_updates[ $type ] ) ) { 
  341. return $all_updates[ $type ]; 
  342. } else { 
  343. return null; 
  344.  
  345. public function set_updates( $type, $updates ) { 
  346. $all_updates = get_option( 'jetpack_updates', array() ); 
  347. $all_updates[ $type ] = $updates; 
  348. update_option( 'jetpack_updates', $all_updates ); 
  349.  
  350. // functions 
  351. public function get_callable( $name ) { 
  352. $value = get_option( 'jetpack_' . $name ); 
  353.  
  354. if ( $value ) { 
  355. return $value; 
  356.  
  357. return null; 
  358.  
  359. public function set_callable( $name, $value ) { 
  360. update_option( 'jetpack_' . $name, $value ); 
  361.  
  362. // network options 
  363. public function get_site_option( $option ) { 
  364. return get_option( 'jetpack_network_' . $option ); 
  365.  
  366. public function update_site_option( $option, $value ) { 
  367. return update_option( 'jetpack_network_' . $option, $value ); 
  368.  
  369. public function delete_site_option( $option ) { 
  370. return delete_option( 'jetpack_network_' . $option ); 
  371.  
  372. // terms 
  373. // terms 
  374. public function get_terms( $taxonomy ) { 
  375. return get_terms( $taxonomy ); 
  376.  
  377. public function get_term( $taxonomy, $term_id, $is_term_id = true ) { 
  378. $t = $this->ensure_taxonomy( $taxonomy ); 
  379. if ( ! $t || is_wp_error( $t ) ) { 
  380. return $t; 
  381.  
  382. return get_term( $term_id, $taxonomy ); 
  383.  
  384. private function ensure_taxonomy( $taxonomy ) { 
  385. if ( ! taxonomy_exists( $taxonomy ) ) { 
  386. // try re-registering synced taxonomies 
  387. $taxonomies = $this->get_callable( 'taxonomies' ); 
  388. if ( ! isset( $taxonomies[ $taxonomy ] ) ) { 
  389. // doesn't exist, or somehow hasn't been synced 
  390. return new WP_Error( 'invalid_taxonomy', "The taxonomy '$taxonomy' doesn't exist" ); 
  391. $t = $taxonomies[ $taxonomy ]; 
  392.  
  393. return register_taxonomy( 
  394. $taxonomy,  
  395. $t->object_type,  
  396. (array) $t 
  397. ); 
  398.  
  399. return true; 
  400.  
  401. public function get_the_terms( $object_id, $taxonomy ) { 
  402. return get_the_terms( $object_id, $taxonomy ); 
  403.  
  404. public function update_term( $term_object ) { 
  405. $taxonomy = $term_object->taxonomy; 
  406. global $wpdb; 
  407. $exists = $wpdb->get_var( $wpdb->prepare( 
  408. "SELECT EXISTS( SELECT 1 FROM $wpdb->terms WHERE term_id = %d )",  
  409. $term_object->term_id 
  410. ) ); 
  411. if ( ! $exists ) { 
  412. $term_object = sanitize_term( clone( $term_object ), $taxonomy, 'db' ); 
  413. $term = array( 
  414. 'term_id' => $term_object->term_id,  
  415. 'name' => $term_object->name,  
  416. 'slug' => $term_object->slug,  
  417. 'term_group' => $term_object->term_group,  
  418. ); 
  419. $term_taxonomy = array( 
  420. 'term_taxonomy_id' => $term_object->term_taxonomy_id,  
  421. 'term_id' => $term_object->term_id,  
  422. 'taxonomy' => $term_object->taxonomy,  
  423. 'description' => $term_object->description,  
  424. 'parent' => (int) $term_object->parent,  
  425. 'count' => (int) $term_object->count,  
  426. ); 
  427. $wpdb->insert( $wpdb->terms, $term ); 
  428. $wpdb->insert( $wpdb->term_taxonomy, $term_taxonomy ); 
  429.  
  430. return true; 
  431.  
  432. return wp_update_term( $term_object->term_id, $taxonomy, (array) $term_object ); 
  433.  
  434. public function delete_term( $term_id, $taxonomy ) { 
  435. return wp_delete_term( $term_id, $taxonomy ); 
  436.  
  437. public function update_object_terms( $object_id, $taxonomy, $terms, $append ) { 
  438. wp_set_object_terms( $object_id, $terms, $taxonomy, $append ); 
  439.  
  440. public function delete_object_terms( $object_id, $tt_ids ) { 
  441. global $wpdb; 
  442.  
  443. if ( is_array( $tt_ids ) && ! empty( $tt_ids ) ) { 
  444. $taxonomies = array(); 
  445. foreach ( $tt_ids as $tt_id ) { 
  446. $term = get_term_by( 'term_taxonomy_id', $tt_id ); 
  447. $taxonomies[ $term->taxonomy ][] = $tt_id; 
  448. $in_tt_ids = "'" . implode( "', '", $tt_ids ) . "'"; 
  449.  
  450. /** 
  451. * Fires immediately before an object-term relationship is deleted. 
  452. * @since 2.9.0 
  453. * @param int $object_id Object ID. 
  454. * @param array $tt_ids An array of term taxonomy IDs. 
  455. */ 
  456. do_action( 'delete_term_relationships', $object_id, $tt_ids ); 
  457. $deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id IN ($in_tt_ids)", $object_id ) ); 
  458. foreach ( $taxonomies as $taxonomy => $taxonomy_tt_ids ) { 
  459. wp_cache_delete( $object_id, $taxonomy . '_relationships' ); 
  460. /** 
  461. * Fires immediately after an object-term relationship is deleted. 
  462. * @since 2.9.0 
  463. * @param int $object_id Object ID. 
  464. * @param array $tt_ids An array of term taxonomy IDs. 
  465. */ 
  466. do_action( 'deleted_term_relationships', $object_id, $taxonomy_tt_ids ); 
  467. wp_update_term_count( $taxonomy_tt_ids, $taxonomy ); 
  468.  
  469. return (bool) $deleted; 
  470.  
  471. return false; 
  472.  
  473. // users 
  474. public function user_count() { 
  475.  
  476.  
  477. public function get_user( $user_id ) { 
  478. return WP_User::get_instance( $user_id ); 
  479.  
  480. public function upsert_user( $user ) { 
  481. $this->invalid_call(); 
  482.  
  483. public function delete_user( $user_id ) { 
  484. $this->invalid_call(); 
  485.  
  486. public function upsert_user_locale( $user_id, $local ) { 
  487. $this->invalid_call(); 
  488.  
  489. public function delete_user_locale( $user_id ) { 
  490. $this->invalid_call(); 
  491.  
  492. public function get_user_locale( $user_id ) { 
  493. return jetpack_get_user_locale( $user_id ); 
  494.  
  495. public function get_allowed_mime_types( $user_id ) { 
  496.  
  497.  
  498. public function checksum_all() { 
  499. $post_meta_checksum = $this->checksum_histogram( 'post_meta', 1 ); 
  500. $comment_meta_checksum = $this->checksum_histogram( 'comment_meta', 1 ); 
  501.  
  502. return array( 
  503. 'posts' => $this->posts_checksum(),  
  504. 'comments' => $this->comments_checksum(),  
  505. 'post_meta'=> reset( $post_meta_checksum ),  
  506. 'comment_meta'=> reset( $comment_meta_checksum ),  
  507. ); 
  508.  
  509. function checksum_histogram( $object_type, $buckets, $start_id = null, $end_id = null, $columns = null, $strip_non_ascii = true ) { 
  510. global $wpdb; 
  511.  
  512. $wpdb->queries = array(); 
  513.  
  514. switch( $object_type ) { 
  515. case "posts": 
  516. $object_count = $this->post_count( null, $start_id, $end_id ); 
  517. $object_table = $wpdb->posts; 
  518. $id_field = 'ID'; 
  519. $where_sql = Jetpack_Sync_Settings::get_blacklisted_post_types_sql(); 
  520. if ( empty( $columns ) ) { 
  521. $columns = Jetpack_Sync_Defaults::$default_post_checksum_columns; 
  522. break; 
  523. case "post_meta": 
  524. $object_table = $wpdb->postmeta; 
  525. $where_sql = Jetpack_Sync_Settings::get_whitelisted_post_meta_sql(); 
  526. $object_count = $this->meta_count( $object_table, $where_sql, $start_id, $end_id ); 
  527. $id_field = 'meta_id'; 
  528.  
  529. if ( empty( $columns ) ) { 
  530. $columns = Jetpack_Sync_Defaults::$default_post_meta_checksum_columns; 
  531. break; 
  532. case "comments": 
  533. $object_count = $this->comment_count( null, $start_id, $end_id ); 
  534. $object_table = $wpdb->comments; 
  535. $id_field = 'comment_ID'; 
  536. $where_sql = Jetpack_Sync_Settings::get_comments_filter_sql(); 
  537. if ( empty( $columns ) ) { 
  538. $columns = Jetpack_Sync_Defaults::$default_comment_checksum_columns; 
  539. break; 
  540. case "comment_meta": 
  541. $object_table = $wpdb->commentmeta; 
  542. $where_sql = Jetpack_Sync_Settings::get_whitelisted_comment_meta_sql(); 
  543. $object_count = $this->meta_count( $object_table, $where_sql, $start_id, $end_id ); 
  544. $id_field = 'meta_id'; 
  545. if ( empty( $columns ) ) { 
  546. $columns = Jetpack_Sync_Defaults::$default_post_meta_checksum_columns; 
  547. break; 
  548. default: 
  549. return false; 
  550.  
  551. $bucket_size = intval( ceil( $object_count / $buckets ) ); 
  552. $previous_max_id = 0; 
  553. $histogram = array(); 
  554.  
  555. $where = '1=1'; 
  556.  
  557. if ( $start_id ) { 
  558. $where .= " AND $id_field >= " . intval( $start_id ); 
  559.  
  560. if ( $end_id ) { 
  561. $where .= " AND $id_field <= " . intval( $end_id ); 
  562.  
  563. do { 
  564. list( $first_id, $last_id ) = $wpdb->get_row( 
  565. "SELECT MIN($id_field) as min_id, MAX($id_field) as max_id FROM ( SELECT $id_field FROM $object_table WHERE $where AND $id_field > $previous_max_id ORDER BY $id_field ASC LIMIT $bucket_size ) as ids",  
  566. ARRAY_N 
  567. ); 
  568.  
  569. // get the checksum value 
  570. $value = $this->table_checksum( $object_table, $columns, $id_field, $where_sql, $first_id, $last_id, $strip_non_ascii ); 
  571.  
  572. if ( is_wp_error( $value ) ) { 
  573. return $value; 
  574.  
  575. if ( $first_id === null || $last_id === null ) { 
  576. break; 
  577. } elseif ( $first_id === $last_id ) { 
  578. $histogram[ $first_id ] = $value; 
  579. } else { 
  580. $histogram[ "{$first_id}-{$last_id}" ] = $value; 
  581.  
  582. $previous_max_id = $last_id; 
  583. } while ( true ); 
  584.  
  585. return $histogram; 
  586.  
  587. private function table_checksum( $table, $columns, $id_column, $where_sql = '1=1', $min_id = null, $max_id = null, $strip_non_ascii = true ) { 
  588. global $wpdb; 
  589.  
  590. // sanitize to just valid MySQL column names 
  591. $sanitized_columns = preg_grep ( '/^[0-9, a-z, A-Z$_]+$/i', $columns ); 
  592.  
  593. if ( $strip_non_ascii ) { 
  594. $columns_sql = implode( ', ', array_map( array( $this, 'strip_non_ascii_sql' ), $sanitized_columns ) ); 
  595. } else { 
  596. $columns_sql = implode( ', ', $sanitized_columns ); 
  597.  
  598. if ( $min_id !== null ) { 
  599. $min_id = intval( $min_id ); 
  600. $where_sql .= " AND $id_column >= $min_id"; 
  601.  
  602. if ( $max_id !== null ) { 
  603. $max_id = intval( $max_id ); 
  604. $where_sql .= " AND $id_column <= $max_id"; 
  605.  
  606. $query = <<<ENDSQL 
  607. SELECT CONV(BIT_XOR(CRC32(CONCAT({$columns_sql}))), 10, 16) 
  608. FROM $table 
  609. WHERE $where_sql 
  610. ENDSQL; 
  611. $result = $wpdb->get_var( $query ); 
  612.  
  613. if ( $wpdb->last_error ) { 
  614. return new WP_Error( 'database_error', $wpdb->last_error ); 
  615.  
  616. return $result; 
  617.  
  618.  
  619. private function meta_count( $table, $where_sql, $min_id, $max_id ) { 
  620. global $wpdb; 
  621.  
  622. if ( $min_id != null ) { 
  623. $where_sql .= ' AND meta_id >= ' . intval( $min_id ); 
  624.  
  625. if ( $max_id != null ) { 
  626. $where_sql .= ' AND meta_id <= ' . intval( $max_id ); 
  627.  
  628. return $wpdb->get_var( "SELECT COUNT(*) FROM $table WHERE $where_sql" ); 
  629.  
  630. /** 
  631. * Wraps a column name in SQL which strips non-ASCII chars. 
  632. * This helps normalize data to avoid checksum differences caused by 
  633. * badly encoded data in the DB 
  634. */ 
  635. function strip_non_ascii_sql( $column_name ) { 
  636. return "REPLACE( CONVERT( $column_name USING ascii ), '?', '' )"; 
  637.  
  638. private function invalid_call() { 
  639. $backtrace = debug_backtrace(); 
  640. $caller = $backtrace[1]['function']; 
  641. throw new Exception( "This function $caller is not supported on the WP Replicastore" );