Created
February 4, 2026 19:31
-
-
Save a-h-abid/c67c98fba4d3492268346a6e0c0e787c to your computer and use it in GitHub Desktop.
Preload script for Laravel application. Made based on version 12. Generated with help from Claude AI.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * Laravel Framework Preloader | |
| * | |
| * Preloads Laravel framework files for improved performance. | |
| * Uses a custom class map loader to avoid triggering Composer's autoload files | |
| * which can contain anonymous classes that cause preload warnings. | |
| * | |
| * Place this file in your project root and configure in php.ini: | |
| * opcache.preload=/path/to/your/project/preload.php | |
| * opcache.preload_user=www-data (or your web server user) | |
| */ | |
| if (!function_exists('opcache_compile_file') || !ini_get('opcache.enable')) { | |
| return; | |
| } | |
| $preloadedClasses = 0; | |
| $skippedClasses = 0; | |
| $startTime = microtime(true); | |
| // Load only the Composer class map (not the full autoloader with file includes) | |
| $classMapFile = __DIR__ . '/vendor/composer/autoload_classmap.php'; | |
| if (!file_exists($classMapFile)) { | |
| error_log('Laravel Preloader: Class map not found. Run "composer dump-autoload --optimize"'); | |
| return; | |
| } | |
| $classMap = require $classMapFile; | |
| // Load essential function files that some classes depend on | |
| $functionFiles = [ | |
| __DIR__ . '/vendor/symfony/deprecation-contracts/function.php', | |
| __DIR__ . '/vendor/symfony/polyfill-php80/bootstrap.php', | |
| __DIR__ . '/vendor/symfony/polyfill-php83/bootstrap.php', | |
| __DIR__ . '/vendor/symfony/polyfill-mbstring/bootstrap.php', | |
| __DIR__ . '/vendor/symfony/polyfill-ctype/bootstrap.php', | |
| __DIR__ . '/vendor/symfony/polyfill-intl-grapheme/bootstrap.php', | |
| __DIR__ . '/vendor/symfony/polyfill-intl-normalizer/bootstrap.php', | |
| ]; | |
| foreach ($functionFiles as $file) { | |
| if (file_exists($file)) { | |
| require_once $file; | |
| } | |
| } | |
| // Register a minimal autoloader that uses only the class map | |
| spl_autoload_register(function (string $class) use ($classMap): void { | |
| if (isset($classMap[$class])) { | |
| require $classMap[$class]; | |
| } | |
| }); | |
| // Only preload namespaces that are safe and commonly used by Laravel | |
| $preloadPrefixes = [ | |
| // PSR interfaces (safe, no dependencies) | |
| 'Psr\\Container\\', | |
| 'Psr\\Log\\', | |
| 'Psr\\SimpleCache\\', | |
| 'Psr\\Http\\Message\\', | |
| 'Psr\\EventDispatcher\\', | |
| 'Psr\\Clock\\', | |
| // Symfony contracts and polyfills (safe base classes) | |
| 'Symfony\\Polyfill\\', | |
| 'Symfony\\Contracts\\', | |
| // Symfony components used by Laravel (order matters - dependencies first) | |
| 'Symfony\\Component\\Clock\\', | |
| 'Symfony\\Component\\Uid\\', | |
| 'Symfony\\Component\\HttpFoundation\\', | |
| 'Symfony\\Component\\Finder\\', | |
| 'Symfony\\Component\\Mime\\', | |
| 'Symfony\\Component\\VarDumper\\', | |
| 'Symfony\\Component\\ErrorHandler\\', | |
| 'Symfony\\Component\\Translation\\', | |
| 'Symfony\\Component\\CssSelector\\', | |
| 'Symfony\\Component\\Process\\', | |
| 'Symfony\\Component\\Mailer\\', | |
| 'Symfony\\Component\\String\\', | |
| // Carbon date library | |
| 'Carbon\\', | |
| // Monolog logging | |
| 'Monolog\\', | |
| // Egulias email validator | |
| 'Egulias\\EmailValidator\\', | |
| // Laravel contracts and support (base classes/traits - must be first) | |
| 'Illuminate\\Contracts\\', | |
| 'Illuminate\\Support\\', | |
| // Laravel core components | |
| 'Illuminate\\Auth\\', | |
| 'Illuminate\\Bus\\', | |
| 'Illuminate\\Cache\\', | |
| 'Illuminate\\Collections\\', | |
| 'Illuminate\\Config\\', | |
| 'Illuminate\\Container\\', | |
| 'Illuminate\\Cookie\\', | |
| 'Illuminate\\Database\\', | |
| 'Illuminate\\Encryption\\', | |
| 'Illuminate\\Events\\', | |
| 'Illuminate\\Filesystem\\', | |
| 'Illuminate\\Foundation\\', | |
| 'Illuminate\\Hashing\\', | |
| 'Illuminate\\Http\\', | |
| 'Illuminate\\Log\\', | |
| 'Illuminate\\Macroable\\', | |
| 'Illuminate\\Pagination\\', | |
| 'Illuminate\\Pipeline\\', | |
| 'Illuminate\\Queue\\', | |
| 'Illuminate\\Redis\\', | |
| 'Illuminate\\Routing\\', | |
| 'Illuminate\\Translation\\', | |
| 'Illuminate\\Validation\\', | |
| 'Illuminate\\View\\', | |
| ]; | |
| // Patterns to exclude (class names) - be aggressive to avoid dependency issues | |
| $excludePatterns = [ | |
| '/Test$/', | |
| '/Tests?\\\\/', | |
| '/Testing\\\\/', | |
| '/Fixture/', | |
| '/Mock/', | |
| '/Stub/', | |
| '/Example/', | |
| '/Benchmark/', | |
| '/Command$/', // Skip console commands (often have complex dependencies) | |
| '/Console\\\\/', // Skip console namespace entirely | |
| '/DependencyInjection\\\\/', // Skip Symfony DI-related classes | |
| '/CompilerPass/', // Skip compiler passes | |
| '/ServiceProvider$/', // Skip service providers (loaded by Laravel at runtime) | |
| '/Artisan/', // Skip artisan-related classes | |
| '/PHPStan\\\\/', // Skip PHPStan integration classes | |
| '/Carbon\\\\Doctrine\\\\/', // Skip Carbon Doctrine integration (requires doctrine/dbal) | |
| '/Carbon\\\\PHPStan\\\\/', // Skip Carbon PHPStan integration | |
| '/Carbon\\\\Laravel\\\\/', // Skip Carbon Laravel service provider | |
| '/@anonymous/', // Skip anonymous classes | |
| '/Loader$/', // Skip file loaders (often have optional dependencies) | |
| '/Generator$/', // Skip generators | |
| '/Dumper$/', // Skip dumpers | |
| '/Symfony\\\\Component\\\\HttpFoundation\\\\Session\\\\Storage\\\\Handler\\\\IdentityMarshaller/', // Requires symfony/cache | |
| '/Symfony\\\\Component\\\\Mailer\\\\Bridge\\\\/', // Skip mailer bridges (optional dependencies) | |
| '/Symfony\\\\Component\\\\Translation\\\\Bridge\\\\/', // Skip translation bridges | |
| '/Symfony\\\\Component\\\\Translation\\\\Loader\\\\/', // Skip translation loaders (optional deps) | |
| '/Symfony\\\\Component\\\\Translation\\\\Dumper\\\\/', // Skip translation dumpers | |
| '/Symfony\\\\Component\\\\Translation\\\\Extractor\\\\/', // Skip translation extractors | |
| '/Symfony\\\\Component\\\\Translation\\\\Writer\\\\/', // Skip translation writers | |
| '/Monolog\\\\Handler\\\\/', // Skip Monolog handlers (many have optional dependencies) | |
| '/Monolog\\\\Formatter\\\\/', // Skip Monolog formatters | |
| '/Monolog\\\\Processor\\\\/', // Skip Monolog processors | |
| ]; | |
| /** | |
| * Check if a class name should be excluded | |
| */ | |
| function shouldExcludeClass(string $className, array $excludePatterns): bool | |
| { | |
| foreach ($excludePatterns as $pattern) { | |
| if (preg_match($pattern, $className)) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| /** | |
| * Check if a class name matches any of the prefixes we want to preload | |
| */ | |
| function matchesPrefix(string $className, array $prefixes): bool | |
| { | |
| foreach ($prefixes as $prefix) { | |
| if (str_starts_with($className, $prefix)) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| /** | |
| * Safely preload a class using the autoloader | |
| * This ensures all dependencies (parent classes, traits, interfaces) are loaded first | |
| */ | |
| function preloadClass(string $className): bool | |
| { | |
| if (class_exists($className, true) || interface_exists($className, true) || trait_exists($className, true)) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| // Get Composer's class map | |
| $composerLoader = require __DIR__ . '/vendor/composer/autoload_classmap.php'; | |
| $classMap = $composerLoader; | |
| // If class map is empty, we need to use the optimized autoloader | |
| // Run: composer dump-autoload --optimize | |
| if (empty($classMap)) { | |
| error_log('Laravel Preloader: Class map is empty. Run "composer dump-autoload --optimize" for best results.'); | |
| } | |
| // Collect classes to preload, organized by prefix for proper ordering | |
| $classesToPreload = []; | |
| foreach ($classMap as $className => $filePath) { | |
| if (matchesPrefix($className, $preloadPrefixes) && !shouldExcludeClass($className, $excludePatterns)) { | |
| $classesToPreload[$className] = $filePath; | |
| } | |
| } | |
| // Sort classes to ensure dependencies are loaded first | |
| // Classes with fewer namespace segments (more generic) should load first | |
| uksort($classesToPreload, function ($a, $b) use ($preloadPrefixes) { | |
| // First, sort by prefix order (dependencies first) | |
| $aIndex = PHP_INT_MAX; | |
| $bIndex = PHP_INT_MAX; | |
| foreach ($preloadPrefixes as $index => $prefix) { | |
| if (str_starts_with($a, $prefix) && $aIndex === PHP_INT_MAX) { | |
| $aIndex = $index; | |
| } | |
| if (str_starts_with($b, $prefix) && $bIndex === PHP_INT_MAX) { | |
| $bIndex = $index; | |
| } | |
| } | |
| if ($aIndex !== $bIndex) { | |
| return $aIndex - $bIndex; | |
| } | |
| // Then sort by namespace depth (shallower first - base classes before derived) | |
| $aDepth = substr_count($a, '\\'); | |
| $bDepth = substr_count($b, '\\'); | |
| if ($aDepth !== $bDepth) { | |
| return $aDepth - $bDepth; | |
| } | |
| // Finally, alphabetical for consistency | |
| return strcmp($a, $b); | |
| }); | |
| $h = fopen(__DIR__ . '/storage/app/preloaded-files.txt', 'w'); | |
| // Preload classes using the autoloader (ensures proper dependency resolution) | |
| foreach ($classesToPreload as $className => $filePath) { | |
| try { | |
| if (preloadClass($className)) { | |
| fwrite($h, $filePath . PHP_EOL); | |
| $preloadedClasses++; | |
| } else { | |
| $skippedClasses++; | |
| } | |
| } catch (Throwable $e) { | |
| $skippedClasses++; | |
| // Only log actual errors, not "class not found" which is expected for some edge cases | |
| if (!str_contains($e->getMessage(), 'not found')) { | |
| error_log("Preload warning for {$className}: " . $e->getMessage()); | |
| } | |
| } | |
| } | |
| fclose($h); | |
| $endTime = microtime(true); | |
| $duration = round(($endTime - $startTime) * 1000, 2); | |
| $peakMemory = memory_get_peak_usage(true); | |
| /** | |
| * Format bytes into human-readable form | |
| */ | |
| function formatBytes(int $bytes, int $precision = 2): string | |
| { | |
| $units = ['B', 'KB', 'MB', 'GB', 'TB']; | |
| $bytes = max($bytes, 0); | |
| $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); | |
| $pow = min($pow, count($units) - 1); | |
| $bytes /= (1 << (10 * $pow)); | |
| return round($bytes, $precision) . ' ' . $units[$pow]; | |
| } | |
| // Log preload statistics (only visible during preload process) | |
| error_log(sprintf( | |
| 'Laravel Preloader: Preloaded %d classes (%d skipped) in %s ms, peak memory: %s', | |
| $preloadedClasses, | |
| $skippedClasses, | |
| $duration, | |
| formatBytes($peakMemory) | |
| )); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment