This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Nette Caching is a PHP library providing flexible caching with multiple storage backends and advanced dependency tracking. It's part of the Nette Framework ecosystem.
Key features:
- Multiple storage backends (FileStorage, MemcachedStorage, SQLiteStorage, MemoryStorage)
- Advanced dependency tracking (tags, priorities, file changes, callbacks)
- Cache stampede prevention in FileStorage
- Atomic operations with file locking
- PSR-16 SimpleCache adapter
- Latte template integration with
{cache}tag - Nette DI integration
Requirements: PHP 8.1-8.5
# Run all tests
vendor/bin/tester tests -C -s -C
# Run specific test directory
vendor/bin/tester tests/Caching -C -s -C
vendor/bin/tester tests/Storages -C -s -C
# Run single test file
php tests/Caching/Cache.bulkLoad.phpt
# Flags used:
# -C = Use system-wide php.ini
# -s -C = Show skipped tests# Run PHPStan (level 5)
composer run phpstan
# Or directly
vendor/bin/phpstan analyse# Nette coding standard checks
composer run testerThe library follows a clean separation of concerns:
Cache (high-level API)
↓
Storage interface (abstraction)
↓
Storage implementations (FileStorage, MemcachedStorage, etc.)
↓
Journal interface (for tags/priorities)
↓
SQLiteJournal implementation
Cache (src/Caching/Cache.php): Primary API for caching operations. Provides namespace isolation, dependency tracking, memoization (wrap(), call()), and output capturing (capture(), was start() in v3.0).
Storage interface (src/Caching/Storage.php): Defines the contract all storage backends must implement:
read(string $key): mixedwrite(string $key, $data, array $dependencies): voidremove(string $key): voidclean(array $conditions): voidlock(string $key): void- Prevents concurrent writes
Journal interface (src/Caching/Storages/Journal.php): Tracks metadata for tags and priorities. Required for:
Cache::Tags- Tag-based invalidationCache::Priority- Priority-based cleanup
Default implementation: SQLiteJournal using SQLite database at {tempDir}/journal.s3db.
All in src/Caching/Storages/:
-
FileStorage - Production default. Files stored in temp directory with atomic operations via file locking (LOCK_SH for reads, LOCK_EX for writes). Implements cache stampede prevention: when cache miss occurs with concurrent requests, only first thread generates value, others wait. File format: 6-byte header with meta size + serialized metadata + data.
-
SQLiteStorage - Single-file database storage. Good for shared hosting environments.
-
MemcachedStorage - Distributed caching via Memcached server. Requires
memcachedPHP extension. -
MemoryStorage - In-memory array storage, lost after request. Used for testing or request-scoped caching.
-
DevNullStorage - No-op storage for testing when you want to disable caching.
Cache dependencies control expiration and invalidation. All use Cache class constants:
Cache::Expire- Time-based expiration (timestamp, seconds, or string like "20 minutes")Cache::Sliding- Extends expiration on each readCache::Files- Invalidate when file(s) modified (checks filemtime)Cache::Items- Invalidate when other cache items expireCache::Tags- Tag-based invalidation (requires Journal)Cache::Priority- Priority-based cleanup (requires Journal)Cache::Callbacks- Custom validation callbacksCache::Constants- Invalidate when PHP constants change
Dependencies can be combined; cache expires when ANY criterion fails.
Nette DI Bridge (src/Bridges/CacheDI/CacheExtension.php):
- Auto-registers Storage service (FileStorage by default)
- Auto-registers Journal service (SQLiteJournal if pdo_sqlite available)
- Validates and creates temp directory
- Services registered:
cache.storage,cache.journal
Latte Bridge (src/Bridges/CacheLatte/):
- Provides
{cache}tag for template caching - Runtime in
Runtime.phpmanages cache lifecycle - Node compilation in
Nodes/CacheNode.php - Automatic invalidation when template source changes
- Supports parameters:
{cache $id, expire: '20 minutes', tags: [tag1, tag2]} - Can be conditional:
{cache $id, if: !$form->isSubmitted()}
PSR-16 Bridge (src/Bridges/Psr/PsrCacheAdapter.php):
- Adapts Nette Storage to PSR-16 SimpleCache interface
- Used for PSR compatibility in third-party integrations
Two specialized classes enable efficient bulk operations:
- BulkReader (
src/Caching/BulkReader.php) - Interface for storages supporting bulk reads - BulkWriter (
src/Caching/BulkWriter.php) - Interface for storages supporting bulk writes
Used by Cache::bulkLoad() and Cache::bulkSave() to reduce storage round-trips.
Tests organized by component in tests/:
Caching/- Cache class testsStorages/- Storage implementation testsBridges.DI/- Nette DI integration testsBridges.Latte3/- Latte 3.x template caching testsBridges.Psr/- PSR-16 adapter tests
Test utilities:
bootstrap.php- Test environment setup withtest()helper functiongetTempDir()- Creates isolated temp directory per test process- Uses Nette Tester with
.phptformat
Three atomic operation types documented in FileStorage.php:
- Reading: open(r+b) → lock(LOCK_SH) → read → close
- Deleting: unlink, if fails lock(LOCK_EX) → truncate → close → unlink
- Writing: open(r+b or wb) → lock(LOCK_EX) → truncate → write data → write meta → close
This ensures atomicity on both NTFS and ext3 filesystems.
FileStorage prevents cache stampede through locking: when multiple concurrent threads request non-existent cache item, lock() ensures only first thread generates value while others wait. Others then use the generated result.
Cache uses internal null byte separator (Cache::NamespaceSeparator = "\x00") to isolate namespaces. Keys are prefixed with {namespace}\x00{key}.
Library uses modern PascalCase constants (e.g., Cache::Expire) with deprecated UPPERCASE aliases (e.g., Cache::EXPIRATION) for backward compatibility.
Version 3.0 compatibility note: In version 3.0, the Storage interface was named IStorage (with I prefix) and constants were UPPERCASE (e.g., Cache::EXPIRE instead of Cache::Expire).
Two approaches for dependency injection:
Approach 1: Inject Storage, create Cache manually
class ClassOne
{
private Nette\Caching\Cache $cache;
public function __construct(Nette\Caching\Storage $storage)
{
$this->cache = new Nette\Caching\Cache($storage, 'my-namespace');
}
}Approach 2: Inject Cache directly
class ClassTwo
{
public function __construct(
private Nette\Caching\Cache $cache,
) {
}
}Configuration for Approach 2:
services:
- ClassTwo( Nette\Caching\Cache(namespace: 'my-namespace') )Services automatically registered by CacheExtension:
| Service Name | Type | Description |
|---|---|---|
cache.storage |
Nette\Caching\Storage |
Primary cache storage (FileStorage by default) |
cache.journal |
Nette\Caching\Storages\Journal |
Journal for tags/priorities (SQLiteJournal, requires pdo_sqlite) |
services:
cache.storage: Nette\Caching\Storages\DevNullStorageservices:
cache.storage: Nette\Caching\Storages\MemcachedStorage('10.0.0.5')services:
cache.storage: Nette\Caching\Storages\SQLiteStorage('%tempDir%/cache.db')services:
cache.journal: MyJournalservices:
cache.storage: Nette\Caching\Storages\DevNullStorageNote: This doesn't affect Latte template caching or DI container caching, as those are managed independently and don't need to be disabled during development.
The PsrCacheAdapter provides PSR-16 SimpleCache compatibility (available since v3.3.1):
$psrCache = new Nette\Bridges\Psr\PsrCacheAdapter($storage);
// PSR-16 interface
$psrCache->set('key', 'value', 3600);
$value = $psrCache->get('key', 'default');
// Supports all PSR-16 methods
$psrCache->getMultiple(['key1', 'key2']);
$psrCache->setMultiple(['key1' => 'val1', 'key2' => 'val2']);
$psrCache->deleteMultiple(['key1', 'key2']);