Chez Badoo, nous passons activement à PHP 7.4 et sommes très enthousiastes à l'idée d'utiliser la nouvelle fonction de préchargement. Il n'y a pas si longtemps, nous avons
parlé de nos expériences avec elle.
Apparemment, la communauté est aussi excitée que nous. Les développeurs de framework
discutent activement de
la possibilité d'introduire une précharge (et certains ont déjà
fait son support ). C'est maintenant au tour du gestionnaire de dépendances de Composer.
Italo Baeza a écrit
un article dans lequel il exprime son opinion sur la manière dont Composer devrait travailler avec la précharge. J'ai décidé de partager une traduction de ce texte, et en même temps une traduction de
son autre article , sur la façon dont les développeurs Composer eux-mêmes ont répondu à la proposition, ainsi que sur un nouvel outil qui facilite le travail avec la précharge.
Comment le compositeur doit précharger en PHP 7.4
Le préchargement est l'une des fonctionnalités importantes que PHP 7.4 offre aux développeurs qui ont besoin de meilleures performances. Cette fonction peut être appelée "échauffement" avant d'implémenter le moteur JIT, qui apparaîtra (ou devrait apparaître) en PHP 8. Avant cela, le préchargement sera suffisant, et qui sait, peut-être qu'ils peuvent travailler en tandem.
La fonction de précharge est expliquée dans
cet article . Le résultat est très simple: php.ini spécifie un script PHP pour lequel lorsque le processus démarre, les fichiers sont chargés en mémoire (préchargement). En combinaison avec OPCache et la fonction de chargement automatique
, les fichiers Composer peuvent également être compilés et liés une fois, après quoi ils seront disponibles pour toutes les demandes ultérieures. Grâce à cela, PHP n'a pas besoin de télécharger et de compiler des fichiers à chaque demande.
Cependant, les développeurs de Composer n'étaient pas d'accord sur la façon dont il devrait aider au préchargement, en plus de fournir des fonctions de démarrage. Les faits sont les suivants:
- le préchargement a été annoncé pour la première fois en PHP 7.4;
- il n'y a pas de directive Composer pour aider à précharger les fichiers;
- pour précharger, vous devez avoir accès à php.ini, c'est-à-dire au processus lui-même;
- le préchargement de tous les fichiers n'améliorera pas nécessairement les performances par rapport au préchargement uniquement des fichiers les plus demandés.
En d'autres termes, seuls ceux qui ont un accès complet aux serveurs peuvent utiliser le préchargement. Cela exclut les serveurs partagés et certaines solutions PaaS qui n'impliquent pas de travailler avec php.ini.
Alors, comment Composer peut-il aider à précharger étant donné qu'il s'agit d'une innovation? Voici mon avis.
Fonctionnement de la précharge
Le mécanisme de préchargement doit être basé sur une liste de fichiers qui seront
chargés et stockés en mémoire au démarrage. Et comme il s'agit d'une liste, nous devons travailler avec un tableau de fichiers et laisser Composer faire tout le travail, plutôt que de
charger chaque fichier manuellement.
Le compositeur doit prendre la liste des fichiers spécifiés par l'application (le projet racine) et tout compiler dans des fichiers que PHP peut utiliser sans aucune difficulté.
Dans le même temps, nous devons pouvoir ajouter et supprimer des packages du mécanisme de précharge.
Le préchargement ne devrait jamais fonctionner au niveau du package, car il est de la responsabilité du développeur d'activer ou de désactiver le préchargement de chaque package.
Le préchargement dans Composer doit être facultatif. Le développeur doit pouvoir le désactiver pour que PHP utilise son propre préchargeur, qui peut fonctionner sur la base de l'analyse OPCache - cela dépend de la charge de l'application et fonctionne beaucoup plus efficacement que le préchargement de tous les fichiers.
Tout commence à preload.json
Afin de ne pas compliquer le système, placez le fichier preload.json à la racine du projet. Il répertorie les fichiers de préchargement que Composer peut sélectionner. Puisqu'il s'agit d'un fichier JSON, le développeur peut même le générer à l'aide d'une commande spéciale. Je pense que ce serait formidable si Composer avait un utilitaire pour créer un tel fichier JSON basé sur un 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" }
L'utilisation de preload.json vous permet de vérifier rapidement si le préchargement est inclus dans le projet: si le fichier est manquant, le préchargement n'est pas pris en charge ou indésirable.
Voyons ce que font les clés.
précompilerCes fichiers seront exécutés par Composer. Chaque script doit renvoyer un tableau de chemins de fichiers absolus pour les ajouter à la liste de préchargement, qui jouera le rôle de la liste principale.
"pre-compile": [ "my-script.php", "my-other-script.php" ]
Ces fichiers seront exécutés dans l'ordre spécifié.
L'objectif est que le développeur crée une liste de fichiers comme bon lui semble, plutôt que de s'appuyer sur un seul fichier JSON. Ces fichiers seront d'abord exécutés. Et oui, vous ne pouvez implémenter preload.json qu'avec cette clé. Puisque nous parlons de fichiers PHP, lors de la compilation d'un tableau, vous pouvez même ajouter d'autres fichiers.
extensionsIl s'agit d'une liste d'extensions de fichiers qui doivent être préchargées. Par défaut, seuls les fichiers avec l'extension php sont pris.
"extensions": ["php", "php5", "php7"]
Par exemple, vous pouvez ajouter un répertoire rempli de fichiers * .phtml, y compris certains fichiers PHP utiles, et Composer ne les sélectionnera pas, et non l'intégralité du contenu du répertoire.
Comme vous le comprenez, ce processus peut être remplacé en ajoutant des fichiers manuellement.
les fichiersCette clé indique à Composer de télécharger tous les fichiers de la liste dont les chemins sont relatifs à l'emplacement de composer.json.
"files": [ "helpers.php", "app/Models/*", "app/Controllers/*/Http/*", "app/Views/Compiled*.php", ]
Il est facile de trouver la liste:
- utiliser des chemins relatifs pour ajouter des fichiers et des répertoires;
- à partir des répertoires, seuls les fichiers enfants stockés dans ces répertoires seront ajoutés (pas de manière récursive);
- les chemins récursifs sont indiqués par un astérisque se terminant (*);
- en utilisant ce symbole, vous pouvez également, par exemple, ajouter certains fichiers et répertoires:
src/Clients/*/Stores
ou src/Model*.php
.
L'ajout de fichiers par masque sans sélectionner ou créer manuellement des scripts spécifiques à l'application est particulièrement utile lors du développement de grandes applications.
Si vous avez juste besoin de précharger tous les fichiers à
l' aide de
la clé de chargement automatique dans le fichier Composer JSON, définissez-la sur
true
.
espace de nomsCette clé indique à Composer de charger des fichiers avec un espace de noms ou un nom de classe donné, comme un
file
ou un
directory
. Le même mécanisme vous permet d'appeler dynamiquement des noms d'espace à partir d'autres packages installés.
"namespaces": [ "App\\Models", "App\\Controllers\\", "App\\Views\\MainView", "Vendor\\Package\\*", ]
Cela est également pratique lorsque vous travaillez sur des applications volumineuses qui dépendent davantage des espaces de noms plutôt que des fichiers qui peuvent changer à tout moment. Composer extrait automatiquement les fichiers en fonction de l'espace de noms et les place dans une liste.
packagesCette clé vous permet de charger d'autres fichiers enregistrés à partir de packages externes, tels que des fichiers auxiliaires ou des classes associées à un espace de noms.
"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" ] } }
Tout est très simple ici: si la valeur est vraie, alors tout le contenu
de la clé de chargement automatique dans le fichier composer.json de ce paquet sera chargé. Sinon, vous pouvez contrôler plus finement l'ajout de précharge.
Si la valeur de la clé est vraie, elle téléchargera tous les fichiers enregistrés en
autoload
. La valeur par défaut est
false
. Cela est également vrai pour la clé d'
namespace
.
Vous pouvez également sélectionner des fichiers ou des espaces de noms individuels avec cette règle. Mais dans ce cas, la clé de
autoload
ne sera pas utilisée.
sortieIl s'agit simplement du nom du fichier de liste de préchargement compilé.
"output": "preload-compiled.php"
Assemblage facile
Notre liste de préchargement est prête, et nous pouvons appeler Composer pour compiler le script de préchargement principal.
composer preload
Par conséquent, preload-compiled.php sera créé avec tous les fichiers que PHP doit précharger. Bien sûr, vous pouvez modifier le nom du fichier comme vous le souhaitez.
Vous devez également remplacer les clés de
preload
paramètres.
composer preload \ --input=my-custom-preload-list.json \ --output=my-preload.php
Désactivé par défaut
Les projets sans preload.json renverront une erreur si vous essayez de compiler un fichier pour le préchargement. La raison en est que Composer ne devinera pas (et ne devrait pas) deviner ce qui doit être préchargé.
Permettez-moi de vous rappeler que la précharge n'interfère pas avec les fonctionnalités normales de Composer. Comme il s'agit d'une commande console, avec développement local, vous pouvez complètement abandonner le préchargement. La seule chose dont le mécanisme de préchargement Composer a besoin est un fichier de chargement automatique, qui devrait être généré s'il est manquant. Eh bien, après tout, presque la 2020e année est dans la cour, le PSR-4 est utilisé partout, non?
Résultat
Vous devriez obtenir un fichier php avec quelque chose comme ceci:
<?php
En fait, ce n'est qu'une liste de fichiers qui seront préchargés à l'aide de la fonction de chargement automatique dans Composer. PHP exécutera ce fichier une fois et il deviendra historique.
J'espère sincèrement que Composer aura un moyen de précharger les fichiers sans avoir à écrire un hack.
Étant donné que la méthode décrite ci-dessus ne fait pas partie du noyau Composer, vous pouvez toujours sélectionner les fichiers les plus importants pour le préchargement en fonction de l'analyse OPCache sans toucher aux fichiers les moins nécessaires. Imaginez qu'au lieu de précharger 1 500 fichiers d'une capacité de 100 Mo, vous ne pouvez télécharger que 150 fichiers d'une capacité de 10 Mo, tout en conservant 99% des performances d'origine.
Nous préchargeons le projet PHP 7.4 en une seule ligne
Peu de temps après avoir écrit un article sur la façon dont Composer peut vous aider à précharger un projet,
Seldaek (un membre de l'équipe de développement de Composer) a
tué tout espoir que Composer aurait une option facile pour précharger le projet dans le processus PHP à partir du gestionnaire de packages.
(...) Je vais vous expliquer: je suis sûr que dans un proche avenir, nous n’ajouterons rien de lié au préchargement dans Composer.
Pourquoi? Le préchargement en PHP est plus un problème de développement (plutôt qu'une dépendance); il est résolu en modifiant manuellement php.ini - seuls les développeurs peuvent le faire s'ils gèrent eux-mêmes PHP.
Mais cela ne m'empêche pas de créer mon propre package pour précharger le projet. Et toi aussi.
Précharge et métriques
Le préchargement peut être un bon outil pour une augmentation
simple et bon marché de la productivité sans traitement sérieux.
Mais le problème n'est pas de
savoir comment précharger, mais
quoi . Le préchargement de frameworks entiers et de milliers de fichiers épuisera rapidement la mémoire, donc le faire aveuglément n'est pas une option, du moins dans les grands projets. Il est conseillé de télécharger uniquement les fichiers les plus demandés. Mais comment les définir?
Heureusement, OPCache permet à
opcache_get_status () de collecter des données sur les fichiers les plus consultés. Vous pouvez non seulement découvrir quels fichiers sont les plus demandés, mais même la quantité de mémoire qu'ils consomment quelque temps après le démarrage de l'application.
Il est recommandé d'attendre soit une semaine soit jusqu'au moment où OPCache enregistre un certain nombre de hits. Tout dépend de l'application, mais vous obtenez le point.
Créons donc une liste de préchargement basée sur les statistiques des fichiers les plus populaires. J'ai fait un paquet pour ça.
Imaginez ... Preloader!
Ce package créera automatiquement une liste de préchargement pour votre application. Il collectera les statistiques d'utilisation d'OPCache, triera les fichiers par le nombre de hits et créera une liste afin que la taille totale du fichier ne dépasse pas le seuil spécifié.

J'ai longtemps été perplexe à la recherche de la meilleure stratégie pour créer une liste. Et je suis arrivé à la conclusion qu'il est préférable d'y ajouter tous les fichiers jusqu'à ce que vous rencontriez la limite de mémoire, qui pour les packages est de 32 Mo par défaut. Les fichiers seront triés par nombre de hits et le package lui-même sera automatiquement exclu.
En d'autres termes, PHP améliorera les performances des applications lors du traitement de la plupart des requêtes.
Et comment l'utiliser? Dites à Composer Autoloader où écrire le script Preloader, et vous avez terminé.
use DarkGhostHunter\Preloader\Preloader; Preloader::make() ->autoload('vendor/autoload.php') ->output('preload.php') ->generate();
Bien sûr, vous devez choisir le moment de générer, mais c'est tout. Vous pouvez même le faire au hasard et réécrire la liste, par exemple, pour chaque 100e demande.
use DarkGhostHunter\Preloader\Preloader; Preloader::make() ->whenOneIn(100) ->autoload('vendor/autoload.php') ->output('preload.php') ->overwrite() ->generate();
Vous obtiendrez un script de préchargement prêt à l'emploi que vous pouvez mettre dans 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',
Et c'est tout. Essayez par vous-même:
darkghosthunter / preloader - Packagist .