Memoize is a lightweight PHP library designed to implement memoization and function caching techniques with ease.
Transform expensive function calls into lightning-fast cache lookups with zero configuration.
|
π Key-based Memoization β‘ Single Execution π·οΈ Namespaces |
π§ LRU Cache π Cache Analytics π Runtime Flags |
composer require tomloprod/memoizeIf you want to get the most out of the package and better organize your memoization, we recommend using namespaces.
When using namespaces, if you use a $key with a null value, the callback wonβt be executed (especially useful in certain cases).
// Organize cache by context
$userSettings = memoize()
->for(UserSettings::class)
->memo($userId, fn() => UserSettings::where('user_id', $userId)->first());
$productCache = memoize()
->for(Product::class)
->memo($productId, fn() => Product::with('variants')->find($productId));You can also not use namespaces and just memoize keys.
// Expensive API call cached by key
$weather = memoize()->memo(
'weather_london',
fn() => Http::get('api.weather.com/london')->json()
);
// Database query with dynamic key
$user = memoize()->memo(
"user_{$id}",
fn() => User::with('profile', 'orders')->find($id)
);// Initialize expensive resources only once
$services = memoize()->once(fn() => [
'redis' => new Redis(),
'elasticsearch' => new Client(),
'logger' => new Logger(),
]);
$redis = $services()['redis']; // Initialized once
$same = $services()['redis']; // Same instanceThe library uses an LRU (Least Recently Used) algorithm to automatically manage memory and prevent unlimited cache growth.
How does LRU work?
- Maintains a record of the access order for cache entries
- When the maximum limit (
maxSize) is reached, automatically removes the least recently used entry - Every time you access an entry (read or write), it moves to the front of the queue
- Older entries remain at the end and are candidates for removal
This ensures that the most relevant and frequently used data remains in memory, while obsolete data is automatically removed.
// Set LRU cache limit (by default, there is no max size)
memoize()->setMaxSize(1000);
// Cache statistics
$stats = memoize()->getStats();
// ['size' => 150, 'maxSize' => 1000, 'head' => [...], 'tail' => [...]]
// Clear specific or all cache
memoize()->forget('user_123');
memoize()->for('App\\Model\\User')->forget('123');
// Or clear all cache
memoize()->flush();Control memoization behavior dynamically during execution with runtime flags. These flags exist only in memory and reset between requests/processes.
How do runtime flags work?
- Flags are stored in memory during the current execution
- They allow conditional behavior without external configuration
- Perfect for debug modes, logging control, and dynamic optimizations
- Automatically cleared when the process ends
// Skip cache during testing
memoize()->enableFlag('bypass_cache');
$userData = memoize()->memo("user_{$id}", function() use ($id) {
if (memoize()->hasFlag('bypass_cache')) {
return User::fresh()->find($id); // Always fetch from DB in tests
}
return User::find($id);
});
// Feature toggles without external dependencies
memoize()->enableFlag('new_algorithm');
$result = memoize()->memo($cacheKey, function() {
if (memoize()->hasFlag('new_algorithm')) {
return $this->calculateWithNewAlgorithm();
}
return $this->calculateWithOldAlgorithm();
});
// Development vs Production behavior
if (app()->environment('local') && memoize()->hasFlag('dev_mode')) {
memoize()->enableFlags(['verbose_logging', 'bypass_cache']);
}
// Model boot method with conditional service calls
class Product extends Model
{
protected static function boot()
{
parent::boot();
static::updated(function ($product) {
// Only call external stock service if flag is not set
if (! memoize()->hasFlag('disableStockService')) {
app(StockService::class)->updateInventory($product);
}
});
}
}Runtime Flag Methods:
| Method | Description |
|
enableFlag(string $flag) | Enable a specific runtime flag |
|
disableFlag(string $flag) | Disable a specific runtime flag |
|
toggleFlag(string $flag) | Toggle flag state (enabled/disabled) |
|
hasFlag(string $flag): bool | Check if a specific flag is enabled |
|
enableFlags(array $flags) | Enable multiple flags at once |
|
disableFlags(array $flags) | Disable multiple flags at once |
|
hasAnyFlag(array $flags): bool | Check if at least one flag is enabled |
|
hasAllFlags(array $flags): bool | Check if all specified flags are enabled |
|
getFlags(): array | Get all currently enabled flags |
|
clearFlags() | Clear all enabled flags |
// Fibonacci with memoization - O(n) instead of O(2^n)
function fibonacci(int $n): int {
return memoize()->memo(
"fib_{$n}",
fn() => $n <= 1 ? $n : fibonacci($n - 1) + fibonacci($n - 2)
);
}
// Complex data aggregation
$salesReport = memoize()->memo(
"sales_report_{$month}",
fn() => Order::whereMonth('created_at', $month)
->with('items.product')
->get()
->groupBy('status')
->map(fn($orders) => $orders->sum('total'))
);| Method | Description |
|
memo(string|int|float|null $key, callable $callback) |
Key-based memoization - Execute callback and cache result by key. Returns cached value on subsequent calls. |
|
once(callable $callback) |
Single execution - Returns a wrapper function that executes the callback only once, caching the result forever. |
|
for(string $class) |
Namespace organization - Set namespace to organize cache by class/context. Automatically cleared after use. |
| Method | Description |
|
has(string|int|float $key): bool | Check if a key exists in cache |
|
forget(string|int|float $key): bool | Remove specific key from cache |
|
flush(): void | Clear all cached values |
|
setMaxSize(?int $maxSize): void | Set maximum entries (LRU eviction) |
|
getStats(): array | Get detailed cache statistics |
- PHP 8.2+
- Composer
composer require tomloprod/memoizeContributions are welcome, and are accepted via pull requests. Please review these guidelines before submitting any pull requests.
Memoize was created by TomΓ‘s LΓ³pez and open-sourced under the MIT license.