This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
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.
Key characteristics:
- Single-class implementation (~500 LOC in
src/RobotLoader/RobotLoader.php) - Intelligent caching with platform-specific optimizations (Linux vs Windows)
- Cache stampede prevention for production environments
- Token-based PHP file parsing using
\PhpToken::tokenize() - Automatic cache invalidation on file changes in development mode
# Run all tests
composer run tester
# Run specific test file
vendor/bin/tester tests/Loaders/RobotLoader.phpt -s
# Run tests in a specific directory
vendor/bin/tester tests/Loaders/ -s
# Run tests with verbose output (-s shows skipped tests)
vendor/bin/tester tests -s -p php# Static analysis (PHPStan level 5)
composer run phpstan# Install dependencies
composer install
# Update dependencies
composer update
# Generate API documentation (if available)
# See https://api.nette.org/robot-loader/The entire library is a single class (Nette\Loaders\RobotLoader) with these primary responsibilities:
- Directory Scanning - Recursively indexes PHP files using
Nette\Utils\Finder - Class Extraction - Token-based parsing to find classes/interfaces/traits/enums
- Caching System - Three-tier cache:
$classes,$missingClasses,$emptyFiles - Autoloading - Registers with PHP's
spl_autoload_register() - Change Detection - mtime-based incremental updates
// Three-tier caching state
private array $classes = []; // class => [file, mtime]
private array $missingClasses = []; // class => retry_counter (max 3)
private array $emptyFiles = []; // file => mtime (optimization)Linux: Direct cache include without locks + atomic rename Windows: Mandatory file locking (files can't be renamed while open)
The code checks PHP_WINDOWS_VERSION_BUILD constant to determine the platform and adjusts locking strategy accordingly.
When multiple concurrent requests hit production before cache exists:
- First request acquires exclusive lock (LOCK_EX)
- Subsequent requests wait with shared lock (LOCK_SH)
- After first request builds cache, others reuse it
- Double-check pattern: re-read cache after acquiring exclusive lock
addDirectory()/excludeDirectory()- Configure scan pathsregister()- Activate autoloader (callsspl_autoload_register())refresh()- Smart cache refresh (scans only changed files)rebuild()- Full rebuild from scratchscanPhp()- Token-based class extraction from PHP filesloadCache()/saveCache()- Atomic cache operations with locking
Basic setup for any PHP application:
$loader = new Nette\Loaders\RobotLoader;
$loader->addDirectory(__DIR__ . '/app');
$loader->addDirectory(__DIR__ . '/libs');
$loader->setCacheDirectory(__DIR__ . '/temp');
$loader->register(); // Activate RobotLoaderWhen used within a Nette Application (recommended approach), RobotLoader setup is simplified through the $configurator object in Bootstrap.php:
$configurator = new Nette\Bootstrap\Configurator;
// ...
$configurator->setCacheDirectory(__DIR__ . '/../temp');
$configurator->createRobotLoader()
->addDirectory(__DIR__)
->addDirectory(__DIR__ . '/../libs')
->register();Benefits:
- Automatic temp directory configuration
- Fluent interface for directory setup
- Auto-refresh automatically disabled in production mode
- Integrated with Nette Application lifecycle
RobotLoader can be used purely for indexing classes without autoloading:
$loader = new Nette\Loaders\RobotLoader;
$loader->addDirectory(__DIR__ . '/app');
$loader->setCacheDirectory(__DIR__ . '/temp');
$loader->refresh(); // Scans directories using cache
$classes = $loader->getIndexedClasses(); // Returns class => file arrayUse rebuild() instead of refresh() to force full rebuild from scratch.
Use RobotLoader when:
- Directory structure doesn't match namespace structure
- Working with legacy code that doesn't follow PSR-4
- You want automatic discovery without strict conventions
- Need to load from multiple disparate directories
Use PSR-4 (Composer) when:
- Building new applications with consistent structure
- Following modern PHP standards strictly
- Directory structure matches namespace structure (e.g.,
App\Core\RouterFactory→/path/to/App/Core/RouterFactory.php)
Both can be used together - PSR-4 for your structured code, RobotLoader for legacy dependencies or non-standard libraries.
Development:
$loader->setAutoRefresh(true); // Default - automatically updates cacheProduction:
$loader->setAutoRefresh(false); // Disable auto-refresh for performance
// Clear cache when deploying: rm -rf temp/cacheIn Nette Application, this is handled automatically based on debug mode.
Uses Nette Tester with .phpt file format:
<?php
/**
* Test: Description of what is being tested
*/
declare(strict_types=1);
use Tester\Assert;
require __DIR__ . '/../bootstrap.php';
// Test code using Assert methods
Assert::same('expected', $actual);
Assert::exception(fn() => $code(), ExceptionClass::class, 'Message pattern %a%');getTempDir() function (in tests/bootstrap.php):
- Creates per-process temp directories (
tests/tmp/<pid>) - Garbage collection with file locking for parallel safety
- Automatically used by tests for cache directories
- RobotLoader.phpt - Basic functionality, directory/file scanning, exclusions
- RobotLoader.rebuild.phpt - Cache rebuild behavior
- RobotLoader.renamed.phpt - File rename detection
- RobotLoader.caseSensitivity.phpt - Case-sensitive class matching
- RobotLoader.relative.phpt - Relative path handling
- RobotLoader.phar.phpt - PHAR archive support
- RobotLoader.stress.phpt - Concurrency testing (50 parallel runs via
@multiple) - RobotLoader.emptyArrayVariadicArgument.phpt - Edge case handling
Follows Nette Coding Standard (based on PSR-12) with these specifics:
declare(strict_types=1)in all PHP files- Tabs for indentation
- Return type and opening brace on separate lines for multi-parameter methods
- Two spaces after
@paramand@returnin phpDoc - Document shut-up operator usage:
@mkdir($dir); // @ - directory may already exist
ParseError- PHP syntax errors in scanned files (configurable)Nette\InvalidStateException- Duplicate class definitionsNette\IOException- Directory not foundNette\InvalidArgumentException- Invalid temp directoryRuntimeException- Cache file write failures, lock acquisition failures
Three GitHub Actions workflows:
- Coding Style - Code checker + coding standard enforcement
- Static Analysis - PHPStan (runs on master branch only)
- Tests - Matrix testing across PHP versions (8.1-8.5)
- PHP: 8.1 - 8.5
- ext-tokenizer: Required for PHP parsing
- nette/utils: ^4.0 (FileSystem, Finder)
- Dev: nette/tester, tracy/tracy, phpstan/phpstan-nette
When modifying RobotLoader:
- Consider backward compatibility (library is mature and widely used)
- Add comprehensive tests covering edge cases
- Update phpDoc annotations for IDE support
- Test on both Linux and Windows if touching file operations
- Consider performance implications (this is a hot path in applications)
# Run single test with Tracy debugger
vendor/bin/tester tests/Loaders/RobotLoader.phpt -s
# Check test temp files (not auto-cleaned during failures)
ls tests/tmp/
# Manual cleanup
rm -rf tests/tmp/*- Cache operations are atomic to prevent corruption
- OPcache invalidation after cache updates (
opcache_invalidate()) - Lazy initialization - cache loaded only on first autoload attempt
- Empty files tracked separately to avoid re-scanning
- Missing class retry limit (3 attempts) prevents infinite loops