Skip to content

Commit 27d763b

Browse files
committed
added CLAUDE.md
1 parent 34a7af8 commit 27d763b

2 files changed

Lines changed: 281 additions & 0 deletions

File tree

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
.gitattributes export-ignore
22
.github/ export-ignore
33
.gitignore export-ignore
4+
CLAUDE.md export-ignore
45
ncs.* export-ignore
56
phpstan*.neon export-ignore
67
tests/ export-ignore

CLAUDE.md

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

0 commit comments

Comments
 (0)