Skip to content

Commit 4b75a9a

Browse files
committed
added CLAUDE.md
1 parent 5925ef9 commit 4b75a9a

File tree

2 files changed

+285
-0
lines changed

2 files changed

+285
-0
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
.github export-ignore
44
ncs.* export-ignore
55
phpstan.neon export-ignore
6+
CLAUDE.md export-ignore
67
tests/ export-ignore
78

89
*.sh eol=lf

CLAUDE.md

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

0 commit comments

Comments
 (0)