Skip to content

Commit

Permalink
Merge pull request #8 from alleyinteractive/feature/in-memory-cache
Browse files Browse the repository at this point in the history
Cache items fetched during the request in memory
  • Loading branch information
dlh01 authored Nov 8, 2024
2 parents 1d10e53 + 2b1fd35 commit 436d68f
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 24 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@

This library adheres to [Semantic Versioning](https://semver.org/) and [Keep a CHANGELOG](https://keepachangelog.com/en/1.0.0/).

## 0.4.0

### Added

- In-memory cache of items fetched during the request.

## 0.3.0

## Changed
### Changed

- When storing items with the PSR-16 adapter, the group name is now automatically prefixed to avoid unintended data loss when flushing the cache.

Expand Down
28 changes: 28 additions & 0 deletions src/class-big-pit.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ final class Big_Pit implements Feature {
*/
private bool $ready = false;

/**
* Cached values.
*
* @phpstan-var array<string, array<string, mixed>>
*
* @var array[]
*/
private array $cache = [];

/**
* Instance.
*
Expand Down Expand Up @@ -79,6 +88,17 @@ public function get( string $key, string $group ): mixed {
return null;
}

if ( isset( $this->cache[ $group ] ) && array_key_exists( $key, $this->cache[ $group ] ) ) {
$value = $this->cache[ $group ][ $key ];

if ( is_object( $value ) ) {
// Don't reuse the same instance across multiple calls.
$value = clone $value;
}

return $value;
}

$value = $wpdb->get_var(
$wpdb->prepare(
"SELECT item_value FROM {$wpdb->big_pit} WHERE item_group = %s AND item_key = %s LIMIT 1",
Expand All @@ -91,6 +111,8 @@ public function get( string $key, string $group ): mixed {
$value = maybe_unserialize( $value );
}

$this->cache[ $group ][ $key ] = $value;

return $value;
}

Expand Down Expand Up @@ -141,6 +163,8 @@ public function set( string $key, mixed $value, string $group ): void {
[ '%s', '%s', '%s' ],
);
}

unset( $this->cache[ $group ][ $key ] );
}

/**
Expand All @@ -164,6 +188,8 @@ public function delete( string $key, string $group ): void {
],
[ '%s', '%s' ],
);

unset( $this->cache[ $group ][ $key ] );
}

/**
Expand All @@ -185,6 +211,8 @@ public function flush_group( string $group ): void {
],
[ '%s' ],
);

unset( $this->cache[ $group ] );
}

/**
Expand Down
21 changes: 21 additions & 0 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,32 @@

namespace Alley\WP\Big_Pit\Tests;

use Alley\WP\Big_Pit;
use Mantle\Testkit\Test_Case as TestkitTest_Case;

/**
* Big Pit Base Test Case
*/
abstract class TestCase extends TestkitTest_Case {
/**
* Create the database table.
*/
public static function setUpBeforeClass(): void {
parent::setUpBeforeClass();

Big_Pit::instance()->boot();
}

/**
* Drop the database table.
*/
public static function tearDownAfterClass(): void {
global $wpdb;

// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange
$wpdb->query( 'DROP TABLE ' . $wpdb->big_pit );
delete_option( 'wp_big_pit_database_version' );

parent::tearDownAfterClass();
}
}
24 changes: 1 addition & 23 deletions tests/Unit/CrudUnitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,12 @@
namespace Alley\WP\Big_Pit\Tests\Unit;

use Alley\WP\Big_Pit;
use PHPUnit\Framework\TestCase;
use Alley\WP\Big_Pit\Tests\TestCase;

/**
* CRUD tests.
*/
class CrudUnitTest extends TestCase {
/**
* Create the database table.
*/
public static function setUpBeforeClass(): void {
parent::setUpBeforeClass();

Big_Pit::instance()->boot();
}

/**
* Drop the database table.
*/
public static function tearDownAfterClass(): void {
global $wpdb;

// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange
$wpdb->query( 'DROP TABLE ' . $wpdb->big_pit );
delete_option( 'wp_big_pit_database_version' );

parent::tearDownAfterClass();
}

/**
* Test CRUD operations.
*/
Expand Down
70 changes: 70 additions & 0 deletions tests/Unit/InMemoryCacheTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php
/**
* Big Pit Tests: InMemoryCacheTest class file
*
* @package wp-big-pit
*/

namespace Alley\WP\Big_Pit\Tests\Unit;

use Alley\WP\Big_Pit;
use Alley\WP\Big_Pit\Tests\TestCase;

/**
* Test in-memory cache.
*/
class InMemoryCacheTest extends TestCase {
/**
* Test for expected number of queries with the in-memory cache.
*/
public function test_in_memory_cache() {
global $wpdb;

$big_pit = Big_Pit::instance();

// Two fetches of the same key should only result in one query.
$num_queries_before = $wpdb->num_queries;
$big_pit->get( 'key1', 'group1' );
$big_pit->get( 'key1', 'group1' );
$this->assertSame( 1, $wpdb->num_queries - $num_queries_before );

$big_pit->set( 'key1', 'value1', 'group1' );

// Value has changed, so there should be another query.
$num_queries_before = $wpdb->num_queries;
$big_pit->get( 'key1', 'group1' );
$this->assertSame( 1, $wpdb->num_queries - $num_queries_before );

// Fetching it again should not result in another query.
$big_pit->get( 'key1', 'group1' );
$this->assertSame( 1, $wpdb->num_queries - $num_queries_before );

$big_pit->delete( 'key1', 'group1' );

// Value has changed, so there should be another query.
$num_queries_before = $wpdb->num_queries;
$big_pit->get( 'key1', 'group1' );
$this->assertSame( 1, $wpdb->num_queries - $num_queries_before );

$big_pit->set( 'key1', 'value1', 'group1' );
$big_pit->set( 'key2', 'value2', 'group1' );
$big_pit->get( 'key1', 'group1' );
$big_pit->get( 'key2', 'group1' );
$big_pit->flush_group( 'group1' );

// All values have changed, so there should be queries for each value in the group that was set.
$num_queries_before = $wpdb->num_queries;
$big_pit->get( 'key1', 'group1' );
$big_pit->get( 'key2', 'group1' );
$this->assertSame( 2, $wpdb->num_queries - $num_queries_before );
}

/**
* Check for improper reuse of a cached object.
*/
public function test_cached_object_reference() {
$big_pit = Big_Pit::instance();
$big_pit->set( 'key1', (object) [ 'foo' => 'bar' ], 'group1' );
$this->assertNotSame( $big_pit->get( 'key1', 'group1' ), $big_pit->get( 'key1', 'group1' ) );
}
}

0 comments on commit 436d68f

Please sign in to comment.