Skip to content

Commit

Permalink
feat!: switch memoize() param order, make cache key optional
Browse files Browse the repository at this point in the history
If not provided, fetches calling function name using `debug_backtrace()`.
  • Loading branch information
kbond committed May 17, 2023
1 parent d2c86cc commit 46a0ea9
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 22 deletions.
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,25 @@ class MyObject

public function method1(): mixed
{
// cache key defaults to the method name "method1"
return $this->memoize(
__FUNCTION__, // memoize requires a "cache key" an easy choice is the function name
fn() => $this->someExpensiveOperation() // called only the first time method1() is called
);
}

public function method2(string $parameter): mixed
public function method2(): mixed
{
return $this->memoize(
fn() => $this->someExpensiveOperation(),
'my_custom_cache_key' // explicitly set the cache key
);
}

public function method3(string $parameter): mixed
{
return $this->memoize(
__FUNCTION__.$parameter, // cache key includes the parameter
fn() => $this->someExpensiveOperation($parameter) // called once per unique parameter
'my_custom_cache_key'.$parameter, // cache key includes the parameter
)
}

Expand Down
10 changes: 9 additions & 1 deletion src/Memoize.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,16 @@ trait Memoize
*
* @return T
*/
protected function memoize(string $key, callable $factory): mixed
protected function memoize(callable $factory, ?string $key = null): mixed
{
if (null === $key) {
$key = \debug_backtrace(options: \DEBUG_BACKTRACE_IGNORE_ARGS, limit: 2)[1]['function'] ?? null;
}

if (!$key) {
throw new \LogicException('Could not automatically determine memoize key. Pass the key explicitly.');
}

return Cache::getInstance()->get($this, $key, $factory);
}

Expand Down
55 changes: 37 additions & 18 deletions tests/MemoizeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@ public function can_memoize_value(): void
$object = new DummyObject();
$factory = new Factory(static fn() => \random_int(1, 100000));

$initial = $object->memoize('key1', $factory);
$initial = $object->memoize($factory, 'key1');

$this->assertSame(1, $factory->calls);
$this->assertIsInt($initial);
$this->assertSame($initial, $object->memoize('key1', $factory));
$this->assertSame($initial, $object->memoize('key1', $factory));
$this->assertSame($initial, $object->memoize('key1', $factory));
$this->assertSame($initial, $object->memoize($factory, 'key1'));
$this->assertSame($initial, $object->memoize($factory, 'key1'));
$this->assertSame($initial, $object->memoize($factory, 'key1'));
$this->assertSame(1, $factory->calls);
$this->assertNotSame($initial, $new = $object->memoize('key2', $factory));
$this->assertSame($new, $object->memoize('key2', $factory));
$this->assertNotSame($initial, $new = $object->memoize($factory, 'key2'));
$this->assertSame($new, $object->memoize($factory, 'key2'));
$this->assertSame(2, $factory->calls);
}

Expand All @@ -48,10 +48,10 @@ public function can_memoize_null(): void
$object = new DummyObject();
$factory = new Factory(static fn() => null);

$this->assertNull($object->memoize('key', $factory));
$this->assertNull($object->memoize('key', $factory));
$this->assertNull($object->memoize('key', $factory));
$this->assertNull($object->memoize('key', $factory));
$this->assertNull($object->memoize($factory, 'key'));
$this->assertNull($object->memoize($factory, 'key'));
$this->assertNull($object->memoize($factory, 'key'));
$this->assertNull($object->memoize($factory, 'key'));
$this->assertSame(1, $factory->calls);
}

Expand All @@ -63,13 +63,13 @@ public function can_clear_single_key(): void
$object = new DummyObject();
$factory = new Factory(static fn() => \random_int(1, 100000));

$initial1 = $object->memoize('key1', $factory);
$initial2 = $object->memoize('key2', $factory);
$initial1 = $object->memoize($factory, 'key1');
$initial2 = $object->memoize($factory, 'key2');

$object->clearMemoized('key1');

$this->assertNotSame($initial1, $object->memoize('key1', $factory));
$this->assertSame($initial2, $object->memoize('key2', $factory));
$this->assertNotSame($initial1, $object->memoize($factory, 'key1'));
$this->assertSame($initial2, $object->memoize($factory, 'key2'));
}

/**
Expand All @@ -80,14 +80,28 @@ public function can_clear_object(): void
$object = new DummyObject();
$factory = new Factory(static fn() => \random_int(1, 100000));

$initial1 = $object->memoize('key1', $factory);
$initial2 = $object->memoize('key2', $factory);
$initial1 = $object->memoize($factory, 'key1');
$initial2 = $object->memoize($factory, 'key2');

$object->clearMemoized();
$object->clearMemoized(); // call twice to ensure "nothing happens" when clearing an unset object

$this->assertNotSame($initial1, $object->memoize('key1', $factory));
$this->assertNotSame($initial2, $object->memoize('key2', $factory));
$this->assertNotSame($initial1, $object->memoize($factory, 'key1'));
$this->assertNotSame($initial2, $object->memoize($factory, 'key2'));
}

/**
* @test
*/
public function can_memoize_without_key(): void
{
$object = new DummyObject();

$value = $object->memoizeSomething();

$this->assertSame($value, $object->memoizeSomething());
$this->assertSame($value, $object->memoizeSomething());
$this->assertSame($value, $object->memoizeSomething());
}
}

Expand All @@ -113,4 +127,9 @@ class DummyObject
memoize as public;
clearMemoized as public;
}

public function memoizeSomething(): int
{
return $this->memoize(static fn() => \random_int(1, 100000));
}
}

0 comments on commit 46a0ea9

Please sign in to comment.