En Badoo estamos haciendo una transición activa a PHP 7.4 y estamos muy entusiasmados con la oportunidad de usar la nueva función de precarga. No hace mucho tiempo
hablamos de nuestros experimentos con ella.
Aparentemente, la comunidad está tan emocionada como nosotros. Los desarrolladores de framework están
discutiendo activamente
la posibilidad de introducir una precarga (y algunos ya han
hecho su apoyo ). Ahora es el turno del administrador de dependencias de Composer.
Italo Baeza escribió
un artículo en el que expresó su opinión sobre cómo Composer debería trabajar con la precarga. Decidí compartir una traducción de este texto, y al mismo tiempo una traducción de
su otro artículo , sobre cómo los propios desarrolladores de Composer respondieron a la propuesta, así como sobre una nueva herramienta que facilita el trabajo con la precarga.
Cómo Composer debería precargarse en PHP 7.4
La precarga es una de las características importantes que PHP 7.4 ofrece a los desarrolladores que necesitan un mejor rendimiento. Esta función se puede llamar "calentamiento" antes de implementar el motor JIT, que aparecerá (o debería aparecer) en PHP 8. Antes de eso, la carga previa será suficiente, y quién sabe, tal vez puedan trabajar en conjunto.
La función de precarga se explica en
este artículo . La conclusión es muy simple: php.ini especifica un script PHP para el cual, cuando se inicia el proceso, los archivos se cargan en la memoria (precarga). En combinación con OPCache y la función de autocargador
, los archivos Composer también se pueden compilar y vincular una vez, después de lo cual estarán disponibles para todas las solicitudes posteriores. Gracias a esto, PHP no necesita descargar y compilar archivos con cada solicitud.
Sin embargo, los desarrolladores de Composer no han acordado cómo debería ayudar a la precarga, además de proporcionar funciones de inicio. Los hechos son los siguientes:
- la precarga se anunció por primera vez en PHP 7.4;
- no existe una directiva Composer para ayudar a precargar archivos;
- para la precarga, necesita acceso a php.ini, es decir, al proceso en sí;
- precargar todos los archivos no necesariamente mejorará el rendimiento en comparación con precargar solo los archivos más solicitados.
En otras palabras, solo aquellos que tienen acceso total a los servidores pueden usar la precarga. Esto excluye los servidores compartidos y algunas soluciones PaaS que no implican trabajar con php.ini.
Entonces, ¿cómo puede Composer ayudar a la precarga dado que esto es una innovación? Aquí está mi opinión.
Cómo debería funcionar la precarga
El mecanismo de precarga debe basarse en una lista de archivos que se cargarán y almacenarán en la memoria al inicio. Y como se trata de una lista, debemos trabajar con una variedad de archivos y dejar que Composer haga todo el trabajo, en lugar de
cargar cada archivo manualmente.
Composer debe tomar la lista de archivos especificados por la aplicación (el proyecto raíz) y compilar todo en archivos que PHP pueda usar sin ninguna dificultad.
Al mismo tiempo, necesitamos la capacidad de agregar y eliminar paquetes del mecanismo de precarga.
La precarga nunca debería funcionar a nivel de paquete, ya que es responsabilidad del desarrollador habilitar o deshabilitar la precarga de cada paquete.
La precarga en Composer debe ser opcional. El desarrollador debe poder deshabilitarlo para que PHP use su propio precargador, que puede funcionar sobre la base del análisis OPCache; depende de la carga de la aplicación y funciona de manera mucho más eficiente que simplemente precargar todos los archivos.
Todo comienza en preload.json
Para no complicar el sistema, coloque el archivo preload.json en la raíz del proyecto. Enumerará los archivos de precarga que Composer puede seleccionar. Como se trata de un archivo JSON, el desarrollador puede incluso generarlo con la ayuda de un comando especial. Creo que sería genial si Composer tuviera una utilidad para crear un archivo JSON basado en scripts.
{ "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" }
El uso de preload.json le permite verificar rápidamente si la precarga está incluida en el proyecto: si falta el archivo, la precarga no es compatible o no es deseable.
Veamos qué hacen las llaves.
precompilarEstos archivos serán ejecutados por Composer. Cada secuencia de comandos debe devolver una matriz de rutas de archivos absolutas para agregarlas a la lista de precarga, que desempeñará el papel de la lista principal.
"pre-compile": [ "my-script.php", "my-other-script.php" ]
Estos archivos se ejecutarán en el orden especificado.
El objetivo es que el desarrollador cree una lista de archivos como mejor le parezca, en lugar de confiar en un solo archivo JSON. Estos archivos se ejecutarán en primer lugar. Y sí, solo puede implementar preload.json con esta clave. Como estamos hablando de archivos PHP, al compilar una matriz, incluso puede agregar otros archivos.
extensionesEsta es una lista de extensiones de archivo que deben precargarse. Por defecto, solo se toman los archivos con la extensión php.
"extensions": ["php", "php5", "php7"]
Por ejemplo, puede agregar un directorio lleno de archivos * .phtml, incluidos algunos archivos PHP útiles, y Composer los seleccionará solo a ellos, y no a todo el contenido del directorio.
Como comprenderá, este proceso se puede reemplazar agregando archivos manualmente.
archivosEsta clave le dice a Composer que descargue todos los archivos de la lista cuyas rutas son relativas a la ubicación de composer.json.
"files": [ "helpers.php", "app/Models/*", "app/Controllers/*/Http/*", "app/Views/Compiled*.php", ]
Encontrar la lista es fácil:
- use rutas relativas para agregar archivos y directorios;
- de los directorios solo se agregarán archivos secundarios almacenados en ellos (no de forma recursiva);
- las rutas recursivas se indican con un asterisco que termina (*);
- usando este símbolo también puede, por ejemplo, agregar ciertos archivos y directorios:
src/Clients/*/Stores
o src/Model*.php
.
Agregar archivos por máscara sin seleccionar o crear scripts específicos de la aplicación es especialmente útil cuando se desarrollan aplicaciones grandes.
Si solo necesita precargar todos los archivos con
la clave de carga automática en el archivo JSON de Composer, configúrelo como
true
.
espacio de nombresEsta clave le dice a Composer que cargue archivos con un espacio de nombres o nombre de clase dado, como un
file
o
directory
. El mismo mecanismo le permite llamar dinámicamente nombres de espacio desde otros paquetes instalados.
"namespaces": [ "App\\Models", "App\\Controllers\\", "App\\Views\\MainView", "Vendor\\Package\\*", ]
Esto también es conveniente cuando se trabaja en aplicaciones grandes que dependen más de espacios de nombres, en lugar de archivos que pueden cambiar en cualquier momento. Composer extraerá automáticamente los archivos de acuerdo con el espacio de nombres y los colocará en una lista.
paquetesEsta clave le permite cargar otros archivos registrados desde paquetes externos, como archivos auxiliares o clases asociadas con un espacio de nombres.
"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" ] } }
Aquí todo es muy simple: si el valor es verdadero, se cargará todo el contenido
de la clave de carga automática en el archivo composer.json de este paquete. De lo contrario, puede controlar más finamente la adición de precarga.
Si el valor de la clave es verdadero, descargará todos los archivos registrados en la
autoload
. El valor predeterminado es
false
. Esto también es cierto para la clave de
namespace
.
También puede seleccionar archivos individuales o espacios de nombres con esta regla. Pero en este caso, la clave de
autoload
no se utilizará.
salidaEste es simplemente el nombre del archivo de lista de precarga compilado.
"output": "preload-compiled.php"
Fácil montaje
Nuestra lista de precarga está lista, y podemos llamar a Composer para compilar el script de precarga principal.
composer preload
Como resultado, preload-compiled.php se creará con todos los archivos que PHP debe precargar. Por supuesto, puede cambiar el nombre del archivo como lo desee.
También debe anular las teclas de
preload
parámetros.
composer preload \ --input=my-custom-preload-list.json \ --output=my-preload.php
Deshabilitado por defecto
Los proyectos sin preload.json devolverán un error si intentas compilar un archivo para precargarlo. La razón es que Composer no adivinará (y no debería) adivinar qué debe precargarse.
Permítame recordarle que la precarga no interfiere con la funcionalidad normal de Composer. Como se trata de un comando de consola, con desarrollo local, puede abandonar por completo la precarga. Lo único que necesita el mecanismo de precarga de Composer es un archivo de carga automática, que debe generarse si falta. Bueno, después de todo, casi el año 2020 está en el patio, PSR-4 se usa en todas partes, ¿verdad?
Resultado
Deberías obtener un archivo php con algo como esto:
<?php
De hecho, esta es solo una lista de archivos que se cargarán previamente usando la función de autocargador en Composer. PHP ejecutará este archivo una vez, y se convertirá en historia.
Espero sinceramente que Composer tenga alguna forma de precargar archivos sin tener que escribir un truco.
Dado que el método descrito anteriormente no es parte del núcleo Composer, aún puede seleccionar los archivos más importantes para la precarga en función del análisis OPCache sin tocar los menos necesarios. Imagine que en lugar de precargar 1.500 archivos con una capacidad de 100 MB, puede descargar solo 150 archivos con una capacidad de 10 MB, mientras mantiene el 99% del rendimiento original.
Precargamos el proyecto PHP 7.4 en una línea
Poco después de escribir un artículo sobre cómo Composer puede ayudarlo a precargar un proyecto,
Seldaek (miembro del equipo de desarrollo de Composer)
eliminó toda esperanza de que Composer tuviera una opción fácil de precargar el proyecto en el proceso PHP desde el administrador de paquetes.
(...) Explicaré: estoy seguro de que en el futuro cercano no agregaremos nada relacionado con la precarga en Composer.
Por qué La precarga en PHP es más un problema de desarrollo (en lugar de una dependencia); se resuelve editando manualmente php.ini: solo los desarrolladores pueden hacerlo si ellos mismos administran PHP.
Pero esto no me impide crear mi propio paquete para precargar el proyecto. Y tu tambien.
Precarga y métricas
La precarga puede ser una buena herramienta para un aumento
simple y económico de la productividad sin un procesamiento serio.
Pero el problema no es
cómo precargar, sino
qué . La carga previa de marcos completos y miles de archivos agotará rápidamente la memoria, por lo que hacerlo a ciegas no es una opción, al menos en proyectos grandes. Es recomendable descargar solo los archivos más solicitados. ¿Pero cómo definirlos?
Afortunadamente, OPCache permite que
opcache_get_status () recopile datos sobre a qué archivos se accede más. No solo puede averiguar qué archivos tienen más demanda, sino incluso cuánta memoria consumen algún tiempo después de que se inicia la aplicación.
Se recomienda esperar una semana o hasta el momento en que OPCache registre un cierto número de visitas. Todo depende de la aplicación, pero entiendes el punto.
Así que creemos una lista de precarga basada en estadísticas de los archivos más populares. Hice un paquete para esto.
Imagina ... ¡Preloader!
Este paquete creará automáticamente una lista de precarga para su aplicación. Recopilará estadísticas de uso de OPCache, ordenará los archivos por el número de visitas y creará una lista para que el tamaño total del archivo no exceda el umbral especificado.

Durante mucho tiempo estuve desconcertado en busca de la mejor estrategia para crear una lista. Y llegué a la conclusión de que es mejor agregarle todos los archivos hasta que se encuentre con el límite de memoria, que para los paquetes es de 32 MB por defecto. Los archivos se ordenarán por la cantidad de visitas y el paquete en sí se excluirá automáticamente.
En otras palabras, PHP mejorará el rendimiento de la aplicación cuando procese la mayoría de las solicitudes.
¿Y cómo usarlo? Dile a Composer Autoloader dónde escribir la secuencia de comandos Preloader, y listo.
use DarkGhostHunter\Preloader\Preloader; Preloader::make() ->autoload('vendor/autoload.php') ->output('preload.php') ->generate();
Por supuesto, tienes que elegir cuándo generar, pero ese es el punto. Incluso puede hacer esto al azar y reescribir la lista, por ejemplo, para cada solicitud número 100.
use DarkGhostHunter\Preloader\Preloader; Preloader::make() ->whenOneIn(100) ->autoload('vendor/autoload.php') ->output('preload.php') ->overwrite() ->generate();
Obtendrá un script de precarga listo para usar que puede poner en 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',
Y eso es todo. Pruébelo usted mismo:
darkghosthunter / preloader - Packagist .