tomloprod/memoize

Memoize is a lightweight PHP library designed to handle memoization with ease.

Fund package maintenance!
www.paypal.com/paypalme/tomloprod

Installs: 158

Dependents: 0

Suggesters: 0

Security: 0

Stars: 39

Watchers: 0

Forks: 2

Open Issues: 0

pkg:composer/tomloprod/memoize

v2.2.0 2025-10-08 08:30 UTC

This package is auto-updated.

Last update: 2025-10-08 08:36:49 UTC


README

🧠 Memoize

High-performance memoization library for PHP

GitHub Workflow Status (master) Total Downloads Latest Version License

🎯 About Memoize

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.

✨ Features

πŸ”‘ Key-based Memoization
Cache function results with custom keys

⚑ Single Execution
Functions run only once, results cached forever

🏷️ Namespaces
Organize cache by classes or contexts

🧠 LRU Cache
Automatic memory management with size limits

πŸ“Š Cache Analytics
Built-in statistics and monitoring

πŸƒ Runtime Flags
Dynamic behavior control during execution

πŸš€ Quick Start

Installation

composer require tomloprod/memoize

Basic Usage

🏷️ Namespace Organization

If 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));

πŸ”‘ Key-based Memoization

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)
);

⚑ Single Execution Functions

// 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 instance

🧠 Memory Management

The 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();

πŸƒ Runtime Flags

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:

MethodDescription

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

πŸ’‘ Advanced Examples

πŸƒβ€β™‚οΈ Performance Optimization

// 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'))
);

πŸ“– API Reference

Core Methods

MethodDescription

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.

Cache Management

MethodDescription

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

βš™οΈ Requirements & Installation

  • PHP 8.2+
  • Composer
composer require tomloprod/memoize

πŸ§‘β€πŸ€β€πŸ§‘ Contributing

Contributions 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.