Skip to content

Commit 53c5b13

Browse files
sjinksrinatkhaziev
andauthored
GH-63: Implement wp_cache_get_multiple() (#88)
* GH-63: Implement wp_cache_get_multiple() * Add tests * More tests * Add more tests * Fix null handling for PHP 8 * Minor tweaks * Update object-cache.php Co-authored-by: Rinat K <[email protected]> * Address CR comments Co-authored-by: Rinat K <[email protected]>
1 parent d8084f4 commit 53c5b13

File tree

2 files changed

+152
-0
lines changed

2 files changed

+152
-0
lines changed

object-cache.php

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ function wp_cache_get( $key, $group = '', $force = false, &$found = null ) {
7676
return $wp_object_cache->get( $key, $group, $force, $found );
7777
}
7878

79+
function wp_cache_get_multiple( $keys, $group = '', $force = false ) {
80+
global $wp_object_cache;
81+
82+
return $wp_object_cache->get_multiple( $keys, $group, $force );
83+
}
84+
7985
/**
8086
* Retrieve multiple cache entries
8187
*
@@ -483,6 +489,25 @@ function get( $id, $group = 'default', $force = false, &$found = null ) {
483489
if ( false === $flags ) {
484490
$found = false;
485491
$value = false;
492+
} elseif ( false === $value && ( $flags & 0xFF01 ) === 0x01 ) {
493+
/*
494+
* The lowest byte is used for flags.
495+
* 0x01 means the value is serialized (MMC_SERIALIZED).
496+
* The second lowest indicates data type: 0 is string, 1 is bool, 3 is long, 7 is double.
497+
* `null` is serialized into a string, thus $flags is 0x0001
498+
* Empty string will correspond to $flags = 0x0000 (not serialized).
499+
* For `false` or `true` $flags will be 0x0100
500+
*
501+
* See:
502+
* - https://github.com/websupport-sk/pecl-memcache/blob/2a5de3c5d9c0bd0acbcf7e6e0b7570f15f89f55b/php7/memcache_pool.h#L61-L76 - PHP 7.x
503+
* - https://github.com/websupport-sk/pecl-memcache/blob/ccf702b14b18fce18a1863e115a7b4c964df952e/src/memcache_pool.h#L57-L76 - PHP 8.x
504+
*
505+
* In PHP 8, they changed the way memcache_get() handles `null`:
506+
* https://github.com/websupport-sk/pecl-memcache/blob/ccf702b14b18fce18a1863e115a7b4c964df952e/src/memcache.c#L2175-L2177
507+
*
508+
* If the return value is `null`, it is silently converted to `false`. We can only rely upon $flags to find out whether `false` is real.
509+
*/
510+
$value = null;
486511
}
487512

488513
$this->cache[ $key ] = [
@@ -572,6 +597,57 @@ function get_multi( $groups ) {
572597
return $return;
573598
}
574599

600+
public function get_multiple( $keys, $group = 'default', $force = false ) {
601+
$mc = $this->get_mc( $group );
602+
603+
$no_mc = in_array( $group, $this->no_mc_groups, true );
604+
605+
$uncached_keys = array();
606+
$return = array();
607+
$return_cache = array();
608+
609+
foreach ( $keys as $id ) {
610+
$key = $this->key( $id, $group );
611+
612+
if ( isset( $this->cache[ $key ] ) && ( ! $force || $no_mc ) ) {
613+
$value = $this->cache[ $key ]['found'] ? $this->cache[ $key ]['value'] : false;
614+
$return[ $id ] = is_object( $value ) ? clone $value : $value;
615+
} else if ( $no_mc ) {
616+
$return[ $id ] = false;
617+
$return_cache[ $key ] = [
618+
'value' => false,
619+
'found' => false,
620+
];
621+
} else {
622+
$uncached_keys[ $id ] = $key;
623+
}
624+
}
625+
626+
if ( $uncached_keys ) {
627+
$this->timer_start();
628+
$uncached_keys_list = array_values( $uncached_keys );
629+
$values = $mc->get( $uncached_keys_list );
630+
$elapsed = $this->timer_stop();
631+
632+
$this->group_ops_stats( 'get_multiple', $uncached_keys_list, $group, null, $elapsed );
633+
634+
foreach ( $uncached_keys as $id => $key ) {
635+
$found = array_key_exists( $key, $values );
636+
$value = $found ? $values[ $key ] : false;
637+
638+
$return[ $id ] = $value;
639+
$return_cache[ $key ] = [
640+
'value' => $value,
641+
'found' => $found,
642+
];
643+
}
644+
}
645+
646+
$this->cache = array_merge( $this->cache, $return_cache );
647+
648+
return $return;
649+
}
650+
575651
function flush_prefix( $group ) {
576652
if ( $group === $this->flush_group || $group === $this->global_flush_group ) {
577653
// Never flush the flush numbers.
@@ -694,6 +770,7 @@ function colorize_debug_line( $line, $trailing_html = '' ) {
694770
'get' => 'green',
695771
'get_local' => 'lightgreen',
696772
'get_multi' => 'fuchsia',
773+
'get_multiple' => 'navy',
697774
'set' => 'purple',
698775
'set_local' => 'orchid',
699776
'add' => 'blue',

tests/test-object-cache.php

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,4 +1098,79 @@ public function test_wp_cache_delete_multiple() {
10981098

10991099
$this->assertSame( $expected, $found );
11001100
}
1101+
1102+
public function test_wp_cache_get_multiple() {
1103+
wp_cache_set( 'foo1', 'bar', 'group1' );
1104+
wp_cache_set( 'foo2', 'bar', 'group1' );
1105+
wp_cache_set( 'foo1', 'bar', 'group2' );
1106+
1107+
$found = wp_cache_get_multiple( array( 'foo1', 'foo2', 'foo3' ), 'group1' );
1108+
1109+
$expected = array(
1110+
'foo1' => 'bar',
1111+
'foo2' => 'bar',
1112+
'foo3' => false,
1113+
);
1114+
1115+
$this->assertSame( $expected, $found );
1116+
}
1117+
1118+
public function test_wp_cache_get_multiple_np() {
1119+
$group = 'do-not-persist-me';
1120+
1121+
$added = $this->object_cache->set( 'foo', 'data 1', $group );
1122+
$this->assertTrue( $added );
1123+
1124+
$this->object_cache->add_non_persistent_groups( [ $group ] );
1125+
1126+
$this->object_cache->cache = [];
1127+
1128+
$expected = [
1129+
'foo' => false,
1130+
];
1131+
1132+
$actual = wp_cache_get_multiple( [ 'foo' ], $group );
1133+
$this->assertSame( $expected, $actual );
1134+
}
1135+
1136+
public function test_wp_cache_get_multiple_consistency() {
1137+
$values = [
1138+
'empty-string' => '',
1139+
'empty-array' => [],
1140+
'zero' => 0,
1141+
'false' => false,
1142+
'null' => null,
1143+
];
1144+
1145+
foreach ( $values as $key => $value ) {
1146+
$result = wp_cache_set( $key, $value, 'group' );
1147+
self::assertTrue( $result );
1148+
}
1149+
1150+
$actual = wp_cache_get_multiple( array_keys( $values ), 'group', true );
1151+
self::assertSame( $values, $actual );
1152+
}
1153+
1154+
/**
1155+
* @dataProvider data_wp_cache_get_consistency
1156+
*/
1157+
public function test_wp_cache_get_consistency( $value ) {
1158+
$result = wp_cache_set( 'key', $value, 'group' );
1159+
self::assertTrue( $result );
1160+
1161+
$found = false;
1162+
$actual = wp_cache_get( 'key', 'group', true, $found );
1163+
self::assertTrue( $found );
1164+
self::assertSame( $value, $actual );
1165+
}
1166+
1167+
public function data_wp_cache_get_consistency() {
1168+
return [
1169+
'empty string' => [ '' ],
1170+
'empty array' => [ [] ],
1171+
'zero' => [ 0 ],
1172+
'false' => [ false ],
1173+
'null' => [ null ],
1174+
];
1175+
}
11011176
}

0 commit comments

Comments
 (0)