No Badoo, estamos migrando ativamente para o PHP 7.4 e estamos muito entusiasmados com a oportunidade de usar a nova função de pré-carregamento. Não faz muito tempo,
conversamos sobre nossos experimentos com ela.
Aparentemente, a comunidade está tão animada quanto nós. Os desenvolvedores de framework estão
discutindo ativamente
a possibilidade de introduzir uma pré-carga (e alguns já
fizeram seu suporte ). Agora é a vez do gerente de dependência do Composer.
Italo Baeza escreveu
um artigo no qual expressou sua opinião sobre como o Composer deveria trabalhar com a pré-carga. Decidi compartilhar uma tradução deste texto e, ao mesmo tempo, uma tradução de
seu outro artigo , sobre como os próprios desenvolvedores do Composer responderam à proposta, bem como sobre uma nova ferramenta que facilita o trabalho com a pré-carga.
Como o compositor deve pré-carregar no PHP 7.4
O pré-carregamento é um dos recursos importantes que o PHP 7.4 oferece aos desenvolvedores que precisam de melhor desempenho. Essa função pode ser chamada de "aquecimento" antes de implementar o mecanismo JIT, que aparecerá (ou deve aparecer) no PHP 8. Antes disso, o pré-carregamento será suficiente e, quem sabe, talvez eles possam trabalhar em conjunto.
O que é a função de pré-carga é explicada
neste artigo . A linha inferior é muito simples: php.ini especifica um script PHP para o qual, quando o processo é iniciado, os arquivos são carregados na memória (pré-carregamento). Em combinação com o OPCache e a função carregador automático
, os arquivos do Composer também podem ser compilados e vinculados uma vez, após o que estarão disponíveis para todas as solicitações subsequentes. Graças a isso, o PHP não precisa baixar e compilar arquivos com todas as solicitações.
No entanto, os desenvolvedores do Composer não concordaram em como ele deve ajudar na pré-carga, além de fornecer funções de inicialização. Os fatos são os seguintes:
- o pré-carregamento foi anunciado pela primeira vez no PHP 7.4;
- não há diretiva Composer para ajudar a pré-carregar arquivos;
- para pré-carregar, você precisa acessar o php.ini, ou seja, o próprio processo;
- pré-carregar todos os arquivos não melhorará necessariamente o desempenho em comparação com pré-carregar apenas os arquivos mais solicitados.
Em outras palavras, apenas aqueles que têm acesso total aos servidores podem usar o pré-carregamento. Isso exclui servidores compartilhados e algumas soluções PaaS que não envolvem o trabalho com o php.ini.
Então, como o Composer pode ajudar na pré-carga, considerando que essa é uma inovação? Aqui está a minha opinião.
Como a pré-carga deve funcionar
O mecanismo para pré-carregamento deve se basear em uma lista de arquivos que serão
carregados e armazenados na memória na inicialização. E como essa é uma lista, precisamos trabalhar com uma matriz de arquivos e deixar o Composer fazer todo o trabalho, em vez de
carregar cada arquivo manualmente.
O Composer deve pegar a lista de arquivos especificados pelo aplicativo (o projeto raiz) e compilar tudo em arquivos que o PHP possa usar sem nenhuma dificuldade.
Ao mesmo tempo, precisamos adicionar e remover pacotes do mecanismo de pré-carregamento.
O pré-carregamento nunca deve funcionar no nível do pacote, pois é responsabilidade do desenvolvedor ativar ou desativar o pré-carregamento de cada pacote.
O pré-carregamento no Composer deve ser opcional. O desenvolvedor deve poder desativá-lo para que o PHP use seu próprio pré-carregador, que pode funcionar com base na análise do OPCache - depende da carga do aplicativo e funciona com muito mais eficiência do que apenas pré-carregar todos os arquivos.
Tudo começa em preload.json
Para não complicar o sistema, coloque o arquivo preload.json na raiz do projeto. Ele listará os arquivos de pré-carregamento que o Composer pode selecionar. Como esse é um arquivo JSON, o desenvolvedor pode gerá-lo com a ajuda de um comando especial. Eu acho que seria ótimo se o Composer tivesse um utilitário para criar um arquivo JSON baseado em script.
{ "pre-compile": [ "my-script.php", "my-other-script.php" ], "extensions": [ "php" ], "files": [ "app/*", "config/", "helpers.php", "app/Models/*", "app/Controllers/*/Http/*", "app/Views/Compiled*.php" ], "namespace": [ "App\\Models", "App\\Controllers\\", "App\\Views\\MainView", "Vendor\\Package\\*", ], "packages": { "symfony/http-client": true, "robert/*-client": true, "vendor/package": { "files": true, "namespace": true }, "foo/bar": { "files": [ "helpers.php", "loaders/*" ], "namespace": [ "Foo\\Bar\\DynamicLoaders\\*", "Foo\\Bar\\Clients" ] } }, "output": "preload-compiled.php" }
O uso do preload.json permite verificar rapidamente se o pré-carregamento está incluído no projeto: se o arquivo estiver ausente, o pré-carregamento não é suportado ou indesejável.
Vamos ver o que as teclas fazem.
pré-compilarEsses arquivos serão executados pelo Composer. Cada script deve retornar uma matriz de caminhos de arquivo absolutos para adicioná-los à lista de pré-carregamento, que desempenhará o papel da lista principal.
"pre-compile": [ "my-script.php", "my-other-script.php" ]
Esses arquivos serão executados na ordem especificada.
O objetivo é que o desenvolvedor crie uma lista de arquivos como achar melhor, em vez de confiar em um único arquivo JSON. Esses arquivos serão executados antes de tudo. E sim, você só pode implementar o preload.json com essa chave. Como estamos falando de arquivos PHP, ao compilar uma matriz, você pode até adicionar outros arquivos.
extensõesEsta é uma lista de extensões que precisam ser pré-carregadas. Por padrão, apenas os arquivos com a extensão php são obtidos.
"extensions": ["php", "php5", "php7"]
Por exemplo, você pode adicionar um diretório preenchido com arquivos * .phtml, incluindo alguns arquivos PHP úteis, e o Composer selecionará apenas eles, e não todo o conteúdo do diretório.
Como você entende, esse processo pode ser substituído adicionando arquivos manualmente.
arquivosEssa chave informa ao Composer para baixar todos os arquivos da lista cujos caminhos são relativos ao local do compositer.json.
"files": [ "helpers.php", "app/Models/*", "app/Controllers/*/Http/*", "app/Views/Compiled*.php", ]
Descobrir a lista é fácil:
- use caminhos relativos para adicionar arquivos e diretórios;
- dos diretórios, apenas os arquivos filhos armazenados neles serão adicionados (não recursivamente);
- caminhos recursivos são indicados por um asterisco (*);
- Usando este símbolo, você também pode, por exemplo, adicionar certos arquivos e diretórios:
src/Clients/*/Stores
ou src/Model*.php
.
Adicionar arquivos por máscara sem selecionar ou criar manualmente scripts específicos de aplicativos é especialmente útil no desenvolvimento de aplicativos grandes.
Se você precisar pré-carregar todos os arquivos usando
a chave de carregamento automático no arquivo JSON do Composer, configure-o como
true
.
namespaceEssa chave informa ao Composer para carregar arquivos com um determinado namespace ou nome de classe, como
file
ou
directory
. O mesmo mecanismo permite chamar dinamicamente nomes de espaço de outros pacotes instalados.
"namespaces": [ "App\\Models", "App\\Controllers\\", "App\\Views\\MainView", "Vendor\\Package\\*", ]
Isso também é conveniente ao trabalhar em aplicativos grandes, que dependem mais de namespaces, em vez de arquivos que podem ser alterados a qualquer momento. O Composer extrai automaticamente os arquivos de acordo com o espaço para nome e os coloca em uma lista.
pacotesEssa chave permite carregar outros arquivos registrados a partir de pacotes externos, como arquivos auxiliares ou classes associadas a um espaço para nome.
"packages": { "symfony/http-client": true, "robert/*-client": true, "vendor/package": { "files": true, "namespace": true }, "foo/bar": { "files": { "helpers.php", "loaders/*" }, "namespace": [ "Foo\\Bar\\DynamicLoaders\\*", "Foo\\Bar\\Clients" ] } }
Tudo é muito simples aqui: se o valor for verdadeiro, será carregado todo o conteúdo
da chave de carregamento automático no arquivo composer.json deste pacote. Caso contrário, você poderá controlar com mais detalhes a adição de pré-carregamento.
Se o valor da chave for verdadeiro, ele fará o download de todos os arquivos registrados no
autoload
. O valor padrão é
false
. Isso também se aplica à chave do
namespace
para
namespace
.
Você também pode selecionar arquivos ou espaços de nome individuais com esta regra. Mas, neste caso, a chave de
autoload
não será usada.
saídaEste é simplesmente o nome do arquivo da lista de pré-carregamento compilado.
"output": "preload-compiled.php"
Montagem fácil
Nossa lista de pré-carregamento está pronta e podemos chamar o Composer para compilar o script principal de pré-carregamento.
composer preload
Como resultado, o preload-compiled.php será criado com todos os arquivos que o PHP deve pré-carregar. Obviamente, você pode alterar o nome do arquivo como desejar.
Você também precisa substituir as chaves de
preload
-
preload
parâmetros.
composer preload \ --input=my-custom-preload-list.json \ --output=my-preload.php
Desativado por padrão
Projetos sem preload.json retornarão um erro se você tentar compilar um arquivo para pré-carregamento. O motivo é que o Composer não vai (e não deve) adivinhar o que deve ser pré-carregado.
Deixe-me lembrá-lo de que a pré-carga não interfere com a funcionalidade normal do Composer. Como esse é um comando do console, com desenvolvimento local, você pode abandonar completamente o pré-carregamento. A única coisa que o mecanismo de pré-carregamento do Composer precisa é de um arquivo de carregamento automático, que deve ser gerado se estiver ausente. Bem, quase 2020 é o ano no quintal, o PSR-4 é usado em todos os lugares, certo?
Resultado
Você deve obter um arquivo php com algo como isto:
<?php
De fato, esta é apenas uma lista de arquivos que serão pré-carregados usando a função de carregamento automático no Composer. O PHP executará esse arquivo uma vez e se tornará histórico.
Eu sinceramente espero que o Composer tenha alguma maneira de pré-carregar arquivos sem ter que escrever um hack.
Como o método descrito acima não faz parte do kernel do Composer, você ainda pode selecionar os arquivos mais importantes para pré-carregamento com base na análise do OPCache sem tocar nos menos necessários. Imagine que, em vez de pré-carregar 1.500 arquivos com capacidade de 100 MB, você pode baixar apenas 150 arquivos com capacidade de 10 MB, mantendo 99% do desempenho original.
Pré-carregamos o projeto PHP 7.4 em uma linha
Logo depois que escrevi um artigo sobre como o Composer pode ajudá-lo a pré-carregar um projeto,
Seldaek (membro da equipe de desenvolvimento do Composer)
acabou com toda a esperança de que o Composer tivesse uma opção fácil de pré-carregar o projeto no processo PHP a partir do gerenciador de pacotes.
(...) vou explicar: tenho certeza de que, no futuro próximo, não adicionaremos nada relacionado à pré-carga no Composer.
Porque O pré-carregamento do PHP é mais um problema de desenvolvimento (e não uma dependência); é resolvido editando manualmente o php.ini - somente os desenvolvedores podem fazer isso se eles próprios gerenciam o PHP.
Mas isso não me impede de criar meu próprio pacote para pré-carregar o projeto. E voce tambem
Pré-carregamento e Métricas
O pré-carregamento pode ser uma boa ferramenta para um aumento
simples e barato da produtividade sem processamento sério.
Mas o problema não é
como pré
- carregar, mas o
que . O pré-carregamento de estruturas inteiras e milhares de arquivos esgotará rapidamente a memória; portanto, fazer isso às cegas não é uma opção, pelo menos em grandes projetos. É aconselhável baixar apenas os arquivos mais solicitados. Mas como defini-los?
Felizmente, o OPCache permite que o
opcache_get_status () colete dados sobre quais arquivos são mais acessados. Você não pode apenas descobrir quais arquivos estão com maior demanda, mas também quanta memória eles consomem algum tempo após o início do aplicativo.
É recomendável aguardar uma semana ou até o momento em que o OPCache registra um certo número de ocorrências. Tudo depende da aplicação, mas você entendeu.
Então, vamos criar uma lista de pré-carregamento com base nas estatísticas dos arquivos mais populares. Eu fiz um pacote para isso.
Imagine ... Preloader!
Este pacote criará automaticamente uma lista de pré-carregamento para seu aplicativo. Ele coletará estatísticas de uso do OPCache, classificará os arquivos pelo número de ocorrências e criará uma lista para que o tamanho total do arquivo não exceda o limite especificado.

Fiquei intrigado por um longo tempo em busca da melhor estratégia de criação de listas. E cheguei à conclusão de que é melhor adicionar todos os arquivos até você atingir o limite de memória, que para pacotes é de 32 MB por padrão. Os arquivos serão classificados pelo número de ocorrências e o próprio pacote será excluído automaticamente.
Em outras palavras, o PHP melhorará o desempenho do aplicativo ao processar a maioria das solicitações.
E como usá-lo? Diga ao Composer Autoloader onde escrever o script do Preloader e pronto.
use DarkGhostHunter\Preloader\Preloader; Preloader::make() ->autoload('vendor/autoload.php') ->output('preload.php') ->generate();
Claro, você precisa escolher quando gerar, mas esse é o ponto. Você pode fazer isso aleatoriamente e reescrever a lista, por exemplo, para cada 100ª solicitação.
use DarkGhostHunter\Preloader\Preloader; Preloader::make() ->whenOneIn(100) ->autoload('vendor/autoload.php') ->output('preload.php') ->overwrite() ->generate();
Você receberá um script de pré-carregamento pronto para colocar no php.ini.
<?php require_once '/www/app/vendor/autoload.php'; $files = [ '/www/app/ClassFoo.php', '/www/app/ClassBar.php', '/www/app/ClassBaz.php', '/www/app/ClassQuz.php', '/www/app/ClassQux.php', '/www/app/vendor/author/package/src/Example.php',
E é isso. Experimente você mesmo:
darkghosthunter / preloader - Packagist .