Skip to content

Commit 5799d14

Browse files
committed
added CLAUDE.md
1 parent 798e7d5 commit 5799d14

File tree

2 files changed

+275
-0
lines changed

2 files changed

+275
-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: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
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 RobotLoader is a high-performance PHP autoloader library that automatically discovers and loads classes, interfaces, traits, and enums without requiring strict PSR-4 naming conventions. It's part of the Nette Framework ecosystem but works as a standalone component.
8+
9+
**Key characteristics:**
10+
- Single-class implementation (~500 LOC in `src/RobotLoader/RobotLoader.php`)
11+
- Intelligent caching with platform-specific optimizations (Linux vs Windows)
12+
- Cache stampede prevention for production environments
13+
- Token-based PHP file parsing using `\PhpToken::tokenize()`
14+
- Automatic cache invalidation on file changes in development mode
15+
16+
## Essential Commands
17+
18+
### Testing
19+
```bash
20+
# Run all tests
21+
composer run tester
22+
23+
# Run specific test file
24+
vendor/bin/tester tests/Loaders/RobotLoader.phpt -s -C
25+
26+
# Run tests in a specific directory
27+
vendor/bin/tester tests/Loaders/ -s -C
28+
29+
# Run tests with verbose output (-s shows skipped tests)
30+
vendor/bin/tester tests -s -C -p php
31+
```
32+
33+
### Code Quality
34+
```bash
35+
# Static analysis (PHPStan level 5)
36+
composer run phpstan
37+
```
38+
39+
### Development
40+
```bash
41+
# Install dependencies
42+
composer install
43+
44+
# Update dependencies
45+
composer update
46+
47+
# Generate API documentation (if available)
48+
# See https://api.nette.org/robot-loader/
49+
```
50+
51+
## Architecture
52+
53+
### Core Component: RobotLoader Class
54+
55+
The entire library is a single class (`Nette\Loaders\RobotLoader`) with these primary responsibilities:
56+
57+
1. **Directory Scanning** - Recursively indexes PHP files using `Nette\Utils\Finder`
58+
2. **Class Extraction** - Token-based parsing to find classes/interfaces/traits/enums
59+
3. **Caching System** - Three-tier cache: `$classes`, `$missingClasses`, `$emptyFiles`
60+
4. **Autoloading** - Registers with PHP's `spl_autoload_register()`
61+
5. **Change Detection** - mtime-based incremental updates
62+
63+
### State Management
64+
65+
```php
66+
// Three-tier caching state
67+
private array $classes = []; // class => [file, mtime]
68+
private array $missingClasses = []; // class => retry_counter (max 3)
69+
private array $emptyFiles = []; // file => mtime (optimization)
70+
```
71+
72+
### Platform-Specific Optimizations
73+
74+
**Linux:** Direct cache include without locks + atomic rename
75+
**Windows:** Mandatory file locking (files can't be renamed while open)
76+
77+
The code checks `PHP_WINDOWS_VERSION_BUILD` constant to determine the platform and adjusts locking strategy accordingly.
78+
79+
### Cache Stampede Prevention
80+
81+
When multiple concurrent requests hit production before cache exists:
82+
1. First request acquires exclusive lock (LOCK_EX)
83+
2. Subsequent requests wait with shared lock (LOCK_SH)
84+
3. After first request builds cache, others reuse it
85+
4. Double-check pattern: re-read cache after acquiring exclusive lock
86+
87+
### Key Methods
88+
89+
- `addDirectory()` / `excludeDirectory()` - Configure scan paths
90+
- `register()` - Activate autoloader (calls `spl_autoload_register()`)
91+
- `refresh()` - Smart cache refresh (scans only changed files)
92+
- `rebuild()` - Full rebuild from scratch
93+
- `scanPhp()` - Token-based class extraction from PHP files
94+
- `loadCache()` / `saveCache()` - Atomic cache operations with locking
95+
96+
## Usage Patterns
97+
98+
### Standalone Usage
99+
100+
Basic setup for any PHP application:
101+
102+
```php
103+
$loader = new Nette\Loaders\RobotLoader;
104+
$loader->addDirectory(__DIR__ . '/app');
105+
$loader->addDirectory(__DIR__ . '/libs');
106+
$loader->setTempDirectory(__DIR__ . '/temp');
107+
$loader->register(); // Activate RobotLoader
108+
```
109+
110+
### Nette Application Integration
111+
112+
When used within a Nette Application (recommended approach), RobotLoader setup is simplified through the `$configurator` object in `Bootstrap.php`:
113+
114+
```php
115+
$configurator = new Nette\Bootstrap\Configurator;
116+
// ...
117+
$configurator->setTempDirectory(__DIR__ . '/../temp');
118+
$configurator->createRobotLoader()
119+
->addDirectory(__DIR__)
120+
->addDirectory(__DIR__ . '/../libs')
121+
->register();
122+
```
123+
124+
**Benefits:**
125+
- Automatic temp directory configuration
126+
- Fluent interface for directory setup
127+
- Auto-refresh automatically disabled in production mode
128+
- Integrated with Nette Application lifecycle
129+
130+
### As PHP Files Analyzer (Without Autoloading)
131+
132+
RobotLoader can be used purely for indexing classes without autoloading:
133+
134+
```php
135+
$loader = new Nette\Loaders\RobotLoader;
136+
$loader->addDirectory(__DIR__ . '/app');
137+
$loader->setTempDirectory(__DIR__ . '/temp');
138+
$loader->refresh(); // Scans directories using cache
139+
$classes = $loader->getIndexedClasses(); // Returns class => file array
140+
```
141+
142+
Use `rebuild()` instead of `refresh()` to force full rebuild from scratch.
143+
144+
### RobotLoader vs PSR-4
145+
146+
**Use RobotLoader when:**
147+
- Directory structure doesn't match namespace structure
148+
- Working with legacy code that doesn't follow PSR-4
149+
- You want automatic discovery without strict conventions
150+
- Need to load from multiple disparate directories
151+
152+
**Use PSR-4 (Composer) when:**
153+
- Building new applications with consistent structure
154+
- Following modern PHP standards strictly
155+
- Directory structure matches namespace structure (e.g., `App\Core\RouterFactory``/path/to/App/Core/RouterFactory.php`)
156+
157+
**Both can be used together** - PSR-4 for your structured code, RobotLoader for legacy dependencies or non-standard libraries.
158+
159+
### Production vs Development Configuration
160+
161+
**Development:**
162+
```php
163+
$loader->setAutoRefresh(true); // Default - automatically updates cache
164+
```
165+
166+
**Production:**
167+
```php
168+
$loader->setAutoRefresh(false); // Disable auto-refresh for performance
169+
// Clear cache when deploying: rm -rf temp/cache
170+
```
171+
172+
In Nette Application, this is handled automatically based on debug mode.
173+
174+
## Testing Framework
175+
176+
Uses **Nette Tester** with `.phpt` file format:
177+
178+
```php
179+
<?php
180+
/**
181+
* Test: Description of what is being tested
182+
*/
183+
declare(strict_types=1);
184+
185+
use Tester\Assert;
186+
require __DIR__ . '/../bootstrap.php';
187+
188+
// Test code using Assert methods
189+
Assert::same('expected', $actual);
190+
Assert::exception(fn() => $code(), ExceptionClass::class, 'Message pattern %a%');
191+
```
192+
193+
### Test Utilities
194+
195+
**`getTempDir()` function** (in `tests/bootstrap.php`):
196+
- Creates per-process temp directories (`tests/tmp/<pid>`)
197+
- Garbage collection with file locking for parallel safety
198+
- Automatically used by tests for cache directories
199+
200+
### Test Coverage Areas
201+
202+
1. **RobotLoader.phpt** - Basic functionality, directory/file scanning, exclusions
203+
2. **RobotLoader.rebuild.phpt** - Cache rebuild behavior
204+
3. **RobotLoader.renamed.phpt** - File rename detection
205+
4. **RobotLoader.caseSensitivity.phpt** - Case-sensitive class matching
206+
5. **RobotLoader.relative.phpt** - Relative path handling
207+
6. **RobotLoader.phar.phpt** - PHAR archive support
208+
7. **RobotLoader.stress.phpt** - Concurrency testing (50 parallel runs via `@multiple`)
209+
8. **RobotLoader.emptyArrayVariadicArgument.phpt** - Edge case handling
210+
211+
## Coding Conventions
212+
213+
Follows **Nette Coding Standard** (based on PSR-12) with these specifics:
214+
215+
- `declare(strict_types=1)` in all PHP files
216+
- Tabs for indentation
217+
- Return type and opening brace on separate lines for multi-parameter methods
218+
- Two spaces after `@param` and `@return` in phpDoc
219+
- Document shut-up operator usage: `@mkdir($dir); // @ - directory may already exist`
220+
221+
### Exception Handling
222+
223+
- `ParseError` - PHP syntax errors in scanned files (configurable)
224+
- `Nette\InvalidStateException` - Duplicate class definitions
225+
- `Nette\IOException` - Directory not found
226+
- `Nette\InvalidArgumentException` - Invalid temp directory
227+
- `RuntimeException` - Cache file write failures, lock acquisition failures
228+
229+
## CI/CD Pipeline
230+
231+
Three GitHub Actions workflows:
232+
233+
1. **Coding Style** - Code checker + coding standard enforcement
234+
2. **Static Analysis** - PHPStan (runs on master branch only)
235+
3. **Tests** - Matrix testing across PHP versions (8.1-8.5)
236+
237+
## Dependencies
238+
239+
- **PHP**: 8.1 - 8.5
240+
- **ext-tokenizer**: Required for PHP parsing
241+
- **nette/utils**: ^4.0 (FileSystem, Finder)
242+
- **Dev**: nette/tester, tracy/tracy, phpstan/phpstan-nette
243+
244+
## Common Development Patterns
245+
246+
### Adding New Functionality
247+
248+
When modifying RobotLoader:
249+
1. Consider backward compatibility (library is mature and widely used)
250+
2. Add comprehensive tests covering edge cases
251+
3. Update phpDoc annotations for IDE support
252+
4. Test on both Linux and Windows if touching file operations
253+
5. Consider performance implications (this is a hot path in applications)
254+
255+
### Debugging Tests
256+
257+
```bash
258+
# Run single test with Tracy debugger
259+
vendor/bin/tester tests/Loaders/RobotLoader.phpt -s -C
260+
261+
# Check test temp files (not auto-cleaned during failures)
262+
ls tests/tmp/
263+
264+
# Manual cleanup
265+
rm -rf tests/tmp/*
266+
```
267+
268+
### Performance Considerations
269+
270+
- Cache operations are atomic to prevent corruption
271+
- OPcache invalidation after cache updates (`opcache_invalidate()`)
272+
- Lazy initialization - cache loaded only on first autoload attempt
273+
- Empty files tracked separately to avoid re-scanning
274+
- Missing class retry limit (3 attempts) prevents infinite loops

0 commit comments

Comments
 (0)