diego-ninja/preloader

从 Opcache 创建 PHP 准备的预加载脚本的辅助工具。

v3.0.1 2024-01-15 18:30 UTC

This package is auto-updated.

Last update: 2024-09-15 20:05:22 UTC


README

 Braden Collum - Unsplash (UL) #9HI8UJMSdZA

Latest Version on Packagist Total Downloads PHP Version License Composer Coverage Status PHPStan Level 8

Opcache 预加载

只需一行代码即可获得最佳选项,以保持您的应用程序始终保持高速。

此包会自动从您的 Opcache 统计信息生成一个PHP 预加载脚本。无需手动更改。此包是darkghosthunter/preloader的分支,目标仅为更新代码和依赖项以适应 php 8.x 和 Symfony 6 组件。目前,它不添加新功能。

如果您需要 php 7.4 支持,请使用上述包。

如果您正在寻找预加载 Laravel 项目,请查看Laragear PReload

目录

要求

安装

使用 Composer 在您的项目中引入此依赖项

composer require diego-ninja/preloader

此包安装不需要 ext-opcache。只需确保它在您的应用程序服务器中已启用

用法

在您的应用程序中任何 Opcache 已启用并运行的地方,调用 Preloader 并指定输出编译脚本的路径

<?php

use Ninja\Preloader\Preloader;

Preloader::make()->writeTo(__DIR__.'/preloader.php');

这将自动收集 Opcache 统计信息,并写入优化的 preload.php 文件。在这种情况下,文件将在调用 Preloader 的同一目录中创建。

www
└── app
    ├── PreloaderCall.php
    └── preload.php

一旦生成,请在启动时将此文件指定为预加载文件 php.ini

opcache.preload=/www/app/preload.php

一旦脚本生成,建议您重新启动 PHP 进程(或在某些情况下重启服务器)以获取生成的预加载脚本。仅生成脚本是不够的。

如果您在 Opcache 禁用或无命中时使用 Preloader,您将得到一个异常。

工作原理

此包仅请求 Opcache 中最常请求的文件,并根据这些文件编译列表。您可以在Medium 上的这篇文章中了解更多关于该预加载的信息。

由于最好的统计数据是在您的应用程序运行一段时间后获得的,因此您可以使用自己的机制在满足某些条件后才编译列表。

请放心,您可以配置要编译什么以及如何编译列表。

配置

您可以配置 Preloader 在满足条件时运行,限制文件列表,以及其他事情。

条件

when()

此方法执行给定的可调用函数,并根据可调用函数返回的值判断是否应该编译列表。

<?php

use Ninja\Preloader\Preloader;

Preloader::make()->when(fn () => $app->cache()->get('should_run'));

如果您可以将条件与您自己的应用程序逻辑结合,例如给定数量的请求或外部信号,这将非常有用。

whenOneIn()

此方法只是一个辅助工具,允许您以一定的随机概率快速生成一个预加载脚本。

<?php

use Ninja\Preloader\Preloader;

Preloader::make()->whenOneIn(50);

例如,上述代码使预加载脚本有五十分之一的机会生成一个编译列表。

列表

append()

您可以将目录列表添加到编译列表中。这些目录内的文件将被追加到编译列表中,不会考虑内存限制。

<?php

use Ninja\Preloader\Preloader;

Preloader::make()->append([
    __DIR__ . '/files/*/more_files', 
    __DIR__ . '/classes/'
]);

如果您添加的文件已经存在于编译列表中,这些文件将不会被包含,以避免有效的重复。

本包包含Symfony Finder,因此作为替代方案,您可以传递一个闭包,该闭包接收Finder实例以及您想要追加的文件。

<?php

use Symfony\Component\Finder\Finder;
use Ninja\Preloader\Preloader;

Preloader::make()->append(function (Finder $find) {
    $find->files()->in('/package')->name(['*.php', '*.twig']);
});

exclude()方法优先于append()。如果您排除了一个后来被追加的文件,则它不会被排除。

exclude()

此方法从Opcache列表中排除目录内的文件,这些文件最终会出现在预加载列表中。排除文件可能会释放编译列表的内存,为其他文件腾出空间。

您可以传递一个路径数组,如果您已经准备好要排除的列表,这是一个好方法。

<?php

use Ninja\Preloader\Preloader;

Preloader::make()->exclude([
    __DIR__ . '/files/*/more_files',
    __DIR__ . '/vendor'
]);

本包包含Symfony Finder,因此作为替代方案,您可以传递一个闭包,该闭包接收Finder实例以及您想要排除的文件。

<?php

use Symfony\Component\Finder\Finder;
use Ninja\Preloader\Preloader;

Preloader::make()->exclude(function (Finder $find) {
    $find->files()->in('/package')->name(['*.php', '*.twig']);
});

exclude()方法优先于append()。如果您排除了一个后来被追加的文件,则它不会被排除。

selfExclude()

自动排除包文件从预加载列表。

默认情况下,该包不被排除,因为它可能是最请求的文件之一。只有在您完全确信一旦启用Opcache预加载它不会被调用时,才建议排除该包。

<?php

use Ninja\Preloader\Preloader;

Preloader::make()->selfExclude();

生成

memoryLimit()

默认情况下,预加载器默认的内存限制为32MB,这对于大多数应用程序来说足够了。预加载器将生成一个文件列表,直到达到内存限制。

您可以在MB中设置自己的内存限制。

<?php

use Ninja\Preloader\Preloader;

Preloader::make()->memoryLimit(32);

这考虑了每个脚本在Opcache中缓存的memory_consumption键,而不是实际文件大小。

此限制与Opcache内存限制没有任何关系。

要禁用限制,使用memoryLimit(0)。这将列出Opcache中的所有可用文件。

useRequire()

默认情况下,预加载器将使用opcache_compile_file()将文件上传到Opcache。这避免了执行项目中的任何文件,但不会解析编译文件中的链接(特性、接口、扩展类等)。您可能会在预加载时收到一些未解决链接的警告(没有太大危险)。

您可以使用useRequire()来更改此设置,它将更改到require_once,并使用Composer自动加载器(通常位于vendor/autoload.php)的路径来解析链接。

<?php

use Ninja\Preloader\Preloader;

Preloader::make()->useRequire(__DIR__ . '/../vendor/autoload.php');

在编译具有未解决链接的文件时,您可能会收到一些警告。这些不是关键警告,因为这些文件通常是应用程序中最少请求的。

ignoreNotFound()

一些应用程序可能会在运行时创建文件,这些文件确实被Opcache缓存,但在应用程序首次部署时并不存在。

为了避免这个问题,您可以使用ignoreNotFound(),它将编译一个脚本,该脚本忽略找不到的文件或不可读的文件。

<?php

use Ninja\Preloader\Preloader;

Preloader::make()->ignoreNotFound();

如果文件可读,但预加载返回错误,它仍将抛出异常。

编译

writeTo()

这将自动创建一个用于预加载应用程序的PHP脚本。如果成功,它将返回true,如果条件不满足,则返回false

<?php

use Ninja\Preloader\Preloader;

Preloader::make()->writeTo(__DIR__ . '/preloader.php');

您可以将false作为第二个参数传递,以防止覆盖写入路径中任何现有的文件。如果找到文件,则不会运行预加载逻辑。

getList()

您可以使用getList()使用户能够以数组的形式检索应包含的原始文件列表。

<?php

use Ninja\Preloader\Preloader;

Preloader::make()->getList();

如果您有自己的脚本,或者只是想对其进行一些修改,这可能会很有用。

安全预加载

此包附带了一个方便的安全预加载器,位于helpers/safe_preloader.php

它的功能非常简单:为PHP注册一个关闭函数,该函数在预加载脚本执行完毕后执行,并注册脚本可能返回的任何错误,以便您可以调试。

要使用它,将文件复制到PHP可访问的路径中,并在真实的预加载脚本中,通过php.ini文件引用它。

opcache.preload=/www/app/safe_preloader.php
<?php
// /www/app/safe_preloader.php

register_shutdown_function(function (): void {
    $error = error_get_last();
    if (!$error) {
        return;
    }
    echo 'Preloader Script has stopped with an error:' . \PHP_EOL;
    echo 'Message: ' . $error['message'] . \PHP_EOL;
    echo 'File: ' . $error['file'] . \PHP_EOL;
});

// require_once /* Path to your preload script */
require_once '/www/app/preloader.php';

从技术角度来看,Opcache在不同的进程中预加载文件,因此使用这个安全预加载器不应该有问题。

示例

好的。假设我们有一个包含数千个文件的代码库。我们不知道任何指标,所以当请求击中1/100的彩票时,我们将生成一个预加载脚本,内存限制为64MB。

<?php
// index.php

use Framework\App;
use Ninja\Preloader\Preloader;

require __DIR__ . '/../vendor/autoload.php';

$app = App::make();

$response = $app->run();

$response->sendToBrowser();

Preloader::make()
    ->whenOneIn(100)
    ->memoryLimit(64)
    ->writeTo(PHP_LOCALSTATEDIR . '/preload.php'); // put it in /var.;

安全

如果您发现任何与安全相关的问题,请通过电子邮件发送到yosoy@diego.ninja,而不是使用问题跟踪器。

许可

MIT许可(MIT)。有关更多信息,请参阅许可文件