PageRenderTime 46ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/forum/bb-includes/backpress/class.wp-object-cache-memcached.php

http://cartonbank.googlecode.com/
PHP | 463 lines | 339 code | 89 blank | 35 comment | 55 complexity | 7c8e6c6a34816aed564372a3cdc446af MD5 | raw file
Possible License(s): GPL-3.0, GPL-2.0, LGPL-2.1, AGPL-1.0, LGPL-3.0
  1. <?php
  2. class WP_Object_Cache
  3. {
  4. // WordPress would need to be initialised with this:
  5. // wp_cache_add_global_groups( array( 'users', 'userlogins', 'usermeta', 'site-options', 'site-lookup', 'blog-lookup', 'blog-details', 'rss' ) );
  6. var $global_groups = array (
  7. '_cache_keys'
  8. );
  9. // WordPress would need to be initialised with this:
  10. // wp_cache_add_non_persistent_groups( array( 'comment', 'counts' ) );
  11. var $no_mc_groups = array();
  12. var $cache = array();
  13. var $mc = array();
  14. var $stats = array();
  15. var $group_ops = array();
  16. var $default_expiration = 0;
  17. function WP_Object_Cache()
  18. {
  19. global $memcached_servers;
  20. if ( isset( $memcached_servers ) ) {
  21. $buckets = $memcached_servers;
  22. } else {
  23. $buckets = array('default' => array('127.0.0.1:11211'));
  24. }
  25. foreach ( $buckets as $bucket => $servers ) {
  26. $this->mc[$bucket] = new Memcache();
  27. foreach ( $servers as $server ) {
  28. list ( $node, $port ) = explode( ':', $server );
  29. $this->mc[$bucket]->addServer( $node, $port, true, 1, 1, 15, true, array( $this, 'failure_callback' ) );
  30. $this->mc[$bucket]->setCompressThreshold( 20000, 0.2 );
  31. }
  32. }
  33. }
  34. function &get_mc( $group )
  35. {
  36. if ( isset( $this->mc[$group] ) ) {
  37. return $this->mc[$group];
  38. }
  39. return $this->mc['default'];
  40. }
  41. function failure_callback( $host, $port )
  42. {
  43. //error_log( "Connection failure for $host:$port\n", 3, '/tmp/memcached.txt' );
  44. }
  45. function close()
  46. {
  47. foreach ( $this->mc as $bucket => $mc ) {
  48. $mc->close();
  49. }
  50. }
  51. function add_global_groups( $groups )
  52. {
  53. if ( !is_array( $groups ) ) {
  54. $groups = (array) $groups;
  55. }
  56. $this->global_groups = array_merge( $this->global_groups, $groups );
  57. $this->global_groups = array_unique( $this->global_groups );
  58. }
  59. function add_non_persistent_groups( $groups )
  60. {
  61. if ( !is_array( $groups ) ) {
  62. $groups = (array) $groups;
  63. }
  64. $this->no_mc_groups = array_merge( $this->no_mc_groups, $groups );
  65. $this->no_mc_groups = array_unique( $this->no_mc_groups );
  66. }
  67. function key( $key, $group )
  68. {
  69. if ( empty( $group ) ) {
  70. $group = 'default';
  71. }
  72. if ( false !== array_search( $group, $this->global_groups ) ) {
  73. $prefix = '';
  74. } else {
  75. $prefix = backpress_get_option( 'application_id' ) . ':';
  76. }
  77. return preg_replace( '/\s+/', '', $prefix . $group . ':' . $key );
  78. }
  79. function get( $id, $group = 'default' )
  80. {
  81. $key = $this->key( $id, $group );
  82. $mc =& $this->get_mc( $group );
  83. if ( isset( $this->cache[$key] ) ) {
  84. $value = $this->cache[$key];
  85. } elseif ( in_array( $group, $this->no_mc_groups ) ) {
  86. $value = false;
  87. } else {
  88. $value = $mc->get($key);
  89. }
  90. @ ++$this->stats['get'];
  91. $this->group_ops[$group][] = "get $id";
  92. if ( NULL === $value ) {
  93. $value = false;
  94. }
  95. $this->cache[$key] = $value;
  96. if ( 'checkthedatabaseplease' == $value ) {
  97. $value = false;
  98. }
  99. return $value;
  100. }
  101. /*
  102. format: $get['group-name'] = array( 'key1', 'key2' );
  103. */
  104. function get_multi( $groups )
  105. {
  106. $return = array();
  107. foreach ( $groups as $group => $ids ) {
  108. $mc =& $this->get_mc( $group );
  109. foreach ( $ids as $id ) {
  110. $key = $this->key( $id, $group );
  111. if ( isset( $this->cache[$key] ) ) {
  112. $return[$key] = $this->cache[$key];
  113. continue;
  114. } elseif ( in_array( $group, $this->no_mc_groups ) ) {
  115. $return[$key] = false;
  116. continue;
  117. } else {
  118. $return[$key] = $mc->get( $key );
  119. }
  120. }
  121. if ( $to_get ) {
  122. $vals = $mc->get_multi( $to_get );
  123. $return = array_merge( $return, $vals );
  124. }
  125. }
  126. @ ++$this->stats['get_multi'];
  127. $this->group_ops[$group][] = "get_multi $id";
  128. $this->cache = array_merge( $this->cache, $return );
  129. return $return;
  130. }
  131. function add( $id, $data, $group = 'default', $expire = 0 )
  132. {
  133. $key = $this->key( $id, $group );
  134. if ( in_array( $group, $this->no_mc_groups ) ) {
  135. $this->cache[$key] = $data;
  136. return true;
  137. }
  138. $mc =& $this->get_mc( $group );
  139. $expire = ( $expire == 0 ) ? $this->default_expiration : $expire;
  140. $result = $mc->add( $key, $data, false, $expire );
  141. @ ++$this->stats['add'];
  142. $this->group_ops[$group][] = "add $id";
  143. if ( false !== $result ) {
  144. $this->cache[$key] = $data;
  145. $this->add_key_to_group_keys_cache( $key, $group );
  146. }
  147. return $result;
  148. }
  149. function set( $id, $data, $group = 'default', $expire = 0 )
  150. {
  151. $key = $this->key($id, $group);
  152. if ( isset( $this->cache[$key] ) && 'checkthedatabaseplease' == $this->cache[$key] ) {
  153. return false;
  154. }
  155. $this->cache[$key] = $data;
  156. if ( in_array( $group, $this->no_mc_groups ) ) {
  157. return true;
  158. }
  159. $expire = ( $expire == 0 ) ? $this->default_expiration : $expire;
  160. $mc =& $this->get_mc( $group );
  161. $result = $mc->set( $key, $data, false, $expire );
  162. if ( false !== $result ) {
  163. $this->add_key_to_group_keys_cache($key, $group);
  164. }
  165. return $result;
  166. }
  167. function replace($id, $data, $group = 'default', $expire = 0) {
  168. $key = $this->key($id, $group);
  169. $expire = ($expire == 0) ? $this->default_expiration : $expire;
  170. $mc =& $this->get_mc($group);
  171. $result = $mc->replace($key, $data, false, $expire);
  172. if ( false !== $result ) {
  173. $this->cache[$key] = $data;
  174. $this->add_key_to_group_keys_cache( $key, $group );
  175. }
  176. return $result;
  177. }
  178. function delete( $id, $group = 'default' )
  179. {
  180. $key = $this->key( $id, $group );
  181. if ( in_array( $group, $this->no_mc_groups ) ) {
  182. unset( $this->cache[$key] );
  183. return true;
  184. }
  185. $mc =& $this->get_mc( $group );
  186. $result = $mc->delete( $key );
  187. @ ++$this->stats['delete'];
  188. $this->group_ops[$group][] = "delete $id";
  189. if ( false !== $result ) {
  190. unset( $this->cache[$key] );
  191. $this->remove_key_from_group_keys_cache( $key, $group );
  192. }
  193. return $result;
  194. }
  195. function flush( $group = null )
  196. {
  197. // Get all the group keys
  198. if ( !$_groups = $this->get( 1, '_group_keys' ) ) {
  199. return true;
  200. }
  201. if ( !is_array( $_groups ) || !count( $_groups ) ) {
  202. return $this->delete( 1, '_group_keys' );
  203. }
  204. if ( is_null( $group ) ) {
  205. $results = array();
  206. foreach ( $_groups as $_group => $_keys ) {
  207. $results[] = $this->delete_all_keys_in_group_key_cache( $_group );
  208. }
  209. if ( in_array( false, $results ) ) {
  210. return false;
  211. }
  212. return true;
  213. }
  214. return $this->delete_all_keys_in_group_key_cache( $group );
  215. }
  216. // Update the cache of group keys or add a new cache if it isn't there
  217. function add_key_to_group_keys_cache( $key, $group )
  218. {
  219. if ( '_group_keys' === $group ) {
  220. return;
  221. }
  222. //error_log( 'Adding key ' . $key . ' to group ' . $group );
  223. // Get all the group keys
  224. if ( !$_groups = $this->get( 1, '_group_keys' ) ) {
  225. $_groups = array( $group => array( $key ) );
  226. return $this->add( 1, $_groups, '_group_keys' );
  227. }
  228. // Don't blow up if it isn't an array
  229. if ( !is_array( $_groups ) ) {
  230. $_groups = array();
  231. }
  232. // If this group isn't in there, then insert it
  233. if ( !isset( $_groups[$group] ) || !is_array( $_groups[$group] ) ) {
  234. $_groups[$group] = array();
  235. }
  236. // If it's already there then do nothing
  237. if ( in_array( $key, $_groups[$group] ) ) {
  238. return true;
  239. }
  240. $_groups[$group][] = $key;
  241. // Remove duplicates
  242. $_groups[$group] = array_unique( $_groups[$group] );
  243. return $this->replace( 1, $_groups, '_group_keys' );
  244. }
  245. // Remove the key from the cache of group keys, delete the cache if it is emptied
  246. function remove_key_from_group_keys_cache( $key, $group )
  247. {
  248. if ( '_group_keys' === $group ) {
  249. return;
  250. }
  251. //error_log( 'Removing key ' . $key . ' from group ' . $group );
  252. // Get all the group keys
  253. if ( !$_groups = $this->get( 1, '_group_keys' ) ) {
  254. return true;
  255. }
  256. // If group keys are somehow borked delete it all
  257. if ( !is_array( $_groups ) ) {
  258. return $this->delete( 1, '_group_keys' );
  259. }
  260. // If it's not there, we're good
  261. if (
  262. !isset( $_groups[$group] ) ||
  263. !is_array( $_groups[$group] ) ||
  264. !in_array( $key, $_groups[$group] )
  265. ) {
  266. return true;
  267. }
  268. // Remove duplicates
  269. $_groups[$group] = array_unique( $_groups[$group] );
  270. // If there is only one key or no keys in the group then delete the group
  271. if ( 2 > count( $_groups[$group] ) ) {
  272. unset( $_groups[$group] );
  273. return $this->replace( 1, $_groups, '_group_keys' );
  274. }
  275. // array_unique() made sure there is only one
  276. if ( $_key = array_search( $key, $_groups[$group] ) ) {
  277. unset( $_groups[$group][$_key] );
  278. }
  279. return $this->replace( 1, $_groups, '_group_keys' );
  280. }
  281. function delete_all_keys_in_group_key_cache( $group )
  282. {
  283. if ( '_group_keys' === $group ) {
  284. return;
  285. }
  286. //error_log( 'Deleting all keys in group ' . $group );
  287. // Get all the group keys
  288. if ( !$_groups = $this->get( 1, '_group_keys' ) ) {
  289. //error_log( '--> !!!! No groups' );
  290. return true;
  291. }
  292. // Check that what we want to loop over is there
  293. if ( !is_array( $_groups ) ) {
  294. //error_log( '--> !!!! Groups is not an array, delete whole key' );
  295. return $this->delete( 1, '_group_keys' );
  296. }
  297. // Check that what we want to loop over is there
  298. if (
  299. !isset( $_groups[$group] ) ||
  300. !is_array( $_groups[$group] )
  301. ) {
  302. //error_log( '--> !!!! No specific group' );
  303. return true;
  304. }
  305. $_groups[$group] = array_unique( $_groups[$group] );
  306. $_remaining_keys = array();
  307. $mc =& $this->get_mc($group);
  308. foreach ( $_groups[$group] as $_key ) {
  309. //error_log( '--> Deleting key ' . $_key );
  310. if ( false !== $mc->delete( $_key ) ) {
  311. //error_log( '--> Deleted key ' . $_key );
  312. unset( $this->cache[$_key] );
  313. }
  314. }
  315. unset( $_groups[$group] );
  316. if ( count( $_groups ) ) {
  317. //error_log( '--> Remove single group' );
  318. return $this->replace( 1, $_groups, '_group_keys' );
  319. }
  320. //error_log( '--> No groups left, delete whole key' );
  321. return $this->delete( 1, '_group_keys' );
  322. }
  323. function incr( $id, $n, $group )
  324. {
  325. $key = $this->key( $id, $group );
  326. $mc =& $this->get_mc( $group );
  327. return $mc->increment( $key, $n );
  328. }
  329. function decr( $id, $n, $group )
  330. {
  331. $key = $this->key( $id, $group );
  332. $mc =& $this->get_mc( $group );
  333. return $mc->decrement( $key, $n );
  334. }
  335. function colorize_debug_line( $line )
  336. {
  337. $colors = array(
  338. 'get' => 'green',
  339. 'set' => 'purple',
  340. 'add' => 'blue',
  341. 'delete' => 'red'
  342. );
  343. $cmd = substr( $line, 0, strpos( $line, ' ' ) );
  344. $cmd2 = "<span style='color:{$colors[$cmd]}'>$cmd</span>";
  345. return $cmd2 . substr( $line, strlen( $cmd ) ) . "\n";
  346. }
  347. function stats()
  348. {
  349. echo "<p>\n";
  350. foreach ( $this->stats as $stat => $n ) {
  351. echo "<strong>$stat</strong> $n";
  352. echo "<br/>\n";
  353. }
  354. echo "</p>\n";
  355. echo "<h3>Memcached:</h3>";
  356. foreach ( $this->group_ops as $group => $ops ) {
  357. if ( !isset( $_GET['debug_queries'] ) && 500 < count( $ops ) ) {
  358. $ops = array_slice( $ops, 0, 500 );
  359. echo "<big>Too many to show! <a href='" . add_query_arg( 'debug_queries', 'true' ) . "'>Show them anyway</a>.</big>\n";
  360. }
  361. echo "<h4>$group commands</h4>";
  362. echo "<pre>\n";
  363. $lines = array();
  364. foreach ( $ops as $op ) {
  365. $lines[] = $this->colorize_debug_line( $op );
  366. }
  367. print_r( $lines );
  368. echo "</pre>\n";
  369. }
  370. if ( $this->debug ) {
  371. var_dump( $this->memcache_debug );
  372. }
  373. }
  374. }