Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions src/wp-includes/class-wp-object-cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,8 @@ public function set( $key, $data, $group = 'default', $expire = 0 ) {

if ( is_object( $data ) ) {
$data = clone $data;
} elseif ( is_array( $data ) ) {
$data = $this->deep_copy( $data );
}

$this->cache[ $group ][ $key ] = $data;
Expand Down Expand Up @@ -377,6 +379,8 @@ public function get( $key, $group = 'default', $force = false, &$found = null )
$this->cache_hits += 1;
if ( is_object( $this->cache[ $group ][ $key ] ) ) {
return clone $this->cache[ $group ][ $key ];
} elseif ( is_array( $this->cache[ $group ][ $key ] ) ) {
return $this->deep_copy( $this->cache[ $group ][ $key ] );
} else {
return $this->cache[ $group ][ $key ];
}
Expand Down Expand Up @@ -641,4 +645,32 @@ public function stats() {
}
echo '</ul>';
}

/**
* Performs a deep copy of an item to ensure that arrays of objects
* are properly duplicated
*
* @param mixed $data The data to be duplicated
* @return mixed copy of the data
*/
public function deep_copy( $data ) {
if ( is_object( $data ) ) {
return clone $data;
} elseif ( is_array( $data ) ) {
$new = array();
foreach ( $data as $key => $value ) {
if ( is_object( $value ) ) {
$new[ $key ] = clone $value;
} elseif ( is_array( $value ) ) {
$new[ $key ] = $this->deep_copy( $value );
} else {
$new[ $key ] = $value;
}
}

return $new;
} else {
return $data;
}
}
}
73 changes: 73 additions & 0 deletions tests/phpunit/tests/cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,79 @@ public function test_object_refs() {
$this->assertSame( 'bravo', $object_a->foo );
}

/**
*
* @ticket 30430
*/
public function test_array_with_objects_deep_copy() {
if ( wp_using_ext_object_cache() ) {
$this->markTestSkipped( 'This test requires that an external object cache is not in use.' );
}

$key = __FUNCTION__;

$shallow_object = new stdClass();
$shallow_object->foo = 'alpha';

$deep_object = new stdClass();
$deep_object->value = 'deep_original';

$array_with_objects = array(
'shallow_obj' => $shallow_object,
'string' => 'gamma',
'nested' => array(
'level2' => array(
'deep_obj' => $deep_object,
'primitive' => 'unchanged',
),
),
);

$this->cache->set( $key, $array_with_objects );

$shallow_object->foo = 'modified_alpha';
$deep_object->value = 'deep_modified';

$cached_array = $this->cache->get( $key );

$this->assertSame( 'alpha', $cached_array['shallow_obj']->foo, 'Shallow cached object should not be affected by changes to original object' );
$this->assertSame( 'deep_original', $cached_array['nested']['level2']['deep_obj']->value, 'Deep cached object should not be affected by changes to original object' );
$this->assertSame( 'gamma', $cached_array['string'], 'String values should remain unchanged' );
$this->assertSame( 'unchanged', $cached_array['nested']['level2']['primitive'], 'Primitive values should remain unchanged' );

$cached_array['shallow_obj']->foo = 'modified_from_cache';
$cached_array['nested']['level2']['deep_obj']->value = 'modified_from_cache';

// Retreiving again should not affect the cached data
$cached_array_again = $this->cache->get( $key );
$this->assertSame( 'alpha', $cached_array_again['shallow_obj']->foo, 'Cached data should not be affected by modifications to retrieved objects' );
$this->assertSame( 'deep_original', $cached_array_again['nested']['level2']['deep_obj']->value, 'Deep cached data should not be affected by modifications to retrieved objects' );
}

/**
*
* @ticket 30430
*/
public function test_primitive_arrays_unchanged() {
$key = __FUNCTION__;

$primitive_array = array(
'string' => 'test',
'number' => 123,
'boolean' => false,
'nested' => array(
'nested_string' => 'nested_test',
'nested_number' => 456,
),
);

$this->cache->set( $key, $primitive_array );

$cached_array = $this->cache->get( $key );

$this->assertSame( $primitive_array, $cached_array, 'Primitive arrays should be cached correctly' );
}

public function test_incr() {
$key = __FUNCTION__;

Expand Down