├── LICENSE.md
├── README.md
├── composer.json
├── config
└── preload.php
├── src
├── Compiler
│ ├── Compiler.php
│ └── Pipes
│ │ ├── EnsureDirectoryExists.php
│ │ ├── FireEvent.php
│ │ ├── LoadPreloaderStub.php
│ │ ├── LoadStatisticsStub.php
│ │ ├── SetOpcacheConfig.php
│ │ ├── SetPreloadConfig.php
│ │ ├── SetStatistics.php
│ │ ├── WriteListFile.php
│ │ ├── WritePreloaderFile.php
│ │ └── WriteStatisticsFile.php
├── Composer.php
├── Condition.php
├── Console
│ └── Commands
│ │ └── Stub.php
├── Events
│ ├── ListGenerated.php
│ └── PreloadGenerated.php
├── Exceptions
│ └── PreloadException.php
├── Facades
│ └── Preload.php
├── Http
│ └── Middleware
│ │ └── PreloadMiddleware.php
├── Jobs
│ └── StorePreloadScript.php
├── Lister
│ ├── Lister.php
│ └── Pipes
│ │ ├── Concerns
│ │ └── RetrievesFilesFromFinder.php
│ │ ├── CutListByMemoryLimit.php
│ │ ├── ExcludePreloadVariable.php
│ │ ├── FireEvent.php
│ │ ├── LoadAcceleratedFiles.php
│ │ ├── LoadIncludedAndExcludedLibraries.php
│ │ ├── LoadOpcacheConfig.php
│ │ ├── MayExcludeFiles.php
│ │ ├── MayIncludeFiles.php
│ │ ├── MayScopeFilesToProjectPath.php
│ │ ├── NormalizeList.php
│ │ └── SortScriptsByHitRatio.php
├── Listing.php
├── Opcache.php
├── PreloadServiceProvider.php
└── Preloader.php
└── stubs
├── preload.php.stub
└── statistics.md
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Italo Israel Baeza Cabrera
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Preload
2 | [](https://packagist.org/packages/laragear/preload)
3 | [](https://github.com/Laragear/Preload/actions)
4 | [](https://codecov.io/gh/Laragear/Preload)
5 | [](https://qlty.sh/gh/Laragear/projects/Preload)
6 | [](https://sonarcloud.io/dashboard?id=Laragear_Preload)
7 | [](https://laravel.com/docs/9.x/octane#introduction)
8 |
9 | Dynamically preload your Laravel application.
10 |
11 | This package generates a [PHP preloading](https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.preload) script from your Opcache statistics automatically. No need to hack your way in.
12 |
13 | ## Become a sponsor
14 |
15 | [](https://github.com/sponsors/DarkGhostHunter)
16 |
17 | Your support allows me to keep this package free, up-to-date and maintainable. Alternatively, you can **spread the word on social media!**
18 |
19 | ## Requirements
20 |
21 | * Laravel 11 or later
22 | * [Opcache & Preloading enabled](https://www.php.net/manual/en/book.opcache.php) (`ext-opcache`).
23 |
24 | ## Installation
25 |
26 | Require this using Composer into your project
27 |
28 | ```bash
29 | composer require laragear/preload
30 | ```
31 |
32 | > [!NOTE]
33 | >
34 | > This package doesn't require the `ext-opcache` extension to install. Just be sure to have it [enabled in your deployment server](https://www.php.net/manual/en/book.opcache.php).
35 |
36 | ## What is Preloading? Does it make my app FAST?
37 |
38 | PHP interpreter needs to read and compile each requested file in your project. When Opcache is enabled, it will keep interpreted files in memory instead of reading them again from the file system, which is miles faster.
39 |
40 | Opcache's Preloading allows to store in memory a given list of files when the PHP process starts, before normal execution. This makes the application _faster_ for first requests, as these files to read are already in memory. With [JIT](https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.jit), these files are also compiled into byte-code, saving another step.
41 |
42 | This package generates a file with a list of the most accessed files of your application, and a script that will load these files on startup. You can point the "loader" script into your `php.ini`:
43 |
44 | ```ini
45 | opcache.preload_user = 'www-data'
46 | opcache.preload = '/www/app/preload.php';
47 | ```
48 |
49 | After that, the next time PHP starts, this list of files will be preloaded automatically.
50 |
51 | > [!NOTE]
52 | >
53 | > If you're behind a shared server, preloading may be not available for your application. Normally, shared servers also share the same PHP process, and its configuration file (`php.ini`) is not available for modification. Check your server if you're not sure if Laragear Preload should be installed.
54 |
55 | ## Usage
56 |
57 | By default, this package pushes a queued job data each 10,000 requests, containing a limited list of the most accessed files of the application. [This condition can be changed](#custom-condition).
58 |
59 | First, you should publish the stub script with the `preload:stub` Artisan command. By default, it will copy a stub preloader into application root directory [by default](#paths).
60 |
61 | ```bash
62 | php artisan preload:stub
63 |
64 | # Stub copied at [/www/app/preload.php]
65 | #
66 | # Remember to edit your [php.ini] file:
67 | # opcache.preload = /www/app/preload.php;
68 | ```
69 |
70 | This way, you can add the preload file path in your `php.ini` as instructed by the command.
71 |
72 | ```ini
73 | opcache.preload = '/www/app/preload.php';
74 | ```
75 |
76 | That's it. At the 10,000th request, the preloader stub will be replaced by a real preload script along with the list of files that should be warmed up by PHP at startup.
77 |
78 | ## Custom condition
79 |
80 | This package includes a simple condition callback: each 10,000 requests, generate a Preloading script.
81 |
82 | If this condition is not enough for your application, or you require a custom condition, you can easily create a callback or even an _invokable_ class with your own logic. The callable will be resolved by the application container, and run after the request has been sent to the browser.
83 |
84 | Once you create the condition, register it through the `condition()` method of the `Preloader` facade. You can do this in your `App\Providers\AppServiceProvider` or `bootstrap/app.php`.
85 |
86 | ```php
87 | use Illuminate\Http\Request;
88 | use Illuminate\Foundation\Application;
89 | use Illuminate\Support\Lottery;
90 | use Laragear\Preload\Facades\Preload;
91 |
92 | return Application::configure()
93 | ->registered(function () {
94 | Preload::condition(function (Request $request) {
95 | if ($request->user()?->isAdmin()) {
96 | return false;
97 | }
98 |
99 | return random_int(0, 100) === 50;
100 | });
101 | })->create();
102 | ```
103 |
104 | You may also return a `Illuminate\Support\Lottery` instance for convenience, which is great for testing purposes.
105 |
106 | ```php
107 | use Illuminate\Support\Lottery;
108 | use Laragear\Preload\Facades\Preload;
109 |
110 | Preload::condition(function (Request $request) {
111 | if ($request->user()?->isAdmin()) {
112 | return false;
113 | }
114 |
115 | return Lottery::odds(2, 100);
116 | });
117 | ```
118 |
119 | ## Include and exclude
120 |
121 | To include or exclude PHP files or entire directory paths from the Preload list, use the `include()` and `exclude()` methods from the `Preload` facade, respectively.
122 |
123 | Both methods accept an array of [glob patterns](https://en.wikipedia.org/wiki/Glob_(programming)) or a callback that receives the [Symfony Finder](https://symfony.com/doc/current/components/finder.html) for greater filtering options. On both cases, only `.php` files will be included in the list.
124 |
125 | ```php
126 | use Laragear\Preload\Facades\Preload;
127 | use Illuminate\Foundation\Application;
128 | use Illuminate\Support\ServiceProvider;
129 | use Symfony\Component\Finder\Finder;
130 |
131 | return Application::configure()
132 | ->booted(function () {
133 | Preload::include(base_path('/services/**'));
134 |
135 | Preload::exclude(function (Finder $find) {
136 | $find->in(base_path('/temp/'))->contains('class ');
137 | });
138 | })
139 | ->create();
140 | ```
141 |
142 | > [!IMPORTANT]
143 | >
144 | > Included files will be _appended_ to the list. This means that exclusion logic will run first.
145 |
146 | ### Excluding and including libraries
147 |
148 | You can easily exclude or include entire libraries from your application by setting them at the `extra.preload` key of your `composer.json`, respectively.
149 |
150 | The `preload` object should contain the library name as key, and `false` to exclude it or `true` to include. If you require fine-tuning, you can use an object with `exclude` and `include` files and a glob pattern for the file or files you wish to exclude or include, respectively. These patterns will be fed to the underlying Symfony Finder.
151 |
152 | ```json
153 | {
154 | "extra": {
155 | "preload": {
156 | "laragear/meta": {
157 | "exclude": ["src/Foo/*", "src/Bar/*/**"],
158 | "include": "src/Request/*"
159 | },
160 | "charlesdp/builder": false
161 | }
162 | }
163 | }
164 | ```
165 |
166 | > [!IMPORTANT]
167 | >
168 | > Included libraries will be _appended_ to the list. This means that exclusion logic will run first.
169 |
170 | ## Configuration
171 |
172 | Some people may not be happy with the "default" behaviour. Luckily, you can configure your own way to generate the script.
173 |
174 | First publish the configuration file:
175 |
176 | ```bash
177 | php artisan vendor:publish --provider="Laragear\Preload\PreloadServiceProvider"
178 | ```
179 |
180 | Let's check the config array:
181 |
182 | ```php
183 | env('PRELOAD_ENABLE'),
187 | 'project_only' => true,
188 | 'memory' => 32,
189 | 'job' => [
190 | 'connection' => env('PRELOAD_JOB_CONNECTION'),
191 | 'queue' => env('PRELOAD_JOB_QUEUE'),
192 | ],
193 | 'path' => base_path(),
194 | 'use_require' => false,
195 | 'autoload' => base_path('vendor/autoload.php'),
196 | 'ignore_not_found' => true,
197 | ];
198 | ```
199 |
200 | #### Enable
201 |
202 | ```php
203 | return [
204 | 'env' => env('PRELOAD_ENABLE'),
205 | ];
206 | ```
207 |
208 | By default, a global middleware is registered automatically on production environments. You can forcefully enable or disable this middleware using an environment variable set to `true` or `false`, respectively.
209 |
210 | ```dotenv
211 | PRELOAD_ENABLE=true
212 | ```
213 |
214 | ### Project Scope
215 |
216 | ```php
217 | return [
218 | 'project_only' => true,
219 | ];
220 | ```
221 |
222 | Some PHP processes may be shared between multiple projects. To avoid preloading files outside the current project, this is set to `true` by default. Disabling it will allow preloading files regardless of the directory, even outside the project path.
223 |
224 | ### Memory Limit
225 |
226 | ```php
227 | return [
228 | 'memory' => 32,
229 | ];
230 | ```
231 |
232 | The memory limit, in MiB (aka "Windows MegaBytes"), of the List. Once this threshold is reached, no more scripts will be included in the list.
233 |
234 | For most applications, 32MB is fine, but you may fine-tune it for your project specifically.
235 |
236 | > [!NOTE]
237 | >
238 | > This is not Opcache memory limit, as its handled separately.
239 |
240 | ### Job configuration
241 |
242 | ```php
243 | return [
244 | 'job' => [
245 | 'connection' => env('PRELOAD_JOB_CONNECTION'),
246 | 'queue' => env('PRELOAD_JOB_QUEUE'),
247 | ],
248 | ];
249 | ```
250 |
251 | When the job receives the list to persist, it will be dispatched to the connection and queue set here. When `null`, the framework uses the defaults. You should use your `.env` file to set them:
252 |
253 | ```dotenv
254 | PRELOAD_JOB_CONNECTION=redis
255 | PRELOAD_JOB_QUEUE=low
256 | ```
257 |
258 | ### Paths
259 |
260 | ```php
261 | return [
262 | 'path' => base_path(),
263 | ];
264 | ```
265 |
266 | This set the directory path where the preloader files should be stored. By default, it uses your project root path.
267 |
268 | If you change the directory path, ensure PHP has permissions to write on it. Whatever you place it, never do it in a public/accessible directory, like `public` or `storage/app/public`.
269 |
270 | > [!IMPORTANT]
271 | >
272 | > Double-check your file permissions to avoid failures on production when reading the file.
273 |
274 | ### Method
275 |
276 | ```php
277 | return [
278 | 'use_require' => true,
279 | 'autoload' => base_path('vendor/autoload.php'),
280 | ];
281 | ```
282 |
283 | Opcache allows preloading files using `require_once` or `opcache_compile_file()`.
284 |
285 | Preload uses `opcache_compile_file()` by default for better manageability on the files preloaded. Some unresolved links may output warnings at startup, but nothing critical.
286 |
287 | Using `require_once` will **execute** the files found. By resolving all the links (imports, parent classes, traits, interfaces, etc.) before compiling it, it may output heavy errors on files that shouldn't be executed like plain scripts. Depending on your application, you may want to use one over the other.
288 |
289 | If you plan use `require_once`, ensure you have set the correct path to the Composer Autoloader, since it will be used to resolve classes, among other files.
290 |
291 | ### Ignore not found files
292 |
293 | ```php
294 | return [
295 | 'ignore_not_found' => true,
296 | ];
297 | ```
298 |
299 | Some files are created by Laravel at runtime and actively cached by Opcache, but on deployment are absent, like [real-time facades](https://laravel.com/docs/facades#real-time-facades) or compiled Blade Views. It's safe to ignore them by default.
300 |
301 | You can disable this for any reason, which will throw an Exception if any file is missing, but is recommended leaving it alone unless you know what you're doing.
302 |
303 | ## Testing
304 |
305 | On `testing` environments, the middleware responsible for executing the condition is never registered in the HTTP Kernel, so your test won't mistakenly create a preload script.
306 |
307 | You may alternatively force the middleware to not be registered by setting the `PRELOAD_ENABLE` environment variable to `false` in your `phpunit.xml`.
308 |
309 | ```xml
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 | ```
319 |
320 | ## FAQ
321 |
322 | * **Can I manually disable the Preloader?**
323 |
324 | [Yes](#enable). When disabled, the global middleware that executes the condition doesn't run at all.
325 |
326 | * **Do I need to restart PHP after the list is generated?**
327 |
328 | No. The files are already in Opcache memory.
329 |
330 | * **Why I can't use something like `php artisan preload:generate` instead or a [scheduled job](https://laravel.com/docs/scheduling)?**
331 |
332 | Because it requires Opcache statistics from the live application.
333 |
334 | Running PHP CLI will gather CLI statistics, different to the web server, which is shows unrealistic statistics.
335 |
336 | * **Does this excludes the package itself from the list? Does make a difference?**
337 |
338 | No, and it does not. Only the global middleware and condition may be heavily requested, but most of this package files won't.
339 |
340 | * **I activated this Preload but my application still doesn't feel _faster_. What's wrong?**
341 |
342 | Initial requests _should_ be faster under a preload script. This does not affect Opcache or the whole application performance in any other way.
343 |
344 | If you still _feel_ your app is slow, remember to benchmark your app, cache your config and views, check your database queries and API calls, and queue expensive logic, among other things. You can also use [Laravel Octane](https://github.com/laravel/octane).
345 |
346 | * **How the list is created?**
347 |
348 | Most hit files in descending order. Each file consumes memory, so the list is cut when the cumulative memory usage reaches the [configurable limit](#memory-limit).
349 |
350 | When classes with links to files not contained in the list are loaded, PHP will issue some warnings, which is normal and intended.
351 |
352 | * **Can I just put all the files in my project?**
353 |
354 | You shouldn't. Including all the files of your application may have diminishing returns compared to, for example, only the most requested. Also, it will make the preloading take more time.
355 |
356 | You can always benchmark your app yourself to prove this is wrong for your exclusive case.
357 |
358 | * **Can I use a custom condition?**
359 |
360 | [Yes.](#custom-condition)
361 |
362 | * **Can I deactivate the middleware? Or check only XXX status?**
363 |
364 | [Yes.](#enable) If you need to check only for a given response status code, you can create a custom middleware.
365 |
366 | * **Does the middleware works on unit testing?**
367 |
368 | Nope. The middleware is not registered if the application is running under Unit Testing environment.
369 |
370 | * **How can I know when a Preload script is successfully generated?**
371 |
372 | The `ListGenerated` and `PreloadGenerated` events are fired when the list is generated during a request, and the script is saved through a queued job, respectively.
373 |
374 | You can [add a Listener](https://laravel.com/docs/events#registering-events-and-listeners) to dispatch an email or a Slack notification.
375 |
376 | ## Laravel Octane Compatibility
377 |
378 | - There are no singletons using a stale application instance.
379 | - There are no singletons using a stale config instance.
380 | - There are no singletons using a stale request instance.
381 | - There are no static properties written during a request.
382 |
383 | Aside from that, the (real) condition callback is always executed each Request using the Service Container, so it can (but shouldn't) resolve a fresh config repository.
384 |
385 | ## Security
386 |
387 | If you discover any security related issues, please [use the online form](https://github.com/Laragear/Preload/security).
388 |
389 | # License
390 |
391 | This specific package version is licensed under the terms of the [MIT License](LICENSE.md), at time of publishing.
392 |
393 | [Laravel](https://laravel.com) is a Trademark of [Taylor Otwell](https://github.com/TaylorOtwell/). Copyright © 2011-2023 Laravel LLC.
394 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "laragear/preload",
3 | "description": "Effortlessly make a Preload script for your Laravel application.",
4 | "type": "library",
5 | "license": "MIT",
6 | "minimum-stability": "dev",
7 | "prefer-stable": true,
8 | "keywords": [
9 | "laravel",
10 | "preloader",
11 | "preload",
12 | "php",
13 | "laragear",
14 | "opcache"
15 | ],
16 | "authors": [
17 | {
18 | "name": "Italo Israel Baeza Cabrera",
19 | "email": "darkghosthunter@gmail.com",
20 | "homepage": "https://github.com/sponsors/DarkGhostHunter"
21 | }
22 | ],
23 | "require": {
24 | "php": "^8.2",
25 | "ext-json": "*",
26 | "symfony/finder": "7.*",
27 | "illuminate/config": "11.*|12.*",
28 | "illuminate/console": "11.*|12.*",
29 | "illuminate/contracts": "11.*|12.*",
30 | "illuminate/events": "11.*|12.*",
31 | "illuminate/http": "11.*|12.*",
32 | "illuminate/pipeline": "11.*|12.*",
33 | "illuminate/queue": "11.*|12.*",
34 | "illuminate/support": "11.*|12.*"
35 | },
36 | "require-dev": {
37 | "orchestra/testbench": "9.*|10.*"
38 | },
39 | "autoload": {
40 | "psr-4": {
41 | "Laragear\\Preload\\": "src/"
42 | }
43 | },
44 | "autoload-dev": {
45 | "psr-4": {
46 | "Tests\\": "tests/"
47 | }
48 | },
49 | "scripts": {
50 | "test": "vendor/bin/phpunit --coverage-clover build/logs/clover.xml",
51 | "test-coverage": "vendor/bin/phpunit --coverage-html coverage"
52 | },
53 | "config": {
54 | "sort-packages": true
55 | },
56 | "extra": {
57 | "laravel": {
58 | "providers": [
59 | "Laragear\\Preload\\PreloadServiceProvider"
60 | ],
61 | "aliases": {
62 | "Preload": "Laragear\\Preload\\Facades\\Preload"
63 | }
64 | }
65 | },
66 | "funding": [
67 | {
68 | "type": "Github Sponsorship",
69 | "url": "https://github.com/sponsors/DarkGhostHunter"
70 | },
71 | {
72 | "type": "Paypal",
73 | "url": "https://paypal.me/darkghosthunter"
74 | }
75 | ]
76 | }
77 |
--------------------------------------------------------------------------------
/config/preload.php:
--------------------------------------------------------------------------------
1 | env('PRELOAD_ENABLE'),
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Root directory
23 | |--------------------------------------------------------------------------
24 | |
25 | | Some servers may share the same PHP main process, which may include files
26 | | outside the scope of this project. Setting this to "true" filters all
27 | | the files reported by Opcache to those inside this project path.
28 | |
29 | */
30 |
31 | 'project_only' => true,
32 |
33 | /*
34 | |--------------------------------------------------------------------------
35 | | Memory Limit
36 | |--------------------------------------------------------------------------
37 | |
38 | | The Preloader script can be configured to handle a limited number of
39 | | files based on their memory consumption. The default is a safe bet
40 | | for most apps, but you can change it for your app specifically.
41 | |
42 | | Measured in MB (MegaBytes). Using `0` or `null` disables the limit.
43 | |
44 | */
45 |
46 | 'memory' => 32,
47 |
48 | /*
49 | |--------------------------------------------------------------------------
50 | | Job configuration
51 | |--------------------------------------------------------------------------
52 | |
53 | | When the job is dispatched to store the script after the list is created,
54 | | it will use the defaults connection and queue. For most applications it
55 | | will be just fine, but you may want to change these here if you want.
56 | |
57 | */
58 |
59 | 'job' => [
60 | 'connection' => env('PRELOAD_JOB_CONNECTION'),
61 | 'queue' => env('PRELOAD_JOB_QUEUE'),
62 | ],
63 |
64 | /*
65 | |--------------------------------------------------------------------------
66 | | Output
67 | |--------------------------------------------------------------------------
68 | |
69 | | There are three files created, a preloader script, file containing some
70 | | Opcache statistics and the preload file list. This option sets where
71 | | these should be located. By default, this uses the app base path.
72 | |
73 | */
74 |
75 | 'path' => base_path(),
76 |
77 | /*
78 | |--------------------------------------------------------------------------
79 | | Preloading method
80 | |--------------------------------------------------------------------------
81 | |
82 | | Opcache supports preloading files by using `require_once` (which executes
83 | | and resolves each file link), and `opcache_compile_file` (which not). If
84 | | you want to use require ensure the Composer Autoloader path is correct.
85 | |
86 | | You should set "use_require" to false unless you need scripts to be
87 | | executed to be resolved.
88 | |
89 | */
90 |
91 | 'use_require' => false,
92 | 'autoload' => base_path('vendor/autoload.php'),
93 |
94 | /*
95 | |--------------------------------------------------------------------------
96 | | Ignore Not Found
97 | |--------------------------------------------------------------------------
98 | |
99 | | Sometimes Opcache will include in the list files that are generated by
100 | | Laravel at runtime which don't exist when deploying the application.
101 | | To avoid errors on preloads, we can tell Preloader to ignore them.
102 | |
103 | */
104 |
105 | 'ignore_not_found' => true,
106 | ];
107 |
--------------------------------------------------------------------------------
/src/Compiler/Compiler.php:
--------------------------------------------------------------------------------
1 | config->get('preload.path');
27 |
28 | $this->files->ensureDirectoryExists($path);
29 |
30 | if (! $this->files->isWritable($path)) {
31 | throw new PreloadException("The path [$path] is not writable.");
32 | }
33 |
34 | return $next($listing);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Compiler/Pipes/FireEvent.php:
--------------------------------------------------------------------------------
1 | dispatcher->dispatch(new PreloadGenerated($listing));
29 |
30 | return $next($listing);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Compiler/Pipes/LoadPreloaderStub.php:
--------------------------------------------------------------------------------
1 | preloader = new Stringable($this->stubContents());
32 |
33 | return $next($listing);
34 | }
35 |
36 | /**
37 | * Return the contents of the stub preload script file.
38 | *
39 | * @return string
40 | */
41 | protected function stubContents(): string
42 | {
43 | try {
44 | return $this->files->get(Preloader::STUB_PRELOAD);
45 | } catch (FileNotFoundException $e) {
46 | throw new PreloadException(
47 | 'Cannot read the stub "'.Preloader::STUB_PRELOAD.'" contents.', $e->getCode(), $e
48 | );
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Compiler/Pipes/LoadStatisticsStub.php:
--------------------------------------------------------------------------------
1 | statistics = $listing->statistics->append($this->stubContents());
31 |
32 | return $next($listing);
33 | }
34 |
35 | /**
36 | * Return the contents of the stub preload script file.
37 | *
38 | * @return string
39 | */
40 | protected function stubContents(): string
41 | {
42 | try {
43 | return $this->files->get(Preloader::STUB_STATISTICS);
44 | } catch (FileNotFoundException $e) {
45 | throw new PreloadException(
46 | 'Cannot read the stub "'.Preloader::STUB_STATISTICS.'" contents.', $e->getCode(), $e
47 | );
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Compiler/Pipes/SetOpcacheConfig.php:
--------------------------------------------------------------------------------
1 | statistics = $listing->statistics->replace(...$this->opcacheConfig($listing->opcache));
21 |
22 | return $next($listing);
23 | }
24 |
25 | /**
26 | * Returns a list of replaceable string with Opcache data.
27 | */
28 | protected function opcacheConfig(array $opcache): array
29 | {
30 | return [
31 | [
32 | '@opcache_memory_used',
33 | '@opcache_memory_free',
34 | '@opcache_memory_wasted',
35 | '@opcache_files',
36 | '@opcache_hit_rate',
37 | '@opcache_misses',
38 | ],
39 | [
40 | number_format($opcache['memory_usage']['used_memory'] / 1024 ** 2, 1, '.', ''),
41 | number_format($opcache['memory_usage']['free_memory'] / 1024 ** 2, 1, '.', ''),
42 | number_format($opcache['memory_usage']['wasted_memory'] / 1024 ** 2, 1, '.', ''),
43 | $opcache['opcache_statistics']['num_cached_scripts'],
44 | number_format($opcache['opcache_statistics']['opcache_hit_rate'], 2, '.', ''),
45 | $opcache['opcache_statistics']['misses'],
46 | ],
47 | ];
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Compiler/Pipes/SetPreloadConfig.php:
--------------------------------------------------------------------------------
1 | requiresMissingAutoload($path = $this->config->get('preload.autoload'))) {
35 | throw new PreloadException("Composer Autoloader is missing in [$path].");
36 | }
37 |
38 | $listing->preloader = $listing->preloader->replace(...$this->statistics());
39 |
40 | return $next($listing);
41 | }
42 |
43 | /**
44 | * Returns a list of replaceable string with statistical data.
45 | */
46 | protected function statistics(): array
47 | {
48 | return [
49 | [
50 | '@autoload',
51 | '@file',
52 | '@failure',
53 | '@mechanism',
54 | ],
55 | [
56 | $this->config->get('preload.use_require')
57 | ? 'require_once \''.realpath($this->config->get('preload.autoloader')).'\';'
58 | : null,
59 | $this->config->get('preload.path').DIRECTORY_SEPARATOR.Preloader::NAME_LIST,
60 | $this->config->get('preload.ignore_not_found')
61 | ? 'continue;'
62 | : 'throw new \Exception("{$file} does not exist or is unreadable.");',
63 | $this->config->get('preload.use_require')
64 | ? 'require_once $file'
65 | : '\opcache_compile_file($file)',
66 | ],
67 | ];
68 | }
69 |
70 | /**
71 | * Check if the Composer Autoload is required and exists.
72 | */
73 | protected function requiresMissingAutoload(string $autoload): bool
74 | {
75 | return $this->config->get('preload.use_require')
76 | && $this->files->missing($autoload);
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/Compiler/Pipes/SetStatistics.php:
--------------------------------------------------------------------------------
1 | statistics = $listing->statistics->replace(...$this->listConfig($listing));
32 |
33 | return $next($listing);
34 | }
35 |
36 | /**
37 | * Returns a list of replaceable string with Preload data.
38 | */
39 | protected function listConfig(Listing $listing): array
40 | {
41 | $memory = $this->config->get('preload.memory');
42 |
43 | return [
44 | [
45 | '@generated_at',
46 | '@output',
47 | '@mechanism',
48 | '@preloader_memory_limit',
49 | '@preloader_included',
50 | '@preloader_excluded',
51 | ],
52 | [
53 | $this->date->now()->toDateTimeString(),
54 | $this->config->get('preload.path').DIRECTORY_SEPARATOR.Preloader::NAME_LIST,
55 | $this->config->get('preload.use_require') ? 'require_once' : 'opcache_compile_file',
56 | $memory ? $memory.'MB' : '(disabled)',
57 | $listing->includeCount,
58 | $listing->excludeCount,
59 | ],
60 | ];
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Compiler/Pipes/WriteListFile.php:
--------------------------------------------------------------------------------
1 | config->get('preload.path').DIRECTORY_SEPARATOR.Preloader::NAME_LIST;
35 |
36 | /** @var \Illuminate\Support\Collection $files */
37 | foreach ($listing->files->chunk(500) as $files) {
38 | if (! $this->files->put($path, $files->implode(PHP_EOL).PHP_EOL, true)) {
39 | throw new PreloadException("Couldn't write list file to [$path].");
40 | }
41 | }
42 |
43 | $listing->files = new Collection();
44 |
45 | return $next($listing);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Compiler/Pipes/WritePreloaderFile.php:
--------------------------------------------------------------------------------
1 | config->get('preload.path').DIRECTORY_SEPARATOR.Preloader::NAME_PRELOAD;
34 |
35 | if ($this->files->put($path, $listing->preloader, true)) {
36 | $listing->preloader = new Stringable;
37 |
38 | return $next($listing);
39 | }
40 |
41 | throw new PreloadException("Couldn't write preload script to [$path].");
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Compiler/Pipes/WriteStatisticsFile.php:
--------------------------------------------------------------------------------
1 | config->get('preload.path').DIRECTORY_SEPARATOR.Preloader::NAME_STATISTICS;
34 |
35 | if ($this->files->put($path, $listing->statistics, true)) {
36 | $listing->statistics = new Stringable;
37 |
38 | return $next($listing);
39 | }
40 |
41 | throw new PreloadException("Couldn't write statistics file to [$path].");
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Composer.php:
--------------------------------------------------------------------------------
1 | app->call($this->callback);
25 |
26 | return (bool) ($result instanceof Lottery ? $result->choose() : $result);
27 | }
28 |
29 | /**
30 | * Use a callback for the condition.
31 | */
32 | public function use(callable $callback): void
33 | {
34 | $this->callback = $callback(...);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Console/Commands/Stub.php:
--------------------------------------------------------------------------------
1 | get('preload.path');
44 | $filePath = $dir.'/'.Preloader::NAME_PRELOAD;
45 |
46 | $file->ensureDirectoryExists($dir);
47 |
48 | if ($file->exists($filePath)) {
49 | $this->info('A preload script file already exists.');
50 |
51 | return;
52 | }
53 |
54 | $file->put($filePath, <<<'STUB'
55 | format('d-m-Y H:i:s');
58 |
59 | echo "[$date] Info: This is a preload stub file to be replaced for the application at runtime.";
60 |
61 | STUB
62 | );
63 |
64 | $this->info("Stub copied at [$filePath].");
65 | $this->newLine();
66 | $this->comment('Remember to edit your [php.ini] file:');
67 | $this->comment("opcache.preload = $filePath");
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/Events/ListGenerated.php:
--------------------------------------------------------------------------------
1 | extend(
35 | static::getFacadeAccessor(),
36 | static function (Preloader $preloader) use ($exclude): Preloader {
37 | $preloader->exclude($exclude);
38 |
39 | return $preloader;
40 | },
41 | );
42 | }
43 |
44 | /**
45 | * Append files from the given paths.
46 | *
47 | * @param (\Closure(\Symfony\Component\Finder\Finder):void)|string|string[] $include
48 | */
49 | public static function include(Closure|string|array $include): void
50 | {
51 | static::getFacadeApplication()->extend(
52 | static::getFacadeAccessor(),
53 | static function (Preloader $preloader) use ($include): Preloader {
54 | $preloader->include($include);
55 |
56 | return $preloader;
57 | },
58 | );
59 | }
60 |
61 | /**
62 | * Determine if the preload list should be generated using a custom condition.
63 | */
64 | public static function use(callable $condition): void
65 | {
66 | static::getFacadeApplication()->extend(
67 | Condition::class,
68 | static function (Condition $instance) use ($condition): Condition {
69 | $instance->use($condition);
70 |
71 | return $instance;
72 | },
73 | );
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/Http/Middleware/PreloadMiddleware.php:
--------------------------------------------------------------------------------
1 | isSuccessful() && $this->conditionIsTrue($request, $response)) {
33 | $app = app();
34 |
35 | [
36 | 'preload.job.connection' => $connection,
37 | 'preload.job.queue' => $queue,
38 | ] = $app->make('config')->getMany(['preload.job.connection', 'preload.job.queue']);
39 |
40 | StorePreloadScript::dispatch($app->make(Preloader::class)->files())
41 | ->onConnection($connection)
42 | ->onQueue($queue);
43 | }
44 | }
45 |
46 | /**
47 | * Checks if the given condition logic is true or false.
48 | */
49 | protected function conditionIsTrue(Request $request, Response $response): bool
50 | {
51 | return app()->call(Condition::class, [
52 | 'request' => $request,
53 | 'response' => $response,
54 | ]);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Jobs/StorePreloadScript.php:
--------------------------------------------------------------------------------
1 |
38 | */
39 | public function middleware(): array
40 | {
41 | return [new WithoutOverlapping(static::OVERLAP_KEY)];
42 | }
43 |
44 | /**
45 | * Execute the job.
46 | */
47 | public function handle(Preloader $preload): void
48 | {
49 | $preload->save($this->listing);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Lister/Lister.php:
--------------------------------------------------------------------------------
1 |
17 | *
18 | * @throws \Illuminate\Contracts\Container\BindingResolutionException
19 | */
20 | public function getFilesFromFinder(Closure $callback): Collection
21 | {
22 | $finder = $this->app->make(Finder::class);
23 |
24 | $callback($finder);
25 |
26 | return Collection::make($finder)->map(static function (SplFileInfo $file): string {
27 | return $file->getRealPath() ?: $file->getPath();
28 | });
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Lister/Pipes/CutListByMemoryLimit.php:
--------------------------------------------------------------------------------
1 | config->get('preload.memory') > 0) {
30 | $this->cutList($listing);
31 | }
32 |
33 | return $next($listing);
34 | }
35 |
36 | /**
37 | * Cut the listing until the memory threshold is set.
38 | */
39 | protected function cutList(Listing $listing): void
40 | {
41 | $limit = round($this->config->get('preload.memory') * 1024 ** 2);
42 |
43 | $listing->files = $listing->files->takeUntil(static function (array $file) use ($limit, &$memory): bool {
44 | $memory += $file['memory_consumption'];
45 |
46 | return $memory > $limit;
47 | });
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Lister/Pipes/ExcludePreloadVariable.php:
--------------------------------------------------------------------------------
1 | files->forget('$PRELOAD$');
19 |
20 | return $next($listing);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Lister/Pipes/FireEvent.php:
--------------------------------------------------------------------------------
1 | dispatcher->dispatch(new ListGenerated($listing));
29 |
30 | return $next($listing);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Lister/Pipes/LoadAcceleratedFiles.php:
--------------------------------------------------------------------------------
1 | files = new Collection($this->opcache->getScripts()); // @phpstan-ignore-line
30 |
31 | if ($listing->files->isEmpty()) {
32 | throw new PreloadException('Opcache has no cached scripts.');
33 | }
34 |
35 | return $next($listing);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Lister/Pipes/LoadIncludedAndExcludedLibraries.php:
--------------------------------------------------------------------------------
1 | files->json($this->app->basePath('composer.json')), 'extra.preload', []);
36 |
37 | foreach ($libraries as $library => $preload) {
38 | if (! $path = $this->composer->getLibraryPath($library)) {
39 | throw new PreloadException("The library [$library] is not installed.");
40 | }
41 |
42 | if (is_bool($preload)) {
43 | $preload = [$preload ? 'include' : 'exclude' => $path];
44 | }
45 |
46 | foreach ($preload as $name => $paths) {
47 | $paths = Arr::wrap($paths);
48 |
49 | $listing->{$name}[] = static function (Finder $finder) use ($paths): void {
50 | $finder->files()->in($paths);
51 | };
52 | }
53 | }
54 |
55 | return $next($listing);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/Lister/Pipes/LoadOpcacheConfig.php:
--------------------------------------------------------------------------------
1 | opcache->isDisabled()) {
30 | throw new PreloadException("Cannot gather Opcache data because it's disabled.");
31 | }
32 |
33 | $listing->opcache['memory_usage'] = Arr::only(
34 | $this->opcache->getMemoryUsage(), ['used_memory', 'free_memory', 'wasted_memory']
35 | );
36 |
37 | $listing->opcache['opcache_statistics'] = Arr::only(
38 | $this->opcache->getStatistics(), ['num_cached_scripts', 'opcache_hit_rate', 'misses']
39 | );
40 |
41 | return $next($listing);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Lister/Pipes/MayExcludeFiles.php:
--------------------------------------------------------------------------------
1 | exclude as $key => $exclude) {
32 | $excluded = $this->getFilesFromFinder($exclude)->flip();
33 |
34 | $listing->excludeCount += $excluded->count();
35 |
36 | $listing->files = $listing->files->diffKeys($excluded);
37 |
38 | unset($listing->exclude[$key]);
39 | }
40 |
41 | return $next($listing);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Lister/Pipes/MayIncludeFiles.php:
--------------------------------------------------------------------------------
1 | files->count();
34 |
35 | foreach ($listing->include as $key => $include) {
36 | $listing->files = $listing->files->merge($this->getFilesFromFinder($include)->values());
37 | unset($listing->include[$key]);
38 | }
39 |
40 | $listing->files = $listing->files->unique();
41 |
42 | $listing->includeCount = max(0, $listing->files->count() - $count);
43 |
44 | return $next($listing);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Lister/Pipes/MayScopeFilesToProjectPath.php:
--------------------------------------------------------------------------------
1 | basePath = $app->basePath();
29 | }
30 |
31 | /**
32 | * Handle the incoming preload listing.
33 | */
34 | public function handle(Listing $listing, Closure $next): Listing
35 | {
36 | if ($this->config->get('preload.project_only')) {
37 | $this->removeNonProjectFiles($listing);
38 | }
39 |
40 | return $next($listing);
41 | }
42 |
43 | /**
44 | * Removes all files that are not inside the project base path.
45 | */
46 | protected function removeNonProjectFiles(Listing $listing): void
47 | {
48 | $listing->files = $listing->files->filter(function (array $file, string $key): bool {
49 | return Str::startsWith($key, $this->basePath);
50 | });
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Lister/Pipes/NormalizeList.php:
--------------------------------------------------------------------------------
1 | files = $listing->files->keys();
19 |
20 | return $next($listing);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Lister/Pipes/SortScriptsByHitRatio.php:
--------------------------------------------------------------------------------
1 | files = $listing->files->sortByDesc(static function (array $file): array {
19 | return [$file['hits'], $file['last_used_timestamp']];
20 | });
21 |
22 | return $next($listing);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Listing.php:
--------------------------------------------------------------------------------
1 | $files
14 | * @param (\Closure(\Symfony\Component\Finder\Finder):void)[] $exclude
15 | * @param (\Closure(\Symfony\Component\Finder\Finder):void)[] $include
16 | * @param string[]|array[] $opcache
17 | */
18 | public function __construct(
19 | public array $exclude = [],
20 | public array $include = [],
21 | public Collection $files = new Collection,
22 | public int $excludeCount = 0,
23 | public int $includeCount = 0,
24 | public Stringable $statistics = new Stringable,
25 | public Stringable $preloader = new Stringable,
26 | public array $opcache = [],
27 | ) {
28 | //
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Opcache.php:
--------------------------------------------------------------------------------
1 | status = \opcache_get_status(true)) {
31 | throw new RuntimeException(
32 | 'Opcache is disabled or non-operative. Further reference: https://www.php.net/manual/en/opcache.configuration'
33 | );
34 | }
35 |
36 | return $this->status;
37 | }
38 |
39 | /**
40 | * Returns if Opcache is enabled.
41 | */
42 | public function isEnabled(): bool
43 | {
44 | return $this->getStatus()['opcache_enabled'];
45 | }
46 |
47 | /**
48 | * Returns if Opcache is disabled.
49 | */
50 | public function isDisabled(): bool
51 | {
52 | return ! $this->isEnabled();
53 | }
54 |
55 | /**
56 | * Returns the scripts used by Opcache.
57 | *
58 | * @return array
59 | */
60 | public function getScripts(): array
61 | {
62 | return $this->getStatus()['scripts'];
63 | }
64 |
65 | /**
66 | * Returns the memory usage of Opcache.
67 | *
68 | * @return array{used_memory: int, free_memory: int, wasted_memory: int}
69 | */
70 | public function getMemoryUsage(): array
71 | {
72 | return $this->getStatus()['memory_usage'];
73 | }
74 |
75 | /**
76 | * Return statistics of Opcache.
77 | *
78 | * @return array{num_cached_scripts: int, opcache_hit_rate: float, misses: int}
79 | */
80 | public function getStatistics(): array
81 | {
82 | return $this->getStatus()['opcache_statistics'];
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/PreloadServiceProvider.php:
--------------------------------------------------------------------------------
1 | mergeConfigFrom(static::CONFIG, 'preload');
25 |
26 | $this->app->singleton(Preloader::class);
27 |
28 | $this->app->singleton(Condition::class, static function (Application $app): Condition {
29 | return new Condition($app, static function (Repository $cache): bool {
30 | return $cache->increment('laragear.preload.count') % 1000 === 0;
31 | });
32 | });
33 | }
34 |
35 | /**
36 | * Bootstrap the application services.
37 | */
38 | public function boot(ConfigContract $config, HttpContract $kernel): void
39 | {
40 | // We will only register the middleware if not Running Unit Tests
41 | if ($this->shouldRun($config) && method_exists($kernel, 'pushMiddleware')) {
42 | $kernel->pushMiddleware(PreloadMiddleware::class);
43 | }
44 |
45 | if ($this->app->runningInConsole()) {
46 | $this->publishes([static::CONFIG => $this->app->configPath('preload.php')], 'config');
47 | $this->commands(Console\Commands\Stub::class);
48 | }
49 | }
50 |
51 | /**
52 | * Checks if Preload should run.
53 | */
54 | protected function shouldRun(ConfigContract $config): bool
55 | {
56 | // If it's null run only on production, otherwise the developer decides.
57 | return $config->get('preload.enabled') ?? $this->app->environment('production');
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Preloader.php:
--------------------------------------------------------------------------------
1 | $include
49 | * @param array $exclude
50 | */
51 | public function __construct(
52 | protected ConfigContract $config,
53 | protected Opcache $opcache,
54 | protected Lister\Lister $lister,
55 | protected Compiler\Compiler $compiler,
56 | protected array $include = [],
57 | protected array $exclude = [],
58 | ) {
59 | //
60 | }
61 |
62 | /**
63 | * Exclude files from the given paths.
64 | *
65 | * @param (\Closure(\Symfony\Component\Finder\Finder):void)|string|string[] $exclude
66 | */
67 | public function exclude(Closure|string|array $exclude): void
68 | {
69 | $this->exclude = $this->normalizeListing($exclude);
70 | }
71 |
72 | /**
73 | * Append files from the given paths.
74 | *
75 | * @param (\Closure(\Symfony\Component\Finder\Finder):void)|string|string[] $append
76 | */
77 | public function include(Closure|string|array $append): void
78 | {
79 | $this->include = $this->normalizeListing($append);
80 | }
81 |
82 | /**
83 | * Normalize the listing from the user.
84 | *
85 | * @param (\Closure(\Symfony\Component\Finder\Finder):void)|string|string[] $files
86 | * @return (\Closure(\Symfony\Component\Finder\Finder):void)[]
87 | */
88 | protected function normalizeListing(Closure|string|array $files): array
89 | {
90 | $files = Arr::wrap($files);
91 |
92 | foreach ($files as $key => $list) {
93 | if (! $list instanceof Closure) {
94 | $files[$key] = static function (Finder $finder) use ($list): void {
95 | $finder->in($list)->name('*.php');
96 | };
97 | }
98 | }
99 |
100 | return $files;
101 | }
102 |
103 | /**
104 | * Creates a new list.
105 | */
106 | public function files(): Listing
107 | {
108 | return $this->lister->send(new Listing($this->exclude, $this->include))->thenReturn();
109 | }
110 |
111 | /**
112 | * Writes a listing to the filesystem.
113 | */
114 | public function save(?Listing $listing = null): Listing
115 | {
116 | return $this->compiler->send($listing ?? $this->files())->thenReturn();
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/stubs/preload.php.stub:
--------------------------------------------------------------------------------
1 | getMessage()}");
11 |
12 | throw $exception;
13 | }
14 |
15 | try {
16 | $i = 0;
17 |
18 | while (($file = fgets($fileHandle)) !== false) {
19 | $i++;
20 | try {
21 | if (!(\is_file($file) && \is_readable($file))) {
22 | @failure
23 | }
24 |
25 | @mechanism;
26 | } catch (\Throwable $previous) {
27 | $exception = new \RuntimeException(
28 | "Preloader Script has stopped with an error on #$i file: [$file]", $previous->getCode(), $previous
29 | );
30 |
31 | \fwrite(\STDERR, "Error: {$exception->getMessage()}");
32 |
33 | throw $exception;
34 | }
35 | }
36 | } finally {
37 | fclose($fileHandle);
38 | unset($file, $fileHandle, $i);
39 | }
40 |
--------------------------------------------------------------------------------
/stubs/statistics.md:
--------------------------------------------------------------------------------
1 | # Preloader Statistics
2 |
3 | **Generated at**: @generated_at
4 | **Loading method**: @mechanism
5 |
6 | | Statistic | Value |
7 | |-------------------|---------------------------|
8 | | List Memory Limit | @preloader_memory_limit |
9 | | Files excluded | @preloader_excluded |
10 | | Files included | @preloader_included |
11 | | - | - |
12 | | Used Memory | @opcache_memory_used MB |
13 | | Free Memory | @opcache_memory_free MB |
14 | | Wasted Memory | @opcache_memory_wasted MB |
15 | | Cached files | @opcache_files |
16 | | Hit rate | @opcache_hit_rate% |
17 | | Misses | @opcache_misses |
18 |
19 | # Information
20 |
21 | This file is generated automatically by the [Laragear Preloader](https://github.com/laragear/preload) library.
22 |
23 | The script `preload.php` fetches each file from the list inside `list.txt` file
24 | into Opcache. To full enable preloading the list of files, add the script into
25 | your `php.ini` as the value of the `opcache.preload` key as it's shown below:
26 |
27 | opcache.preload=@output
28 |
29 | For more information, [Laragear Preloader](https://github.com/laragear/preload).
30 |
31 |
--------------------------------------------------------------------------------