From a5f3c904f60f222dc9aa3e1749076ac1fd650fdb Mon Sep 17 00:00:00 2001 From: "Joseph.Rawlings" Date: Sun, 21 Dec 2025 07:23:17 -0500 Subject: [PATCH] feat: implement OPcache preload script for improved performance --- preload.php | 348 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 271 insertions(+), 77 deletions(-) diff --git a/preload.php b/preload.php index dc6fc5fd9..fb3aa5c1b 100644 --- a/preload.php +++ b/preload.php @@ -1,103 +1,297 @@ getMethods() as $method) { - if ($method->isPublic() && !$method->isAbstract()) { - $method->getStaticVariables(); + try { + if (opcache_compile_file($path)) { + $preloadedCount++; + return true; } + } catch (Throwable $e) { + // Silently skip files that can't be preloaded (syntax errors, etc.) } - - // Preload parent classes - $parent = $rc->getParentClass(); - if ($parent) { - preloadClass($parent->getName()); - } - - // Preload interfaces - foreach ($rc->getInterfaces() as $interface) { - preloadClass($interface->getName()); - } + + $skippedCount++; + return false; } -function preloadDirectory($dir, $pattern) { +/** + * Recursively preload all PHP files in a directory + */ +function preloadDirectory(string $dir, array $excludePatterns = []): void +{ if (!is_dir($dir)) { return; } $iterator = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($dir) + new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS), + RecursiveIteratorIterator::LEAVES_ONLY ); foreach ($iterator as $file) { - if ($file->isFile() && preg_match($pattern, $file->getPathname())) { - require_once $file->getPathname(); + if (!$file->isFile() || $file->getExtension() !== 'php') { + continue; + } + + $path = $file->getRealPath(); + + // Skip excluded patterns + foreach ($excludePatterns as $pattern) { + if (preg_match($pattern, $path)) { + continue 2; + } + } + + preloadFile($path); + } +} + +/** + * Preload specific files from a list + */ +function preloadFiles(array $files): void +{ + foreach ($files as $file) { + if (is_file($file)) { + preloadFile($file); } } } -// Preload framework classes -foreach ($classesToPreload as $class) { - preloadClass($class); +// ============================================================================= +// 1. LARAVEL CACHED/COMPILED FILES (Most Critical) +// ============================================================================= + +$cachedFiles = [ + $basePath . '/bootstrap/cache/packages.php', + $basePath . '/bootstrap/cache/services.php', + $basePath . '/bootstrap/cache/config.php', + $basePath . '/bootstrap/cache/routes-v7.php', + $basePath . '/bootstrap/cache/events.php', + $basePath . '/bootstrap/cache/filament.php', + $basePath . '/bootstrap/cache/livewire-components.php', +]; + +preloadFiles($cachedFiles); + +// ============================================================================= +// 2. COMPOSER AUTOLOADER (Essential for class resolution) +// ============================================================================= + +preloadFiles([ + $basePath . '/vendor/autoload.php', + $basePath . '/vendor/composer/autoload_real.php', + $basePath . '/vendor/composer/ClassLoader.php', + $basePath . '/vendor/composer/autoload_static.php', +]); + +// ============================================================================= +// 3. LARAVEL CORE FRAMEWORK +// ============================================================================= + +$laravelCoreDirs = [ + // Foundation & Core + $basePath . '/vendor/laravel/framework/src/Illuminate/Foundation', + $basePath . '/vendor/laravel/framework/src/Illuminate/Container', + $basePath . '/vendor/laravel/framework/src/Illuminate/Contracts', + + // HTTP Layer + $basePath . '/vendor/laravel/framework/src/Illuminate/Http', + $basePath . '/vendor/laravel/framework/src/Illuminate/Routing', + $basePath . '/vendor/laravel/framework/src/Illuminate/Pipeline', + + // Database & Eloquent + $basePath . '/vendor/laravel/framework/src/Illuminate/Database', + + // Support utilities + $basePath . '/vendor/laravel/framework/src/Illuminate/Support', + $basePath . '/vendor/laravel/framework/src/Illuminate/Collections', + + // Session & Auth + $basePath . '/vendor/laravel/framework/src/Illuminate/Auth', + $basePath . '/vendor/laravel/framework/src/Illuminate/Session', + $basePath . '/vendor/laravel/framework/src/Illuminate/Cookie', + + // Views & Blade + $basePath . '/vendor/laravel/framework/src/Illuminate/View', + + // Events & Queue + $basePath . '/vendor/laravel/framework/src/Illuminate/Events', + $basePath . '/vendor/laravel/framework/src/Illuminate/Queue', + + // Validation & Cache + $basePath . '/vendor/laravel/framework/src/Illuminate/Validation', + $basePath . '/vendor/laravel/framework/src/Illuminate/Cache', + + // Encryption & Hashing + $basePath . '/vendor/laravel/framework/src/Illuminate/Encryption', + $basePath . '/vendor/laravel/framework/src/Illuminate/Hashing', +]; + +// Exclude test files and stubs +$excludePatterns = [ + '/[Tt]est[s]?\.php$/', + '/[Ss]tub[s]?\.php$/', + '/[Ff]ixture[s]?/', +]; + +foreach ($laravelCoreDirs as $dir) { + preloadDirectory($dir, $excludePatterns); } -// Preload application directories -preloadDirectory(__DIR__ . '/app/Http/Controllers', '/\.php$/'); -preloadDirectory(__DIR__ . '/app/Models', '/\.php$/'); -preloadDirectory(__DIR__ . '/app/Jobs', '/\.php$/'); -preloadDirectory(__DIR__ . '/app/Providers', '/\.php$/'); +// ============================================================================= +// 4. LIVEWIRE (Critical for your app) +// ============================================================================= -// Free memory -gc_collect_cycles(); +preloadDirectory($basePath . '/vendor/livewire/livewire/src', $excludePatterns); +preloadDirectory($basePath . '/vendor/livewire/volt/src', $excludePatterns); + +// ============================================================================= +// 5. FILAMENT (Admin Panel - Critical for your app) +// ============================================================================= + +$filamentDirs = [ + $basePath . '/vendor/filament/filament/src', + $basePath . '/vendor/filament/support/src', + $basePath . '/vendor/filament/forms/src', + $basePath . '/vendor/filament/tables/src', + $basePath . '/vendor/filament/actions/src', + $basePath . '/vendor/filament/infolists/src', + $basePath . '/vendor/filament/notifications/src', + $basePath . '/vendor/filament/widgets/src', +]; + +foreach ($filamentDirs as $dir) { + preloadDirectory($dir, $excludePatterns); +} + +// ============================================================================= +// 6. COMMON DEPENDENCIES +// ============================================================================= + +$commonDeps = [ + // Symfony HTTP Foundation + $basePath . '/vendor/symfony/http-foundation/Request.php', + $basePath . '/vendor/symfony/http-foundation/Response.php', + $basePath . '/vendor/symfony/http-foundation/HeaderBag.php', + $basePath . '/vendor/symfony/http-foundation/ParameterBag.php', + $basePath . '/vendor/symfony/http-foundation/InputBag.php', + $basePath . '/vendor/symfony/http-foundation/FileBag.php', + $basePath . '/vendor/symfony/http-foundation/ServerBag.php', + $basePath . '/vendor/symfony/http-foundation/Cookie.php', + $basePath . '/vendor/symfony/http-foundation/Session/Session.php', + + // Symfony HTTP Kernel + $basePath . '/vendor/symfony/http-kernel/HttpKernel.php', + + // PSR Interfaces + $basePath . '/vendor/psr/container/src/ContainerInterface.php', + $basePath . '/vendor/psr/http-message/src/MessageInterface.php', + $basePath . '/vendor/psr/http-message/src/RequestInterface.php', + $basePath . '/vendor/psr/http-message/src/ResponseInterface.php', + $basePath . '/vendor/psr/log/src/LoggerInterface.php', + + // Carbon (Date handling) + $basePath . '/vendor/nesbot/carbon/src/Carbon/Carbon.php', + $basePath . '/vendor/nesbot/carbon/src/Carbon/CarbonInterface.php', + $basePath . '/vendor/nesbot/carbon/src/Carbon/CarbonImmutable.php', +]; + +preloadFiles($commonDeps); + +// ============================================================================= +// 7. APPLICATION CODE (Your code) +// ============================================================================= + +$appDirs = [ + // Models (Most frequently accessed) + $basePath . '/app/Models', + + // HTTP Layer + $basePath . '/app/Http/Controllers', + $basePath . '/app/Http/Middleware', + $basePath . '/app/Http/Requests', + + // Livewire Components + $basePath . '/app/Livewire', + + // Filament Admin + $basePath . '/app/Admin/Resources', + $basePath . '/app/Admin/Pages', + $basePath . '/app/Admin/Widgets', + + // Service Providers + $basePath . '/app/Providers', + + // Jobs & Events + $basePath . '/app/Jobs', + $basePath . '/app/Events', + $basePath . '/app/Listeners', + + // Helpers & Classes + $basePath . '/app/Classes', + $basePath . '/app/Helpers', + $basePath . '/app/Traits', + + // Policies & Rules + $basePath . '/app/Policies', + $basePath . '/app/Rules', +]; + +foreach ($appDirs as $dir) { + preloadDirectory($dir); +} + +// ============================================================================= +// 8. BLADE COMPILED VIEWS (if using view caching) +// ============================================================================= + +// Only preload if compiled views exist and are recent +$compiledViewsPath = $basePath . '/storage/framework/views'; +if (is_dir($compiledViewsPath)) { + // Limit to most recently modified compiled views (top 200) + $viewFiles = glob($compiledViewsPath . '/*.php'); + if ($viewFiles) { + // Sort by modification time, newest first + usort($viewFiles, fn($a, $b) => filemtime($b) - filemtime($a)); + $viewFiles = array_slice($viewFiles, 0, 200); + preloadFiles($viewFiles); + } +} + +// ============================================================================= +// SUMMARY (logged to error_log for debugging) +// ============================================================================= + +if (php_sapi_name() === 'cli') { + echo "OPcache Preload Complete: {$preloadedCount} files preloaded, {$skippedCount} skipped\n"; + echo "Memory usage: " . round(memory_get_usage(true) / 1024 / 1024, 2) . " MB\n"; +} else { + error_log("OPcache Preload: {$preloadedCount} files preloaded, {$skippedCount} skipped"); +}