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