Skip to content

Commit 23e9053

Browse files
committed
added CLAUDE.md
1 parent 7b24de0 commit 23e9053

1 file changed

Lines changed: 286 additions & 0 deletions

File tree

CLAUDE.md

Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
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.
8+
9+
**Key features:**
10+
- Multiple storage backends (FileStorage, MemcachedStorage, SQLiteStorage, MemoryStorage)
11+
- Advanced dependency tracking (tags, priorities, file changes, callbacks)
12+
- Cache stampede prevention in FileStorage
13+
- Atomic operations with file locking
14+
- PSR-16 SimpleCache adapter
15+
- Latte template integration with `{cache}` tag
16+
- Nette DI integration
17+
18+
**Requirements:** PHP 8.1-8.5
19+
20+
## Essential Commands
21+
22+
### Testing
23+
24+
```bash
25+
# Run all tests
26+
php.bat vendor/bin/tester tests -C -s
27+
28+
# Run specific test directory
29+
php.bat vendor/bin/tester tests/Caching -C -s
30+
php.bat vendor/bin/tester tests/Storages -C -s
31+
32+
# Run single test file
33+
php.bat tests/Caching/Cache.bulkLoad.phpt
34+
35+
# Flags used:
36+
# -C = Use system-wide php.ini
37+
# -s = Show skipped tests
38+
```
39+
40+
**IMPORTANT on Windows:** Always use `php.bat` instead of `php` command. Do NOT use bash shell commands or `cmd.exe /c` for running tests.
41+
42+
### Static Analysis
43+
44+
```bash
45+
# Run PHPStan (level 5)
46+
composer run phpstan
47+
48+
# Or directly
49+
vendor/bin/phpstan analyse
50+
```
51+
52+
### Linting
53+
54+
```bash
55+
# Nette coding standard checks
56+
composer run tester
57+
```
58+
59+
## Architecture Overview
60+
61+
### Core Layering
62+
63+
The library follows a clean separation of concerns:
64+
65+
```
66+
Cache (high-level API)
67+
68+
Storage interface (abstraction)
69+
70+
Storage implementations (FileStorage, MemcachedStorage, etc.)
71+
72+
Journal interface (for tags/priorities)
73+
74+
SQLiteJournal implementation
75+
```
76+
77+
**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).
78+
79+
**Storage interface** (`src/Caching/Storage.php`): Defines the contract all storage backends must implement:
80+
- `read(string $key): mixed`
81+
- `write(string $key, $data, array $dependencies): void`
82+
- `remove(string $key): void`
83+
- `clean(array $conditions): void`
84+
- `lock(string $key): void` - Prevents concurrent writes
85+
86+
**Journal interface** (`src/Caching/Storages/Journal.php`): Tracks metadata for tags and priorities. Required for:
87+
- `Cache::Tags` - Tag-based invalidation
88+
- `Cache::Priority` - Priority-based cleanup
89+
90+
Default implementation: SQLiteJournal using SQLite database at `{tempDir}/journal.s3db`.
91+
92+
### Storage Implementations
93+
94+
All in `src/Caching/Storages/`:
95+
96+
- **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.
97+
98+
- **SQLiteStorage** - Single-file database storage. Good for shared hosting environments.
99+
100+
- **MemcachedStorage** - Distributed caching via Memcached server. Requires `memcached` PHP extension.
101+
102+
- **MemoryStorage** - In-memory array storage, lost after request. Used for testing or request-scoped caching.
103+
104+
- **DevNullStorage** - No-op storage for testing when you want to disable caching.
105+
106+
### Dependency System
107+
108+
Cache dependencies control expiration and invalidation. All use Cache class constants:
109+
110+
- `Cache::Expire` - Time-based expiration (timestamp, seconds, or string like "20 minutes")
111+
- `Cache::Sliding` - Extends expiration on each read
112+
- `Cache::Files` - Invalidate when file(s) modified (checks filemtime)
113+
- `Cache::Items` - Invalidate when other cache items expire
114+
- `Cache::Tags` - Tag-based invalidation (requires Journal)
115+
- `Cache::Priority` - Priority-based cleanup (requires Journal)
116+
- `Cache::Callbacks` - Custom validation callbacks
117+
- `Cache::Constants` - Invalidate when PHP constants change
118+
119+
Dependencies can be combined; cache expires when ANY criterion fails.
120+
121+
### Bridge Components
122+
123+
**Nette DI Bridge** (`src/Bridges/CacheDI/CacheExtension.php`):
124+
- Auto-registers Storage service (FileStorage by default)
125+
- Auto-registers Journal service (SQLiteJournal if pdo_sqlite available)
126+
- Validates and creates temp directory
127+
- Services registered: `cache.storage`, `cache.journal`
128+
129+
**Latte Bridge** (`src/Bridges/CacheLatte/`):
130+
- Provides `{cache}` tag for template caching
131+
- Runtime in `Runtime.php` manages cache lifecycle
132+
- Node compilation in `Nodes/CacheNode.php`
133+
- Automatic invalidation when template source changes
134+
- Supports parameters: `{cache $id, expire: '20 minutes', tags: [tag1, tag2]}`
135+
- Can be conditional: `{cache $id, if: !$form->isSubmitted()}`
136+
137+
**PSR-16 Bridge** (`src/Bridges/Psr/PsrCacheAdapter.php`):
138+
- Adapts Nette Storage to PSR-16 SimpleCache interface
139+
- Used for PSR compatibility in third-party integrations
140+
141+
### Bulk Operations
142+
143+
Two specialized classes enable efficient bulk operations:
144+
145+
- **BulkReader** (`src/Caching/BulkReader.php`) - Interface for storages supporting bulk reads
146+
- **BulkWriter** (`src/Caching/BulkWriter.php`) - Interface for storages supporting bulk writes
147+
148+
Used by `Cache::bulkLoad()` and `Cache::bulkSave()` to reduce storage round-trips.
149+
150+
## Testing Structure
151+
152+
Tests organized by component in `tests/`:
153+
- `Caching/` - Cache class tests
154+
- `Storages/` - Storage implementation tests
155+
- `Bridges.DI/` - Nette DI integration tests
156+
- `Bridges.Latte3/` - Latte 3.x template caching tests
157+
- `Bridges.Psr/` - PSR-16 adapter tests
158+
159+
Test utilities:
160+
- `bootstrap.php` - Test environment setup with `test()` helper function
161+
- `getTempDir()` - Creates isolated temp directory per test process
162+
- Uses Nette Tester with `.phpt` format
163+
164+
## Development Notes
165+
166+
### File Locking Strategy (FileStorage)
167+
168+
Three atomic operation types documented in FileStorage.php:
169+
1. **Reading**: open(r+b) → lock(LOCK_SH) → read → close
170+
2. **Deleting**: unlink, if fails lock(LOCK_EX) → truncate → close → unlink
171+
3. **Writing**: open(r+b or wb) → lock(LOCK_EX) → truncate → write data → write meta → close
172+
173+
This ensures atomicity on both NTFS and ext3 filesystems.
174+
175+
### Cache Stampede Prevention
176+
177+
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.
178+
179+
### Namespace Handling
180+
181+
Cache uses internal null byte separator (`Cache::NamespaceSeparator = "\x00"`) to isolate namespaces. Keys are prefixed with `{namespace}\x00{key}`.
182+
183+
### Constants Naming
184+
185+
Library uses modern PascalCase constants (e.g., `Cache::Expire`) with deprecated UPPERCASE aliases (e.g., `Cache::EXPIRATION`) for backward compatibility.
186+
187+
**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`).
188+
189+
## Using Cache in Code
190+
191+
Two approaches for dependency injection:
192+
193+
**Approach 1: Inject Storage, create Cache manually**
194+
```php
195+
class ClassOne
196+
{
197+
private Nette\Caching\Cache $cache;
198+
199+
public function __construct(Nette\Caching\Storage $storage)
200+
{
201+
$this->cache = new Nette\Caching\Cache($storage, 'my-namespace');
202+
}
203+
}
204+
```
205+
206+
**Approach 2: Inject Cache directly**
207+
```php
208+
class ClassTwo
209+
{
210+
public function __construct(
211+
private Nette\Caching\Cache $cache,
212+
) {
213+
}
214+
}
215+
```
216+
217+
Configuration for Approach 2:
218+
```neon
219+
services:
220+
- ClassTwo( Nette\Caching\Cache(namespace: 'my-namespace') )
221+
```
222+
223+
## DI Services
224+
225+
Services automatically registered by CacheExtension:
226+
227+
| Service Name | Type | Description |
228+
|--------------|------|-------------|
229+
| `cache.storage` | `Nette\Caching\Storage` | Primary cache storage (FileStorage by default) |
230+
| `cache.journal` | `Nette\Caching\Storages\Journal` | Journal for tags/priorities (SQLiteJournal, requires pdo_sqlite) |
231+
232+
## Configuration Examples
233+
234+
### Change Storage Backend
235+
236+
```neon
237+
services:
238+
cache.storage: Nette\Caching\Storages\DevNullStorage
239+
```
240+
241+
### Use MemcachedStorage
242+
243+
```neon
244+
services:
245+
cache.storage: Nette\Caching\Storages\MemcachedStorage('10.0.0.5')
246+
```
247+
248+
### Use SQLiteStorage
249+
250+
```neon
251+
services:
252+
cache.storage: Nette\Caching\Storages\SQLiteStorage('%tempDir%/cache.db')
253+
```
254+
255+
### Custom Journal
256+
257+
```neon
258+
services:
259+
cache.journal: MyJournal
260+
```
261+
262+
### Disable Caching (for testing)
263+
264+
```neon
265+
services:
266+
cache.storage: Nette\Caching\Storages\DevNullStorage
267+
```
268+
269+
**Note:** 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](https://doc.nette.org/troubleshooting#How-to-Disable-Cache-During-Development).
270+
271+
## PSR-16 Usage
272+
273+
The `PsrCacheAdapter` provides PSR-16 SimpleCache compatibility (available since v3.3.1):
274+
275+
```php
276+
$psrCache = new Nette\Bridges\Psr\PsrCacheAdapter($storage);
277+
278+
// PSR-16 interface
279+
$psrCache->set('key', 'value', 3600);
280+
$value = $psrCache->get('key', 'default');
281+
282+
// Supports all PSR-16 methods
283+
$psrCache->getMultiple(['key1', 'key2']);
284+
$psrCache->setMultiple(['key1' => 'val1', 'key2' => 'val2']);
285+
$psrCache->deleteMultiple(['key1', 'key2']);
286+
```

0 commit comments

Comments
 (0)