PivotPHP Core provides two complementary static file management solutions, each optimized for different use cases and project scales.
| Aspect | SimpleStaticFileManager | StaticFileManager |
|---|---|---|
| Strategy | Individual routes per file | Dynamic resolution with cache |
| Best for | Small projects (<100 files) | Medium/Large projects (100+ files) |
| Memory Usage | Linear per file | Optimized with intelligent cache |
| Performance | High for few files | Optimized for many files |
| Features | Basic file serving | Advanced: ETag, compression, security |
| Complexity | Minimal | Full-featured |
Direct approach that registers each file as an individual route in the router.
- One file = One route: Each physical file becomes a specific route
- No wildcards: Direct mapping without pattern matching
- High performance: Optimal for small file counts
- Simple caching: File metadata stored in memory
- ✅ Small projects with <100 static files
- ✅ When you need total control over served files
- ✅ When routing performance is critical
- ✅ Simple websites or APIs with minimal assets
use PivotPHP\Core\Core\Application;
use PivotPHP\Core\Routing\SimpleStaticFileManager;
$app = new Application();
// Register entire directory - creates individual routes
SimpleStaticFileManager::registerDirectory(
'/assets', // Route prefix
'public/assets', // Physical path
$app // Application instance
);
// This creates routes like:
// GET /assets/css/style.css → public/assets/css/style.css
// GET /assets/js/app.js → public/assets/js/app.js
// GET /assets/images/logo.png → public/assets/images/logo.pngSimpleStaticFileManager::configure([
'max_file_size' => 5242880, // 5MB max
'allowed_extensions' => [
'css', 'js', 'png', 'jpg', 'svg'
],
'cache_control_max_age' => 3600 // 1 hour cache
]);// Get performance statistics
$stats = SimpleStaticFileManager::getStats();
echo "Registered files: {$stats['registered_files']}\n";
echo "Total hits: {$stats['total_hits']}\n";
echo "Memory usage: " . round($stats['memory_usage_bytes'] / 1024, 2) . " KB\n";
// List all registered file routes
$files = SimpleStaticFileManager::getRegisteredFiles();
foreach ($files as $route) {
echo "Route: {$route}\n";
}- Directory Scan: Recursively scans physical directory
- Validation: Checks file size, extension, readability
- Route Creation: Generates individual route for each file
- Handler Registration: Creates optimized handler with file metadata
- Memory Storage: Stores file info for quick access
'js' => 'application/javascript',
'css' => 'text/css',
'html' => 'text/html',
'json' => 'application/json',
'png' => 'image/png',
'jpg' => 'image/jpeg',
'svg' => 'image/svg+xml',
'pdf' => 'application/pdf',
'txt' => 'text/plain',
'woff2' => 'font/woff2'
// ... and more- Content-Type: Automatically detected from file extension
- Content-Length: File size in bytes
- Cache-Control: Configurable max-age
- ETag: MD5 hash based on file path, modification time, and size
- Last-Modified: File modification timestamp
Advanced static file serving with Express.js-like functionality, intelligent caching, and production-ready features.
- Dynamic Resolution: Resolves files on-demand with wildcard patterns
- Intelligent Cache: Metadata caching with configurable limits
- Advanced Features: ETag, compression, security, index files
- Facade Pattern: Uses SimpleStaticFileManager for directory registration
- ✅ Medium/Large projects with 100+ static files
- ✅ Single Page Applications (SPAs) with asset management
- ✅ Production environments requiring cache optimization
- ✅ When you need Express.js static() functionality
- ✅ Projects requiring advanced security features
use PivotPHP\Core\Core\Application;
use PivotPHP\Core\Routing\StaticFileManager;
$app = new Application();
// Register directory (delegates to SimpleStaticFileManager)
StaticFileManager::registerDirectory(
'/public',
'public/assets',
$app,
[
'index' => ['index.html', 'index.htm'],
'dotfiles' => 'ignore',
'redirect' => true
]
);// Register with wildcard pattern (legacy method)
$handler = StaticFileManager::register(
'/static', // Route prefix
'public/static', // Physical directory
[
'index' => ['index.html'],
'dotfiles' => 'ignore',
'extensions' => false,
'fallthrough' => true,
'redirect' => true
]
);
// Use handler in route with wildcard
$app->get('/static/*', $handler);StaticFileManager::configure([
'enable_cache' => true,
'max_file_size' => 10485760, // 10MB
'max_cache_entries' => 10000, // Cache limit
'allowed_extensions' => [
'js', 'css', 'html', 'png', 'jpg', 'svg', 'woff2'
],
'security_check' => true, // Path traversal protection
'send_etag' => true, // ETag headers
'send_last_modified' => true, // Last-Modified headers
'cache_control_max_age' => 86400 // 24 hours
]);// List all files in registered path
$files = StaticFileManager::listFiles('/public', 'css/', 2);
foreach ($files as $file) {
echo "Route: {$file['path']}\n";
echo "Size: {$file['size']} bytes\n";
echo "Modified: " . date('Y-m-d H:i:s', $file['modified']) . "\n";
echo "MIME: {$file['mime']}\n\n";
}// Generate complete route map
$routeMap = StaticFileManager::generateRouteMap();
foreach ($routeMap as $prefix => $info) {
echo "Prefix: {$prefix}\n";
echo "Physical Path: {$info['physical_path']}\n";
echo "File Count: {$info['file_count']}\n";
foreach ($info['files'] as $file) {
echo " - {$file['path']} ({$file['extension']})\n";
}
}$stats = StaticFileManager::getStats();
echo "Registered paths: {$stats['registered_paths']}\n";
echo "Cached files: {$stats['cached_files']}\n";
echo "Total hits: {$stats['total_hits']}\n";
echo "Cache hits: {$stats['cache_hits']}\n";
echo "Cache misses: {$stats['cache_misses']}\n";
echo "Memory usage: {$stats['memory_usage_mb']} MB\n";// Clear file cache
StaticFileManager::clearCache();
// Get specific path info
$pathInfo = StaticFileManager::getPathInfo('/public');
if ($pathInfo) {
echo "Physical path: {$pathInfo['physical_path']}\n";
echo "Options: " . json_encode($pathInfo['options']) . "\n";
}// The Application class uses StaticFileManager by default
$app = new Application();
$app->staticFiles('/assets', 'public/assets');
// This internally calls:
// StaticFileManager::registerDirectory('/assets', 'public/assets', $app);
// Which then delegates to:
// SimpleStaticFileManager::registerDirectory('/assets', 'public/assets', $app);// StaticFileManager acts as facade
class StaticFileManager
{
public static function registerDirectory($prefix, $path, $app, $options = [])
{
// Delegates to SimpleStaticFileManager for actual registration
SimpleStaticFileManager::registerDirectory($prefix, $path, $app, $options);
}
public static function register($prefix, $path, $options = [])
{
// Legacy pattern-based method with advanced features
return self::createFileHandler($prefix);
}
}| Operation | SimpleStaticFileManager | StaticFileManager |
|---|---|---|
| Registration Time | ~50ms | ~20ms |
| Memory Usage | ~2MB | ~500KB |
| Request Time | ~0.1ms | ~0.3ms |
| Cache Efficiency | N/A | 95%+ |
// SimpleStaticFileManager: Linear growth
memory_usage = file_count * avg_file_metadata_size
// StaticFileManager: Logarithmic growth with cache
memory_usage = cache_limit * avg_cache_entry_size// Automatic security checks in StaticFileManager
private static function containsPathTraversal(string $path): bool
{
return strpos($path, '..') !== false ||
strpos($path, '\\') !== false ||
strpos($path, '\0') !== false;
}// Both managers validate file extensions
$extension = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
if (!in_array($extension, self::$config['allowed_extensions'], true)) {
return null; // File type not allowed
}// Configurable file size limits
if ($fileSize > self::$config['max_file_size']) {
return null; // File too large
}- Building small websites or APIs
- You have <100 static files
- You need maximum routing performance
- You want explicit control over served files
- Memory usage is not a concern
- Building SPAs or large applications
- You have 100+ static files
- You need Express.js-like functionality
- You want advanced caching and optimization
- You need production-ready features
// Focus on developer experience
StaticFileManager::configure([
'enable_cache' => false, // Disable for hot reload
'security_check' => true, // Always enabled
'send_etag' => false, // Disable for development
'cache_control_max_age' => 0 // No browser cache
]);// Focus on performance and security
StaticFileManager::configure([
'enable_cache' => true,
'max_cache_entries' => 50000,
'security_check' => true,
'send_etag' => true,
'send_last_modified' => true,
'cache_control_max_age' => 86400 // 24 hours
]);public/
├── assets/
│ ├── css/ # Stylesheets
│ ├── js/ # JavaScript
│ ├── images/ # Images
│ └── fonts/ # Web fonts
├── uploads/ # User uploads (separate handling)
└── static/ # Static pages
// Check if file is in allowed extensions
$stats = SimpleStaticFileManager::getStats();
if ($stats['registered_files'] === 0) {
echo "No files registered - check file extensions and permissions\n";
}// Monitor SimpleStaticFileManager memory
$stats = SimpleStaticFileManager::getStats();
if ($stats['memory_usage_bytes'] > 10 * 1024 * 1024) { // 10MB
echo "Consider switching to StaticFileManager for better memory efficiency\n";
}// Check StaticFileManager cache efficiency
$stats = StaticFileManager::getStats();
$hitRate = $stats['cache_hits'] / max(1, $stats['total_hits']);
if ($hitRate < 0.8) {
echo "Cache hit rate low: " . ($hitRate * 100) . "%\n";
echo "Consider increasing max_cache_entries\n";
}// Optimize for fewer, critical files only
SimpleStaticFileManager::configure([
'allowed_extensions' => ['css', 'js'], // Only essential files
'max_file_size' => 1048576, // 1MB limit
]);// Tune cache for your workload
StaticFileManager::configure([
'max_cache_entries' => $file_count * 1.5, // 150% of actual files
'enable_cache' => true,
]);registerDirectory(string $prefix, string $path, Application $app, array $options = []): voidconfigure(array $config): voidgetStats(): arraygetRegisteredFiles(): arrayclearCache(): void
registerDirectory(string $prefix, string $path, Application $app, array $options = []): voidregister(string $prefix, string $path, array $options = []): callableconfigure(array $config): voidgetStats(): arraygetRegisteredPaths(): arraygetPathInfo(string $prefix): ?arraylistFiles(string $prefix, string $subPath = '', int $maxDepth = 3): arraygenerateRouteMap(): arrayclearCache(): void
Both StaticFileManager implementations serve different needs in the PivotPHP ecosystem:
- SimpleStaticFileManager: Direct, high-performance solution for small projects
- StaticFileManager: Feature-rich, production-ready solution for larger applications
Choose based on your project size, performance requirements, and feature needs. Both are actively maintained and fully supported.