A PSR-4 compliant autoloader with performance tracking, singleton support, and statistics for PHP 7.4+
- PSR-4 Compliant - Follows PHP-FIG standards
- Automatic Discovery - Recursively scans directories
- Class Map Caching - Fast lookups with exportable cache
- Class Instantiation - Built-in factory with
make() - Singleton Support - Managed singleton instances
- Performance Tracking - Detailed load time metrics
- Enhanced Statistics - Cache hit rates, memory usage, and more
- Load Analysis - Find slowest/fastest loading classes
- Zero Configuration - Auto-detects base directory
- Production Ready - Export class map for optimal performance
- Quick Start
- Basic Usage
- Advanced Features
- Performance Optimization
- API Reference
- Examples
- Best Practices
- Benchmarks
<?php
require_once 'path/to/Autoloader.php';
// Create and register autoloader
$autoloader = Autoloader::create('/path/to/src');
// That's it! All classes are now auto-loadable
use Namespace\Classname;
use Namespace\Classname\Subclass;
$class = new Classname();
$subclass = new Subclass();<?php
Autoloader::create(__DIR__ . '/src');
// All done! Use any class
$instance = new \Namespace\Classname();// Auto-detect base directory
$autoloader = new Autoloader();
// Specify base directory
$autoloader = new Autoloader('/path/to/src');
// Create and register in one call
$autoloader = Autoloader::create('/path/to/src');
// Create, register, and preload all classes
$autoloader = Autoloader::createAndPreload('/path/to/src');$autoloader = new Autoloader('/path/to/src');
// Set custom namespace
$autoloader->setNamespace('MyApp\\');
// Set base directory
$autoloader->setBaseDir('/custom/path');
// Exclude directories from scanning
$autoloader->addExcludedDir('vendor');
$autoloader->addExcludedDir('tests');
// Add file extensions
$autoloader->addExtension('.inc');
// Register
$autoloader->register();The autoloader includes a factory for creating instances:
use Namespace\Classname;
use Namespace\Classname\Subclass;
// Create new instance (no arguments)
$subclass = $autoloader->make(Subclass::class);
// Create with constructor arguments
$classname = $autoloader->make(Classname::class, [$param, ['key' => 'value']]);
// Supports up to 5 arguments or variadic
$instance = $autoloader->make(YourClass::class, [
$arg1, $arg2, $arg3, $arg4, $arg5
]);Why use make() instead of new?
- Automatic class loading verification
- Consistent instance creation
- Integration with singleton pattern
- Better error handling
- Testability and mocking support
Built-in singleton support with automatic caching:
// Create singleton (cached for subsequent calls)
$classname = $autoloader->singleton(Classname::class, [$parameter]);
// Get same instance (no new instantiation)
$sameInstance = $autoloader->singleton(Classname::class);
// $classname === $sameInstance ✓
// Check if singleton exists
if ($autoloader->hasInstance(Classname::class)) {
$instance = $autoloader->getInstance(Classname::class);
}
// Remove singleton from cache
$autoloader->forgetInstance(Classname::class);
// Clear all singletons
$autoloader->clearInstances();
// Get all cached instances
$instances = $autoloader->getInstances();Real-World Example:
class Application {
protected Autoloader $autoloader;
public function __construct(Autoloader $autoloader) {
$this->autoloader = $autoloader;
}
public function service(string $class) {
// Always returns the same instance
return $this->autoloader->singleton($class);
}
}
$app = new Application($autoloader);
$classname = $app->service(Classname::class);Track every class load with detailed metrics:
// Get load information for specific class
$info = $autoloader->getLoadInfo(Classname::class);
echo "Load Time: " . ($info['time'] * 1000) . "ms\n";
echo "Load Method: " . $info['method'] . "\n"; // classmap, psr4, or discovery
echo "Timestamp: " . $info['timestamp'] . "\n";
// Get all loaded classes with info
$allLoads = $autoloader->getLoadInfo();
foreach ($allLoads as $class => $info) {
$ms = round($info['time'] * 1000, 4);
echo "{$class}: {$ms}ms via {$info['method']}\n";
}
// Get classes loaded by specific method
$classmapLoads = $autoloader->getLoadedByMethod('classmap'); // Fast lookups
$psr4Loads = $autoloader->getLoadedByMethod('psr4'); // Standard PSR-4
$discoveryLoads = $autoloader->getLoadedByMethod('discovery'); // Auto-discovered
// Find slowest loading classes
$slowest = $autoloader->getSlowestLoads(10);
foreach ($slowest as $class => $info) {
$ms = round($info['time'] * 1000, 2);
echo "{$class}: {$ms}ms\n";
}
// Find fastest loading classes
$fastest = $autoloader->getFastestLoads(10);
// Get failed load attempts
$failed = $autoloader->getFailedLoads();
foreach ($failed as $failure) {
echo "Failed: {$failure['class']} at {$failure['timestamp']}\n";
}Statistics and analytics:
$stats = $autoloader->getStats();
// Overview
echo "Total Classes: {$stats['total_classes']}\n";
echo "Loaded Classes: {$stats['loaded_classes']}\n";
echo "Failed Loads: {$stats['failed_loads']}\n";
echo "Namespaces: {$stats['namespaces']}\n";
// Performance metrics
$perf = $stats['performance'];
echo "Total Loads: {$perf['total_loads']}\n";
echo "Cache Hits: {$perf['cache_hits']}\n";
echo "Cache Misses: {$perf['cache_misses']}\n";
echo "Cache Hit Rate: {$perf['cache_hit_rate']}%\n";
echo "Total Load Time: {$perf['total_load_time_ms']}ms\n";
echo "Average Load Time: {$perf['avg_load_time_ms']}ms\n";
echo "Min Load Time: {$perf['min_load_time_ms']}ms\n";
echo "Max Load Time: {$perf['max_load_time_ms']}ms\n";
// Memory usage
$mem = $stats['memory_usage'];
echo "Cached Instances: {$mem['instances_cached']}\n";
echo "Memory Usage: {$mem['estimated_mb']} MB\n";
// Classes by namespace
foreach ($stats['by_namespace'] as $namespace => $count) {
echo "{$namespace}: {$count} classes\n";
}
// Formatted output
echo $autoloader->printStats();Example Output:
Autoloader Statistics
================================
Base Directory: /var/www/src/
Namespace: Namespace\
Total Classes: 48
Loaded Classes: 12
Failed Loads: 0
Namespaces: 8
Performance:
------------
Total Loads: 12
Cache Hits: 10
Cache Misses: 2
Cache Hit Rate: 83.33%
Total Load Time: 2.5ms
Avg Load Time: 0.21ms
Min Load Time: 0.15ms
Max Load Time: 0.45ms
Memory Usage:
-------------
Cached Instances: 3
Estimated Memory: 2.5 MB
Classes by Namespace:
---------------------
Namespace\Classes: 5
Namespace\SubNamespace\Subclass: 3
Namespace\Traits: 2
Namespace\Interfaces: 2
Optimize performance by caching the class map:
// Development: Build and export class map
$autoloader = new Autoloader('/path/to/src');
$autoloader->buildClassMap();
$autoloader->exportClassMap('/cache/classmap.php');
// Production: Import pre-built class map (much faster!)
$autoloader = new Autoloader('/path/to/src');
$autoloader->importClassMap('/cache/classmap.php');
$autoloader->register();
// Verify exported class map
$missing = $autoloader->verifyClassMap();
if (!empty($missing)) {
error_log("Missing files: " . count($missing));
}Exported Class Map Format:
<?php
/**
* Autoloader Class Map
* Generated: 2026-02-15 20:00:00
* Classes: 48
*/
return [
'Namespace\\Classes\\Classname' => '/src/Namespace/Classes/Classname.php',
'Namespace\\SubNamespace\\Subclass' => '/src/Namespace/SubNamespace/Subclass.php',
'Namespace\\Traits\\Trait' => '/src/Namespace/Traits/Trait.php',
'Namespace\\Interfaces\\Interface' => '/src/Namespace/Traits/Interface.php',
// ... all 48 classes
];Best for: Production environments
// Build once during deployment
$autoloader = new Autoloader('/path/to/src');
$autoloader->exportClassMap('/cache/classmap.php');
// Load fast every request
$autoloader = new Autoloader('/path/to/src');
$autoloader->importClassMap('/cache/classmap.php');
$autoloader->register();Performance: ~0.1ms per request (no directory scanning!)
Best for: Long-running processes
$autoloader = new Autoloader('/path/to/src');
$autoloader->register();
// Preload critical classes
$autoloader->preloadNamespace('Namespace');
$autoloader->preloadNamespace('Namespace\\Subnamespace');Performance: Initial cost, then instant access
Best for: Development, small applications
$autoloader = Autoloader::create('/path/to/src');
// Classes loaded on-demandPerformance: ~0.5ms per first class access
| Strategy | First Request | Subsequent Requests | Best For |
|---|---|---|---|
| Exported Class Map | 0.1ms | 0.1ms | Production |
| Preloading | 50ms | 0ms | Long-running |
| Lazy Loading | 0.5ms/class | 0.5ms/class | Development |
| Method | Description |
|---|---|
register(bool $prepend = false): bool |
Register the autoloader |
unregister(): bool |
Unregister the autoloader |
| Method | Description |
|---|---|
loadClass(string $class): bool |
Load a class file |
preloadAll(): int |
Preload all classes |
preloadNamespace(string $namespace): int |
Preload specific namespace |
| Method | Description |
|---|---|
make(string $class, array $args = [], bool $singleton = false): object |
Create instance |
singleton(string $class, array $args = []): object |
Get/create singleton |
getInstance(string $class): ?object |
Get existing singleton |
hasInstance(string $class): bool |
Check if singleton exists |
forgetInstance(string $class): void |
Remove singleton |
clearInstances(): void |
Clear all singletons |
getInstances(): array |
Get all singletons |
| Method | Description |
|---|---|
buildClassMap(?string $directory = null): void |
Build class map |
getClassMap(): array |
Get class map |
exportClassMap(string $filepath): bool |
Export class map |
importClassMap(string $filepath): bool |
Import class map |
clearCache(): self |
Clear class map cache |
verifyClassMap(): array |
Check for missing files |
| Method | Description |
|---|---|
getStats(): array |
Get complete statistics |
printStats(): string |
Get formatted statistics |
getLoadInfo(?string $class = null): array |
Get load information |
getLoadedByMethod(string $method): array |
Filter by load method |
getSlowestLoads(int $limit = 10): array |
Get slowest loads |
getFastestLoads(int $limit = 10): array |
Get fastest loads |
getFailedLoads(): array |
Get failed load attempts |
resetStats(): void |
Reset statistics |
| Method | Description |
|---|---|
setNamespace(string $namespace): self |
Set namespace prefix |
setBaseDir(string $baseDir): self |
Set base directory |
addExcludedDir(string $dir): self |
Exclude directory |
addExtension(string $extension): self |
Add file extension |
| Method | Description |
|---|---|
isLoaded(string $class): bool |
Check if class is loaded |
hasClass(string $class): bool |
Check if class in map |
getFilePath(string $class): ?string |
Get class file path |
<?php
class Container {
protected Autoloader $autoloader;
public function __construct(string $basePath) {
$this->autoloader = Autoloader::create($basePath);
}
public function get(string $class) {
return $this->autoloader->singleton($class);
}
public function make(string $class, array $args = []) {
return $this->autoloader->make($class, $args);
}
}
// Usage
$container = new Container(__DIR__ . '/src');
$classname = $container->get(Classname::class); // Singleton
$subclass = $container->make(Subclass::class); // New instance<?php
class PerformanceMonitor {
protected Autoloader $autoloader;
public function __construct(Autoloader $autoloader) {
$this->autoloader = $autoloader;
}
public function analyze(): array {
$stats = $this->autoloader->getStats();
$issues = [];
// Check cache hit rate
if ($stats['performance']['cache_hit_rate'] < 80) {
$issues[] = "Low cache hit rate: {$stats['performance']['cache_hit_rate']}%";
}
// Check for slow loads
$slowest = $this->autoloader->getSlowestLoads(5);
foreach ($slowest as $class => $info) {
$ms = round($info['time'] * 1000, 2);
if ($ms > 10) {
$issues[] = "Slow load: {$class} took {$ms}ms";
}
}
// Check failed loads
$failed = $this->autoloader->getFailedLoads();
if (!empty($failed)) {
$issues[] = count($failed) . " classes failed to load";
}
return $issues;
}
public function report(): string {
$issues = $this->analyze();
if (empty($issues)) {
return " Performance is optimal!";
}
return " Issues:\n- " . implode("\n- ", $issues);
}
}
// Usage
$monitor = new PerformanceMonitor($autoloader);
echo $monitor->report();<?php
class ServiceLoader {
protected Autoloader $autoloader;
protected array $services = [];
public function __construct(Autoloader $autoloader) {
$this->autoloader = $autoloader;
}
public function register(string $name, string $class, array $args = []): void {
$this->services[$name] = ['class' => $class, 'args' => $args];
}
public function get(string $name) {
if (!isset($this->services[$name])) {
throw new Exception("Service not registered: {$name}");
}
$service = $this->services[$name];
return $this->autoloader->singleton($service['class'], $service['args']);
}
}
// Usage
$services = new ServiceLoader($autoloader);
$services->register('classname', Classname::class, [$parameter]);
$services->register('subclass', Subclass::class);
$services->register('interface', Interface::class);
$classname = $services->get('classname'); // Instantiated on first access
$subclass = $services->get('subclass'); // Instantiated on first access<?php
function renderDebugDashboard(Autoloader $autoloader): string {
$stats = $autoloader->getStats();
$perf = $stats['performance'];
ob_start();
?>
<div style="font-family: monospace; padding: 20px; background: #f5f5f5;">
<h2>Autoloader Performance Dashboard</h2>
<div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin: 20px 0;">
<div style="background: white; padding: 15px; border-radius: 5px;">
<strong><?= $stats['loaded_classes'] ?></strong><br>
<small>Classes Loaded</small>
</div>
<div style="background: white; padding: 15px; border-radius: 5px;">
<strong><?= $perf['cache_hit_rate'] ?>%</strong><br>
<small>Cache Hit Rate</small>
</div>
<div style="background: white; padding: 15px; border-radius: 5px;">
<strong><?= $perf['avg_load_time_ms'] ?>ms</strong><br>
<small>Avg Load Time</small>
</div>
<div style="background: white; padding: 15px; border-radius: 5px;">
<strong><?= $stats['memory_usage']['instances_cached'] ?></strong><br>
<small>Cached Instances</small>
</div>
</div>
<h3>Slowest Loads</h3>
<table style="width: 100%; background: white; border-collapse: collapse;">
<tr style="background: #333; color: white;">
<th style="padding: 10px; text-align: left;">Class</th>
<th style="padding: 10px; text-align: right;">Time (ms)</th>
<th style="padding: 10px; text-align: center;">Method</th>
</tr>
<?php foreach ($autoloader->getSlowestLoads(5) as $class => $info): ?>
<tr style="border-bottom: 1px solid #ddd;">
<td style="padding: 10px;"><?= basename(str_replace('\\', '/', $class)) ?></td>
<td style="padding: 10px; text-align: right;"><?= round($info['time'] * 1000, 2) ?></td>
<td style="padding: 10px; text-align: center;"><?= $info['method'] ?></td>
</tr>
<?php endforeach; ?>
</table>
</div>
<?php
return ob_get_clean();
}
// Usage
echo renderDebugDashboard($autoloader);-
Use exported class map in production
$autoloader->importClassMap('/cache/classmap.php');
-
Preload frequently used classes
$autoloader->preloadNamespace('Classname\\Modules');
-
Monitor performance in development
$stats = $autoloader->getStats(); if ($stats['performance']['cache_hit_rate'] < 80) { // Investigate }
-
Use singletons for service classes
$service = $autoloader->singleton(Service::class, [$modx]);
-
Clear statistics in production
$autoloader->resetStats(); // After warming up
-
Don't scan directories on every request in production
// BAD $autoloader = new Autoloader(); $autoloader->buildClassMap(); // Every request! // GOOD $autoloader->importClassMap('/cache/classmap.php');
-
Don't preload everything
// BAD $autoloader->preloadAll(); // 100ms startup! // GOOD $autoloader->preloadNamespace('Namespace'); // Only what you need
-
Don't ignore failed loads
$failed = $autoloader->getFailedLoads(); if (!empty($failed)) { error_log("Failed loads: " . count($failed)); }
-
Don't use multiple autoloaders for same namespace
// BAD $auto1 = Autoloader::create('/path1'); $auto2 = Autoloader::create('/path1'); // Duplicate!
Performance benchmarks on PHP 8.1, Intel i7, SSD:
| Method | Classes | Time | Avg per Class |
|---|---|---|---|
| Lazy Loading | 48 | 24ms | 0.5ms |
| Preload All | 48 | 50ms | 1.04ms |
| Preload Namespace (12) | 12 | 6ms | 0.5ms |
| Cached Class Map | 48 | 4.8ms | 0.1ms |
| Scenario | Memory | Instances |
|---|---|---|
| Empty Autoloader | ~100KB | 0 |
| Class Map Built | ~150KB | 0 |
| 10 Classes Loaded | ~250KB | 0 |
| 10 Singletons Cached | ~400KB | 10 |
| All Classes Preloaded | ~1.2MB | 0 |
| Cache Hit Rate | Avg Load Time |
|---|---|
| 0% (no cache) | 0.5ms |
| 50% | 0.3ms |
| 80% | 0.15ms |
| 95% | 0.1ms |
| 100% (all cached) | 0.05ms |
| Environment | Setup | First Request | Avg Request |
|---|---|---|---|
| Development | Lazy loading | 25ms | 0.5ms/class |
| Staging | Preloaded | 50ms | 0.1ms |
| Production | Cached map | 5ms | 0.05ms |
// Check class map
$classMap = $autoloader->getClassMap();
print_r($classMap);
// Verify files exist
$missing = $autoloader->verifyClassMap();
if (!empty($missing)) {
foreach ($missing as $class => $file) {
echo "Missing: {$class} -> {$file}\n";
}
}
// Check failed loads
$failed = $autoloader->getFailedLoads();
print_r($failed);// Find slow loads
$slowest = $autoloader->getSlowestLoads(10);
foreach ($slowest as $class => $info) {
echo "{$class}: " . round($info['time'] * 1000, 2) . "ms\n";
}
// Check cache hit rate
$stats = $autoloader->getStats();
echo "Cache hit rate: {$stats['performance']['cache_hit_rate']}%\n";
// If low, export class map
$autoloader->exportClassMap('/cache/classmap.php');// Verify namespace
$stats = $autoloader->getStats();
echo "Namespace: {$stats['namespace']}\n";
// Check if class exists in map
if (!$autoloader->hasClass('Your\\Class\\Name')) {
echo "Class not found in class map\n";
$autoloader->buildClassMap(); // Rebuild
}Created by daemon.devin
Inspired by:
- PSR-4 Autoloading Standard
- Composer Autoloader
- PHP-DI Container