Features

Cache

The cache component is based on Symfony's Cache, providing access to many different adapters through a convenient, simple interface.

Getting started

By default, Tempest uses a filesystem-based caching strategy. You may use a different cache back-end by creating a configuration file for the desired cache adapter.

Once your cache is configured, you may interact with it by using the Tempest\Cache\Cache interface. This is usually done through dependency injection:

src/OrderService.php
use Tempest\Cache\Cache;
use Tempest\DateTime\Duration;

final readonly class OrderService
{
    public function __construct(
        private Cache $cache,
    ) {}

    public function getOrdersCount(): int
    {
        return $this->cache->resolve(
            key: 'orders_count',
            resolve: fn () => $this->fetchOrdersCountFromDatabase(),
            expiration: Duration::hours(12)
        );
    }

    // …
}

The cache interface

Once you have access to the the Cache interface, you gain access to a few useful methods for working with cache items. All methods are documented, so you are free to explore the source to get an understanding of what you can do with it.

Below are a few useful methods that you may need more often than the others:

/**
 * Gets a value from the cache by the given key.
 */
$cache->get($key);

/**
 * Sets a value in the cache for the given key.
 */
$cache->put($key, $value);

/**
 * Gets a value from the cache by the given key, or resolve it using the given callback.
 */
$cache->resolve($key, function () {
    return $this->expensiveOperation();
});

Clearing the cache

The cache may programmatically by cleared by calling the clear() method on a cache instance. However, it is sometimes useful to manually clear it. To do so, you may call the cache:clear command:

./tempest cache:clear

By default, this would clear the main cache. If there are multiple configured caches, you will be prompted to choose which one to clear.

Locks

You may create a lock by calling the lock() method on a cache instance. After being created, the lock needs to be acquired by calling the acquire(), and released by calling the release() method.

Alternatively, the execute() method may be used to acquire a lock, execute a callback, and release the lock automatically when the callback is done.

// Create the lock
$lock = $cache->lock('processing', Duration::seconds(30));

// Acquire the lock, do something and release it.
if ($lock->acquire()) {
    $this->process();

    $lock->release();
}

// Or using a callback, with an optional wait
// time if the lock is not yet available.
$lock->execute($this->process(...), wait: Duration::seconds(30));

Lock ownership

Normally, a lock cannot be acquired if it is already held by another process. However, if you know the owner token, you may still access a lock by specifying the owner parameter.

This may be useful to release a lock in an async command, for instance.

$cache->lock("processing:{$processId}", owner: $processId)
    ->release();

Configuration

Tempest provides a different configuration object for each cache provider. Below are the ones that are currently supported:

Testing

By extending Tempest\Framework\Testing\IntegrationTest from your test case, you gain access to the cache testing utilities through the cache property.

These utilities include a way to replace the cache with a testing implementation, as well as a few assertion methods related to cache items and locks.

Faking the cache

You may generate a fake, testing-only cache by calling the fake() method on the cache property. This will replace the cache implementation in the container, and provide useful assertion methods.

// Replace the cache with a fake implementation
$cache = $this->cache->fake();

// Asserts that the specified cache key exists
$cache->assertCached('users_count');

// Asserts that the cache is empty
$cache->assertEmpty();

Testing locks

Calling the lock() method on the cache testing utility will return a testing lock, which provides a few more testing utilities.

$cache = $this->cache->fake();

// Call some application code
// …

$this->cache->assertNotLocked('processing');